OSSP CVS Repository

ossp - Difference in ossp-pkg/val/val.c versions 1.1 and 1.2
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/val/val.c 1.1 -> 1.2

--- 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);
+}
+

CVSTrac 2.0.1