hash.c

00001 #include "nasal.h"
00002 #include "data.h"
00003 
00004 #define MIN_HASH_SIZE 4
00005 
00006 #define EQUAL(a, b) (IDENTICAL(a, b) || naEqual(a, b))
00007 
00008 #define HASH_MAGIC 2654435769u
00009 
00010 #define INSERT(hh, hkey, hval, hcol) do {                       \
00011         unsigned int cc = (hcol), iidx=(hh)->size++;            \
00012         if(iidx < (1<<(hh)->lgalloced)) {                       \
00013             struct HashNode* hnn = &(hh)->nodes[iidx];  \
00014             hnn->key = (hkey); hnn->val = (hval);               \
00015             hnn->next = (hh)->table[cc];                        \
00016             (hh)->table[cc] = hnn;                              \
00017         }} while(0)
00018 
00019 // Computes a hash code for a given scalar
00020 static unsigned int hashcode(naRef r)
00021 {
00022     if(IS_NUM(r))
00023     {
00024         // Numbers get the number as a hash.  Just use the bits and
00025         // xor them together.  Note assumption that sizeof(double) >=
00026         // 2*sizeof(int).
00027         unsigned int* p = (unsigned int*)&(r.num);
00028         return p[0] ^ p[1];
00029     } else if(PTR(r).str->hashcode) {
00030         return PTR(r).str->hashcode;
00031     } else {
00032         // This is Daniel Bernstein's djb2 hash function that I found
00033         // on the web somewhere.  It appears to work pretty well.
00034         unsigned int i, hash = 5831;
00035         for(i=0; i<PTR(r).str->len; i++)
00036             hash = (hash * 33) ^ PTR(r).str->data[i];
00037         PTR(r).str->hashcode = hash;
00038         return hash;
00039     }
00040 }
00041 
00042 // Which column in a given hash does the key correspond to.
00043 static unsigned int hashcolumn(struct HashRec* h, naRef key)
00044 {
00045     // Multiply by a big number, and take the top N bits.  Note
00046     // assumption that sizeof(unsigned int) == 4.
00047     return (HASH_MAGIC * hashcode(key)) >> (32 - h->lgalloced);
00048 }
00049 
00050 static struct HashRec* resize(struct naHash* hash)
00051 {
00052     struct HashRec *h, *h0 = hash->rec;
00053     int lga, cols, need = h0 ? h0->size - h0->dels : MIN_HASH_SIZE;
00054 
00055     if(need < MIN_HASH_SIZE) need = MIN_HASH_SIZE;
00056     for(lga=0; 1<<lga <= need; lga++);
00057     cols = 1<<lga;
00058     h = naAlloc(sizeof(struct HashRec) +
00059                 cols * (sizeof(struct HashNode*) + sizeof(struct HashNode)));
00060     naBZero(h, sizeof(struct HashRec) + cols * sizeof(struct HashNode*));
00061 
00062     h->lgalloced = lga;
00063     h->nodes = (struct HashNode*)(((char*)h)
00064                                   + sizeof(struct HashRec)
00065                                   + cols * sizeof(struct HashNode*));
00066     for(lga=0; h0 != 0 && lga<(1<<h0->lgalloced); lga++) {
00067         struct HashNode* hn = h0->table[lga];
00068         while(hn) {
00069             INSERT(h, hn->key, hn->val, hashcolumn(h, hn->key));
00070             hn = hn->next;
00071         }
00072     }
00073     naGC_swapfree((void**)&hash->rec, h);
00074     return h;
00075 }
00076 
00077 // Special, optimized version of naHash_get for the express purpose of
00078 // looking up symbols in the local variables hash (OP_LOCAL is by far
00079 // the most common opcode and deserves some special case
00080 // optimization).  Elides all the typing checks that are normally
00081 // required, presumes that the key is a string and has had its
00082 // hashcode precomputed, checks only for object identity, and inlines
00083 // the column computation.
00084 int naHash_sym(struct naHash* hash, struct naStr* sym, naRef* out)
00085 {
00086     struct HashRec* h = hash->rec;
00087     if(h) {
00088         int col = (HASH_MAGIC * sym->hashcode) >> (32 - h->lgalloced);
00089         struct HashNode* hn = h->table[col];
00090         while(hn) {
00091             if(PTR(hn->key).str == sym) {
00092                 *out = hn->val;
00093                 return 1;
00094             }
00095             hn = hn->next;
00096         }
00097     }
00098     return 0;
00099 }
00100 
00101 static struct HashNode* find(struct naHash* hash, naRef key)
00102 {
00103     struct HashRec* h = hash->rec;
00104     struct HashNode* hn;
00105     if(!h) return 0;
00106     for(hn = h->table[hashcolumn(h, key)]; hn; hn = hn->next)
00107         if(EQUAL(key, hn->key))
00108             return hn;
00109     return 0;
00110 }
00111 
00112 // Make a temporary string on the stack
00113 static void tmpStr(naRef* out, struct naStr* str, const char* key)
00114 {
00115     str->len = 0;
00116     str->type = T_STR;
00117     str->data = (unsigned char*)key;
00118     str->hashcode = 0;
00119     while(key[str->len]) str->len++;
00120     *out = naNil();
00121     SETPTR(*out, str);
00122 }
00123 
00124 int naMember_cget(naRef obj, const char* field, naRef* out)
00125 {
00126     naRef key;
00127     struct naStr str;
00128     tmpStr(&key, &str, field);
00129     return naMember_get(obj, key, out);
00130 }
00131 
00132 naRef naHash_cget(naRef hash, char* key)
00133 {
00134     struct naStr str;
00135     naRef result, key2;
00136     tmpStr(&key2, &str, key);
00137     if(naHash_get(hash, key2, &result))
00138         return result;
00139     return naNil();
00140 }
00141 
00142 void naHash_cset(naRef hash, char* key, naRef val)
00143 {
00144     struct naStr str;
00145     naRef key2;
00146     tmpStr(&key2, &str, key);
00147     naHash_tryset(hash, key2, val);
00148 }
00149 
00150 int naHash_get(naRef hash, naRef key, naRef* out)
00151 {
00152     if(IS_HASH(hash)) {
00153         struct HashNode* n = find(PTR(hash).hash, key);
00154         if(n) { *out = n->val; return 1; }
00155     }
00156     return 0;
00157 }
00158 
00159 // Simpler version.  Don't create a new node if the value isn't there
00160 int naHash_tryset(naRef hash, naRef key, naRef val)
00161 {
00162     if(IS_HASH(hash)) {
00163         struct HashNode* n = find(PTR(hash).hash, key);
00164         if(n) n->val = val;
00165         return n != 0;
00166     }
00167     return 0;
00168 }
00169 
00170 // Special purpose optimization for use in function call setups.  Sets
00171 // a value that is known *not* to be present in the hash table.  As
00172 // for naHash_sym, the key must be a string with a precomputed hash
00173 // code.
00174 void naHash_newsym(struct naHash* hash, naRef* sym, naRef* val)
00175 {
00176     int col;
00177     struct HashRec* h = hash->rec;
00178     while(!h || h->size >= 1<<h->lgalloced)
00179         h = resize(hash);
00180     col = (HASH_MAGIC * PTR(*sym).str->hashcode) >> (32 - h->lgalloced);
00181     INSERT(h, *sym, *val, col);
00182 }
00183 
00184 // The cycle check is an integrity requirement for multithreading,
00185 // where raced inserts can potentially cause cycles.  This ensures
00186 // that the "last" thread to hold a reference to an inserted node
00187 // breaks any cycles that might have happened (at the expense of
00188 // potentially dropping items out of the hash).  Under normal
00189 // circumstances, chains will be very short and this will be fast.
00190 static void chkcycle(struct HashNode* node, int count)
00191 {
00192     struct HashNode* hn = node;
00193     while(hn && (hn = hn->next) != 0)
00194         if(count-- <= 0) { node->next = 0; return; }
00195 }
00196 
00197 void naHash_set(naRef hash, naRef key, naRef val)
00198 {
00199     int col;
00200     struct HashRec* h;
00201     struct HashNode* n;
00202     if(!IS_HASH(hash)) return;
00203     if((n = find(PTR(hash).hash, key))) { n->val = val; return; }
00204     h = PTR(hash).hash->rec;
00205     while(!h || h->size >= 1<<h->lgalloced)
00206         h = resize(PTR(hash).hash);
00207     col = hashcolumn(h, key);
00208     INSERT(h, key, val, hashcolumn(h, key));
00209     chkcycle(h->table[col], h->size - h->dels);
00210 }
00211 
00212 void naHash_delete(naRef hash, naRef key)
00213 {
00214     struct HashRec* h = PTR(hash).hash->rec;
00215     int col;
00216     struct HashNode *last=0, *hn;
00217     if(!IS_HASH(hash) || !h) return;
00218     col = hashcolumn(h, key);
00219     hn = h->table[col];
00220     while(hn) {
00221         if(EQUAL(hn->key, key)) {
00222             if(last == 0) h->table[col] = hn->next;
00223             else last->next = hn->next;
00224             h->dels++;
00225             return;
00226         }
00227         last = hn;
00228         hn = hn->next;
00229     }
00230 }
00231 
00232 void naHash_keys(naRef dst, naRef hash)
00233 {
00234     int i;
00235     struct HashRec* h = PTR(hash).hash->rec;
00236     if(!IS_HASH(hash) || !h) return;
00237     for(i=0; i<(1<<h->lgalloced); i++) {
00238         struct HashNode* hn = h->table[i];
00239         while(hn) {
00240             naVec_append(dst, hn->key);
00241             hn = hn->next;
00242         }
00243     }
00244 }
00245 
00246 int naHash_size(naRef hash)
00247 {
00248     struct HashRec* h = PTR(hash).hash->rec;
00249     if(!IS_HASH(hash) || !h) return 0;
00250     return h->size - h->dels;
00251 }
00252 
00253 void naHash_gcclean(struct naHash* h)
00254 {
00255     naFree(h->rec);
00256     h->rec = 0;
00257 }

Generated on Mon Dec 17 09:30:54 2007 for SimGear by  doxygen 1.5.1