--- val.c 2002/01/09 10:44:28 1.1
+++ val.c 2002/01/16 14:17:56 1.2
@@ -34,7 +34,10 @@
#endif
/* include system API headers */
-#include <stdio.h> /* for "s[n]printf()" */
+#include <stdio.h> /* for "size_t" */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
/* include own API header */
#include "val.h"
@@ -47,3 +50,888 @@
#define TRUE (!FALSE)
#endif
+/*
+** ____ ____
+** ____ LINEAR HASING SUB-LIBRARY ____
+**
+** This part implements a Dynamic Hash Table, based on WITOLD LITWIN
+** and PER-AKE LARSON's ``Linear Hashing'' algorithm (1980/1988),
+** implemented on top of a two-level virtual array with separate
+** collision chains as the backend data structure. Some ideas were
+** taken over from MIKAEL PETTERSON's Linear Hashing enhancements
+** (1993). The code is derived from OSSP act. See there for details.
+*/
+
+struct lh_st;
+typedef struct lh_st lh_t;
+
+typedef int (*lh_cb_t)(void *ctx, const void *keyptr, int keylen, const void *datptr, int datlen);
+
+#if 0
+lh_t *lh_create (void);
+int lh_insert (lh_t *h, const void *keyptr, int keylen, const void *datptr, int datlen, int override);
+int lh_lookup (lh_t *h, const void *keyptr, int keylen, void **datptr, int *datlen);
+int lh_delete (lh_t *h, const void *keyptr, int keylen);
+int lh_apply (lh_t *h, lh_cb_t cb, void *ctx);
+int lh_destroy(lh_t *h);
+#endif
+
+/* fixed size (number of pointers) of the directory and of each segment */
+#define INITDIRSIZE 256 /* can be an arbitrary value */
+#define SEGMENTSIZE 512 /* has to be a power of 2 for below arithmetic */
+
+/* the borders for the hash table load */
+#define MINLOADFCTR 1 /* should be between 0 and 1 */
+#define MAXLOADFCTR 2 /* should be between 2 and 4 */
+
+/* the per-element structure (keep as small as possible!) */
+typedef struct element_st element_t;
+struct element_st {
+ element_t *e_next; /* pointer to next element in collision chain */
+ unsigned long e_hash; /* cached hash value of element (rehashing optimization) */
+ void *e_keyptr; /* pointer to key (= also pointer to key+data memory chunk) */
+ void *e_datptr; /* pointer to data in key+data memory chunk */
+ void *e_endptr; /* pointer to end of key+data memory chunk */
+};
+
+/* the hash table segments */
+typedef struct segment_st segment_t;
+struct segment_st {
+ element_t *s_element[SEGMENTSIZE]; /* array of pointers to elements */
+};
+
+/* the top-level hash table structure */
+struct lh_st {
+ unsigned int h_p; /* pointer to start of unsplit region */
+ unsigned int h_pmax; /* pointer to end of unsplit region */
+ int h_slack; /* grow/shrink indicator */
+ unsigned h_dirsize; /* current size of directory */
+ segment_t **h_dir; /* pointer to directory */
+};
+
+/* on-the-fly calculate index into directory and segment from virtual array index */
+#define DIRINDEX(addr) (int)((addr) / SEGMENTSIZE)
+#define SEGINDEX(addr) (int)((addr) % SEGMENTSIZE)
+
+/* on-the-fly calculate lengths of key and data to reduce memory in element_t */
+#define el_keylen(el) ((char *)((el)->e_datptr)-(char *)((el)->e_keyptr))
+#define el_datlen(el) ((char *)((el)->e_endptr)-(char *)((el)->e_datptr))
+
+/*
+ * BJDDJ Hash Function (Bob Jenkins, Dr. Dobbs Journal):
+ * This is a very complex but also very good hash function, as proposed
+ * in the March'97 issue of Dr. Dobbs Journal (DDJ) by Bob Jenkins (see
+ * http://burtleburtle.net/bob/hash/doobs.html for online version). He
+ * showed that this hash function has both very good distribution and
+ * performance and our own hash function comparison confirmed this. The
+ * only difference to the original function of B.J. here is that our
+ * version doesn't provide the `level' (= previous hash) argument for
+ * consistency reasons with the other hash functions (i.e. same function
+ * signature). It can be definetely recommended as a good general
+ * purpuse hash function.
+ */
+static long
+lh_hash(
+ register const unsigned char *k,
+ register size_t length)
+{
+ register long a,b,c,len;
+
+ /* some abbreviations */
+#define ub4 long
+#define mix(a,b,c) { \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<< 8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>> 5); \
+ a -= b; a -= c; a ^= (c>> 3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+ }
+
+ /* setup the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = 0;
+
+ /* handle most of the key */
+ while (len >= 12) {
+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[ 2]<<16) +((ub4)k[ 3]<<24));
+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[ 6]<<16) +((ub4)k[ 7]<<24));
+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16) +((ub4)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /* handle the last 11 bytes */
+ c += length;
+ switch(len) {
+ /* all the case statements fall through */
+ case 11: c+=((ub4)k[10]<<24);
+ case 10: c+=((ub4)k[ 9]<<16);
+ case 9 : c+=((ub4)k[ 8]<< 8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((ub4)k[ 7]<<24);
+ case 7 : b+=((ub4)k[ 6]<<16);
+ case 6 : b+=((ub4)k[ 5]<< 8);
+ case 5 : b+=k[4];
+ case 4 : a+=((ub4)k[ 3]<<24);
+ case 3 : a+=((ub4)k[ 2]<<16);
+ case 2 : a+=((ub4)k[ 1]<< 8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+
+#undef ub4
+#undef mix
+
+ /* report the result */
+ return c;
+}
+
+/* create the hash table structure */
+static lh_t *lh_create(void)
+{
+ lh_t *h;
+
+ /* allocate hash structure */
+ if ((h = (lh_t *)malloc(sizeof(lh_t))) == NULL)
+ return NULL;
+
+ /* allocate and clear hash table directory */
+ h->h_dirsize = INITDIRSIZE;
+ if ((h->h_dir = (segment_t **)malloc(h->h_dirsize * sizeof(segment_t *))) == NULL) {
+ free(h);
+ return NULL;
+ }
+ memset(h->h_dir, 0, h->h_dirsize * sizeof(segment_t *));
+
+ /* allocate and clear first segment of hash table array */
+ if ((h->h_dir[0] = (segment_t *)malloc(sizeof(segment_t))) == NULL) {
+ free(h->h_dir);
+ free(h);
+ return NULL;
+ }
+ memset(h->h_dir[0], 0, sizeof(segment_t));
+
+ /* initialize hash table control attributes */
+ h->h_p = 0;
+ h->h_pmax = SEGMENTSIZE;
+ h->h_slack = SEGMENTSIZE * MAXLOADFCTR;
+
+ return h;
+}
+
+/* expand the hash table */
+static void lh_expand(lh_t *h)
+{
+ unsigned int pmax0;
+ unsigned int newaddr;
+ segment_t *seg;
+ element_t **pelold;
+ element_t *el, *headofold, *headofnew, *next;
+ unsigned int n;
+
+ /* calculate next new address */
+ pmax0 = h->h_pmax;
+ newaddr = pmax0 + h->h_p;
+
+ /* have to enlarge directory? */
+ if (h->h_dirsize <= DIRINDEX(newaddr)) {
+ n = h->h_dirsize * sizeof(segment_t *);
+ h->h_dirsize *= 2;
+ if ((h->h_dir = (segment_t **)realloc(
+ h->h_dir, h->h_dirsize*sizeof(segment_t *))) == NULL)
+ return;
+ memset((char *)h->h_dir+n, 0, n);
+ }
+
+ /* have to create a new table segment? */
+ if (SEGINDEX(newaddr) == 0) {
+ if ((seg = (segment_t *)malloc(sizeof(segment_t))) == NULL)
+ return;
+ memset(seg, 0, sizeof(segment_t));
+ h->h_dir[DIRINDEX(newaddr)] = seg;
+ }
+
+ /* locate P-element */
+ pelold = &h->h_dir[DIRINDEX(h->h_p)]->s_element[SEGINDEX(h->h_p)];
+
+ /* adjust the state variables */
+ if (++(h->h_p) >= h->h_pmax) {
+ h->h_pmax = (h->h_pmax << 1); /* == h->h_pmax * 2 */
+ h->h_p = 0;
+ }
+ h->h_slack += MAXLOADFCTR;
+
+ /* relocate and split between P-element new element */
+ headofold = NULL;
+ headofnew = NULL;
+ for (el = *pelold; el != NULL; el = next) {
+ next = el->e_next;
+ if (el->e_hash & pmax0) {
+ el->e_next = headofnew;
+ headofnew = el;
+ } else {
+ el->e_next = headofold;
+ headofold = el;
+ }
+ }
+ *pelold = headofold;
+ h->h_dir[DIRINDEX(newaddr)]->s_element[SEGINDEX(newaddr)] = headofnew;
+
+ return;
+}
+
+/* shrink hash table */
+static void lh_shrink(lh_t *h)
+{
+ segment_t *lastseg;
+ element_t **pel;
+ unsigned int oldlast;
+ unsigned int dirsize;
+ void *dir;
+
+ /* calculate old last element */
+ oldlast = h->h_p + h->h_pmax - 1;
+
+ /* we cannot shrink below one segment */
+ if (oldlast == 0)
+ return;
+
+ /* adjust the state variables */
+ if (h->h_p == 0) {
+ h->h_pmax = (h->h_pmax >> 1); /* == h->h_pmax / 2 */;
+ h->h_p = h->h_pmax - 1;
+ } else
+ h->h_p--;
+ h->h_slack -= MAXLOADFCTR;
+
+ /* relocate the lost old last element to the end of the P-element */
+ pel = &h->h_dir[DIRINDEX(h->h_p)]->s_element[SEGINDEX(h->h_p)];
+ while (*pel != NULL)
+ pel = &((*pel)->e_next);
+ lastseg = h->h_dir[DIRINDEX(oldlast)];
+ *pel = lastseg->s_element[SEGINDEX(oldlast)];
+ lastseg->s_element[SEGINDEX(oldlast)] = NULL;
+
+ /* if possible, deallocate the last segment */
+ if (SEGINDEX(oldlast) == 0) {
+ h->h_dir[DIRINDEX(oldlast)] = NULL;
+ free(lastseg);
+ }
+
+ /* if possible, deallocate the end of the directory */
+ if (h->h_dirsize > INITDIRSIZE && DIRINDEX(oldlast) < h->h_dirsize/2) {
+ dirsize = (h->h_dirsize >> 1); /* == h->h_dirsize / 2 */
+ if ((dir = (segment_t **)realloc(
+ h->h_dir, dirsize*sizeof(segment_t *))) != NULL) {
+ h->h_dirsize = dirsize;
+ h->h_dir = dir;
+ }
+ }
+ return;
+}
+
+/* insert element into hash table */
+static int lh_insert(lh_t *h, const void *keyptr, int keylen, const void *datptr, int datlen, int override)
+{
+ unsigned int hash, addr;
+ element_t *el, **pel;
+ int bFound;
+ void *vp;
+
+ /* argument consistency check */
+ if (h == NULL || keyptr == NULL || keylen <= 0)
+ return FALSE;
+
+ /* calculate hash address */
+ hash = lh_hash(keyptr, keylen);
+ addr = hash % h->h_pmax; /* unsplit region */
+ if (addr < h->h_p)
+ addr = hash % (h->h_pmax << 1); /* split region */
+
+ /* locate hash element's collision list */
+ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)];
+
+ /* check whether element is already in the hash table */
+ bFound = FALSE;
+ for (el = *pel; el != NULL; el = el->e_next) {
+ if ( el->e_hash == hash
+ && el_keylen(el) == keylen
+ && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) {
+ bFound = TRUE;
+ break;
+ }
+ }
+
+ /* only override on request */
+ if (bFound && !override)
+ return FALSE;
+
+ /* create a duplicate of key and data */
+ if ((vp = malloc(keylen+datlen)) == NULL)
+ return FALSE;
+ memmove(vp, keyptr, keylen);
+ memmove((char *)vp+keylen, datptr, datlen);
+ keyptr = vp;
+ datptr = (char *)vp+keylen;
+
+ if (bFound) {
+ /* reuse existing element by freeing old contents */
+ if (el->e_keyptr != NULL)
+ free(el->e_keyptr);
+ }
+ else {
+ /* allocate new element and chain into list */
+ if ((el = (element_t *)malloc(sizeof(element_t))) == 0) {
+ free(vp);
+ return FALSE;
+ }
+ while (*pel != NULL)
+ pel = &((*pel)->e_next);
+ el->e_next = *pel;
+ *pel = el;
+ }
+
+ /* insert contents into element structure */
+ el->e_keyptr = (void *)keyptr;
+ el->e_datptr = (void *)datptr;
+ el->e_endptr = (char *)datptr+datlen;
+ el->e_hash = hash;
+
+ /* do we need to expand the table now? */
+ if (--(h->h_slack) < 0)
+ lh_expand(h);
+
+ return TRUE;
+}
+
+/* lookup an element in hash table */
+static int lh_lookup(lh_t *h, const void *keyptr, int keylen, void **datptr, int *datlen)
+{
+ unsigned int hash, addr;
+ element_t *el, **pel;
+
+ /* argument consistency check */
+ if (h == NULL || keyptr == NULL || keylen <= 0)
+ return FALSE;
+
+ /* calculate hash address */
+ hash = lh_hash(keyptr, keylen);
+ addr = hash % h->h_pmax; /* unsplit region */
+ if (addr < h->h_p)
+ addr = hash % (h->h_pmax << 1); /* split region */
+
+ /* locate hash element collision list */
+ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)];
+
+ /* search for element in collision list */
+ for (el = *pel; el != NULL; el = el->e_next) {
+ if ( el->e_hash == hash
+ && el_keylen(el) == keylen
+ && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) {
+ /* provide results */
+ if (datptr != NULL)
+ *datptr = el->e_datptr;
+ if (datlen != NULL)
+ *datlen = el_datlen(el);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* delete an element in hash table */
+static int lh_delete(lh_t *h, const void *keyptr, int keylen)
+{
+ unsigned int hash, addr;
+ element_t *el, *lel, **pel;
+ int rv;
+
+ /* argument consistency check */
+ if (h == NULL || keyptr == NULL || keylen <= 0)
+ return FALSE;
+
+ /* calculate hash address */
+ hash = lh_hash(keyptr, keylen);
+ addr = hash % h->h_pmax; /* unsplit region */
+ if (addr < h->h_p)
+ addr = hash % (h->h_pmax << 1); /* split region */
+
+ /* locate hash element collision chain */
+ pel = &h->h_dir[DIRINDEX(addr)]->s_element[SEGINDEX(addr)];
+
+ /* search for element in collision chain */
+ rv = FALSE;
+ for (lel = NULL, el = *pel; el != NULL; lel = el, el = el->e_next) {
+ if ( el->e_hash == hash
+ && el_keylen(el) == keylen
+ && memcmp(el->e_keyptr, keyptr, el_keylen(el)) == 0) {
+ /* free key+data memory chunk */
+ if (el->e_keyptr != NULL)
+ free(el->e_keyptr);
+ /* remove element from collision chain */
+ if (lel == NULL)
+ *pel = el->e_next;
+ else
+ lel->e_next = el->e_next;
+ /* deallocate element structure */
+ free(el);
+ rv = TRUE;
+ break;
+ }
+ }
+
+ /* do we need to shrink the table now? */
+ if (++(h->h_slack) > ((h->h_pmax + h->h_p) * (MAXLOADFCTR-MINLOADFCTR)))
+ lh_shrink(h);
+
+ return rv;
+}
+
+/* apply a callback for all elements in the hash table */
+static int lh_apply(lh_t *h, lh_cb_t cb, void *ctx)
+{
+ element_t *el;
+ unsigned int i, j;
+
+ /* argument consistency check */
+ if (h == NULL || cb == NULL)
+ return FALSE;
+
+ /* interate over all segment's entries */
+ for (i = 0; i < h->h_dirsize; i++) {
+ if (h->h_dir[i] == NULL)
+ continue;
+ /* interate over all collision chains */
+ for (j = 0; j < SEGMENTSIZE; j++) {
+ if (h->h_dir[i]->s_element[j] == NULL)
+ continue;
+ /* interate over all elements in collision chain */
+ el = h->h_dir[i]->s_element[j];
+ for (; el != NULL; el = el->e_next) {
+ if (!cb(ctx, el->e_keyptr, el_keylen(el), el->e_datptr, el_datlen(el)))
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/* destroy the whole hash table */
+static int lh_destroy(lh_t *h)
+{
+ element_t *el, **pel;
+ unsigned int i, j;
+
+ /* argument consistency check */
+ if (h == NULL)
+ return FALSE;
+
+ /* deallocate all segment's entries */
+ for (i = 0; i < h->h_dirsize; i++) {
+ if (h->h_dir[i] == NULL)
+ continue;
+ /* deallocate all collision chains */
+ for (j = 0; j < SEGMENTSIZE; j++) {
+ if (h->h_dir[i]->s_element[j] == NULL)
+ continue;
+ /* deallocate all elements in collision chain */
+ pel = &h->h_dir[i]->s_element[j];
+ for (el = *pel; el != NULL; el = el->e_next) {
+ /* deallocate key+data memory chunk */
+ if (el->e_keyptr != NULL)
+ free(el->e_keyptr);
+ }
+ }
+ free(h->h_dir[i]);
+ }
+
+ /* deallocate hash table directory */
+ free(h->h_dir);
+
+ /* deallocate hash table top-level structure */
+ free(h);
+
+ return TRUE;
+}
+
+/*
+** ____ ____
+** ____ VALUE LIBRARY ____
+**
+*/
+
+/* usually val_object_t.data is a pointer val_object_t.data.p, but VAL_INLINE
+ * signals val_object_t.data is actual data val_object_t.data.[csilfd]
+ */
+enum {
+ VAL_INLINE = 1<<31
+};
+
+typedef struct {
+ int type;
+ union {
+ val_t *v;
+ void *p;
+ char c;
+ short s;
+ int i;
+ long l;
+ float f;
+ double d;
+ } data;
+ char *desc;
+} val_object_t;
+
+struct val_s {
+ lh_t *lh;
+};
+
+static void *val_storage(val_object_t *obj)
+{
+ void *storage;
+
+ if (obj->type & VAL_INLINE) {
+ switch (obj->type & ~VAL_INLINE) {
+ case VAL_TYPE_VAL:
+ storage = &obj->data.v;
+ break;
+ case VAL_TYPE_PTR:
+ storage = &obj->data.p;
+ break;
+ case VAL_TYPE_CHAR:
+ storage = &obj->data.c;
+ break;
+ case VAL_TYPE_SHORT:
+ storage = &obj->data.s;
+ break;
+ case VAL_TYPE_INT:
+ storage = &obj->data.i;
+ break;
+ case VAL_TYPE_LONG:
+ storage = &obj->data.l;
+ break;
+ case VAL_TYPE_FLOAT:
+ storage = &obj->data.f;
+ break;
+ case VAL_TYPE_DOUBLE:
+ storage = &obj->data.d;
+ break;
+ default:
+ storage = NULL;
+ }
+ }
+ else
+ storage = obj->data.p;
+ return storage;
+}
+
+val_rc_t val_create(val_t **valp)
+{
+ val_t *val;
+
+ if (valp == NULL)
+ return VAL_ERR_ARG;
+ if ((val = (val_t *)malloc(sizeof(val_t))) == NULL)
+ return VAL_ERR_SYS;
+ if ((val->lh = lh_create()) == NULL) {
+ free(val);
+ return VAL_ERR_SYS;
+ }
+ *valp = val;
+ return VAL_OK;
+}
+
+static int (val_destroy_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen)
+{
+ val_object_t *obj;
+
+ obj = (val_object_t *)datptr;
+ if (obj->desc != NULL)
+ free(obj->desc);
+ return TRUE;
+}
+
+val_rc_t val_destroy(val_t *val)
+{
+ if (val == NULL)
+ return VAL_ERR_ARG;
+ lh_apply(val->lh, val_destroy_cb, NULL);
+ if (!lh_destroy(val->lh))
+ return VAL_ERR_SYS;
+ free(val);
+ return VAL_OK;
+}
+
+val_rc_t val_reg(val_t *val, const char *name, int type, const char *desc, void *storage)
+{
+ val_object_t *obj;
+ val_object_t newobj;
+ const char *cp;
+ val_t *child;
+
+ if (val == NULL || name == NULL || type == 0)
+ return VAL_ERR_ARG;
+
+
+
+ if ((cp = strchr(name, '.')) != NULL) {
+ if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ if (!(obj->type & VAL_TYPE_VAL))
+ return VAL_ERR_USE;
+ child = *(val_t **)(val_storage(obj));
+ return val_reg(child, cp+1, type, desc, storage);
+ }
+
+
+
+ if (desc != NULL)
+ newobj.desc = strdup(desc);
+ else
+ newobj.desc = NULL;
+ if (storage == NULL) {
+ newobj.type = (type | VAL_INLINE);
+ newobj.data.l = 0;
+ }
+ else {
+ newobj.type = (type & ~VAL_INLINE);
+ newobj.data.p = storage;
+ }
+ if (!lh_insert(val->lh, name, strlen(name), &newobj, sizeof(newobj), 1))
+ return VAL_ERR_SYS;
+
+ return VAL_OK;
+}
+
+val_rc_t val_vset(val_t *val, const char *name, va_list ap)
+{
+ val_object_t *obj;
+ void *storage;
+ const char *cp;
+ val_t *child;
+
+ if (val == NULL || name == NULL || ap == NULL)
+ return VAL_ERR_ARG;
+ if ((cp = strchr(name, '.')) != NULL) {
+ if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ if (!(obj->type & VAL_TYPE_VAL))
+ return VAL_ERR_USE;
+ child = *(val_t **)(val_storage(obj));
+ return val_vset(child, cp+1, ap);
+ }
+ if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ storage = val_storage(obj);
+ switch (obj->type & ~VAL_INLINE) {
+ case VAL_TYPE_VAL:
+ *(val_t **)storage = (val_t *)va_arg(ap, void *);
+ break;
+ case VAL_TYPE_PTR:
+ *(char **)storage = (char *)va_arg(ap, void *);
+ break;
+ case VAL_TYPE_CHAR:
+ *(char *)storage = (char)va_arg(ap, int);
+ break;
+ case VAL_TYPE_SHORT:
+ *(short *)storage = (short)va_arg(ap, int);
+ break;
+ case VAL_TYPE_INT:
+ *(int *)storage = (int)va_arg(ap, int);
+ break;
+ case VAL_TYPE_LONG:
+ *(long *)storage = (long)va_arg(ap, long);
+ break;
+ case VAL_TYPE_FLOAT:
+ *(float *)storage = (float)va_arg(ap, double);
+ break;
+ case VAL_TYPE_DOUBLE:
+ *(double *)storage = (double)va_arg(ap, double);
+ break;
+ }
+ return VAL_OK;
+}
+
+val_rc_t val_set(val_t *val, const char *name, ...)
+{
+ val_rc_t rc;
+ va_list ap;
+
+ if (val == NULL || name == NULL)
+ return VAL_ERR_ARG;
+ va_start(ap, name);
+ rc = val_vset(val, name, ap);
+ va_end(ap);
+ return rc;
+}
+
+val_rc_t val_vget(val_t *val, const char *name, va_list ap)
+{
+ val_object_t *obj;
+ void *storage;
+ const char *cp;
+ val_t *child;
+
+ if (val == NULL || name == NULL || ap == NULL)
+ return VAL_ERR_ARG;
+ if ((cp = strchr(name, '.')) != NULL) {
+ if (!lh_lookup(val->lh, name, cp-name, (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ if (!(obj->type & VAL_TYPE_VAL))
+ return VAL_ERR_USE;
+ child = *(val_t **)(val_storage(obj));
+ return val_vget(child, cp+1, ap);
+ }
+ if (!lh_lookup(val->lh, name, strlen(name), (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ storage = val_storage(obj);
+ switch (obj->type & ~VAL_INLINE) {
+ case VAL_TYPE_VAL:
+ *((val_t **)va_arg(ap, void *)) = *(val_t **)storage;
+ break;
+ case VAL_TYPE_PTR:
+ *((char **)va_arg(ap, void *)) = *(char **)storage;
+ break;
+ case VAL_TYPE_CHAR:
+ *((char *)va_arg(ap, int *)) = *(char *)storage;
+ break;
+ case VAL_TYPE_SHORT:
+ *((short *)va_arg(ap, int *)) = *(short *)storage;
+ break;
+ case VAL_TYPE_INT:
+ *((int *)va_arg(ap, int *)) = *(int *)storage;
+ break;
+ case VAL_TYPE_LONG:
+ *((long *)va_arg(ap, long *)) = *(long *)storage;
+ break;
+ case VAL_TYPE_FLOAT:
+ *((float *)va_arg(ap, double *)) = *(float *)storage;
+ break;
+ case VAL_TYPE_DOUBLE:
+ *((double *)va_arg(ap, double *)) = *(double *)storage;
+ break;
+ }
+ return VAL_OK;
+}
+
+val_rc_t val_get(val_t *val, const char *name, ...)
+{
+ val_rc_t rc;
+ va_list ap;
+
+ if (val == NULL || name == NULL)
+ return VAL_ERR_ARG;
+ va_start(ap, name);
+ rc = val_vget(val, name, ap);
+ va_end(ap);
+ return rc;
+}
+
+#define VAL_MAXNAME 1024
+
+typedef struct {
+ val_t *val;
+ char *name;
+ int prefixlen;
+ int depth;
+ val_cb_t cb;
+ void *ctx;
+ val_rc_t rc;
+} val_apply_ctx_t;
+
+static val_rc_t val_apply_internal(val_t *, const char *, int, int, val_cb_t, void *);
+
+static int (val_apply_cb)(void *_ctx, const void *keyptr, int keylen, const void *datptr, int datlen)
+{
+ val_apply_ctx_t *ctx = (val_apply_ctx_t *)_ctx;
+ char name[VAL_MAXNAME+1];
+ int prefixlen;
+
+ /* on-the-fly create NUL-terminated name string */
+ if ((strlen(ctx->name) + 1 + keylen) > VAL_MAXNAME) {
+ ctx->rc = VAL_ERR_MEM;
+ return FALSE;
+ }
+ if (strlen(ctx->name) > 0) {
+ strcpy(name, ctx->name);
+ strcat(name, ".");
+ prefixlen = ctx->prefixlen + 1;
+ }
+ else {
+ *name = '\0';
+ prefixlen = ctx->prefixlen;
+ }
+ strncat(name, (char *)keyptr, keylen);
+ if ((ctx->rc = val_apply_internal(ctx->val, name, prefixlen, ctx->depth, ctx->cb, ctx->ctx)) != VAL_OK)
+ return FALSE;
+ return TRUE;
+}
+
+static val_rc_t val_apply_internal(val_t *val, const char *name, int prefixlen, int depth, val_cb_t cb, void *ctx)
+{
+ val_object_t *obj;
+ val_t *child;
+ char *cp;
+ val_rc_t rc;
+ val_apply_ctx_t val_ctx;
+
+fprintf(stderr, "DEBUG: val_apply_internal name=<%s>, prefixlen=%d, depth=%d\n", name, prefixlen, depth);
+ if (name[prefixlen] == '\0') {
+ /* prefix="foo.bar.", remainder="" */
+ //if (--depth > 0) {
+ val_ctx.val = val;
+ val_ctx.name = (char *)name;
+ val_ctx.prefixlen = prefixlen;
+ val_ctx.depth = depth;
+ val_ctx.cb = cb;
+ val_ctx.ctx = ctx;
+ val_ctx.rc = VAL_OK;
+ if (!lh_apply(val->lh, val_apply_cb, &val_ctx))
+ return VAL_ERR_SYS;
+ //}
+ }
+ else {
+ if ((cp = strchr(name+prefixlen, '.')) != NULL) {
+ /* prefix="foo.bar.", remainder="quux.baz" */
+ if (!lh_lookup(val->lh, name+prefixlen, cp-(name+prefixlen), (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ if (!(obj->type & VAL_TYPE_VAL))
+ return VAL_ERR_USE;
+ child = *(val_t **)(val_storage(obj));
+ if (depth == 0)
+ return VAL_OK;
+ return val_apply_internal(child, name, cp-name+1, --depth, cb, ctx);
+ }
+ else {
+ /* prefix="foo.bar.quux.", remainder="baz" */
+ if (!lh_lookup(val->lh, name+prefixlen, strlen(name+prefixlen), (void **)&obj, NULL))
+ return VAL_ERR_ARG;
+ /* execute VAL callback */
+ if ((rc = cb(ctx, name, (obj->type & ~VAL_INLINE), obj->desc, val_storage(obj))) != VAL_OK)
+ return rc;
+//fprintf(stderr, "DEBUG: depth=%d obj->type=%.8lx\n", depth, obj->type);
+ if (obj->type & VAL_TYPE_VAL) {
+ if (depth == 0)
+ return VAL_OK;
+ child = *(val_t **)(val_storage(obj));
+ return val_apply_internal(child, name, strlen(name), --depth, cb, ctx);
+ }
+ }
+ }
+ return VAL_OK;
+}
+
+val_rc_t val_apply(val_t *val, const char *name, int depth, val_cb_t cb, void *ctx)
+{
+ if (val == NULL || name == NULL || depth < 0 || cb == NULL)
+ return VAL_ERR_ARG;
+
+ return val_apply_internal(val, name, 0, depth, cb, ctx);
+}
+
|