From 9ff337301dd6a29bcb41abaa3090fecb5106941e Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Mon, 31 Aug 2015 07:41:41 +0000 Subject: [PATCH] add odict --- Makefile | 1 + include/re.h | 1 + include/re_hash.h | 2 + include/re_odict.h | 44 +++++++++ src/hash/func.c | 233 +++++++++++++++++++++++++++++++++++++++++++++ src/odict/entry.c | 146 ++++++++++++++++++++++++++++ src/odict/mod.mk | 9 ++ src/odict/odict.c | 124 ++++++++++++++++++++++++ src/odict/type.c | 59 ++++++++++++ 9 files changed, 619 insertions(+) create mode 100644 include/re_odict.h create mode 100644 src/odict/entry.c create mode 100644 src/odict/mod.mk create mode 100644 src/odict/odict.c create mode 100644 src/odict/type.c diff --git a/Makefile b/Makefile index ed9bf3e..0a2a6d8 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ MODULES += fmt tmr main mem dbg sys lock mqueue MODULES += mod conf MODULES += bfcp MODULES += aes srtp +MODULES += odict INSTALL := install ifeq ($(DESTDIR),) diff --git a/include/re.h b/include/re.h index edec85b..df8cf80 100644 --- a/include/re.h +++ b/include/re.h @@ -39,6 +39,7 @@ extern "C" { #include "re_mod.h" #include "re_mqueue.h" #include "re_net.h" +#include "re_odict.h" #include "re_rtp.h" #include "re_sdp.h" #include "re_uri.h" diff --git a/include/re_hash.h b/include/re_hash.h index 873fa34..8858dbc 100644 --- a/include/re_hash.h +++ b/include/re_hash.h @@ -29,3 +29,5 @@ uint32_t hash_joaat_str(const char *str); uint32_t hash_joaat_str_ci(const char *str); uint32_t hash_joaat_pl(const struct pl *pl); uint32_t hash_joaat_pl_ci(const struct pl *pl); +uint32_t hash_fast(const char *k, size_t len); +uint32_t hash_fast_str(const char *str); diff --git a/include/re_odict.h b/include/re_odict.h new file mode 100644 index 0000000..216e6a1 --- /dev/null +++ b/include/re_odict.h @@ -0,0 +1,44 @@ +/** + * @file re_odict.h Interface to Ordered Dictionary + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +struct odict { + struct list lst; + struct hash *ht; +}; + +struct odict_entry { + struct le le, he; + char *key; + union { + struct odict *odict; /* ODICT_OBJECT / OJECT_ARRAY */ + char *str; /* ODICT_STRING */ + int64_t integer; /* ODICT_INT */ + double dbl; /* ODICT_DOUBLE */ + bool boolean; /* ODICT_BOOL */ + } u; + enum odict_type { + ODICT_OBJECT, + ODICT_ARRAY, + ODICT_STRING, + ODICT_INT, + ODICT_DOUBLE, + ODICT_BOOL, + ODICT_NULL, + } type; +}; + +int odict_alloc(struct odict **op, uint32_t hash_size); +const struct odict_entry *odict_lookup(const struct odict *o, const char *key); +size_t odict_count(const struct odict *o, bool nested); +int odict_debug(struct re_printf *pf, const struct odict *o); + +int odict_entry_add(struct odict *o, const char *key, + enum odict_type type, ...); +int odict_entry_debug(struct re_printf *pf, const struct odict_entry *e); + +bool odict_type_iscontainer(enum odict_type type); +bool odict_type_isreal(enum odict_type type); +const char *odict_type_name(enum odict_type type); diff --git a/src/hash/func.c b/src/hash/func.c index 93bf850..3943c94 100644 --- a/src/hash/func.c +++ b/src/hash/func.c @@ -134,3 +134,236 @@ uint32_t hash_joaat_pl_ci(const struct pl *pl) { return pl ? hash_joaat_ci(pl->p, pl->l) : 0; } + + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || \ + defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || \ + defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +#define mix(a,b,c) { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ + } + + +#define final(a,b,c) \ + { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ + } + + +static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; + + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + +#ifndef VALGRIND + switch (length) { + + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; + } + +#else /* make valgrind happy */ + + const uint8_t *k8 = (const uint8_t *)k; + switch (length) { + + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } + else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; + const uint8_t *k8; + + while (length > 12) { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + k8 = (const uint8_t *)k; + + switch (length) { + + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; + } + } + else { + const uint8_t *k = (const uint8_t *)key; + + while (length > 12) { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /* all the case statements fall through */ + switch (length) { + + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/** + * Calculate hash-value using fast hash algorithm. + * + * @param k Pointer to key + * @param len Key length + * + * @return Calculated hash-value + */ +uint32_t hash_fast(const char *k, size_t len) +{ + static volatile int random_seed = 0x304a0012; + + if (!k) + return 0; + + return hashlittle(k, len, random_seed); +} + + +/** + * Calculate hash-value for a NULL-terminated string + * + * @param str String + * + * @return Calculated hash-value + */ +uint32_t hash_fast_str(const char *str) +{ + return hash_fast(str, str_len(str)); +} diff --git a/src/odict/entry.c b/src/odict/entry.c new file mode 100644 index 0000000..fa04aa9 --- /dev/null +++ b/src/odict/entry.c @@ -0,0 +1,146 @@ +/** + * @file odict/entry.c Ordered Dictionary -- entry + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +#include "re_types.h" +#include "re_fmt.h" +#include "re_mem.h" +#include "re_list.h" +#include "re_hash.h" +#include "re_odict.h" + + +static void destructor(void *arg) +{ + struct odict_entry *e = arg; + + switch (e->type) { + + case ODICT_OBJECT: + case ODICT_ARRAY: + mem_deref(e->u.odict); + break; + + case ODICT_STRING: + mem_deref(e->u.str); + break; + + default: + break; + } + + hash_unlink(&e->he); + list_unlink(&e->le); + mem_deref(e->key); +} + + +int odict_entry_add(struct odict *o, const char *key, + enum odict_type type, ...) +{ + struct odict_entry *e; + va_list ap; + int err; + + if (!o || !key) + return EINVAL; + + e = mem_zalloc(sizeof(*e), destructor); + if (!e) + return ENOMEM; + + e->type = type; + + err = str_dup(&e->key, key); + if (err) + goto out; + + va_start(ap, type); + + switch (e->type) { + + case ODICT_OBJECT: + case ODICT_ARRAY: + e->u.odict = mem_ref(va_arg(ap, struct odict *)); + break; + + case ODICT_STRING: + err = str_dup(&e->u.str, va_arg(ap, const char *)); + break; + + case ODICT_INT: + e->u.integer = va_arg(ap, int64_t); + break; + + case ODICT_DOUBLE: + e->u.dbl = va_arg(ap, double); + break; + + case ODICT_BOOL: + e->u.boolean = va_arg(ap, int); + break; + + case ODICT_NULL: + break; + + default: + err = EINVAL; + break; + } + + va_end(ap); + + if (err) + goto out; + + list_append(&o->lst, &e->le, e); + hash_append(o->ht, hash_fast_str(e->key), &e->he, e); + + out: + if (err) + mem_deref(e); + + return err; +} + + +int odict_entry_debug(struct re_printf *pf, const struct odict_entry *e) +{ + int err; + + if (!e) + return 0; + + err = re_hprintf(pf, "%s", e->key); + + switch (e->type) { + + case ODICT_OBJECT: + case ODICT_ARRAY: + err |= re_hprintf(pf, ":%H", odict_debug, e->u.odict); + break; + + case ODICT_STRING: + err |= re_hprintf(pf, ":%s", e->u.str); + break; + + case ODICT_INT: + err |= re_hprintf(pf, ":%lli", e->u.integer); + break; + + case ODICT_DOUBLE: + err |= re_hprintf(pf, ":%f", e->u.dbl); + break; + + case ODICT_BOOL: + err |= re_hprintf(pf, ":%s", e->u.boolean ? "true" : "false"); + break; + + case ODICT_NULL: + break; + } + + return err; +} diff --git a/src/odict/mod.mk b/src/odict/mod.mk new file mode 100644 index 0000000..19f11ed --- /dev/null +++ b/src/odict/mod.mk @@ -0,0 +1,9 @@ +# +# mod.mk +# +# Copyright (C) 2010 - 2015 Creytiv.com +# + +SRCS += odict/entry.c +SRCS += odict/odict.c +SRCS += odict/type.c diff --git a/src/odict/odict.c b/src/odict/odict.c new file mode 100644 index 0000000..8e87968 --- /dev/null +++ b/src/odict/odict.c @@ -0,0 +1,124 @@ +/** + * @file odict.c Ordered Dictionary + * + * Copyright (C) 2010 Creytiv.com + */ +#include "re_types.h" +#include "re_fmt.h" +#include "re_mem.h" +#include "re_list.h" +#include "re_hash.h" +#include "re_odict.h" + + +static void destructor(void *arg) +{ + struct odict *o = arg; + + hash_clear(o->ht); + list_flush(&o->lst); + mem_deref(o->ht); +} + + +int odict_alloc(struct odict **op, uint32_t hash_size) +{ + struct odict *o; + int err; + + if (!op || !hash_size) + return EINVAL; + + o = mem_zalloc(sizeof(*o), destructor); + if (!o) + return ENOMEM; + + err = hash_alloc(&o->ht, hash_valid_size(hash_size)); + if (err) + goto out; + + out: + if (err) + mem_deref(o); + else + *op = o; + + return err; +} + + +const struct odict_entry *odict_lookup(const struct odict *o, const char *key) +{ + struct le *le; + + if (!o || !key) + return NULL; + + le = list_head(hash_list(o->ht, hash_fast_str(key))); + + while (le) { + const struct odict_entry *e = le->data; + + if (!str_cmp(e->key, key)) + return e; + + le = le->next; + } + + return NULL; +} + + +size_t odict_count(const struct odict *o, bool nested) +{ + struct le *le; + size_t n = 0; + + if (!o) + return 0; + + if (!nested) + return list_count(&o->lst); + + for (le=o->lst.head; le; le=le->next) { + + const struct odict_entry *e = le->data; + + switch (e->type) { + + case ODICT_OBJECT: + case ODICT_ARRAY: + n += odict_count(e->u.odict, true); + break; + + default: + n += 1; /* count all entries */ + break; + } + } + + return n; +} + + +int odict_debug(struct re_printf *pf, const struct odict *o) +{ + struct le *le; + int err; + + if (!o) + return 0; + + err = re_hprintf(pf, "{"); + + for (le=o->lst.head; le; le=le->next) { + + const struct odict_entry *e = le->data; + + err |= re_hprintf(pf, " %H", odict_entry_debug, e); + } + + err |= re_hprintf(pf, " }"); + + return err; +} diff --git a/src/odict/type.c b/src/odict/type.c new file mode 100644 index 0000000..bb0dab9 --- /dev/null +++ b/src/odict/type.c @@ -0,0 +1,59 @@ +/** + * @file type.c Ordered Dictionary -- value types + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +#include "re_types.h" +#include "re_fmt.h" +#include "re_mem.h" +#include "re_list.h" +#include "re_hash.h" +#include "re_odict.h" + + +bool odict_type_iscontainer(enum odict_type type) +{ + switch (type) { + + case ODICT_OBJECT: + case ODICT_ARRAY: + return true; + + default: + return false; + } +} + + +bool odict_type_isreal(enum odict_type type) +{ + switch (type) { + + case ODICT_STRING: + case ODICT_INT: + case ODICT_DOUBLE: + case ODICT_BOOL: + case ODICT_NULL: + return true; + + default: + return false; + } +} + + +const char *odict_type_name(enum odict_type type) +{ + switch (type) { + + case ODICT_OBJECT: return "Object"; + case ODICT_ARRAY: return "Array"; + case ODICT_STRING: return "String"; + case ODICT_INT: return "Integer"; + case ODICT_DOUBLE: return "Double"; + case ODICT_BOOL: return "Boolean"; + case ODICT_NULL: return "Null"; + default: return "???"; + } +}