| /* ----------------------------------------------------------------------------- |
| * This file is part of SWIG, which is licensed as a whole under version 3 |
| * (or any later version) of the GNU General Public License. Some additional |
| * terms also apply to certain portions of SWIG. The full details of the SWIG |
| * license and copyrights can be found in the LICENSE and COPYRIGHT files |
| * included with the SWIG source code as distributed by the SWIG developers |
| * and at http://www.swig.org/legal.html. |
| * |
| * hash.c |
| * |
| * Implements a simple hash table object. |
| * ----------------------------------------------------------------------------- */ |
| |
| #include "dohint.h" |
| |
| extern DohObjInfo DohHashType; |
| |
| /* Hash node */ |
| typedef struct HashNode { |
| DOH *key; |
| DOH *object; |
| struct HashNode *next; |
| } HashNode; |
| |
| /* Hash object */ |
| typedef struct Hash { |
| DOH *file; |
| int line; |
| HashNode **hashtable; |
| int hashsize; |
| int nitems; |
| } Hash; |
| |
| /* Key interning structure */ |
| typedef struct KeyValue { |
| char *cstr; |
| DOH *sstr; |
| struct KeyValue *left; |
| struct KeyValue *right; |
| } KeyValue; |
| |
| static KeyValue *root = 0; |
| static int max_expand = 1; |
| |
| /* Find or create a key in the interned key table */ |
| static DOH *find_key(DOH *doh_c) { |
| char *c = (char *) doh_c; |
| KeyValue *r, *s; |
| int d = 0; |
| /* OK, sure, we use a binary tree for maintaining interned |
| symbols. Then we use their hash values for accessing secondary |
| hash tables. */ |
| r = root; |
| s = 0; |
| while (r) { |
| s = r; |
| d = strcmp(r->cstr, c); |
| if (d == 0) |
| return r->sstr; |
| if (d < 0) |
| r = r->left; |
| else |
| r = r->right; |
| } |
| /* fprintf(stderr,"Interning '%s'\n", c); */ |
| r = (KeyValue *) DohMalloc(sizeof(KeyValue)); |
| r->cstr = (char *) DohMalloc(strlen(c) + 1); |
| strcpy(r->cstr, c); |
| r->sstr = NewString(c); |
| DohIntern(r->sstr); |
| r->left = 0; |
| r->right = 0; |
| if (!s) { |
| root = r; |
| } else { |
| if (d < 0) |
| s->left = r; |
| else |
| s->right = r; |
| } |
| return r->sstr; |
| } |
| |
| #define HASH_INIT_SIZE 7 |
| |
| /* Create a new hash node */ |
| static HashNode *NewNode(DOH *k, void *obj) { |
| HashNode *hn = (HashNode *) DohMalloc(sizeof(HashNode)); |
| hn->key = k; |
| Incref(hn->key); |
| hn->object = obj; |
| Incref(obj); |
| hn->next = 0; |
| return hn; |
| } |
| |
| /* Delete a hash node */ |
| static void DelNode(HashNode *hn) { |
| Delete(hn->key); |
| Delete(hn->object); |
| DohFree(hn); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * DelHash() |
| * |
| * Delete a hash table. |
| * ----------------------------------------------------------------------------- */ |
| |
| static void DelHash(DOH *ho) { |
| Hash *h = (Hash *) ObjData(ho); |
| HashNode *n, *next; |
| int i; |
| |
| for (i = 0; i < h->hashsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| next = n->next; |
| DelNode(n); |
| n = next; |
| } |
| } |
| DohFree(h->hashtable); |
| h->hashtable = 0; |
| h->hashsize = 0; |
| DohFree(h); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_clear() |
| * |
| * Clear all of the entries in the hash table. |
| * ----------------------------------------------------------------------------- */ |
| |
| static void Hash_clear(DOH *ho) { |
| Hash *h = (Hash *) ObjData(ho); |
| HashNode *n, *next; |
| int i; |
| |
| for (i = 0; i < h->hashsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| next = n->next; |
| DelNode(n); |
| n = next; |
| } |
| h->hashtable[i] = 0; |
| } |
| h->nitems = 0; |
| } |
| |
| /* resize the hash table */ |
| static void resize(Hash *h) { |
| HashNode *n, *next, **table; |
| int oldsize, newsize; |
| int i, p, hv; |
| |
| if (h->nitems < 2 * h->hashsize) |
| return; |
| |
| /* Too big. We have to rescale everything now */ |
| oldsize = h->hashsize; |
| |
| /* Calculate a new size */ |
| newsize = 2 * oldsize + 1; |
| p = 3; |
| while (p < (newsize >> 1)) { |
| if (((newsize / p) * p) == newsize) { |
| newsize += 2; |
| p = 3; |
| continue; |
| } |
| p = p + 2; |
| } |
| |
| table = (HashNode **) DohMalloc(newsize * sizeof(HashNode *)); |
| for (i = 0; i < newsize; i++) { |
| table[i] = 0; |
| } |
| |
| /* Walk down the old set of nodes and re-place */ |
| h->hashsize = newsize; |
| for (i = 0; i < oldsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| hv = Hashval(n->key) % newsize; |
| next = n->next; |
| n->next = table[hv]; |
| table[hv] = n; |
| n = next; |
| } |
| } |
| DohFree(h->hashtable); |
| h->hashtable = table; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_setattr() |
| * |
| * Set an attribute in the hash table. Deletes the existing entry if it already |
| * exists. |
| * ----------------------------------------------------------------------------- */ |
| |
| static int Hash_setattr(DOH *ho, DOH *k, DOH *obj) { |
| int hv; |
| HashNode *n, *prev; |
| Hash *h = (Hash *) ObjData(ho); |
| |
| if (!obj) { |
| return DohDelattr(ho, k); |
| } |
| if (!DohCheck(k)) |
| k = find_key(k); |
| if (!DohCheck(obj)) { |
| obj = NewString((char *) obj); |
| Decref(obj); |
| } |
| hv = (Hashval(k)) % h->hashsize; |
| n = h->hashtable[hv]; |
| prev = 0; |
| while (n) { |
| if (Cmp(n->key, k) == 0) { |
| /* Node already exists. Just replace its contents */ |
| if (n->object == obj) { |
| /* Whoa. Same object. Do nothing */ |
| return 1; |
| } |
| Delete(n->object); |
| n->object = obj; |
| Incref(obj); |
| return 1; /* Return 1 to indicate a replacement */ |
| } else { |
| prev = n; |
| n = n->next; |
| } |
| } |
| /* Add this to the table */ |
| n = NewNode(k, obj); |
| if (prev) |
| prev->next = n; |
| else |
| h->hashtable[hv] = n; |
| h->nitems++; |
| resize(h); |
| return 0; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_getattr() |
| * |
| * Get an attribute from the hash table. Returns 0 if it doesn't exist. |
| * ----------------------------------------------------------------------------- */ |
| typedef int (*binop) (DOH *obj1, DOH *obj2); |
| |
| |
| static DOH *Hash_getattr(DOH *h, DOH *k) { |
| DOH *obj = 0; |
| Hash *ho = (Hash *) ObjData(h); |
| DOH *ko = DohCheck(k) ? k : find_key(k); |
| int hv = Hashval(ko) % ho->hashsize; |
| DohObjInfo *k_type = ((DohBase*)ko)->type; |
| HashNode *n = ho->hashtable[hv]; |
| if (k_type->doh_equal) { |
| binop equal = k_type->doh_equal; |
| while (n) { |
| DohBase *nk = (DohBase *)n->key; |
| if ((k_type == nk->type) && equal(ko, nk)) obj = n->object; |
| n = n->next; |
| } |
| } else { |
| binop cmp = k_type->doh_cmp; |
| while (n) { |
| DohBase *nk = (DohBase *)n->key; |
| if ((k_type == nk->type) && (cmp(ko, nk) == 0)) obj = n->object; |
| n = n->next; |
| } |
| } |
| return obj; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_delattr() |
| * |
| * Delete an object from the hash table. |
| * ----------------------------------------------------------------------------- */ |
| |
| static int Hash_delattr(DOH *ho, DOH *k) { |
| HashNode *n, *prev; |
| int hv; |
| Hash *h = (Hash *) ObjData(ho); |
| |
| if (!DohCheck(k)) |
| k = find_key(k); |
| hv = Hashval(k) % h->hashsize; |
| n = h->hashtable[hv]; |
| prev = 0; |
| while (n) { |
| if (Cmp(n->key, k) == 0) { |
| /* Found it, kill it */ |
| |
| if (prev) { |
| prev->next = n->next; |
| } else { |
| h->hashtable[hv] = n->next; |
| } |
| DelNode(n); |
| h->nitems--; |
| return 1; |
| } |
| prev = n; |
| n = n->next; |
| } |
| return 0; |
| } |
| |
| static DohIterator Hash_firstiter(DOH *ho) { |
| DohIterator iter; |
| Hash *h = (Hash *) ObjData(ho); |
| iter.object = ho; |
| iter._current = 0; |
| iter.item = 0; |
| iter.key = 0; |
| iter._index = 0; /* Index in hash table */ |
| while ((iter._index < h->hashsize) && !h->hashtable[iter._index]) |
| iter._index++; |
| |
| if (iter._index >= h->hashsize) { |
| return iter; |
| } |
| iter._current = h->hashtable[iter._index]; |
| iter.item = ((HashNode *) iter._current)->object; |
| iter.key = ((HashNode *) iter._current)->key; |
| |
| /* Actually save the next slot in the hash. This makes it possible to |
| delete the item being iterated over without trashing the universe */ |
| iter._current = ((HashNode *) iter._current)->next; |
| return iter; |
| } |
| |
| static DohIterator Hash_nextiter(DohIterator iter) { |
| Hash *h = (Hash *) ObjData(iter.object); |
| if (!iter._current) { |
| iter._index++; |
| while ((iter._index < h->hashsize) && !h->hashtable[iter._index]) { |
| iter._index++; |
| } |
| if (iter._index >= h->hashsize) { |
| iter.item = 0; |
| iter.key = 0; |
| iter._current = 0; |
| return iter; |
| } |
| iter._current = h->hashtable[iter._index]; |
| } |
| iter.key = ((HashNode *) iter._current)->key; |
| iter.item = ((HashNode *) iter._current)->object; |
| |
| /* Store the next node to iterator on */ |
| iter._current = ((HashNode *) iter._current)->next; |
| return iter; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_keys() |
| * |
| * Return a list of keys |
| * ----------------------------------------------------------------------------- */ |
| |
| static DOH *Hash_keys(DOH *so) { |
| DOH *keys; |
| Iterator i; |
| |
| keys = NewList(); |
| for (i = First(so); i.key; i = Next(i)) { |
| Append(keys, i.key); |
| } |
| return keys; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * DohSetMaxHashExpand() |
| * |
| * Controls how many Hash objects are displayed in full in Hash_str |
| * ----------------------------------------------------------------------------- */ |
| |
| void DohSetMaxHashExpand(int count) { |
| max_expand = count; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * DohGetMaxHashExpand() |
| * |
| * Returns how many Hash objects are displayed in full in Hash_str |
| * ----------------------------------------------------------------------------- */ |
| |
| int DohGetMaxHashExpand(void) { |
| return max_expand; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_str() |
| * |
| * Create a string representation of a hash table (mainly for debugging). |
| * ----------------------------------------------------------------------------- */ |
| |
| static DOH *Hash_str(DOH *ho) { |
| int i, j; |
| HashNode *n; |
| DOH *s; |
| static int expanded = 0; |
| static const char *tab = " "; |
| Hash *h = (Hash *) ObjData(ho); |
| |
| s = NewStringEmpty(); |
| if (ObjGetMark(ho)) { |
| Printf(s, "Hash(%p)", ho); |
| return s; |
| } |
| if (expanded >= max_expand) { |
| /* replace each hash attribute with a '.' */ |
| Printf(s, "Hash(%p) {", ho); |
| for (i = 0; i < h->hashsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| Putc('.', s); |
| n = n->next; |
| } |
| } |
| Putc('}', s); |
| return s; |
| } |
| ObjSetMark(ho, 1); |
| Printf(s, "Hash(%p) {\n", ho); |
| for (i = 0; i < h->hashsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| for (j = 0; j < expanded + 1; j++) |
| Printf(s, tab); |
| expanded += 1; |
| Printf(s, "'%s' : %s, \n", n->key, n->object); |
| expanded -= 1; |
| n = n->next; |
| } |
| } |
| for (j = 0; j < expanded; j++) |
| Printf(s, tab); |
| Printf(s, "}"); |
| ObjSetMark(ho, 0); |
| return s; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Hash_len() |
| * |
| * Return number of entries in the hash table. |
| * ----------------------------------------------------------------------------- */ |
| |
| static int Hash_len(DOH *ho) { |
| Hash *h = (Hash *) ObjData(ho); |
| return h->nitems; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * CopyHash() |
| * |
| * Make a copy of a hash table. Note: this is a shallow copy. |
| * ----------------------------------------------------------------------------- */ |
| |
| static DOH *CopyHash(DOH *ho) { |
| Hash *h, *nh; |
| HashNode *n; |
| DOH *nho; |
| |
| int i; |
| h = (Hash *) ObjData(ho); |
| nh = (Hash *) DohMalloc(sizeof(Hash)); |
| nh->hashsize = h->hashsize; |
| nh->hashtable = (HashNode **) DohMalloc(nh->hashsize * sizeof(HashNode *)); |
| for (i = 0; i < nh->hashsize; i++) { |
| nh->hashtable[i] = 0; |
| } |
| nh->nitems = 0; |
| nh->line = h->line; |
| nh->file = h->file; |
| if (nh->file) |
| Incref(nh->file); |
| |
| nho = DohObjMalloc(&DohHashType, nh); |
| for (i = 0; i < h->hashsize; i++) { |
| n = h->hashtable[i]; |
| while (n) { |
| Hash_setattr(nho, n->key, n->object); |
| n = n->next; |
| } |
| } |
| return nho; |
| } |
| |
| |
| |
| static void Hash_setfile(DOH *ho, DOH *file) { |
| DOH *fo; |
| Hash *h = (Hash *) ObjData(ho); |
| |
| if (!DohCheck(file)) { |
| fo = NewString(file); |
| Decref(fo); |
| } else |
| fo = file; |
| Incref(fo); |
| Delete(h->file); |
| h->file = fo; |
| } |
| |
| static DOH *Hash_getfile(DOH *ho) { |
| Hash *h = (Hash *) ObjData(ho); |
| return h->file; |
| } |
| |
| static void Hash_setline(DOH *ho, int line) { |
| Hash *h = (Hash *) ObjData(ho); |
| h->line = line; |
| } |
| |
| static int Hash_getline(DOH *ho) { |
| Hash *h = (Hash *) ObjData(ho); |
| return h->line; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * type information |
| * ----------------------------------------------------------------------------- */ |
| |
| static DohHashMethods HashHashMethods = { |
| Hash_getattr, |
| Hash_setattr, |
| Hash_delattr, |
| Hash_keys, |
| }; |
| |
| DohObjInfo DohHashType = { |
| "Hash", /* objname */ |
| DelHash, /* doh_del */ |
| CopyHash, /* doh_copy */ |
| Hash_clear, /* doh_clear */ |
| Hash_str, /* doh_str */ |
| 0, /* doh_data */ |
| 0, /* doh_dump */ |
| Hash_len, /* doh_len */ |
| 0, /* doh_hash */ |
| 0, /* doh_cmp */ |
| 0, /* doh_equal */ |
| Hash_firstiter, /* doh_first */ |
| Hash_nextiter, /* doh_next */ |
| Hash_setfile, /* doh_setfile */ |
| Hash_getfile, /* doh_getfile */ |
| Hash_setline, /* doh_setline */ |
| Hash_getline, /* doh_getline */ |
| &HashHashMethods, /* doh_mapping */ |
| 0, /* doh_sequence */ |
| 0, /* doh_file */ |
| 0, /* doh_string */ |
| 0, /* doh_positional */ |
| 0, |
| }; |
| |
| /* ----------------------------------------------------------------------------- |
| * NewHash() |
| * |
| * Create a new hash table. |
| * ----------------------------------------------------------------------------- */ |
| |
| DOH *DohNewHash(void) { |
| Hash *h; |
| int i; |
| h = (Hash *) DohMalloc(sizeof(Hash)); |
| h->hashsize = HASH_INIT_SIZE; |
| h->hashtable = (HashNode **) DohMalloc(h->hashsize * sizeof(HashNode *)); |
| for (i = 0; i < h->hashsize; i++) { |
| h->hashtable[i] = 0; |
| } |
| h->nitems = 0; |
| h->file = 0; |
| h->line = 0; |
| return DohObjMalloc(&DohHashType, h); |
| } |