jcs
/subtext
/amendments
/46
bile: More work
jcs made amendment 46 over 3 years ago
--- bile.c	Mon Jan  3 22:21:19 2022
+++ bile.c	Tue Jan  4 16:30:53 2022
@@ -19,11 +19,12 @@
 #include "util.h"
 
 void bile_write_map(struct bile *bile);
-void bile_sort_objects(struct bile *bile);
+void bile_sort_by_pos(struct bile *bile);
 void bile_free(struct bile *bile, unsigned long id);
+size_t bile_xwriteat(struct bile *bile, size_t pos, size_t len, void *data);
 
 struct bile *
-bile_create(Str255 filename, OSType creator)
+bile_create(Str255 filename, OSType creator, OSType type)
 {
 	struct bile *bile;
 	size_t len;
@@ -31,7 +32,7 @@ bile_create(Str255 filename, OSType creator)
 	short error, fh;
 	
 	/* create file */
-	error = Create(filename, 0, creator, BILE_FILE_TYPE);
+	error = Create(filename, 0, creator, type);
 	if (error)
 		panic("bile_create: failed to create %s: %d", PtoCstr(filename),
 		  error);
@@ -159,22 +160,38 @@ bile_close(struct bile *bile)
 
 /* XXX: these become invalid once we re-sort, should we return a copy? */
 struct bile_object *
-bile_find(struct bile *bile, unsigned long id)
+bile_find(struct bile *bile, OSType type, unsigned long id)
 {
 	struct bile_object *o;
 	unsigned long n;
 	
-	for (n = 0; n < bile->nobjects; n++) {
-		o = &bile->map[n];
-		if (o->id == id)
+	/* look backwards, optimizing for newer data */
+	for (n = bile->nobjects; n > 0; n--) {
+		o = &bile->map[n - 1];
+		if (o->type == type && o->id == id)
 			return o;
 	}
 	
 	return NULL;
 }
 
+size_t
+bile_next_id(struct bile *bile, OSType type)
+{
+	struct bile_object *o;
+	size_t n, id = 1;
+	
+	for (n = 0; n < bile->nobjects; n++) {
+		o = &bile->map[n - 1];
+		if (o->type == type && o->id >= id)
+			id = o->id + 1;
+	}
+	
+	return id;
+}
+
 struct bile_object *
-bile_alloc(struct bile *bile, size_t size)
+bile_alloc(struct bile *bile, OSType type, unsigned long id, size_t size)
 {
 	struct bile_object *new, *o;
 	size_t last_pos = BILE_HEADER_LEN;
@@ -194,13 +211,15 @@ bile_alloc(struct bile *bile, size_t size)
 	bile->nobjects++;
 	new->pos = last_pos;
 	new->size = size;
-	
-	bile_sort_objects(bile);
+	new->type = type;
+	new->id = id;
 
+	bile_sort_by_pos(bile);
+
 	/* find our new object pointer after sorting */
-	for (n = 0; n < bile->nobjects; n++) {
-		o = &bile->map[n];
-		if (o->pos == last_pos)
+	for (n = bile->nobjects; n > 0; n--) {
+		o = &bile->map[n - 1];
+		if (o->type == type && o->pos == last_pos)
 			return o;
 	}
 	
@@ -208,12 +227,11 @@ bile_alloc(struct bile *bile, size_t size)
 }
 
 void
-bile_sort_objects(struct bile *bile)
+bile_sort_by_pos(struct bile *bile)
 {
 	short i, j;
 	struct bile_object o;
 	
-	/* sort maps by position */
 	for (i = 0; i < bile->nobjects; i++) {
 		for (j = 0; j < bile->nobjects - i - 1; j++) {
 			if (bile->map[j].pos > bile->map[j + 1].pos) {
@@ -226,18 +244,52 @@ bile_sort_objects(struct bile *bile)
 }
 
 void
-bile_free(struct bile *bile, unsigned long id)
+bile_delete(struct bile *bile, OSType type, unsigned long id)
 {
-	/* TODO */
+	struct bile_object *o;
+	size_t pos, size, wsize;
+	short error;
+	char *zero;
+	
+	o = bile_find(bile, type, id);
+	if (o == NULL)
+		return;
+	
+	o->type = BILE_PURGE_TYPE;
+	pos = o->pos;
+	size = o->size + BILE_OBJECT_SIZE;
+	
+	bile_write_map(bile);
+	
+	error = SetFPos(bile->vrefid, fsFromStart, pos);
+	if (error)
+		goto write_error;
+
+	zero = xmalloczero(128);
+	while (size > 0) {
+		wsize = MIN(128, size);
+		size -= wsize;
+		
+		error = FSWrite(bile->vrefid, &wsize, zero);
+		if (error)
+			goto write_error;
+	}
+	
+	return;
+	
+write_error:
+	panic("bile_delete: failed writing to %s: %d", PtoCstr(bile->filename),
+	  error);
 }
 
 size_t
-bile_read(struct bile *bile, unsigned long id, char **data)
+bile_read(struct bile *bile, OSType type, unsigned long id, char **data)
 {
-	struct bile_object *o = bile_find(bile, id);
+	struct bile_object *o, verify;
 	size_t rsize;
 	short error;
 	
+	o = bile_find(bile, type, id);
 	if (o == NULL)
 		return -1;
 	
@@ -252,8 +304,20 @@ bile_read(struct bile *bile, unsigned long id, char **
 	if (error)
 		goto read_error;
 
-	/* TODO: read object header and verify size and id */
-	
+	rsize = BILE_OBJECT_SIZE;
+	error = FSRead(bile->vrefid, &rsize, &verify);
+	if (error)
+		goto read_error;
+	if (verify.id != id)
+		panic("bile_read: object %ld pos %ld wrong id %ld, expected %ld",
+		  id, o->pos, verify.id, id);
+	if (verify.type != type)
+		panic("bile_read: object %ld pos %ld wrong type %ld, expected %ld",
+		  id, o->pos, verify.type, type);
+	if (verify.size != o->size)
+		panic("bile_read: object %ld pos %ld wrong size %ld, expected %ld",
+		  id, o->pos, verify.size, o->size);
+
 	rsize = o->size;
 	error = FSRead(bile->vrefid, &rsize, *data);
 	if (error)
@@ -269,51 +333,28 @@ read_error:
 }
 
 size_t
-bile_write(struct bile *bile, unsigned long id, char *data, size_t size)
+bile_write(struct bile *bile, OSType type, unsigned long id, char *data,
+  size_t size)
 {
 	struct bile_object *old, *new_obj;
-	size_t ret, wsize;
+	size_t ret;
 	short error;
 	
-	new_obj = bile_alloc(bile, size);
-	/* we must find after alloc because it re-sorts */
-	old = bile_find(bile, id);
-	new_obj->id = id;
+	old = bile_find(bile, type, id);
+	if (old)
+		old->type = BILE_PURGE_TYPE;
+
+	new_obj = bile_alloc(bile, type, id, size);
 	
-	if (new_obj->pos >= bile->file_size)
-		error = SetFPos(bile->vrefid, fsFromLEOF, 0);
-	else
-		error = SetFPos(bile->vrefid, fsFromStart, new_obj->pos);
-	if (error)
-		goto write_error;
+	bile_xwriteat(bile, new_obj->pos, BILE_OBJECT_SIZE, new_obj);
+	bile_xwriteat(bile, new_obj->pos + BILE_OBJECT_SIZE, size, data);
 
-	wsize = BILE_OBJECT_SIZE;
-	error = FSWrite(bile->vrefid, &wsize, new_obj);
-	if (error)
-		goto write_error;
-
-	wsize = size;
-	error = FSWrite(bile->vrefid, &wsize, data);
-	if (error)
-		goto write_error;
-	if (wsize != size)
-		panic("bile_open: needed to write %ld, wrote %ld", size, wsize);
-
-	error = SetFPos(bile->vrefid, fsFromLEOF, 0);
-	if (error)
-		goto write_error;
+	SetFPos(bile->vrefid, fsFromLEOF, 0);
 	GetFPos(bile->vrefid, &bile->file_size);
 
-	if (old)
-		old->id = BILE_PURGE_ID;
-
 	bile_write_map(bile);
 	
 	return ret;
-	
-write_error:
-	panic("bile_write: failed writing %ld to %s: %d", size,
-	  PtoCstr(bile->filename), error);
 }
 
 void
@@ -321,58 +362,39 @@ bile_write_map(struct bile *bile)
 {
 	struct bile_object *obj, *new_map_obj, *new_map;
 	size_t new_map_size, new_nobjects;
-	size_t size, n, new_n;
+	size_t pos, n;
 	short error;
-	char *magic;
-	
-	new_nobjects = 1; /* for the new map */
-	for (n = 0; n < bile->nobjects; n++) {
-		obj = &bile->map[n];
-		
-		if (obj->pos == bile->map_pos || obj->id == BILE_PURGE_ID)
-			/* ignore old map and purgable objects */
-			continue;
-			
-		new_nobjects++;
-	}
 
+	/* allocate a new map slightly larger than we need */
+	new_nobjects = bile->nobjects;
+	if (!bile->map_pos)
+		new_nobjects++;
 	new_map_size = BILE_OBJECT_SIZE * new_nobjects;
-	new_map_obj = bile_alloc(bile, new_map_size);
-	new_map_obj->id = BILE_MAP_ID;
-
-	/* write new map object header */
-	if (new_map_obj->pos == bile->file_size)
-		error = SetFPos(bile->vrefid, fsFromLEOF, 0);
-	else
-		error = SetFPos(bile->vrefid, fsFromStart, new_map_obj->pos);
-	if (error)
-		goto write_error;
-
-	size = BILE_OBJECT_SIZE;
-	error = FSWrite(bile->vrefid, &size, &new_map_obj);
-	if (error)
-		goto write_error;
-	
+	new_map_obj = bile_alloc(bile, BILE_MAP_TYPE, 0, new_map_size);
 	new_map = xmallocarray(BILE_OBJECT_SIZE, new_nobjects);
-	new_n = 0;
-	for (n = 0; n < bile->nobjects; n++) {
+	
+	for (n = 0, new_nobjects = 0; n < bile->nobjects; n++) {
 		obj = &bile->map[n];
 		
-		if (obj->pos == bile->map_pos || obj->id == BILE_PURGE_ID)
-			/* ignore old map and purgable objects */
+		if (obj->type == BILE_MAP_TYPE && obj->pos == bile->map_pos)
+			/* don't include old map in new one */
 			continue;
-			
-		new_map[new_n++] = *obj;
+		if (obj->type == BILE_PURGE_TYPE)
+			continue;
+
+		new_map[new_nobjects++] = *obj;
 	}
 	
-	size = new_map_size;
-	error = FSWrite(bile->vrefid, &size, new_map);
-	if (error)
-		goto write_error;
+	/* shrink to new object count */
+	new_map_size = BILE_OBJECT_SIZE * new_nobjects;
+	new_map_obj->size = new_map_size;
+	bile->nobjects = new_nobjects;
 	
-	error = SetFPos(bile->vrefid, fsFromLEOF, 0);
-	if (error)
-		goto write_error;
+	pos = bile_xwriteat(bile, new_map_obj->pos, BILE_OBJECT_SIZE,
+	  &new_map_obj);
+	pos = bile_xwriteat(bile, pos, new_map_size, new_map);
+	
+	SetFPos(bile->vrefid, fsFromLEOF, 0);
 	GetFPos(bile->vrefid, &bile->file_size);
 
 	/* successfully wrote new map, switch over */
@@ -382,29 +404,44 @@ bile_write_map(struct bile *bile)
 	bile->map_size = new_map_obj->size;
 
 	/* write new header to point at new map object */
-	magic = xstrdup(BILE_MAGIC);
-	size = BILE_MAGIC_LEN;
-	error = SetFPos(bile->vrefid, fsFromStart, 0);
-	if (error)
-		goto write_error;
-	error = FSWrite(bile->vrefid, &size, magic);
-	free(magic);
-	if (error)
-		goto write_error;
-	size = sizeof(long);
-	error = FSWrite(bile->vrefid, &size, &bile->map_pos);
-	if (error)
-		goto write_error;
-	error = FSWrite(bile->vrefid, &size, &bile->map_size);
-	if (error)
-		goto write_error;
+	pos = bile_xwriteat(bile, BILE_MAGIC_LEN, sizeof(long),
+	  &bile->map_pos);
+	pos = bile_xwriteat(bile, pos, sizeof(long), &bile->map_size);
 		
 	/* TODO: flush? */
+}
+
+size_t
+bile_xwriteat(struct bile *bile, size_t pos, size_t len, void *data)
+{
+	short error;
+	size_t wsize;
+	long asize;
 	
-	return;
-	
-write_error:
-	panic("bile_write_map: failed writing to %s: %d",
-	  PtoCstr(bile->filename), error);
+	if (pos == bile->file_size) {
+		error = SetFPos(bile->vrefid, fsFromLEOF, 0);
+	} else if (pos > bile->file_size) {
+		/* may as well allocate to cover len too */
+		asize = pos + len - bile->file_size;
+		error = Allocate(bile->vrefid, &asize);
+		if (error)
+			panic("bile_xwriteat: failed Allocate of %lu in %s: %d",
+			  pos + len - bile->file_size, PtoCstr(bile->filename), error);
+		error = SetFPos(bile->vrefid, fsFromLEOF, 0);
+	} else
+		error = SetFPos(bile->vrefid, fsFromStart, pos);
+	if (error)
+		panic("bile_xwriteat: failed SetFPos to %lu in %s: %d",
+		  pos, PtoCstr(bile->filename), error);
 
+	wsize = len;
+	error = FSWrite(bile->vrefid, &wsize, data);
+	if (error)
+		panic("bile_xwriteat: failed writing %lu at %lu to %s: %d",
+		  len, pos, PtoCstr(bile->filename), error);
+	if (wsize != len)
+		panic("bile_xwriteat: short write of %lu at %lu to %s: %lu",
+		  len, pos, PtoCstr(bile->filename), wsize);
+	
+	return pos + len;
 }
--- bile.h	Mon Jan  3 22:22:07 2022
+++ bile.h	Tue Jan  4 16:21:13 2022
@@ -25,25 +25,27 @@
  *   [ map contents - (map_size) ]
  *     [ object[0] position - long ]
  *     [ object[0] size - long ]
+ *     [ object[0] type - long ]
  *     [ object[0] id - long ]
  *     [ ... ]
  * [ object[0] start (map points to this as its position) ]
  *   [ object[0] position - long ]
  *   [ object[0] size - long ]
+ *   [ object[0] type - long ]
  *   [ object[0] id - long ]
  * [ object[1] start ]
  *   [ .. ]
  */
 #define BILE_MAGIC		"BILE1"
 #define BILE_MAGIC_LEN	5
-#define BILE_MAP_ID		(ULONG_MAX)
-#define BILE_PURGE_ID	(ULONG_MAX - 1)
-#define BILE_FILE_TYPE	'BILE'
 #define BILE_HEADER_LEN	(BILE_MAGIC_LEN + (sizeof(long) + sizeof(long)))
+#define BILE_MAP_TYPE	'_BLM'
+#define BILE_PURGE_TYPE	'_BLP'
 
 struct bile_object {
 	unsigned long pos;
 	unsigned long size;
+	OSType type;
 	unsigned long id;
 };
 #define BILE_OBJECT_SIZE (sizeof(struct bile_object))
@@ -59,11 +61,16 @@ struct bile {
 	size_t nobjects;
 };
 
-struct bile * bile_create(Str255 filename, OSType creator);
+struct bile * bile_create(Str255 filename, OSType creator, OSType type);
 struct bile * bile_open(Str255 filename);
 void bile_close(struct bile *bile);
-struct bile_object * bile_find(struct bile *bile, unsigned long id);
-struct bile_object * bile_alloc(struct bile *bile, size_t size);
-size_t bile_read(struct bile *bile, unsigned long id, char **data);
-size_t bile_write(struct bile *bile, unsigned long id, char *data,
-  size_t size);
+struct bile_object * bile_find(struct bile *bile, OSType type,
+  unsigned long id);
+size_t bile_next_id(struct bile *bile, OSType type);
+struct bile_object * bile_alloc(struct bile *bile, OSType type,
+  unsigned long id, size_t size);
+void bile_delete(struct bile *bile, OSType type, unsigned long id);
+size_t bile_read(struct bile *bile, OSType type, unsigned long id,
+  char **data);
+size_t bile_write(struct bile *bile, OSType type, unsigned long id,
+  char *data, size_t size);