diff --git a/include/picrin/xfile.h b/include/picrin/xfile.h new file mode 100644 index 00000000..86fcb458 --- /dev/null +++ b/include/picrin/xfile.h @@ -0,0 +1,78 @@ +#ifndef XFILE_H__ +#define XFILE_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include + +typedef struct { + int ungot; + int flags; + /* operators */ + struct { + void *cookie; + int (*read)(void *, char *, int); + int (*write)(void *, const char *, int); + long (*seek)(void *, long, int); + int (*flush)(void *); + int (*close)(void *); + } vtable; +} xFILE; + +/* generic file constructor */ +xFILE *xfunopen(void *cookie, int (*read)(void *, char *, int), int (*write)(void *, const char *, int), long (*seek)(void *, long, int), int (*flush)(void *), int (*close)(void *)); + +/* resource aquisition */ +xFILE *xfopen(const char *, const char *); +xFILE *xfpopen(FILE *); +xFILE *xmopen(); +int xfclose(xFILE *); + +/* buffer management */ +int xfflush(xFILE *); + +/* direct IO with buffering */ +size_t xfread(void *, size_t, size_t, xFILE *); +size_t xfwrite(const void *, size_t, size_t, xFILE *); + +/* indicator positioning */ +long xfseek(xFILE *, long offset, int whence); +long xftell(xFILE *); +void xrewind(xFILE *); + +/* stream status */ +void xclearerr(xFILE *); +int xfeof(xFILE *); +int xferror(xFILE *); + +/* character IO */ +int xfgetc(xFILE *); +char *xfgets(char *, int, xFILE *); +int xfputc(int, xFILE *); +int xfputs(const char *, xFILE *); +char xgetc(xFILE *); +int xgetchar(void); +int xputc(int, xFILE *); +int xputchar(int); +int xputs(char *); +int xungetc(int, xFILE *); + +/* formatted I/O */ +int xprintf(const char *, ...); +int xfprintf(xFILE *, const char *, ...); +int xvfprintf(xFILE *, const char *, va_list); + +/* standard I/O */ +extern xFILE *xstdin; +extern xFILE *xstdout; +extern xFILE *xstderr; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/picrin/xhash.h b/include/picrin/xhash.h new file mode 100644 index 00000000..a249e2ff --- /dev/null +++ b/include/picrin/xhash.h @@ -0,0 +1,407 @@ +#ifndef XHASH_H__ +#define XHASH_H__ + +/* + * Copyright (c) 2013-2014 by Yuichi Nishiwaki + */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include +#include +#include + +/* simple object to object hash table */ + +#define XHASH_INIT_SIZE 11 +#define XHASH_RESIZE_RATIO 0.75 + +#define XHASH_ALIGNMENT 3 /* quad word alignment */ +#define XHASH_MASK (~((1 << XHASH_ALIGNMENT) - 1)) +#define XHASH_ALIGN(i) ((((i) - 1) & XHASH_MASK) + (1 << XHASH_ALIGNMENT)) + +typedef struct xh_entry { + struct xh_entry *next; + int hash; + const char *key; /* == val + XHASH_ALIGN(vwidth) */ + char val[]; +} xh_entry; + +#define xh_key(e,type) (*(type *)((e)->key)) +#define xh_val(e,type) (*(type *)((e)->val)) + +typedef int (*xh_hashf)(const void *, void *); +typedef int (*xh_equalf)(const void *, const void *, void *); + +typedef struct xhash { + xh_entry **buckets; + size_t size, count, kwidth, vwidth; + xh_hashf hashf; + xh_equalf equalf; + void *data; +} xhash; + +static inline void xh_init_(xhash *x, size_t, size_t, xh_hashf, xh_equalf, void *); +static inline xh_entry *xh_get_(xhash *x, const void *key); +static inline xh_entry *xh_put_(xhash *x, const void *key, void *val); +static inline void xh_del_(xhash *x, const void *key); +static inline void xh_clear(xhash *x); +static inline void xh_destroy(xhash *x); + +/* string map */ +static inline void xh_init_str(xhash *x, size_t width); +static inline xh_entry *xh_get_str(xhash *x, const char *key); +static inline xh_entry *xh_put_str(xhash *x, const char *key, void *); +static inline void xh_del_str(xhash *x, const char *key); + +/* object map */ +static inline void xh_init_ptr(xhash *x, size_t width); +static inline xh_entry *xh_get_ptr(xhash *x, const void *key); +static inline xh_entry *xh_put_ptr(xhash *x, const void *key, void *); +static inline void xh_del_ptr(xhash *x, const void *key); + +/* int map */ +static inline void xh_init_int(xhash *x, size_t width); +static inline xh_entry *xh_get_int(xhash *x, int key); +static inline xh_entry *xh_put_int(xhash *x, int key, void *); +static inline void xh_del_int(xhash *x, int key); + +typedef struct xh_iter { + xhash *x; + xh_entry *e, *next; + size_t bidx; +} xh_iter; + +static inline void xh_begin(xh_iter *it, xhash *x); +static inline int xh_next(xh_iter *it); + + +static inline void +xh_bucket_realloc(xhash *x, size_t newsize) +{ + x->size = newsize; + x->buckets = realloc(x->buckets, (x->size + 1) * sizeof(xh_entry *)); + memset(x->buckets, 0, (x->size + 1) * sizeof(xh_entry *)); +} + +static inline void +xh_init_(xhash *x, size_t kwidth, size_t vwidth, xh_hashf hashf, xh_equalf equalf, void *data) +{ + x->size = 0; + x->buckets = NULL; + x->count = 0; + x->kwidth = kwidth; + x->vwidth = vwidth; + x->hashf = hashf; + x->equalf = equalf; + x->data = data; + + xh_bucket_realloc(x, XHASH_INIT_SIZE); +} + +static inline xh_entry * +xh_get_(xhash *x, const void *key) +{ + int hash; + size_t idx; + xh_entry *e; + + hash = x->hashf(key, x->data); + idx = ((unsigned)hash) % x->size; + for (e = x->buckets[idx]; e; e = e->next) { + if (e->hash == hash && x->equalf(key, e->key, x->data)) + break; + } + return e; +} + +static inline void +xh_resize_(xhash *x, size_t newsize) +{ + xhash y; + xh_iter it; + size_t idx; + + xh_init_(&y, x->kwidth, x->vwidth, x->hashf, x->equalf, x->data); + xh_bucket_realloc(&y, newsize); + + xh_begin(&it, x); + while (xh_next(&it)) { + idx = ((unsigned)it.e->hash) % y.size; + /* reuse entry object */ + it.e->next = y.buckets[idx]; + y.buckets[idx] = it.e; + y.count++; + } + + free(x->buckets); + + /* copy all members from y to x */ + memcpy(x, &y, sizeof(xhash)); +} + +static inline xh_entry * +xh_put_(xhash *x, const void *key, void *val) +{ + int hash; + size_t idx; + xh_entry *e; + + if ((e = xh_get_(x, key))) { + memcpy(e->val, val, x->vwidth); + return e; + } + + if (x->count + 1 > x->size * XHASH_RESIZE_RATIO) { + xh_resize_(x, x->size * 2 + 1); + } + + hash = x->hashf(key, x->data); + idx = ((unsigned)hash) % x->size; + e = (xh_entry *)malloc(offsetof(xh_entry, val) + XHASH_ALIGN(x->vwidth) + x->kwidth); + e->next = x->buckets[idx]; + e->hash = hash; + e->key = e->val + XHASH_ALIGN(x->vwidth); + memcpy((void *)e->key, key, x->kwidth); + memcpy(e->val, val, x->vwidth); + + x->count++; + + return x->buckets[idx] = e; +} + +static inline void +xh_del_(xhash *x, const void *key) +{ + int hash; + size_t idx; + xh_entry *e, *d; + + hash = x->hashf(key, x->data); + idx = ((unsigned)hash) % x->size; + if (x->buckets[idx]->hash == hash && x->equalf(key, x->buckets[idx]->key, x->data)) { + e = x->buckets[idx]->next; + free(x->buckets[idx]); + x->buckets[idx] = e; + } + else { + for (e = x->buckets[idx]; ; e = e->next) { + if (e->next->hash == hash && x->equalf(key, e->next->key, x->data)) + break; + } + d = e->next->next; + free(e->next); + e->next = d; + } + + x->count--; +} + +static inline void +xh_clear(xhash *x) +{ + size_t i; + xh_entry *e, *d; + + for (i = 0; i < x->size; ++i) { + e = x->buckets[i]; + while (e) { + d = e->next; + free(e); + e = d; + } + x->buckets[i] = NULL; + } + + x->count = 0; +} + +static inline void +xh_destroy(xhash *x) +{ + xh_clear(x); + free(x->buckets); +} + +/* string map */ + +static inline int +xh_str_hash(const void *key, void *data) +{ + const char *str = *(const char **)key; + int hash = 0; + + (void)data; + + while (*str) { + hash = hash * 31 + *str++; + } + return hash; +} + +static inline int +xh_str_equal(const void *key1, const void *key2, void *data) +{ + (void)data; + + return strcmp(*(const char **)key1, *(const char **)key2) == 0; +} + +static inline void +xh_init_str(xhash *x, size_t width) +{ + xh_init_(x, sizeof(const char *), width, xh_str_hash, xh_str_equal, NULL); +} + +static inline xh_entry * +xh_get_str(xhash *x, const char *key) +{ + return xh_get_(x, &key); +} + +static inline xh_entry * +xh_put_str(xhash *x, const char *key, void *val) +{ + return xh_put_(x, &key, val); +} + +static inline void +xh_del_str(xhash *x, const char *key) +{ + xh_del_(x, &key); +} + +/* object map */ + +static inline int +xh_ptr_hash(const void *key, void *data) +{ + (void)data; + + return (size_t)*(const void **)key; +} + +static inline int +xh_ptr_equal(const void *key1, const void *key2, void *data) +{ + (void) data; + + return *(const void **)key1 == *(const void **)key2; +} + +static inline void +xh_init_ptr(xhash *x, size_t width) +{ + xh_init_(x, sizeof(const void *), width, xh_ptr_hash, xh_ptr_equal, NULL); +} + +static inline xh_entry * +xh_get_ptr(xhash *x, const void *key) +{ + return xh_get_(x, &key); +} + +static inline xh_entry * +xh_put_ptr(xhash *x, const void *key, void *val) +{ + return xh_put_(x, &key, val); +} + +static inline void +xh_del_ptr(xhash *x, const void *key) +{ + xh_del_(x, &key); +} + +/* int map */ + +static inline int +xh_int_hash(const void *key, void *data) +{ + (void)data; + + return *(int *)key; +} + +static inline int +xh_int_equal(const void *key1, const void *key2, void *data) +{ + (void)data; + + return *(int *)key1 == *(int *)key2; +} + +static inline void +xh_init_int(xhash *x, size_t width) +{ + xh_init_(x, sizeof(int), width, xh_int_hash, xh_int_equal, NULL); +} + +static inline xh_entry * +xh_get_int(xhash *x, int key) +{ + return xh_get_(x, &key); +} + +static inline xh_entry * +xh_put_int(xhash *x, int key, void *val) +{ + return xh_put_(x, &key, val); +} + +static inline void +xh_del_int(xhash *x, int key) +{ + xh_del_(x, &key); +} + +/** iteration */ + +static inline void +xh_begin(xh_iter *it, xhash *x) +{ + size_t bidx; + + it->x = x; + + for (bidx = 0; bidx < x->size; ++bidx) { + if (x->buckets[bidx]) + break; + } + it->e = NULL; + it->next = x->buckets[bidx]; + it->bidx = bidx; +} + +static inline int +xh_next(xh_iter *it) +{ + size_t bidx; + + if (! it->next) { + return 0; + } + + it->e = it->next; + if (it->next->next) { + it->next = it->next->next; + return 1; + } + for (bidx = it->bidx + 1; bidx < it->x->size; ++bidx) { + if (it->x->buckets[bidx]) + break; + } + it->next = it->x->buckets[bidx]; + it->bidx = bidx; + return 1; +} + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/picrin/xrope.h b/include/picrin/xrope.h new file mode 100644 index 00000000..89842de0 --- /dev/null +++ b/include/picrin/xrope.h @@ -0,0 +1,329 @@ +#ifndef XROPE_H__ +#define XROPE_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include +#include + +/* public APIs */ + +typedef struct xrope xrope; + +/** + * | name | frees buffer? | end with NULL? | complexity | misc + * | ---- | ---- | ---- | ---- | --- + * | xr_new_cstr | no | yes | O(1) | xr_new(_lit) + * | xr_new_imbed | no | no | O(1) | + * | xr_new_move | yes | yes | O(1) | + * | xr_new_copy | yes | no | O(n) | + */ + +#define xr_new(cstr) xr_new_cstr(cstr, strlen(cstr)) +#define xr_new_lit(cstr) xr_new_cstr(cstr, sizeof(cstr) - 1) +static inline xrope *xr_new_cstr(const char *, size_t); +static inline xrope *xr_new_imbed(const char *, size_t); +static inline xrope *xr_new_move(const char *, size_t); +static inline xrope *xr_new_copy(const char *, size_t); + +static inline void XROPE_INCREF(xrope *); +static inline void XROPE_DECREF(xrope *); + +static inline size_t xr_len(xrope *); +static inline char xr_at(xrope *, size_t); +static inline xrope *xr_cat(xrope *, xrope *); +static inline xrope *xr_sub(xrope *, size_t, size_t); +static inline const char *xr_cstr(xrope *); /* returns NULL-terminated string */ + + +/* impl */ + +typedef struct { + char *str; + int refcnt; + size_t len; + char autofree, zeroterm; +} xr_chunk; + +#define XR_CHUNK_INCREF(c) do { \ + (c)->refcnt++; \ + } while (0) + +#define XR_CHUNK_DECREF(c) do { \ + xr_chunk *c__ = (c); \ + if (! --c__->refcnt) { \ + if (c__->autofree) \ + free(c__->str); \ + free(c__); \ + } \ + } while (0) + +struct xrope { + int refcnt; + size_t weight; + xr_chunk *chunk; + size_t offset; + struct xrope *left, *right; +}; + +static inline void +XROPE_INCREF(xrope *x) { + x->refcnt++; +} + +static inline void +XROPE_DECREF(xrope *x) { + if (! --x->refcnt) { + if (x->chunk) { + XR_CHUNK_DECREF(x->chunk); + free(x); + return; + } + XROPE_DECREF(x->left); + XROPE_DECREF(x->right); + free(x); + } +} + +static inline xrope * +xr_new_cstr(const char *cstr, size_t len) +{ + xr_chunk *c; + xrope *x; + + c = (xr_chunk *)malloc(sizeof(xr_chunk)); + c->refcnt = 1; + c->str = (char *)cstr; + c->len = len; + c->autofree = 0; + c->zeroterm = 1; + + x = (xrope *)malloc(sizeof(xrope)); + x->refcnt = 1; + x->left = NULL; + x->right = NULL; + x->weight = c->len; + x->offset = 0; + x->chunk = c; + + return x; +} + +static inline xrope * +xr_new_imbed(const char *str, size_t len) +{ + xr_chunk *c; + xrope *x; + + c = (xr_chunk *)malloc(sizeof(xr_chunk)); + c->refcnt = 1; + c->str = (char *)str; + c->len = len; + c->autofree = 0; + c->zeroterm = 0; + + x = (xrope *)malloc(sizeof(xrope)); + x->refcnt = 1; + x->left = NULL; + x->right = NULL; + x->weight = c->len; + x->offset = 0; + x->chunk = c; + + return x; +} + +static inline xrope * +xr_new_move(const char *cstr, size_t len) +{ + xr_chunk *c; + xrope *x; + + c = (xr_chunk *)malloc(sizeof(xr_chunk)); + c->refcnt = 1; + c->str = (char *)cstr; + c->len = len; + c->autofree = 1; + c->zeroterm = 1; + + x = (xrope *)malloc(sizeof(xrope)); + x->refcnt = 1; + x->left = NULL; + x->right = NULL; + x->weight = c->len; + x->offset = 0; + x->chunk = c; + + return x; +} + +static inline xrope * +xr_new_copy(const char *str, size_t len) +{ + char *buf; + xr_chunk *c; + xrope *x; + + buf = (char *)malloc(len + 1); + buf[len] = '\0'; + memcpy(buf, str, len); + + c = (xr_chunk *)malloc(sizeof(xr_chunk)); + c->refcnt = 1; + c->str = buf; + c->len = len; + c->autofree = 1; + c->zeroterm = 1; + + x = (xrope *)malloc(sizeof(xrope)); + x->refcnt = 1; + x->left = NULL; + x->right = NULL; + x->weight = c->len; + x->offset = 0; + x->chunk = c; + + return x; +} + +static inline size_t +xr_len(xrope *x) +{ + return x->weight; +} + +static inline char +xr_at(xrope *x, size_t i) +{ + if (x->weight <= i) { + return -1; + } + if (x->chunk) { + return x->chunk->str[x->offset + i]; + } + return (i < x->left->weight) + ? xr_at(x->left, i) + : xr_at(x->right, i - x->left->weight); +} + +static inline xrope * +xr_cat(xrope *x, xrope *y) +{ + xrope *z; + + z = (xrope *)malloc(sizeof(xrope)); + z->refcnt = 1; + z->left = x; + z->right = y; + z->weight = x->weight + y->weight; + z->offset = 0; + z->chunk = NULL; + + XROPE_INCREF(x); + XROPE_INCREF(y); + + return z; +} + +static inline struct xrope * +xr_sub(xrope *x, size_t i, size_t j) +{ + assert(i <= j); + assert(j <= x->weight); + + if (i == 0 && x->weight == j) { + XROPE_INCREF(x); + return x; + } + + if (x->chunk) { + xrope *y; + + y = (xrope *)malloc(sizeof(xrope)); + y->refcnt = 1; + y->left = NULL; + y->right = NULL; + y->weight = j - i; + y->offset = x->offset + i; + y->chunk = x->chunk; + + XR_CHUNK_INCREF(x->chunk); + + return y; + } + + if (j <= x->left->weight) { + return xr_sub(x->left, i, j); + } + else if (x->left->weight <= i) { + return xr_sub(x->right, i - x->left->weight, j - x->left->weight); + } + else { + xrope *r, *l; + + l = xr_sub(x->left, i, x->left->weight); + r = xr_sub(x->right, 0, j - x->left->weight); + x = xr_cat(l, r); + + XROPE_DECREF(l); + XROPE_DECREF(r); + + return x; + } +} + +static inline void +xr_fold(xrope *x, xr_chunk *c, size_t offset) +{ + if (x->chunk) { + memcpy(c->str + offset, x->chunk->str + x->offset, x->weight); + XR_CHUNK_DECREF(x->chunk); + + x->chunk = c; + x->offset = offset; + XR_CHUNK_INCREF(c); + return; + } + xr_fold(x->left, c, offset); + xr_fold(x->right, c, offset + x->left->weight); + + XROPE_DECREF(x->left); + XROPE_DECREF(x->right); + x->left = x->right = NULL; + x->chunk = c; + x->offset = offset; + XR_CHUNK_INCREF(c); +} + +static inline const char * +xr_cstr(xrope *x) +{ + xr_chunk *c; + + if (x->chunk && x->offset == 0 && x->weight == x->chunk->len && x->chunk->zeroterm) { + return x->chunk->str; /* reuse cached chunk */ + } + + c = (xr_chunk *)malloc(sizeof(xr_chunk)); + c->refcnt = 1; + c->len = x->weight; + c->autofree = 1; + c->zeroterm = 1; + c->str = (char *)malloc(c->len + 1); + c->str[c->len] = '\0'; + + xr_fold(x, c, 0); + + XR_CHUNK_DECREF(c); + return c->str; +} + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/include/picrin/xvect.h b/include/picrin/xvect.h new file mode 100644 index 00000000..bd72070e --- /dev/null +++ b/include/picrin/xvect.h @@ -0,0 +1,185 @@ +#ifndef XVECT_H__ +#define XVECT_H__ + +/* + * Copyright (c) 2014 by Yuichi Nishiwaki + */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include + +typedef struct xvect { + char *data; + size_t size, mask, head, tail, width; +} xvect; + +static inline void xv_init(xvect *, size_t); +static inline void xv_destroy(xvect *); + +static inline size_t xv_size(xvect *); + +static inline void xv_reserve(xvect *, size_t); +static inline void xv_shrink(xvect *, size_t); + +static inline void *xv_get(xvect *, size_t); +static inline void xv_set(xvect *, size_t, void *); + +static inline void xv_push(xvect *, void *); +static inline void *xv_pop(xvect *); + +static inline void *xv_shift(xvect *); +static inline void xv_unshift(xvect *, void *); + +static inline void xv_splice(xvect *, size_t, ptrdiff_t); +static inline void xv_insert(xvect *, size_t, void *); + +static inline void +xv_init(xvect *x, size_t width) +{ + x->data = NULL; + x->width = width; + x->size = 0; + x->mask = -1; + x->head = 0; + x->tail = 0; +} + +static inline void +xv_destroy(xvect *x) +{ + free(x->data); +} + +static inline size_t +xv_size(xvect *x) +{ + return x->tail < x->head + ? x->tail + x->size - x->head + : x->tail - x->head; +} + +static inline size_t +xv_round2(size_t x) +{ + x -= 1; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + x |= (x >> 32); + x++; + return x; +} + +static inline void +xv_rotate(xvect *x) +{ + if (x->tail < x->head) { + char buf[x->size * x->width]; + + /* perform rotation */ + memcpy(buf, x->data, sizeof buf); + memcpy(x->data, buf + x->head * x->width, (x->size - x->head) * x->width); + memcpy(x->data + (x->size - x->head) * x->width, buf, x->tail * x->width); + x->tail = x->size - x->head + x->tail; + x->head = 0; + } +} + +static inline void +xv_adjust(xvect *x, size_t size) +{ + size = xv_round2(size); + if (size != x->size) { + xv_rotate(x); + x->data = realloc(x->data, size * x->width); + x->size = size; + x->mask = size - 1; + } +} + +static inline void +xv_reserve(xvect *x, size_t mincapa) +{ + if (x->size < mincapa + 1) { + xv_adjust(x, mincapa + 1); /* capa == size - 1 */ + } +} + +static inline void +xv_shrink(xvect *x, size_t maxcapa) +{ + if (x->size > maxcapa + 1) { + xv_adjust(x, maxcapa + 1); /* capa == size - 1 */ + } +} + +static inline void * +xv_get(xvect *x, size_t i) +{ + return x->data + ((x->head + x->size + i) & x->mask) * x->width; +} + +static inline void +xv_set(xvect *x, size_t i, void *src) +{ + memcpy(xv_get(x, i), src, x->width); +} + +static inline void +xv_push(xvect *x, void *src) +{ + xv_reserve(x, xv_size(x) + 1); + xv_set(x, xv_size(x), src); + x->tail = (x->tail + 1) & x->mask; +} + +static inline void * +xv_pop(xvect *x) +{ + x->tail = (x->tail + x->size - 1) & x->mask; + return xv_get(x, xv_size(x)); +} + +static inline void * +xv_shift(xvect *x) +{ + x->head = (x->head + 1) & x->mask; + return xv_get(x, -1); +} + +static inline void +xv_unshift(xvect *x, void *src) +{ + xv_reserve(x, xv_size(x) + 1); + xv_set(x, -1, src); + x->head = (x->head + x->size - 1) & x->mask; +} + +static inline void +xv_splice(xvect *x, size_t i, ptrdiff_t c) +{ + xv_reserve(x, xv_size(x) - c); + xv_rotate(x); + memmove(xv_get(x, i), xv_get(x, i + c), (xv_size(x) - i - c) * x->width); + x->tail -= c; +} + +static inline void +xv_insert(xvect *x, size_t i, void *src) +{ + xv_splice(x, i, -1); + xv_set(x, i, src); +} + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/xfile.c b/xfile.c new file mode 100644 index 00000000..7b2942a2 --- /dev/null +++ b/xfile.c @@ -0,0 +1,445 @@ +#include "xfile.h" + +#include +#include +#include + +#define min(a,b) (((a)>(b))?(b):(a)) +#define max(a,b) (((a)<(b))?(b):(a)) + +#define XF_EOF 1 +#define XF_ERR 2 + +xFILE * +xfunopen(void *cookie, int (*read)(void *, char *, int), int (*write)(void *, const char *, int), long (*seek)(void *, long, int), int (*flush)(void *), int (*close)(void *)) +{ + xFILE *file; + + file = (xFILE *)malloc(sizeof(xFILE)); + if (! file) { + return NULL; + } + file->ungot = -1; + file->flags = 0; + /* set vtable */ + file->vtable.cookie = cookie; + file->vtable.read = read; + file->vtable.write = write; + file->vtable.seek = seek; + file->vtable.flush = flush; + file->vtable.close = close; + + return file; +} + +xFILE * +xfopen(const char *filename, const char *mode) +{ + FILE *fp; + xFILE *file; + + fp = fopen(filename, mode); + if (! fp) { + return NULL; + } + + file = xfpopen(fp); + if (! file) { + return NULL; + } + + return file; +} + +int +xfclose(xFILE *file) +{ + int r; + + r = file->vtable.close(file->vtable.cookie); + if (r == EOF) { + return -1; + } + + free(file); + return 0; +} + +int +xfflush(xFILE *file) +{ + return file->vtable.flush(file->vtable.cookie); +} + +size_t +xfread(void *ptr, size_t block, size_t nitems, xFILE *file) +{ + char *dst = (char *)ptr; + char buf[block]; + size_t i, offset; + int n; + + for (i = 0; i < nitems; ++i) { + offset = 0; + if (file->ungot != -1 && block > 0) { + buf[0] = file->ungot; + offset += 1; + file->ungot = -1; + } + while (offset < block) { + n = file->vtable.read(file->vtable.cookie, buf + offset, block - offset); + if (n < 0) { + file->flags |= XF_ERR; + goto exit; + } + if (n == 0) { + file->flags |= XF_EOF; + goto exit; + } + offset += n; + } + memcpy(dst, buf, block); + dst += block; + } + + exit: + return i; +} + +size_t +xfwrite(const void *ptr, size_t block, size_t nitems, xFILE *file) +{ + char *dst = (char *)ptr; + size_t i, offset; + int n; + + for (i = 0; i < nitems; ++i) { + offset = 0; + while (offset < block) { + n = file->vtable.write(file->vtable.cookie, dst + offset, block - offset); + if (n < 0) { + file->flags |= XF_ERR; + goto exit; + } + offset += n; + } + dst += block; + } + + exit: + return i; +} + +long +xfseek(xFILE *file, long offset, int whence) +{ + file->ungot = -1; + return file->vtable.seek(file->vtable.cookie, offset, whence); +} + +long +xftell(xFILE *file) +{ + return xfseek(file, 0, SEEK_CUR); +} + +void +xrewind(xFILE *file) +{ + xfseek(file, 0, SEEK_SET); +} + +void +xclearerr(xFILE *file) +{ + file->flags = 0; +} + +int +xfeof(xFILE *file) +{ + return file->flags & XF_EOF; +} + +int +xferror(xFILE *file) +{ + return file->flags & XF_ERR; +} + +int +xfgetc(xFILE *file) +{ + char buf[1]; + + xfread(buf, 1, 1, file); + + if (xfeof(file)) { + return EOF; + } + + return buf[0]; +} + +int +xungetc(int c, xFILE *file) +{ + file->ungot = c; + if (c != EOF) { + file->flags &= ~XF_EOF; + } + return c; +} + +int +xgetchar(void) +{ + return xfgetc(xstdin); +} + +int +xfputc(int c, xFILE *file) +{ + char buf[1]; + + buf[0] = c; + xfwrite(buf, 1, 1, file); + + return buf[0]; +} + +int +xputchar(int c) +{ + return xfputc(c, xstdout); +} + +int +xfputs(const char *str, xFILE *file) +{ + int len; + + len = strlen(str); + xfwrite(str, len, 1, file); + + return 0; +} + +int +xprintf(const char *fmt, ...) +{ + va_list ap; + int n; + + va_start(ap, fmt); + n = xvfprintf(xstdout, fmt, ap); + va_end(ap); + return n; +} + +int +xfprintf(xFILE *stream, const char *fmt, ...) +{ + va_list ap; + int n; + + va_start(ap, fmt); + n = xvfprintf(stream, fmt, ap); + va_end(ap); + return n; +} + +int +xvfprintf(xFILE *stream, const char *fmt, va_list ap) +{ + va_list ap2; + + va_copy(ap2, ap); + { + char buf[vsnprintf(NULL, 0, fmt, ap2)]; + + vsnprintf(buf, sizeof buf + 1, fmt, ap); + + if (xfwrite(buf, sizeof buf, 1, stream) < 1) { + return -1; + } + + va_end(ap2); + return sizeof buf; + } +} + +/* + * Derieved xFILE Classes + */ + +static FILE * +unpack(void *cookie) +{ + switch ((long)cookie) { + default: return cookie; + case 0: return stdin; + case 1: return stdout; + case -1: return stderr; + } +} + +static int +file_read(void *cookie, char *ptr, int size) +{ + FILE *file = unpack(cookie); + int r; + + r = fread(ptr, 1, size, file); + if (r < size && ferror(file)) { + return -1; + } + if (r == 0 && feof(file)) { + clearerr(file); + } + return r; +} + +static int +file_write(void *cookie, const char *ptr, int size) +{ + FILE *file = unpack(cookie); + int r; + + r = fwrite(ptr, 1, size, file); + if (r < size) { + return -1; + } + return r; +} + +static long +file_seek(void *cookie, long pos, int whence) +{ + return fseek(unpack(cookie), pos, whence); +} + +static int +file_flush(void *cookie) +{ + return fflush(unpack(cookie)); +} + +static int +file_close(void *cookie) +{ + return fclose(unpack(cookie)); +} + +xFILE * +xfpopen(FILE *fp) +{ + xFILE *file; + + file = xfunopen(fp, file_read, file_write, file_seek, file_flush, file_close); + if (! file) { + return NULL; + } + + return file; +} + +#define FILE_VTABLE file_read, file_write, file_seek, file_flush, file_close + +static xFILE xfile_stdin = { -1, 0, { (void *)0, FILE_VTABLE } }; +static xFILE xfile_stdout = { -1, 0, { (void *)1, FILE_VTABLE } }; +static xFILE xfile_stderr = { -1, 0, { (void *)-1, FILE_VTABLE } }; + +xFILE *xstdin = &xfile_stdin; +xFILE *xstdout = &xfile_stdout; +xFILE *xstderr = &xfile_stderr; + +struct membuf { + char *buf; + long pos, end, capa; +}; + +static int +mem_read(void *cookie, char *ptr, int size) +{ + struct membuf *mem; + + mem = (struct membuf *)cookie; + + size = min(size, mem->end - mem->pos); + memcpy(ptr, mem->buf + mem->pos, size); + mem->pos += size; + return size; +} + +static int +mem_write(void *cookie, const char *ptr, int size) +{ + struct membuf *mem; + + mem = (struct membuf *)cookie; + + if (mem->pos + size >= mem->capa) { + mem->capa = (mem->pos + size) * 2; + mem->buf = realloc(mem->buf, mem->capa); + } + memcpy(mem->buf + mem->pos, ptr, size); + mem->pos += size; + mem->end = max(mem->pos, mem->end); + return size; +} + +static long +mem_seek(void *cookie, long pos, int whence) +{ + struct membuf *mem; + + mem = (struct membuf *)cookie; + + switch (whence) { + case SEEK_SET: + mem->pos = pos; + break; + case SEEK_CUR: + mem->pos += pos; + break; + case SEEK_END: + mem->pos = mem->end + pos; + break; + } + + return mem->pos; +} + +static int +mem_flush(void *cookie) +{ + (void)cookie; + + return 0; +} + +static int +mem_close(void *cookie) +{ + struct membuf *mem; + + mem = (struct membuf *)cookie; + free(mem->buf); + free(mem); + return 0; +} + +xFILE * +xmopen() +{ + struct membuf *mem; + + mem = (struct membuf *)malloc(sizeof(struct membuf)); + mem->buf = (char *)malloc(BUFSIZ); + mem->pos = 0; + mem->end = 0; + mem->capa = BUFSIZ; + + return xfunopen(mem, mem_read, mem_write, mem_seek, mem_flush, mem_close); +}