#include <stdio.h> #include "lint.h" #include "config.h" #include "interpret.h" #include "object.h" /* * Object name hash table. Object names are unique, so no special * problems - like stralloc.c. For non-unique hashed names, we need * a better package (if we want to be able to get at them all) - we * cant move them to the head of the hash chain, for example. * * Note: if you change an object name, you must remove it and reenter it. */ char *xalloc (); /* * hash table - list of pointers to heads of object chains. * Each object in chain has a pointer, next_hash, to the next object. * OTABLE_SIZE is in config.h, and should be a prime, probably between * 100 and 1000. You can have a quite small table and still get very good * performance! Our database is 8Meg; we use about 500. */ static struct object **obj_table = 0; static init_otable () { int x; obj_table = (struct object **) xalloc (sizeof (struct object *) * OTABLE_SIZE); for (x = 0; x < OTABLE_SIZE; x++) obj_table[x] = 0; } /* * Object hash function, ripped off from stralloc.c. */ static int ObjHash (s) char *s; { int h = 0; if (!obj_table) init_otable (); while (*s) h = (h * P1 + *(s++) * P2 + P3) % OTABLE_SIZE; return (h); } /* * Looks for obj in table, moves it to head. */ static int obj_searches = 0, obj_probes = 0, objs_found = 0; static struct object * find_obj_n (s) char *s; { struct object *curr, *prev; int h = ObjHash (s); curr = obj_table[h]; prev = 0; obj_searches++; while (curr) { obj_probes++; if (!strcmp (curr->name, s)) { /* found it */ if (prev) { /* not at head of list */ prev->next_hash = curr->next_hash; curr->next_hash = obj_table[h]; obj_table[h] = curr; } objs_found++; return (curr); /* pointer to object */ } prev = curr; curr = curr->next_hash; } return (0); /* not found */ } /* * Add an object to the table - can't have duplicate names. */ static int objs_in_table = 0; void enter_object_hash (ob) struct object *ob; { struct object *s; int h = ObjHash (ob->name); s = find_obj_n (ob->name); if (s) { if (s != ob) fatal ("Duplicate object \"%s\" in object hash table", ob->name); else fatal ("Entering object \"%s\" twice in object table", ob->name); if (s->next_hash) fatal ("Object \"%s\" not found in object table but next link not null", ob->name); } ob->next_hash = obj_table[h]; obj_table[h] = ob; objs_in_table++; return; } /* * Remove an object from the table - generally called when it * is removed from the next_all list - i.e. in destruct. */ void remove_object_hash (ob) struct object *ob; { struct object *s; int h = ObjHash (ob->name); s = find_obj_n (ob->name); if (s != ob) fatal ("Remove object \"%s\": found a different object!", ob->name); obj_table[h] = ob->next_hash; ob->next_hash = 0; objs_in_table--; return; } /* * Lookup an object in the hash table; if it isn't there, return null. * This is only different to find_object_n in that it collects different * stats; more finds are actually done than the user ever asks for. */ static int user_obj_lookups = 0, user_obj_found = 0; struct object * lookup_object_hash (s) char *s; { struct object *ob = find_obj_n (s); user_obj_lookups++; if (ob) user_obj_found++; return (ob); } /* * Print stats, returns the total size of the object table. All objects * are in table, so their size is included as well. */ static char sbuf[100]; int show_otable_status () { add_message ("Object hash table status:\n"); #if 0 add_message ("Objects in table: %d (%d bytes)\n", objs_in_table, objs_in_table * sizeof (struct object)); #endif sprintf (sbuf, "%.2f", objs_in_table / (float) OTABLE_SIZE); add_message ("Average hash chain length: %s\n", sbuf); sprintf (sbuf, "%.2f", (float) obj_probes / obj_searches); add_message ("Searches/average search length: %d (%s)\n", obj_searches, sbuf); add_message ("External lookups succeeded (succeed): %d (%d)\n", user_obj_lookups, user_obj_found); add_message ("Table overhead: %d bytes\n", OTABLE_SIZE * sizeof (struct object *)); return (OTABLE_SIZE * sizeof (struct object *) + objs_in_table * sizeof (struct object)); }