Mercurial > hg > th-libs
view th_ioctx.c @ 675:fb4093ad1f7b
Add MemIO ioctx functionality.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 25 Feb 2020 06:15:08 +0200 |
parents | 24cbab6e88c6 |
children | 7e207f1023d9 |
line wrap: on
line source
/* * Simple I/O abstraction and context handling layer * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012-2020 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include "th_ioctx.h" #include "th_string.h" #include "th_endian.h" #include <stdio.h> static void th_io_update_atime(th_ioctx *ctx) { ctx->atime = time(NULL); } static void th_io_init(th_ioctx *ctx) { memset(ctx, 0, sizeof(th_ioctx)); ctx->line = 1; } void th_io_init_stdio(th_ioctx *ctx, FILE *fh) { th_io_init(ctx); ctx->fops = &th_stdio_io_ops; ctx->data = (void *) fh; } th_ioctx * th_io_new(const th_ioctx_ops *fops, const char *filename) { th_ioctx *ctx = th_malloc(sizeof(th_ioctx)); if (ctx == NULL) return NULL; th_io_init(ctx); ctx->allocated = TRUE; ctx->fops = fops; ctx->filename = th_strdup(filename); ctx->fallocated = TRUE; if (filename != NULL && ctx->filename == NULL) goto err; return ctx; err: th_io_close(ctx); return NULL; } int th_io_open(th_ioctx *ctx, const char *mode) { if (ctx == NULL) return THERR_NULLPTR; if (mode != NULL && (ctx->mode = th_strdup(mode)) == NULL) return THERR_MALLOC; ctx->mallocated = TRUE; if (ctx->fops->fopen != NULL) ctx->status = ctx->fops->fopen(ctx); return ctx->status; } int th_io_fopen(th_ioctx **pctx, const th_ioctx_ops *fops, const char *filename, const char *mode) { th_ioctx *ctx; int res; if ((*pctx = ctx = th_io_new(fops, filename)) == NULL) return THERR_MALLOC; if ((res = th_io_open(ctx, mode)) != THERR_OK) goto err; return THERR_OK; err: th_io_close(ctx); *pctx = NULL; return res; } void th_io_close(th_ioctx *ctx) { if (ctx != NULL) { if (ctx->fops != NULL && ctx->fops->fclose != NULL) ctx->fops->fclose(ctx); if (ctx->fallocated) th_free_r(&ctx->filename); if (ctx->mallocated) th_free_r(&ctx->mode); if (ctx->allocated) th_free(ctx); } } BOOL th_io_set_handlers(th_ioctx *ctx, void (*error)(th_ioctx *, const int, const char *msg), void (*msg)(th_ioctx *, const int, const char *msg)) { if (ctx == NULL) return FALSE; ctx->error = error; ctx->msg = msg; return TRUE; } int th_io_error_v(th_ioctx *ctx, const int err, const char *fmt, va_list ap) { char *msg = th_strdup_vprintf(fmt, ap); if (ctx->error != NULL) ctx->error((struct th_ioctx *) ctx, err, msg); else THERR("'%s' #%" PRIu_SIZE_T ": %s\n", ctx->filename, ctx->line, msg); th_free(msg); return err; } int th_io_error(th_ioctx *ctx, const int err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); th_io_error_v(ctx, err, fmt, ap); va_end(ap); return err; } void th_io_msg_v(th_ioctx *ctx, const int level, const char *fmt, va_list ap) { if (ctx->msg != NULL) { char *msg = th_strdup_vprintf(fmt, ap); ctx->msg((struct th_ioctx *) ctx, level, msg); th_free(msg); } else THMSG_V(level, fmt, ap); } void th_io_msg(th_ioctx *ctx, const int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); th_io_msg_v(ctx, level, fmt, ap); va_end(ap); } int thfreset(th_ioctx *ctx) { if (ctx == NULL) return THERR_NULLPTR; if (ctx->fops == NULL || ctx->fops->freset == NULL) return THERR_OK; return ctx->fops->freset(ctx); } int thferror(th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->ferror(ctx); } int thfseek(th_ioctx *ctx, const off_t offset, int whence) { th_io_update_atime(ctx); return ctx->fops->fseek(ctx, offset, whence); } off_t thfsize(th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->fsize(ctx); } off_t thftell(th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->ftell(ctx); } BOOL thfeof(th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->feof(ctx); } int thfgetc(th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->fgetc(ctx); } int thfputc(int v, th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->fputc(v, ctx); } size_t thfread(void *ptr, size_t size, size_t nmemb, th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->fread(ptr, size, nmemb, ctx); } size_t thfwrite(const void *ptr, size_t size, size_t nmemb, th_ioctx *ctx) { th_io_update_atime(ctx); return ctx->fops->fwrite(ptr, size, nmemb, ctx); } char *thfgets(char *str, int size, th_ioctx *ctx) { char *ptr = str, *end = str + size - 1; int c; if (size <= 0) return NULL; while (ptr < end && (c = ctx->fops->fgetc(ctx)) != EOF) { *ptr++ = c; if (c == '\n') break; } *ptr = 0; return (ptr > str) ? str : NULL; } int thfputs(const char *ptr, th_ioctx *ctx) { if (ctx->fops->fputs != NULL) return ctx->fops->fputs(ptr, ctx); const char *p = ptr; int rval = 0; while (*p && (rval = ctx->fops->fputc(*p, ctx)) != EOF) p++; return rval; } int thvfprintf(th_ioctx *ctx, const char *fmt, va_list ap) { if (ctx->fops->vfprintf != NULL) return ctx->fops->vfprintf(ctx, fmt, ap); else { char *msg = th_strdup_printf(fmt, ap); int rval = thfputs(msg, ctx); th_free(msg); return rval; } } int thfprintf(th_ioctx *ctx, const char *fmt, ...) { int rval; va_list ap; va_start(ap, fmt); rval = thvfprintf(ctx, fmt, ap); va_end(ap); return rval; } BOOL thfread_str(th_ioctx *ctx, void *ptr, const size_t len) { return (thfread(ptr, sizeof(uint8_t), len, ctx) == len); } BOOL thfread_u8(th_ioctx *ctx, uint8_t *val) { return (thfread(val, sizeof(uint8_t), 1, ctx) == 1); } BOOL thfwrite_str(th_ioctx *ctx, const void *ptr, const size_t len) { return (thfwrite(ptr, sizeof(uint8_t), len, ctx) == len); } BOOL thfwrite_u8(th_ioctx *ctx, const uint8_t val) { return (thfwrite(&val, sizeof(uint8_t), 1, ctx) == 1); } // // File routines for endian-dependant data // #define TH_DEFINE_FUNC(xname, xtype, xmacro) \ BOOL thfread_ ## xname (th_ioctx *ctx, xtype *v) \ { \ xtype result; \ if (thfread(&result, sizeof( xtype ), 1, ctx) != 1) \ return FALSE; \ *v = TH_ ## xmacro ## _TO_NATIVE (result); \ return TRUE; \ } \ \ BOOL thfwrite_ ## xname (th_ioctx *ctx, const xtype v) \ { \ xtype result = TH_NATIVE_TO_ ## xmacro (v); \ if (thfwrite(&result, sizeof( xtype ), 1, ctx) != 1) \ return FALSE; \ return TRUE; \ } TH_DEFINE_FUNC(le16, uint16_t, LE16) TH_DEFINE_FUNC(le32, uint32_t, LE32) TH_DEFINE_FUNC(be16, uint16_t, BE16) TH_DEFINE_FUNC(be32, uint32_t, BE32) TH_DEFINE_FUNC(le64, uint64_t, LE64) TH_DEFINE_FUNC(be64, uint64_t, BE64) #undef TH_DEFINE_FUNC // // stdio wrappers for I/O contexts // #define CTX_FH ((FILE *) ctx->data) static int th_stdio_fopen(th_ioctx *ctx) { ctx->data = (void *) fopen(ctx->filename, ctx->mode); ctx->status = th_get_error(); return (ctx->data != NULL) ? THERR_OK : THERR_FOPEN; } static void th_stdio_fclose(th_ioctx *ctx) { if (CTX_FH != NULL) { fclose(CTX_FH); ctx->data = NULL; } } static int th_stdio_ferror(th_ioctx *ctx) { return ctx->status; } static off_t th_stdio_ftell(th_ioctx *ctx) { return ftello(CTX_FH); } static int th_stdio_fseek(th_ioctx *ctx, const off_t pos, const int whence) { int ret = fseeko(CTX_FH, pos, whence); ctx->status = th_get_error(); return ret; } static int th_stdio_freset(th_ioctx *ctx) { if (CTX_FH != NULL) return th_stdio_fseek(ctx, 0, SEEK_SET); else return THERR_OK; } static off_t th_stdio_fsize(th_ioctx *ctx) { off_t savePos, fileSize; // Check if the size is cached if (ctx->size != 0) return ctx->size; // Get file size if ((savePos = th_stdio_ftell(ctx)) < 0) return -1; if (th_stdio_fseek(ctx, 0, SEEK_END) != 0) return -1; if ((fileSize = th_stdio_ftell(ctx)) < 0) return -1; if (th_stdio_fseek(ctx, savePos, SEEK_SET) != 0) return -1; ctx->size = fileSize; return fileSize; } static BOOL th_stdio_feof(th_ioctx *ctx) { return feof(CTX_FH); } static int th_stdio_fgetc(th_ioctx *ctx) { int ret = fgetc(CTX_FH); ctx->status = th_get_error(); return ret; } static int th_stdio_fputc(int v, th_ioctx *ctx) { int ret = fputc(v, CTX_FH); ctx->status = th_get_error(); return ret; } static size_t th_stdio_fread(void *ptr, size_t size, size_t nmemb, th_ioctx *ctx) { size_t ret = fread(ptr, size, nmemb, CTX_FH); ctx->status = th_get_error(); return ret; } static size_t th_stdio_fwrite(const void *ptr, size_t size, size_t nmemb, th_ioctx *ctx) { size_t ret = fwrite(ptr, size, nmemb, CTX_FH); ctx->status = th_get_error(); return ret; } static char * th_stdio_fgets(char *str, int size, th_ioctx *ctx) { char *ret = fgets(str, size, CTX_FH); ctx->status = th_get_error(); return ret; } static int th_stdio_fputs(const char *str, th_ioctx *ctx) { int ret = fputs(str, CTX_FH); ctx->status = th_get_error(); return ret; } static int th_stdio_vfprintf(th_ioctx *ctx, const char *fmt, va_list ap) { int ret = vfprintf(CTX_FH, fmt, ap); ctx->status = th_get_error(); return ret; } const th_ioctx_ops th_stdio_io_ops = { "stdio", th_stdio_fopen, th_stdio_fclose, th_stdio_freset, th_stdio_ferror, th_stdio_fseek, th_stdio_fsize, th_stdio_ftell, th_stdio_feof, th_stdio_fgetc, th_stdio_fputc, th_stdio_fread, th_stdio_fwrite, th_stdio_fgets, th_stdio_fputs, th_stdio_vfprintf, }; static BOOL th_mem_realloc(th_ioctx *ctx, const size_t newSize) { size_t grow; if (ctx->maxSize > 0 && newSize > ctx->maxSize) { ctx->status = THERR_BOUNDS; return FALSE; } if (newSize < ctx->memAlloc) return TRUE; grow = (ctx->minAlloc > 0) ? ctx->minAlloc : 8 * 1024; if (newSize - ctx->memAlloc > grow) grow += newSize - ctx->memAlloc; if (ctx->maxSize > 0 && ctx->memAlloc + grow >= ctx->maxSize) { ctx->status = THERR_BOUNDS; return FALSE; } ctx->memAlloc += grow; if ((ctx->memData = th_realloc(ctx->memData, ctx->memAlloc)) == NULL) { ctx->status = THERR_MALLOC; return FALSE; } ctx->memSize = newSize; return TRUE; } static int th_mem_freset(th_ioctx *ctx) { ctx->memOffset = 0; return THERR_OK; } static int th_mem_ferror(th_ioctx *ctx) { return ctx->status; } static int th_mem_fseek(th_ioctx *ctx, const off_t offset, const int whence) { off_t newPos; // Calculate the new position switch (whence) { case SEEK_SET: newPos = offset; break; case SEEK_CUR: newPos = ctx->memOffset + offset; break; case SEEK_END: newPos = ctx->memSize + offset; break; default: return -1; } // Set the new position ctx->memOffset = newPos; // Check the new position if (newPos < 0) return -1; //if (!th_mem_realloc(ctx, newPos)) // return -1; return 0; } static off_t th_mem_fsize(th_ioctx *ctx) { return ctx->memSize; } static off_t th_mem_ftell(th_ioctx *ctx) { return ctx->memOffset; } static BOOL th_mem_feof(th_ioctx *ctx) { return ((size_t) ctx->memOffset) >= ctx->memSize; } static int th_mem_fgetc(th_ioctx *ctx) { // Check for EOF if ((size_t) ctx->memOffset < ctx->memSize) return ctx->memData[ctx->memOffset++]; else return EOF; } static size_t th_mem_fread(void *buf, size_t size, size_t nmemb, th_ioctx *ctx) { size_t length = size * nmemb; // Check if we can read the whole chunk if (((size_t) ctx->memOffset + length) >= ctx->memSize) { nmemb = (ctx->memSize - ctx->memOffset) / size; length = size * nmemb; } memcpy(buf, ctx->memData + ctx->memOffset, length); ctx->memOffset += length; return nmemb; } static int th_mem_fputc(int ch, th_ioctx *ctx) { // Check for EOF if (!th_mem_realloc(ctx, ctx->memOffset + 1)) return EOF; ctx->memData[ctx->memOffset++] = ch; return ch; } static size_t th_mem_fwrite(const void *buf, size_t size, size_t nmemb, th_ioctx *ctx) { size_t length = size * nmemb; // Check if we can write the whole chunk if (!th_mem_realloc(ctx, ctx->memOffset + length)) { nmemb = (ctx->memSize - ctx->memOffset) / size; length = size * nmemb; } if (length > 0) { memcpy(ctx->memData + ctx->memOffset, buf, length); ctx->memOffset += length; } return nmemb; } const th_ioctx_ops th_mem_io_ops = { "MemIO", NULL, NULL, th_mem_freset, th_mem_ferror, th_mem_fseek, th_mem_fsize, th_mem_ftell, th_mem_feof, th_mem_fgetc, th_mem_fputc, th_mem_fread, th_mem_fwrite, NULL, NULL, NULL };