Index: ossp-pkg/val/val.c RCS File: /v/ossp/cvs/ossp-pkg/val/val.c,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/val/val.c,v' 2>/dev/null --- 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 /* for "s[n]printf()" */ +#include /* for "size_t" */ +#include +#include +#include /* 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); +} + Index: ossp-pkg/val/val.h RCS File: /v/ossp/cvs/ossp-pkg/val/val.h,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/val/val.h,v' 2>/dev/null --- val.h 2002/01/09 10:44:28 1.1 +++ val.h 2002/01/16 14:17:56 1.2 @@ -31,5 +31,40 @@ #ifndef __VAL_H__ #define __VAL_H__ +#include + +enum { + VAL_TYPE_VAL = 1<<0, + VAL_TYPE_PTR = 1<<1, + VAL_TYPE_CHAR = 1<<2, + VAL_TYPE_SHORT = 1<<3, + VAL_TYPE_INT = 1<<4, + VAL_TYPE_LONG = 1<<5, + VAL_TYPE_FLOAT = 1<<6, + VAL_TYPE_DOUBLE = 1<<7 +}; + +typedef enum { + VAL_OK, + VAL_ERR_ARG, + VAL_ERR_USE, + VAL_ERR_MEM, + VAL_ERR_SYS +} val_rc_t; + +struct val_s; +typedef struct val_s val_t; + +typedef val_rc_t (*val_cb_t)(void *, const char *, int, const char *, void *); + +val_rc_t val_create (val_t **); +val_rc_t val_destroy (val_t *); +val_rc_t val_reg (val_t *, const char *, int, const char *, void *); +val_rc_t val_set (val_t *, const char *, ...); +val_rc_t val_get (val_t *, const char *, ...); +val_rc_t val_vset (val_t *, const char *, va_list); +val_rc_t val_vget (val_t *, const char *, va_list); +val_rc_t val_apply (val_t *, const char *, int, val_cb_t, void *); + #endif /* __VAL_H__ */