/* * AUTHOR * N. Nielsen * * LICENSE * This software is in the public domain. * * The software is provided "as is", without warranty of any kind, * express or implied, including but not limited to the warranties * of merchantability, fitness for a particular purpose, and * noninfringement. In no event shall the author(s) be liable for any * claim, damages, or other liability, whether in an action of * contract, tort, or otherwise, arising from, out of, or in connection * with the software or the use or other dealings in the software. * * SUPPORT * Send bug reports to: */ #include #include #include #include "binfile.h" #define BF_FILE 0x00000010 #define BF_ERROR 0x00000040 /* bfctx: --------------------------------------------------- * This is the structure that becomes a BFILE * to the outside world */ typedef struct _bfctx { union { FILE* file; /* Valid when working with a FILE */ void* data; /* Valid when working with memory */ } d; size_t cur; /* Current position */ size_t len; /* Size of the memory buffer */ int flags; } bfctx; /* bfWriteRaw: ----------------------------------------------- * Write data to appropriate buffer/file */ int bfWriteRaw(BFILE h, const void* data, size_t len) { bfctx* ctx = (bfctx*)h; /* file */ if(ctx->flags & BF_FILE) { len = fwrite(data, len, 1, ctx->d.file); ctx->cur += len; return len; } /* memory */ else { /* Check if enough space */ if((ctx->cur + len) > ctx->len) { /* If we ran out of space try and reallocate */ if(ctx->flags & BF_REALLOC) { void* old = ctx->d.data; ctx->len = ctx->len + (((len / 1024) + 1) * 1024); ctx->d.data = realloc(ctx->d.data, ctx->len); if(!ctx->d.data) free(old); } /* * If there's no more space and we can't * reallocate then it's an error */ if(!ctx->d.data || !(ctx->flags & BF_REALLOC)) { ctx->flags |= BF_ERROR; return 0; } } /* write the data in */ memcpy(((unsigned char*)ctx->d.data) + ctx->cur, data, len); ctx->cur += len; return len; } } /* bfReadRaw: ------------------------------------------------------ * Read data from either buffer or file */ int bfReadRaw(BFILE h, void* data, size_t len) { bfctx* ctx = (bfctx*)h; /* file */ if(ctx->flags & BF_FILE) { len = fread(data, 1, len, ctx->d.file); ctx->cur += len; return len; } /* memory */ else { if((ctx->len - ctx->cur) < len) len = ctx->len - ctx->cur; memcpy(data, ((unsigned char*)ctx->d.data) + ctx->cur, len); ctx->cur += len; return len; } } /* bfSeek: --------------------------------------------------------- * Seek forward in either buffer or file */ int bfSeek(bfctx* ctx, long offset) { /* file */ if(ctx->flags & BF_FILE) { int ret = fseek(ctx->d.file, offset, SEEK_CUR); if(ret == 0) ctx->cur += offset; return ret; } /* memory */ else { if((long)(ctx->len - ctx->cur) < offset) return 1; ctx->cur += offset; return 0; } } /* bfError: ------------------------------------------------- * Check for errors on output */ int bfError(BFILE h) { bfctx* ctx = (bfctx*)h; if(ctx->flags & BF_FILE) return ferror(ctx->d.file); else return ctx->flags & BF_ERROR; } /* bfStartFile: ----------------------------------------------- * Get a BFILE based on a FILE output * file should be opened for appropriate input or output */ BFILE bfStartFile(FILE* file) { bfctx* ctx = (bfctx*)malloc(sizeof(bfctx)); if(!ctx) return 0; memset(ctx, 0, sizeof(bfctx)); ctx->d.file = file; ctx->flags = BF_FILE; return ctx; } /* bfStartMem: ------------------------------------------------- * Get a BFILE based on memory * If flags have BF_REALLOC set then NULL can be passed * and/or we can reallocate the buffer */ BFILE bfStartMem(void* mem, size_t len, int flags) { bfctx* ctx = (bfctx*)malloc(sizeof(bfctx)); if(!ctx) return 0; memset(ctx, 0, sizeof(bfctx)); ctx->len = len; ctx->flags = flags; ctx->flags &= ~(BF_FILE | BF_ERROR); ctx->d.data = mem; return ctx; } /* bfClose: ----------------------------------------------------- * Close a BFILE * Returns either the file or memory buffer, and returns size * in the len argument */ void bfClose(BFILE h) { if(h) free(h); } /* bfInternal: -------------------------------------------------- * Get the internal file handle or memory pointer for this * binfile */ void* bfInternal(BFILE h) { bfctx* ctx = (bfctx*)h; if(ctx->flags & BF_FILE) return ctx->d.file; else return ctx->d.data; } /* bfCount: ---------------------------------------------------- * Returns the number of bytes written or read to this binfile */ size_t bfCount(BFILE h) { bfctx* ctx = (bfctx*)h; return ctx->cur; } /* bfWriteEnd: ------------------------------------------------- * Puts down an end of tags marker in the binfile */ int bfWriteEnd(BFILE h) { bfval opt; int c; opt.id = BINTYPE_END; opt.len = 0; opt.type = BINTYPE_END; c = bfWriteRaw(h, &opt, sizeof(opt)); return bfError(h) ? 0 : c; } /* bfWriteValue: ----------------------------------------------- * Writes a tagged value to the binfile */ int bfWriteValue(BFILE h, const bfval* val, const void* data) { int c = bfWriteRaw(h, val, sizeof(bfval)); c += bfWriteRaw(h, data, val->len); return bfError(h) ? 0 : c; } /* bfReadValueInfo: -------------------------------------------- * Gets the next tag (without data) and returns the length * of the memory required to hold it. */ int bfReadValueInfo(BFILE h, bfval* val) { int c = bfReadRaw(h, val, sizeof(bfval)); return c == sizeof(bfval) && !bfError(h); } /* bfReadValueData: --------------------------------------------- * Gets the data for a value. val should be same one returned * from bfReadValueInfo */ int bfReadValueData(BFILE h, const bfval* val, void* data) { size_t c; if((c = bfReadRaw(h, data, val->len)) != val->len || bfError(h)) return 0; /* Special handling for these guys */ switch(val->type) { case BINTYPE_ASCII: { char* str = (char*)data; str[val->len] = 0; } break; case BINTYPE_INT32: case BINTYPE_INT16: /* TODO: do endianness here */ break; }; return c; } /* bfSkipValueData: ------------------------------------------------ * Skips the data for a tagged value */ int bfSkipValueData(BFILE h, const bfval* val) { bfctx* ctx = (bfctx*)h; return bfSeek(ctx, val->len) == 0; } /* bfWriteString: -------------------------------------------------- * Convenience function for writing out strings to file */ int bfWriteString(BFILE h, short id, const char* data) { bfval val; val.id = id; val.type = BINTYPE_ASCII; val.len = strlen(data); return bfWriteValue(h, &val, (void*)data); } /* bfWriteInt: ----------------------------------------------------- * Convenience function for writing out 4 byte integers to file */ int bfWriteInt(BFILE h, short id, int data) { bfval val; val.id = id; val.type = BINTYPE_INT32; val.len = BINSIZE_INT32; /* TODO: take care of endianess here */ return bfWriteValue(h, &val, &data); }