changeset 202:b392293047da

Refactor I/O contexts. Breaks API and all that.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 11 Feb 2016 22:27:56 +0200
parents e6a278f140c4
children 7acdd3ab6900
files th_ioctx.c th_ioctx.h
diffstat 2 files changed, 487 insertions(+), 79 deletions(-) [+]
line wrap: on
line diff
--- a/th_ioctx.c	Thu Feb 11 21:38:52 2016 +0200
+++ b/th_ioctx.c	Thu Feb 11 22:27:56 2016 +0200
@@ -1,74 +1,90 @@
 /*
  * Standard I/O context helpers
  * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ * (C) Copyright 2012,2016 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>
 
-/* Simple STD I/O contexts
- */
-BOOL th_ioctx_init(th_ioctx_t *ctx, const char *filename,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg))
 
+th_ioctx *th_io_new(const th_ioctx_ops *fops)
 {
-    if (ctx == NULL || filename == NULL)
-        return FALSE;
+    th_ioctx *ctx = th_malloc0(sizeof(th_ioctx));
+    if (ctx == NULL)
+        return NULL;
+
+    ctx->fops = fops;
+
+    return ctx;
+}
+
 
-    memset(ctx, 0, sizeof(*ctx));
-    ctx->error = error;
-    ctx->msg   = msg;
-    ctx->line  = 1;
+int th_io_open(th_ioctx *ctx, const char *filename, const char *mode)
+{
+    if (ctx == NULL)
+        return THERR_NULLPTR;
+
+    th_free_r(&ctx->filename);
+    th_free_r(&ctx->mode);
 
-    if ((ctx->filename = th_strdup(filename)) == NULL)
-        return FALSE;
-    
-    return TRUE;
+    ctx->filename = th_strdup(filename);
+    ctx->mode = th_strdup(mode);
+
+    if (ctx->fops->fopen != NULL)
+        ctx->errno = ctx->fops->fopen(ctx);
+
+    return ctx->errno;
 }
 
 
-BOOL th_ioctx_open(th_ioctx_t *ctx, const char *filename, const char *mode,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg))
+void th_io_close(th_ioctx *ctx)
+{
+    if (ctx != NULL)
+    {
+        if (ctx->fops->fclose != NULL)
+            ctx->fops->fclose(ctx);
+    }
+}
+
+
+void th_io_free(th_ioctx *ctx)
 {
-    if (!th_ioctx_init(ctx, filename, error, msg) || mode == NULL)
+    if (ctx != NULL)
+    {
+        th_io_close(ctx);
+
+        th_free_r(&ctx->filename);
+        th_free_r(&ctx->mode);
+
+        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;
 
-    if ((ctx->fp = fopen(filename, mode)) == NULL)
-        return FALSE;
+    ctx->error = error;
+    ctx->msg = msg;
 
     return TRUE;
 }
 
 
-void th_ioctx_close(th_ioctx_t *ctx)
+void th_io_error_v(th_ioctx *ctx, const int err, const char *fmt, va_list ap)
 {
-    if (ctx != NULL)
-    {
-        th_free(ctx->filename);
-        ctx->filename = NULL;
-
-        if (ctx->fp != NULL)
-            fclose(ctx->fp);
-        ctx->fp = NULL;
-    }
-}
-
-
-void th_ioctx_error(th_ioctx_t *ctx, const int err, const char *fmt, ...)
-{
-    char *msg;
-    va_list ap;
-
-    va_start(ap, fmt);
-    msg = th_strdup_vprintf(fmt, ap);
-    va_end(ap);
+    char *msg = th_strdup_vprintf(fmt, ap);
 
     if (ctx->error != NULL)
-        ctx->error(ctx, err, msg);
+        ctx->error((struct th_ioctx *) ctx, err, msg);
     else
         THERR("'%s' #%" TH_PRIu_SIZE_T ": %s\n", ctx->filename, ctx->line, msg);
 
@@ -76,24 +92,334 @@
 }
 
 
-th_ioctx_t *th_ioctx_new(const char *filename,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg))
+void 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);
+}
+
+
+void th_io_msg_v(th_ioctx *ctx, const int level, const char *fmt, va_list ap)
 {
-    th_ioctx_t *ctx = th_malloc0(sizeof(th_ioctx_t));
+    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)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->ferror(ctx);
+}
+
+
+int thfseek(th_ioctx *ctx, const off_t offset, int whence)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fseek(ctx, offset, whence);
+}
+
+
+off_t thfsize(th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fsize(ctx);
+}
+
+
+off_t thftell(th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->ftell(ctx);
+}
+
+
+BOOL thfeof(th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->feof(ctx);
+}
+
+
+int thfgetc(th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fgetc(ctx);
+}
+
+
+int thfputc(int v, th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fputc(v, ctx);
+}
+
+
+size_t thfread(void *ptr, size_t size, size_t nmemb, th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fread(ptr, size, nmemb, ctx);
+}
+
+
+size_t thfwrite(const void *ptr, size_t size, size_t nmemb, th_ioctx *ctx)
+{
+    ctx->atime = time(NULL);
+    return ctx->fops->fwrite(ptr, size, nmemb, ctx);
+}
+
+
+char *thfgets(char *str, int size, th_ioctx *ctx)
+{
+    char *ptr = str, *end = str + size - 1, c;
+
+    if (size <= 0)
         return NULL;
-    
-    th_ioctx_init(ctx, filename, error, msg);
-    ctx->allocated = TRUE;
-    return ctx;
+
+    while (ptr < end && (c = ctx->fops->fgetc(ctx)) != EOF)
+    {
+        *ptr++ = c;
+        if (c == '\n')
+            break;
+    }
+    *ptr++ = 0;
+
+    return (ptr > str) ? str : NULL;
+}
+
+
+BOOL thfread_str(th_ioctx *ctx, void *ptr, const size_t len)
+{
+    return (thfread(ptr, sizeof(uint8_t), len, ctx) == len);
+}
+
+
+BOOL thfread_byte(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_byte(th_ioctx *ctx, const uint8_t val)
+{
+    return (thfwrite(&val, sizeof(uint8_t), 1, ctx) == 1);
 }
 
 
-void th_ioctx_free(th_ioctx_t *ctx)
+//
+// 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)
+
+#ifdef TH_HAVE_64BIT
+TH_DEFINE_FUNC(le64, uint64_t, LE64)
+TH_DEFINE_FUNC(be64, uint64_t, BE64)
+#endif
+
+#undef TH_DEFINE_FUNC
+
+
+//
+// stdio wrappers for I/O contexts
+//
+#define CTX_FH ((FILE *) ctx->data)
+
+
+static int th_stdio_fopen(th_ioctx *ctx)
 {
-    th_ioctx_close(ctx);
+    ctx->data = (void *) fopen(ctx->filename, ctx->mode);
+    ctx->errno = th_get_error();
+    return (CTX_FH != 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->errno;
+}
+
+
+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->errno = 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;
+}
+
 
-    if (ctx->allocated)
-        th_free(ctx);
+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->errno = th_get_error();
+    return ret;
+}
+
+
+static int th_stdio_fputc(int v, th_ioctx *ctx)
+{
+    int ret = fputc(v, CTX_FH);
+    ctx->errno = 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->errno = 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->errno = th_get_error();
+    return ret;
+}
+
+
+static int th_stdio_vfprintf(th_ioctx *ctx, const char *format, va_list ap)
+{
+    int ret = vfprintf(CTX_FH, format, ap);
+    ctx->errno = 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_vfprintf,
+};
+
--- a/th_ioctx.h	Thu Feb 11 21:38:52 2016 +0200
+++ b/th_ioctx.h	Thu Feb 11 22:27:56 2016 +0200
@@ -1,7 +1,7 @@
 /*
- * Standard I/O context helpers
+ * Simple I/O abstraction and context handling layer
  * Programmed and designed by Matti 'ccr' Hamalainen
- * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ * (C) Copyright 2012,2016 Tecnic Software productions (TNSP)
  *
  * Please read file 'COPYING' for information on license and distribution.
  */
@@ -9,7 +9,7 @@
 #define TH_IOCTX_H
 
 #include "th_util.h"
-#include <stdio.h>
+#include <time.h>
 
 
 #ifdef __cplusplus
@@ -17,35 +17,117 @@
 #endif
 
 
-/* Simple STD I/O contexts
- */
-typedef struct _th_ioctx_t
+// Typedefs and structures
+//
+struct th_ioctx;
+struct th_ioctx_ops;
+
+
+typedef struct th_ioctx
 {
-    BOOL allocated;
     char *filename;
-    FILE *fp;
+    char *mode;
+    void *data;
+    time_t atime;
+    int64_t size;
+    int errno;
     size_t line;
 
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg);
-    void (*msg)(struct _th_ioctx_t *, const char *msg);
-} th_ioctx_t;
+    void (*error)(struct th_ioctx *, const int err, const char *msg);
+    void (*msg)(struct th_ioctx *, const int level, const char *msg);
+
+    const struct th_ioctx_ops *fops;
+} th_ioctx;
+
+
+typedef struct th_ioctx_ops
+{
+    char    *name;
+
+    int     (*fopen)(th_ioctx *ctx);
+    void    (*fclose)(th_ioctx *ctx);
+
+    int     (*freset)(th_ioctx *ctx);
+    int     (*ferror)(th_ioctx *ctx);
+    int     (*fseek)(th_ioctx *ctx, const off_t, const int whence);
+    off_t   (*fsize)(th_ioctx *ctx);
+    off_t   (*ftell)(th_ioctx *ctx);
+    BOOL    (*feof)(th_ioctx *ctx);
+    int     (*fgetc)(th_ioctx *ctx);
+    int     (*fputc)(int, th_ioctx *ctx);
+    size_t  (*fread)(void *ptr, const size_t, const size_t, th_ioctx *ctx);
+    size_t  (*fwrite)(const void *ptr, const size_t, const size_t, th_ioctx *ctx);
+    int     (*vfprintf)(th_ioctx *ctx, const char *format, va_list ap);
+
+} th_ioctx_ops;
+
+
+//
+// Some basic iops
+//
+extern const th_ioctx_ops th_stdio_io_ops;
+
 
 
-BOOL th_ioctx_init(th_ioctx_t *ctx, const char *filename,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg));
+//
+// I/O context management functions
+//
+th_ioctx *   th_io_new(const th_ioctx_ops *fops);
+int          th_io_open(th_ioctx *ctx, const char *filename, const char *mode);
+void         th_io_close(th_ioctx *ctx);
+void         th_io_free(th_ioctx *ctx);
 
-BOOL th_ioctx_open(th_ioctx_t *ctx, const char *filename, const char *mode,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg));
+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));
+
+void         th_io_error_v(th_ioctx *ctx, const int err, const char *fmt, va_list ap);
+void         th_io_msg_v(th_ioctx *ctx, const int level, const char *fmt, va_list ap);
+void         th_io_error(th_ioctx *ctx, const int err, const char *fmt, ...);
+void         th_io_msg(th_ioctx *ctx, const int level, const char *fmt, ...);
+
 
-void th_ioctx_close(th_ioctx_t *ctx);
-void th_ioctx_error(th_ioctx_t *ctx, const int err, const char *fmt, ...);
+//
+// Basic I/O operations
+//
+int          thfreset(th_ioctx *ctx);
+int          thferror(th_ioctx *ctx);
+int          thfseek(th_ioctx *ctx, const off_t, const int whence);
+off_t        thfsize(th_ioctx *ctx);
+off_t        thftell(th_ioctx *ctx);
+BOOL         thfeof(th_ioctx *ctx);
+int          thfgetc(th_ioctx *ctx);
+int          thfputc(int ch, th_ioctx *ctx);
+size_t       thfread(void *ptr, const size_t, const size_t, th_ioctx *ctx);
+size_t       thfwrite(const void *, const size_t, const size_t, th_ioctx *ctx);
+char *       thfgets(char *ptr, int size, th_ioctx *ctx);
+
+int          thvfprintf(th_ioctx *ctx, const char *fmt, va_list ap);
+int          thfprintf(th_ioctx *ctx, const char *fmt, ...);
 
-th_ioctx_t * th_ioctx_new(const char *filename,
-    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
-    void (*msg)(struct _th_ioctx_t *, const char *msg));
-void th_ioctx_free(th_ioctx_t *ctx);
+int          thfread_str(th_ioctx *ctx, void *ptr, const size_t len);
+BOOL         thfread_byte(th_ioctx *ctx, uint8_t *);
+int          thfwrite_str(th_ioctx *ctx, const void *ptr, const size_t len);
+BOOL         thfwrite_byte(th_ioctx *ctx, const uint8_t);
+
+
+//
+// Endian-handling file read/write routines
+//
+#define TH_DEFINE_HEADER(xname, xtype)                       \
+BOOL    thfread_ ## xname (th_ioctx *ctx, xtype *v);        \
+BOOL    thfwrite_ ## xname (th_ioctx *ctx, const xtype v);
+
+TH_DEFINE_HEADER(le16, uint16_t)
+TH_DEFINE_HEADER(le32, uint32_t)
+
+TH_DEFINE_HEADER(be16, uint16_t)
+TH_DEFINE_HEADER(be32, uint32_t)
+
+TH_DEFINE_HEADER(be64, uint64_t)
+TH_DEFINE_HEADER(le64, uint64_t)
+
+#undef TH_DEFINE_HEADER
 
 
 #ifdef __cplusplus