+ rfx_free(sa);
+}
+
+// ------------------------------- type_t -------------------------------
+
+char ptr_equals(const void*o1, const void*o2)
+{
+ return o1==o2;
+}
+unsigned int ptr_hash(const void*o)
+{
+ return string_hash3((const char*)&o, sizeof(o));
+}
+void* ptr_dup(const void*o)
+{
+ return (void*)o;
+}
+void ptr_free(void*o)
+{
+ return;
+}
+
+char charptr_equals(const void*o1, const void*o2)
+{
+ if(!o1 || !o2)
+ return o1==o2;
+ return !strcmp(o1,o2);
+}
+unsigned int charptr_hash(const void*o)
+{
+ if(!o)
+ return 0;
+ return string_hash2(o);
+}
+void* charptr_dup(const void*o)
+{
+ if(!o)
+ return 0;
+ return strdup(o);
+}
+void charptr_free(void*o)
+{
+ if(o) {
+ rfx_free(o);
+ }
+}
+
+char stringstruct_equals(const void*o1, const void*o2)
+{
+ if(!o1 || !o2)
+ return o1==o2;
+ string_t*s1 = (string_t*)o1;
+ string_t*s2 = (string_t*)o2;
+ int l = s1->len<s2->len?s1->len:s2->len;
+ int r = memcmp(s1->str, s2->str, l);
+ if(r)
+ return 0;
+ else
+ return s1->len==s2->len;
+}
+unsigned int stringstruct_hash(const void*o)
+{
+ if(!o) return 0;
+ return string_hash(o);
+}
+string_t*string_dup3(string_t*o)
+{
+ if(!o) return 0;
+ if(!o->str) {
+ string_t*s = malloc(sizeof(string_t));
+ s->str=0;
+ s->len=0;
+ return s;
+ }
+ string_t*s = rfx_alloc(sizeof(string_t)+o->len+1);
+ s->len = o->len;
+ s->str = (const char*)(s+1);
+ memcpy((char*)s->str, o->str, s->len);
+ ((char*)s->str)[s->len]=0;
+ return s;
+}
+void stringstruct_free(void*o)
+{
+ if(o)
+ string_free(o);
+}
+
+type_t ptr_type = {
+ equals: ptr_equals,
+ hash: ptr_hash,
+ dup: ptr_dup,
+ free: ptr_free,
+};
+
+type_t charptr_type = {
+ equals: charptr_equals,
+ hash: charptr_hash,
+ dup: charptr_dup,
+ free: charptr_free,
+};
+
+type_t stringstruct_type = {
+ equals: stringstruct_equals,
+ hash: stringstruct_hash,
+ dup: (dup_func)string_dup3,
+ free: stringstruct_free,
+};
+
+// ------------------------------- dictionary_t -------------------------------
+
+#define INITIAL_SIZE 1
+
+static int max(int x, int y) {
+ return x>y?x:y;
+}
+
+dict_t*dict_new()
+{
+ dict_t*d = rfx_alloc(sizeof(dict_t));
+ dict_init(d, INITIAL_SIZE);
+ return d;
+}
+dict_t*dict_new2(type_t*t)
+{
+ dict_t*d = rfx_alloc(sizeof(dict_t));
+ dict_init(d, INITIAL_SIZE);
+ d->key_type = t;
+ return d;
+}
+void dict_init(dict_t*h, int size)
+{
+ memset(h, 0, sizeof(dict_t));
+ h->hashsize = size;
+ h->slots = h->hashsize?(dictentry_t**)rfx_calloc(sizeof(dictentry_t*)*h->hashsize):0;
+ h->num = 0;
+ h->key_type = &charptr_type;
+}
+void dict_init2(dict_t*h, type_t*t, int size)
+{
+ memset(h, 0, sizeof(dict_t));
+ h->hashsize = size;
+ h->slots = h->hashsize?(dictentry_t**)rfx_calloc(sizeof(dictentry_t*)*h->hashsize):0;
+ h->num = 0;
+ h->key_type = t;
+}
+
+dict_t*dict_clone(dict_t*o)
+{
+ dict_t*h = rfx_alloc(sizeof(dict_t));
+ memcpy(h, o, sizeof(dict_t));
+ h->slots = h->hashsize?(dictentry_t**)rfx_calloc(sizeof(dictentry_t*)*h->hashsize):0;
+ int t;
+ for(t=0;t<o->hashsize;t++) {
+ dictentry_t*e = o->slots[t];
+ while(e) {
+ dictentry_t*n = (dictentry_t*)rfx_alloc(sizeof(dictentry_t));
+ memcpy(n, e, sizeof(dictentry_t));
+ n->key = h->key_type->dup(e->key);
+ n->data = e->data;
+ n->next = h->slots[t];
+ h->slots[t] = n;
+ e = e->next;
+ }
+ }
+ return h;
+}
+
+static void dict_expand(dict_t*h, int newlen)
+{
+ assert(h->hashsize < newlen);
+ dictentry_t**newslots = (dictentry_t**)rfx_calloc(sizeof(dictentry_t*)*newlen);
+ int t;
+ for(t=0;t<h->hashsize;t++) {
+ dictentry_t*e = h->slots[t];
+ while(e) {
+ dictentry_t*next = e->next;
+ unsigned int newhash = e->hash%newlen;
+ e->next = newslots[newhash];
+ newslots[newhash] = e;
+ e = next;
+ }
+ }
+ if(h->slots)
+ rfx_free(h->slots);
+ h->slots = newslots;
+ h->hashsize = newlen;
+}
+
+dictentry_t* dict_put(dict_t*h, const void*key, void* data)
+{
+ unsigned int hash = h->key_type->hash(key);
+ dictentry_t*e = (dictentry_t*)rfx_alloc(sizeof(dictentry_t));
+
+ if(!h->hashsize)
+ dict_expand(h, 1);
+
+ unsigned int hash2 = hash % h->hashsize;
+
+ e->key = h->key_type->dup(key);
+ e->hash = hash; //for resizing
+ e->next = h->slots[hash2];
+ e->data = data;
+ h->slots[hash2] = e;
+ h->num++;
+ return e;
+}
+void dict_put2(dict_t*h, const char*s, void*data)
+{
+ assert(h->key_type == &charptr_type);
+ dict_put(h, s, data);
+}
+void dict_dump(dict_t*h, FILE*fi, const char*prefix)
+{
+ int t;
+ for(t=0;t<h->hashsize;t++) {
+ dictentry_t*e = h->slots[t];
+ while(e) {
+ if(h->key_type!=&charptr_type) {
+ fprintf(fi, "%s%08x=%08x\n", prefix, e->key, e->data);
+ } else {
+ fprintf(fi, "%s%s=%08x\n", prefix, e->key, e->data);
+ }
+ e = e->next;
+ }
+ }
+}
+
+int dict_count(dict_t*h)
+{
+ return h->num;
+}
+
+static inline dictentry_t* dict_do_lookup(dict_t*h, const void*key)
+{
+ if(!h->num) {
+ return 0;
+ }
+
+ unsigned int ohash = h->key_type->hash(key);
+ unsigned int hash = ohash % h->hashsize;
+
+ /* check first entry for match */
+ dictentry_t*e = h->slots[hash];
+ if(e && h->key_type->equals(e->key, key)) {
+ return e;
+ } else if(e) {
+ e = e->next;
+ }
+
+ /* if dict is 2/3 filled, double the size. Do
+ this the first time we have to actually iterate
+ through a slot to find our data */
+ if(e && h->num*3 >= h->hashsize*2) {
+ int newsize = h->hashsize;
+ while(h->num*3 >= newsize*2) {
+ newsize = newsize<15?15:(newsize+1)*2-1;
+ }
+ dict_expand(h, newsize);
+ hash = ohash % h->hashsize;
+ e = h->slots[hash];
+ if(e && h->key_type->equals(e->key, key)) {
+ // omit move to front
+ return e;
+ } else if(e) {
+ e = e->next;
+ }
+ }
+
+ /* check subsequent entries for a match */
+ dictentry_t*last = h->slots[hash];
+ while(e) {
+ if(h->key_type->equals(e->key, key)) {
+ /* move to front- makes a difference of about 10% in most applications */
+ last->next = e->next;
+ e->next = h->slots[hash];
+ h->slots[hash] = e;
+ return e;
+ }
+ last=e;
+ e = e->next;
+ }
+ return 0;
+}
+void* dict_lookup(dict_t*h, const void*key)
+{
+ dictentry_t*e = dict_do_lookup(h, key);
+ if(e)
+ return e->data;
+ return 0;
+}
+char dict_contains(dict_t*h, const void*key)
+{
+ dictentry_t*e = dict_do_lookup(h, key);
+ return !!e;
+}
+
+char dict_del(dict_t*h, const void*key)
+{
+ if(!h->num)
+ return 0;
+ unsigned int hash = h->key_type->hash(key) % h->hashsize;
+ dictentry_t*head = h->slots[hash];
+ dictentry_t*e = head, *prev=0;
+ while(e) {
+ if(h->key_type->equals(e->key, key)) {
+ dictentry_t*next = e->next;
+ h->key_type->free(e->key);
+ memset(e, 0, sizeof(dictentry_t));
+ rfx_free(e);
+ if(e == head) {
+ h->slots[hash] = next;
+ } else {
+ assert(prev);
+ prev->next = next;
+ }
+ h->num--;
+ return 1;
+ }
+ prev = e;
+ e = e->next;
+ }
+ return 0;
+}
+
+dictentry_t* dict_get_slot(dict_t*h, const void*key)
+{
+ if(!h->num)
+ return 0;
+ unsigned int ohash = h->key_type->hash(key);
+ unsigned int hash = ohash % h->hashsize;
+ return h->slots[hash];
+}
+
+void dict_foreach_keyvalue(dict_t*h, void (*runFunction)(void*data, const void*key, void*val), void*data)
+{
+ int t;
+ for(t=0;t<h->hashsize;t++) {
+ dictentry_t*e = h->slots[t];
+ while(e) {
+ dictentry_t*next = e->next;
+ if(runFunction) {
+ runFunction(data, e->key, e->data);
+ }
+ e = e->next;
+ }
+ }
+}
+void dict_foreach_value(dict_t*h, void (*runFunction)(void*))
+{
+ int t;
+ for(t=0;t<h->hashsize;t++) {
+ dictentry_t*e = h->slots[t];
+ while(e) {
+ dictentry_t*next = e->next;
+ if(runFunction) {
+ runFunction(e->data);
+ }
+ e = e->next;
+ }
+ }
+}
+
+void dict_free_all(dict_t*h, char free_keys, void (*free_data_function)(void*))
+{
+ int t;
+ for(t=0;t<h->hashsize;t++) {
+ dictentry_t*e = h->slots[t];
+ while(e) {
+ dictentry_t*next = e->next;
+ if(free_keys) {
+ h->key_type->free(e->key);
+ }
+ if(free_data_function) {
+ free_data_function(e->data);
+ }
+ memset(e, 0, sizeof(dictentry_t));
+ rfx_free(e);
+ e = next;
+ }
+ h->slots[t]=0;
+ }
+ rfx_free(h->slots);
+ memset(h, 0, sizeof(dict_t));