AmendHub

Download:

jcs

/

amend

/

amendments

/

40

bile: Add latest bile API code


jcs made amendment 40 over 2 years ago
--- bile.c Tue Jan 11 19:28:31 2022 +++ bile.c Tue Jan 11 19:28:31 2022 @@ -0,0 +1,713 @@ +/* + * 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" + +/* for errors not specific to a bile */ +static short _bile_error; + +struct bile_object * bile_alloc(struct bile *bile, const OSType type, + const unsigned long id, const size_t size); +struct bile_object * bile_object_in_map(struct bile *bile, + const OSType type, const unsigned long id); +short bile_write_map(struct bile *bile); +void bile_sort_by_pos(struct bile *bile); +size_t bile_xwriteat(struct bile *bile, const size_t pos, + const void *data, const size_t len); + +/* Public API */ + +short +bile_error(struct bile *bile) +{ + if (bile == NULL) + return _bile_error; + + return bile->last_error; +} + +struct bile * +bile_create(const Str255 filename, short vrefnum, const OSType creator, + const OSType type) +{ + struct bile *bile = NULL; + size_t len; + char *magic; + short fh; + + _bile_error = 0; + + /* create file */ + _bile_error = Create(filename, vrefnum, creator, type); + if (_bile_error) + return NULL; + + _bile_error = FSOpen(filename, vrefnum, &fh); + if (_bile_error) + return NULL; + + bile = xmalloczero(sizeof(struct bile)); + bile->vrefnum = vrefnum; + bile->frefnum = fh; + bile->map_ptr.type = BILE_TYPE_MAPPTR; + memcpy(bile->filename, filename, sizeof(bile->filename)); + + /* write magic */ + len = BILE_MAGIC_LEN; + magic = xstrdup(BILE_MAGIC); + _bile_error = FSWrite(bile->frefnum, &len, magic); + free(magic); + if (_bile_error) + goto create_bail; + + /* write header pointing to blank map */ + len = sizeof(bile->map_ptr); + _bile_error = FSWrite(bile->frefnum, &len, &bile->map_ptr); + if (_bile_error) + goto create_bail; + + 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; + +create_bail: + FSClose(bile->frefnum); + if (bile != NULL) + free(bile); + return NULL; +} + +struct bile * +bile_open(const Str255 filename, short vrefnum) +{ + struct bile *bile = NULL; + struct bile_object map_obj; + size_t file_size, map_size, size; + short fh; + char magic[BILE_MAGIC_LEN + 1]; + + _bile_error = 0; + + /* open file */ + _bile_error = FSOpen(filename, vrefnum, &fh); + if (_bile_error) + return NULL; + + _bile_error = SetFPos(fh, fsFromLEOF, 0); + if (_bile_error) + goto open_bail; + GetFPos(fh, &file_size); + SetFPos(fh, fsFromStart, 0); + + /* verify magic */ + size = BILE_MAGIC_LEN; + _bile_error = FSRead(fh, &size, &magic); + if (_bile_error) + goto open_bail; + + if (strncmp(magic, BILE_MAGIC, BILE_MAGIC_LEN) != 0) { + _bile_error = -1; + goto open_bail; + } + + bile = xmalloczero(sizeof(struct bile)); + bile->vrefnum = vrefnum; + bile->frefnum = fh; + memcpy(bile->filename, filename, sizeof(bile->filename)); + bile->file_size = file_size; + + /* load map pointer */ + size = sizeof(bile->map_ptr); + _bile_error = FSRead(bile->frefnum, &size, &bile->map_ptr); + if (_bile_error) + goto open_bail; + + if (bile->map_ptr.pos + bile->map_ptr.size > file_size) + panic("bile_open: map points to %lu + %lu, but file is only %lu", + bile->map_ptr.pos, bile->map_ptr.size, file_size); + if (bile->map_ptr.size % BILE_OBJECT_SIZE != 0) + panic("bile_open: map pointer size is not a multiple of object " + "size (%lu): %lu", BILE_OBJECT_SIZE, bile->map_ptr.size); + + if (bile->map_ptr.size) { + /* read and verify map object header map_ptr points to */ + _bile_error = SetFPos(bile->frefnum, fsFromStart, + bile->map_ptr.pos); + if (_bile_error) + goto open_bail; + + size = sizeof(struct bile_object); + _bile_error = FSRead(bile->frefnum, &size, &map_obj); + if (_bile_error) + goto open_bail; + + if (map_obj.pos != bile->map_ptr.pos) + panic("bile_open: map pointer points to %lu but object there " + "has position %lu", bile->map_ptr.pos, map_obj.pos); + if (map_obj.size != bile->map_ptr.size) + panic("bile_open: map is supposed to have size %lu but object " + "pointed to has size %lu", bile->map_ptr.size, map_obj.size); + + /* read entire map */ + size = map_obj.size; + bile->map = xmalloczero(size); + _bile_error = FSRead(bile->frefnum, &size, bile->map); + if (_bile_error) + goto open_bail; + bile->nobjects = map_obj.size / BILE_OBJECT_SIZE; + } + + return bile; + +open_bail: + FSClose(bile->frefnum); + if (bile != NULL) + free(bile); + return NULL; +} + +void +bile_close(struct bile *bile) +{ + if (bile == NULL) + panic("bile_close: bogus bile"); + + _bile_error = 0; + + FSClose(bile->frefnum); + if (bile->map != NULL) + free(bile->map); +} + +struct bile_object * +bile_find(struct bile *bile, const OSType type, const unsigned long id) +{ + struct bile_object *o, *ocopy; + unsigned long n; + + o = bile_object_in_map(bile, type, id); + if (o == NULL) + return NULL; + + ocopy = xmalloc(BILE_OBJECT_SIZE); + memcpy(ocopy, o, BILE_OBJECT_SIZE); + + return ocopy; +} + +size_t +bile_count_by_type(struct bile *bile, const OSType type) +{ + struct bile_object *o; + size_t n, count = 0; + + if (bile == NULL) + panic("bile_count_by_type: bogus bile"); + + _bile_error = bile->last_error = 0; + + for (n = 0; n < bile->nobjects; n++) { + o = &bile->map[n - 1]; + if (o->type == type) + count++; + } + + return count; +} + +struct bile_object * +bile_get_nth_of_type(struct bile *bile, const size_t index, + const OSType type) +{ + struct bile_object *o, *ocopy; + size_t n, count = 0; + + if (bile == NULL) + panic("bile_get_nth_of_type: bogus bile"); + + _bile_error = bile->last_error = 0; + + for (n = 0; n < bile->nobjects; n++) { + o = &bile->map[n - 1]; + if (o->type != type) + continue; + + if (count == index) { + ocopy = xmalloc(BILE_OBJECT_SIZE); + memcpy(ocopy, o, BILE_OBJECT_SIZE); + return ocopy; + } + count++; + } + + return NULL; +} + +size_t +bile_next_id(struct bile *bile, const OSType type) +{ + struct bile_object *o; + size_t n, id = 1; + + if (bile == NULL) + panic("bile_next_id: bogus bile"); + + _bile_error = bile->last_error = 0; + + 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; +} + + +short +bile_delete(struct bile *bile, const OSType type, const unsigned long id) +{ + static char zero[128] = { 0 }; + struct bile_object *o; + size_t pos, size, wsize; + + if (bile == NULL) + panic("bile_delete: bogus bile"); + + _bile_error = bile->last_error = 0; + + o = bile_object_in_map(bile, type, id); + if (o == NULL) { + _bile_error = bile->last_error = -1; + return -1; + } + + o->type = BILE_TYPE_PURGE; + pos = o->pos; + size = o->size + BILE_OBJECT_SIZE; + + bile_write_map(bile); + if (_bile_error) + return -1; + + _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromStart, + pos); + if (_bile_error) + return -1; + + while (size > 0) { + wsize = MIN(128, size); + size -= wsize; + + _bile_error = bile->last_error = FSWrite(bile->frefnum, &wsize, + zero); + if (_bile_error) + return -1; + } + + return 0; +} + +size_t +bile_read_object(struct bile *bile, const struct bile_object *o, + char *data, const size_t len) +{ + struct bile_object verify; + size_t rsize, wantlen, ret; + + if (bile == NULL) + panic("bile_read_object: bogus bile"); + if (o == NULL) + panic("bile_read_object: NULL object passed"); + if (data == NULL) + panic("bile_read_object: NULL data pointer passed"); + if (len == 0) + panic("bile_read_object: zero len"); + + _bile_error = bile->last_error = 0; + + if (o->pos + BILE_OBJECT_SIZE + o->size > bile->file_size) + panic("bile_read_object: object %ld pos %ld size %ld > file size %ld", + o->id, o->pos, o->size, bile->file_size); + + _bile_error = SetFPos(bile->frefnum, fsFromStart, o->pos); + if (_bile_error) + panic("bile_read_object: object %lu points to bogus position %lu", + o->id, o->pos); + + rsize = BILE_OBJECT_SIZE; + _bile_error = bile->last_error = FSRead(bile->frefnum, &rsize, &verify); + if (_bile_error) + return 0; + + if (verify.id != o->id) + panic("bile_read_object: object %ld pos %ld wrong id %ld, " + "expected %ld", o->id, o->pos, verify.id, o->id); + if (verify.type != o->type) + panic("bile_read_object: object %ld pos %ld wrong type %ld, " + "expected %ld", o->id, o->pos, verify.type, o->type); + if (verify.size != o->size) + panic("bile_read_object: object %ld pos %ld wrong size %ld, " + "expected %ld", o->id, o->pos, verify.size, o->size); + + wantlen = len; + if (wantlen > o->size) + wantlen = o->size; + + rsize = wantlen; + _bile_error = bile->last_error = FSRead(bile->frefnum, &rsize, data); + if (_bile_error) + return 0; + + if (rsize != wantlen) + panic("bile_read: needed to read %ld, read %ld", wantlen, rsize); + + return rsize; +} + +size_t +bile_read(struct bile *bile, const OSType type, const unsigned long id, + char *data, const size_t len) +{ + struct bile_object *o; + size_t ret; + + if (bile == NULL) + panic("bile_read: bogus bile"); + if (data == NULL) + panic("bile_read: NULL data pointer passed"); + + _bile_error = bile->last_error = 0; + + o = bile_find(bile, type, id); + if (o == NULL) { + _bile_error = bile->last_error = -1; + return 0; + } + + ret = bile_read_object(bile, o, data, len); + free(o); + + return ret; +} + +size_t +bile_read_alloc(struct bile *bile, const OSType type, + const unsigned long id, char **data) +{ + struct bile_object *o; + size_t ret; + + if (bile == NULL) + panic("bile_read: bogus bile"); + if (data == NULL) + panic("bile_read: NULL data pointer passed"); + + _bile_error = bile->last_error = 0; + + o = bile_find(bile, type, id); + if (o == NULL) { + _bile_error = bile->last_error = -1; + return 0; + } + + *data = xmalloczero(o->size); + ret = bile_read_object(bile, o, *data, o->size); + free(o); + + return ret; +} + +size_t +bile_write(struct bile *bile, const OSType type, const unsigned long id, + const void *data, const size_t len) +{ + struct bile_object *old, *new_obj; + size_t wrote; + short error; + + if (bile == NULL) + panic("bile_write: bogus bile"); + if (len == 0) + panic("bile_write: zero len passed"); + if (data == NULL) + panic("bile_write: NULL data pointer passed"); + + _bile_error = bile->last_error = 0; + + if ((old = bile_object_in_map(bile, type, id)) != NULL) + old->type = BILE_TYPE_PURGE; + + new_obj = bile_alloc(bile, type, id, len); + + bile_xwriteat(bile, new_obj->pos, new_obj, BILE_OBJECT_SIZE); + if (bile->last_error) + return 0; + wrote = bile_xwriteat(bile, new_obj->pos + BILE_OBJECT_SIZE, data, len); + if (wrote != len || bile->last_error) + return 0; + + SetFPos(bile->frefnum, fsFromLEOF, 0); + GetFPos(bile->frefnum, &bile->file_size); + + bile_write_map(bile); + if (bile->last_error) + return 0; + + return wrote; +} + + +void +bile_fsck(struct bile *bile) +{ + struct bile_object o; + size_t n; + char data; + + if (bile == NULL) + panic("bile_fsck: bogus bile"); + + _bile_error = bile->last_error = 0; + + for (n = 0; n < bile->nobjects; n++) + bile_read_object(bile, &bile->map[n], &data, 1); +} + +/* Private API */ + +struct bile_object * +bile_object_in_map(struct bile *bile, const OSType type, + const unsigned long id) +{ + struct bile_object *o; + size_t n; + + if (bile == NULL) + panic("bile_find: bogus bile"); + + _bile_error = bile->last_error = 0; + + /* 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; +} + +struct bile_object * +bile_alloc(struct bile *bile, const OSType type, const unsigned long id, + const size_t size) +{ + struct bile_object *newo, *o; + size_t last_pos = BILE_HEADER_LEN; + size_t n; + + if (bile == NULL) + panic("bile_alloc: bogus bile"); + + _bile_error = bile->last_error = 0; + + 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; + } + + newo = &bile->map[bile->nobjects]; + bile->nobjects++; + newo->pos = last_pos; + newo->size = size; + newo->type = type; + newo->id = id; + + bile_sort_by_pos(bile); + + /* find our new object pointer after sorting */ + for (n = bile->nobjects; n > 0; n--) { + o = &bile->map[n - 1]; + if (o->type == type && o->pos == last_pos) + return o; + } + + panic("bile_alloc: couldn't find newly added object?"); +} + +short +bile_write_map(struct bile *bile) +{ + IOParam pb; + struct bile_object *obj, *new_map_obj, *new_map, + *new_map_obj_in_new_map = NULL; + size_t new_map_size, new_nobjects, new_map_id; + size_t n; + short ret; + + if (bile == NULL) + panic("bile_write_map: bogus bile"); + + _bile_error = bile->last_error = 0; + + /* allocate a new map slightly larger than we need */ + new_nobjects = bile->nobjects; + if (bile->map_ptr.pos) + new_map_id = bile->map_ptr.id + 1; + else { + /* new file, map never written, allocate another object for map */ + new_nobjects++; + new_map_id = 1; + } + new_map_size = BILE_OBJECT_SIZE * new_nobjects; + new_map_obj = bile_alloc(bile, BILE_TYPE_MAP, new_map_id, + new_map_size); + new_map = xmallocarray(BILE_OBJECT_SIZE, new_nobjects); + + for (n = 0, new_nobjects = 0; n < bile->nobjects; n++) { + obj = &bile->map[n]; + + if (obj->type == BILE_TYPE_MAP && obj->pos == bile->map_ptr.pos) + /* don't include old map in new one */ + continue; + if (obj->type == BILE_TYPE_PURGE) + continue; + + if (obj->type == BILE_TYPE_MAP && obj->pos == new_map_obj->pos) + new_map_obj_in_new_map = &new_map[new_nobjects]; + + new_map[new_nobjects++] = *obj; + } + + /* shrink to new object count */ + new_map_size = BILE_OBJECT_SIZE * new_nobjects; + new_map_obj->size = new_map_size; + new_map_obj_in_new_map->size = new_map_size; + + /* write object header */ + bile_xwriteat(bile, new_map_obj->pos, new_map_obj, BILE_OBJECT_SIZE); + if (bile->last_error) + return -1; + + /* and then the map contents */ + bile_xwriteat(bile, new_map_obj->pos + BILE_OBJECT_SIZE, new_map, + new_map_size); + if (bile->last_error) + return -1; + + SetFPos(bile->frefnum, fsFromLEOF, 0); + GetFPos(bile->frefnum, &bile->file_size); + + memset(&pb, 0, sizeof(pb)); + pb.ioRefNum = bile->frefnum; + PBFlushFile(&pb, false); + + /* successfully wrote new map, switch over */ + free(bile->map); + bile->nobjects = new_nobjects; + bile->map = new_map; + bile->map_ptr.pos = new_map_obj->pos; + bile->map_ptr.size = new_map_obj->size; + bile->map_ptr.id = new_map_obj->id; + + /* write new pointer to point at new map object */ + bile_xwriteat(bile, BILE_MAGIC_LEN, &bile->map_ptr, + sizeof(bile->map_ptr)); + if (bile->last_error) + return -1; + + PBFlushFile(&pb, false); + + return 0; +} + +size_t +bile_xwriteat(struct bile *bile, const size_t pos, const void *data, + const size_t len) +{ + short error; + size_t wsize; + long asize; + + if (bile == NULL) + panic("bile_xwriteat: bogus bile"); + + _bile_error = bile->last_error = 0; + + if (pos == bile->file_size) { + _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromLEOF, + 0); + } else if (pos > bile->file_size) { + /* may as well allocate to cover len too */ + asize = pos + len - bile->file_size; + _bile_error = Allocate(bile->frefnum, &asize); + if (_bile_error) + return 0; + _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromLEOF, + 0); + } else + _bile_error = bile->last_error = SetFPos(bile->frefnum, + fsFromStart, pos); + + if (_bile_error) + return 0; + + wsize = len; + _bile_error = bile->last_error = FSWrite(bile->frefnum, &wsize, data); + if (_bile_error) + return 0; + if (wsize != len) + panic("bile_xwriteat: short write of %lu at %lu to %s: %lu", + len, pos, PtoCstr(bile->filename), wsize); + + SetFPos(bile->frefnum, fsFromLEOF, 0); + GetFPos(bile->frefnum, &bile->file_size); + + return wsize; +} + +void +bile_sort_by_pos(struct bile *bile) +{ + size_t n, j; + struct bile_object o; + + if (bile == NULL) + panic("bile_sort_by_pos: bogus bile"); + + _bile_error = bile->last_error = 0; + + for (n = 0; n < bile->nobjects; n++) { + for (j = 0; j < bile->nobjects - n - 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; + } + } + } +} --- bile.h Mon Jan 10 21:07:52 2022 +++ bile.h Mon Jan 10 21:07:52 2022 @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef __BILE_H__ +#define __BILE_H__ + +#include "util.h" + +/* + * File format: + * [ bile header - BILE_HEADER_LEN ] + * [ BILE_MAGIC - BILE_MAGIC_LEN ] + * [ map pointer object ] + * [ pointer position - long ] + * [ pointer size - long ] + * [ pointer type (_BL>) - long ] + * [ pointer 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 ] + * [ .. ] + * [ map object - (pointed to by map pointer position) ] + * [ map position - long ] + * [ map size - long ] + * [ map type (_BLM) - long ] + * [ map id - long ] + * [ map contents ] + */ +#define BILE_MAGIC "BILE1" +#define BILE_MAGIC_LEN 5 +#define BILE_TYPE_MAP '_BLM' +#define BILE_TYPE_MAPPTR '_BL>' +#define BILE_TYPE_PURGE '_BLP' + +struct bile_object { + unsigned long pos; + unsigned long size; + OSType type; + unsigned long id; +}; +#define BILE_OBJECT_SIZE (sizeof(struct bile_object)) +#define BILE_HEADER_LEN (BILE_MAGIC_LEN + BILE_OBJECT_SIZE) + +struct bile { + struct bile_object map_ptr; + short vrefnum, frefnum, last_error; + Str255 filename; + size_t file_size; + struct bile_object *map; /* array of bile_objects */ + size_t nobjects; +}; + +short bile_error(struct bile *bile); + +struct bile * bile_create(const Str255 filename, short vrefnum, + const OSType creator, const OSType type); +struct bile * bile_open(const Str255 filename, short vrefnum); +void bile_close(struct bile *bile); + +struct bile_object * bile_find(struct bile *bile, const OSType type, + const unsigned long id); +size_t bile_count_by_type(struct bile *bile, + const OSType type); +struct bile_object * bile_get_nth_of_type(struct bile *bile, + const size_t index, const OSType type); +size_t bile_next_id(struct bile *bile, const OSType type); +short bile_delete(struct bile *bile, const OSType type, + const unsigned long id); +size_t bile_read_object(struct bile *bile, + const struct bile_object *o, char *data, + const size_t len); +size_t bile_read(struct bile *bile, const OSType type, + const unsigned long id, char *data, + const size_t len); +size_t bile_read_alloc(struct bile *bile, + const OSType type, const unsigned long id, + char **data); +size_t bile_write(struct bile *bile, OSType type, + const unsigned long id, const void *data, + const size_t len); +void bile_fsck(struct bile *bile); + +#endif