+
+/* Optimized naHash_get for looking up local variables (OP_LOCAL is by
+ * far the most common opcode and deserves some special case
+ * optimization). Assumes that the key is an interned symbol
+ * (i.e. the hash code is precomputed, and we only need to test for
+ * pointer identity). */
+int naiHash_sym(struct naHash* hash, struct naStr* sym, naRef* out)
+{
+ HashRec* hr = hash->rec;
+ if(hr) {
+ int* tab = TAB(hr);
+ HashEnt* ents = ENTS(hr);
+ unsigned int hc = sym->hashcode;
+ int cell, mask = POW2(hr->lgsz+1) - 1, step = (2*hc+1) & mask;
+ for(cell=HBITS(hr,hc); tab[cell] != ENT_EMPTY; cell=(cell+step)&mask)
+ if(tab[cell]!=ENT_DELETED && sym==PTR(ents[tab[cell]].key).str) {
+ *out = ents[tab[cell]].val;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/* As above, a special naHash_set for setting local variables.
+ * Assumes that the key is interned, and also that it isn't already
+ * present in the hash. */
+void naiHash_newsym(struct naHash* hash, naRef* sym, naRef* val)
+{
+ HashRec* hr = hash->rec;
+ int mask, step, cell, ent;
+ struct naStr *s = PTR(*sym).str;
+ if(!hr || hr->next >= POW2(hr->lgsz))
+ hr = resize(hash);
+ mask = POW2(hr->lgsz+1) - 1;
+ step = (2*s->hashcode+1) & mask;
+ cell = HBITS(hr, s->hashcode);
+ while(TAB(hr)[cell] != ENT_EMPTY)
+ cell = (cell + step) & mask;
+ ent = hr->next++;
+ if(ent >= NCELLS(hr)) return; /* race protection, don't overrun */
+ TAB(hr)[cell] = ent;
+ hr->size++;
+ ENTS(hr)[TAB(hr)[cell]].key = *sym;
+ ENTS(hr)[TAB(hr)[cell]].val = *val;
+}
+