AmendHub

Download:

jcs

/

subtext

/

amendments

/

44

bile: Add initial binary file API

This will eventually replace resource files for the backend which are
not well-suited for writing new/modified records

jcs made amendment 44 over 2 years ago
--- bile.c Mon Jan 3 22:04:09 2022 +++ bile.c Mon Jan 3 22:21:19 2022 @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <string.h> +#include "bile.h" +#include "util.h" + +void bile_write_map(struct bile *bile); +void bile_sort_objects(struct bile *bile); +void bile_free(struct bile *bile, unsigned long id); + +struct bile * +bile_create(Str255 filename, OSType creator) +{ + struct bile *bile; + size_t len; + char *magic; + short error, fh; + + /* create file */ + error = Create(filename, 0, creator, BILE_FILE_TYPE); + if (error) + panic("bile_create: failed to create %s: %d", PtoCstr(filename), + error); + + error = FSOpen(filename, 0, &fh); + if (error) + panic("bile_create: failed to open %s: %d", PtoCstr(filename), + error); + + bile = xmalloczero(sizeof(struct bile)); + bile->vrefid = fh; + memcpy(bile->filename, filename, sizeof(bile->filename)); + + /* write magic */ + len = BILE_MAGIC_LEN; + magic = xstrdup(BILE_MAGIC); + error = FSWrite(bile->vrefid, &len, magic); + free(magic); + if (error) + goto write_error; + + /* write empty map */ + len = sizeof(long); + error = FSWrite(bile->vrefid, &len, &bile->map_pos); + if (error) + goto write_error; + + len = sizeof(long); + error = FSWrite(bile->vrefid, &len, &bile->map_size); + if (error) + goto write_error; + + GetFPos(fh, &bile->file_size); + + if (bile->file_size != BILE_HEADER_LEN) + panic("bile_create: incorrect length after writing: %ld (!= %ld)", + bile->file_size, (long)BILE_HEADER_LEN); + + return bile; + +write_error: + panic("bile_create: failed to write to %s: %d", PtoCstr(filename), + error); +} + +struct bile * +bile_open(Str255 filename) +{ + struct bile *bile; + size_t file_size, map_size, size; + short error, fh; + char magic[BILE_MAGIC_LEN + 1]; + + /* open file */ + error = FSOpen(filename, 0, &fh); + if (error) + panic("bile_open: failed to open %s: %d", PtoCstr(filename), + error); + + error = SetFPos(fh, fsFromLEOF, 0); + if (error) + panic("bile_open: SetFPos: %d", error); + GetFPos(fh, &file_size); + + SetFPos(fh, fsFromStart, 0); + + /* verify magic */ + size = BILE_MAGIC_LEN; + error = FSRead(fh, &size, &magic); + if (error) + panic("bile_open: failed reading from %s: %d", PtoCstr(filename), + error); + + if (strncmp(magic, BILE_MAGIC, BILE_MAGIC_LEN) != 0) + panic("bile_open: bad magic in %s", PtoCstr(filename)); + + bile = xmalloczero(sizeof(struct bile)); + bile->vrefid = fh; + memcpy(bile->filename, filename, sizeof(filename)); + bile->file_size = file_size; + + /* load map */ + size = sizeof(long); + error = FSRead(fh, &size, &bile->map_pos); + if (error) + goto read_error; + + size = sizeof(long); + error = FSRead(fh, &size, &bile->map_size); + if (error) + goto read_error; + + if (bile->map_pos + bile->map_size > file_size) + panic("bile_open: map points to %lu + %lu, but file is only %lu", + bile->map_pos, bile->map_size, file_size); + + if (bile->map_size) { + error = SetFPos(bile->vrefid, fsFromStart, bile->map_pos + + BILE_OBJECT_SIZE); + if (error) + goto read_error; + + /* read size */ + size = bile->map_size; + bile->map = xmalloczero(bile->map_size); + error = FSRead(fh, &size, bile->map); + if (error) + goto read_error; + bile->nobjects = bile->map_size / BILE_OBJECT_SIZE; + } + + return bile; + +read_error: + panic("bile_open: failed reading from %s: %d", PtoCstr(filename), + error); +} + +void +bile_close(struct bile *bile) +{ + FSClose(bile->vrefid); + free(bile->map); +} + +/* XXX: these become invalid once we re-sort, should we return a copy? */ +struct bile_object * +bile_find(struct bile *bile, 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) + return o; + } + + return NULL; +} + +struct bile_object * +bile_alloc(struct bile *bile, size_t size) +{ + struct bile_object *new, *o; + size_t last_pos = BILE_HEADER_LEN; + short n; + + bile->map = xreallocarray(bile->map, bile->nobjects + 1, + BILE_OBJECT_SIZE); + + /* find a last_pos we can use */ + for (n = 0; n < bile->nobjects; n++) { + if (bile->map[n].pos - last_pos >= (size + BILE_OBJECT_SIZE)) + break; + last_pos = bile->map[n].pos + BILE_OBJECT_SIZE + bile->map[n].size; + } + + new = &bile->map[bile->nobjects]; + bile->nobjects++; + new->pos = last_pos; + new->size = size; + + bile_sort_objects(bile); + + /* find our new object pointer after sorting */ + for (n = 0; n < bile->nobjects; n++) { + o = &bile->map[n]; + if (o->pos == last_pos) + return o; + } + + panic("bile_alloc: couldn't find newly added object?"); +} + +void +bile_sort_objects(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) { + o = bile->map[j]; + bile->map[j] = bile->map[j + 1]; + bile->map[j + 1] = o; + } + } + } +} + +void +bile_free(struct bile *bile, unsigned long id) +{ + /* TODO */ +} + +size_t +bile_read(struct bile *bile, unsigned long id, char **data) +{ + struct bile_object *o = bile_find(bile, id); + size_t rsize; + short error; + + if (o == NULL) + return -1; + + if (*data == NULL) + *data = xmalloc(o->size); + + if (o->pos + BILE_OBJECT_SIZE + o->size > bile->file_size) + panic("bile_read: object %ld pos %ld size %ld > file size %ld", + id, o->pos, o->size, bile->file_size); + + error = SetFPos(bile->vrefid, fsFromStart, o->pos + BILE_OBJECT_SIZE); + if (error) + goto read_error; + + /* TODO: read object header and verify size and id */ + + rsize = o->size; + error = FSRead(bile->vrefid, &rsize, *data); + if (error) + goto read_error; + if (rsize != o->size) + panic("bile_open: needed to read %ld, read %ld", o->size, rsize); + + return o->size; + +read_error: + panic("bile_open: failed reading from %s: %d", PtoCstr(bile->filename), + error); +} + +size_t +bile_write(struct bile *bile, unsigned long id, char *data, size_t size) +{ + struct bile_object *old, *new_obj; + size_t ret, wsize; + 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; + + 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; + + 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; + 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 +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; + 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++; + } + + 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 = xmallocarray(BILE_OBJECT_SIZE, new_nobjects); + new_n = 0; + 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_map[new_n++] = *obj; + } + + size = new_map_size; + error = FSWrite(bile->vrefid, &size, new_map); + if (error) + goto write_error; + + error = SetFPos(bile->vrefid, fsFromLEOF, 0); + if (error) + goto write_error; + GetFPos(bile->vrefid, &bile->file_size); + + /* successfully wrote new map, switch over */ + free(bile->map); + bile->map = new_map; + bile->map_pos = new_map_obj->pos; + 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; + + /* TODO: flush? */ + + return; + +write_error: + panic("bile_write_map: failed writing to %s: %d", + PtoCstr(bile->filename), error); + +} --- bile.h Mon Jan 3 21:18:20 2022 +++ bile.h Mon Jan 3 22:22:07 2022 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "util.h" + +/* + * File format: + * [ bile header - BILE_HEADER_LEN ] + * [ BILE_MAGIC - BILE_MAGIC_LEN ] + * [ map position - long ] + * [ map size - long ] + * [ map contents - (map_size) ] + * [ object[0] position - long ] + * [ object[0] size - 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] 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))) + +struct bile_object { + unsigned long pos; + unsigned long size; + unsigned long id; +}; +#define BILE_OBJECT_SIZE (sizeof(struct bile_object)) + +struct bile { + unsigned long map_pos; + unsigned long map_size; + + short vrefid; + Str255 filename; + size_t file_size; + struct bile_object *map; + size_t nobjects; +}; + +struct bile * bile_create(Str255 filename, OSType creator); +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);