/* ** hash.c ** hash manager for lua ** Luiz Henrique de Figueiredo - 17 Aug 90 */ char *rcs_hash="$Id: hash.c,v 2.1 1994/04/20 22:07:57 celes Exp celes $"; #include #include #include "mm.h" #include "opcode.h" #include "hash.h" #include "inout.h" #include "table.h" #include "lua.h" #define streq(s1,s2) (strcmp(s1,s2)==0) #define strneq(s1,s2) (strcmp(s1,s2)!=0) #define new(s) ((s *)malloc(sizeof(s))) #define newvector(n,s) ((s *)calloc(n,sizeof(s))) #define nhash(t) ((t)->nhash) #define nodelist(t) ((t)->list) #define list(t,i) ((t)->list[i]) #define markarray(t) ((t)->mark) #define ref_tag(n) (tag(&(n)->ref)) #define ref_nvalue(n) (nvalue(&(n)->ref)) #define ref_svalue(n) (svalue(&(n)->ref)) typedef struct ArrayList { Hash *array; struct ArrayList *next; } ArrayList; static ArrayList *listhead = NULL; static int head (Hash *t, Object *ref) /* hash function */ { if (tag(ref) == T_NUMBER) return (((int)nvalue(ref))%nhash(t)); else if (tag(ref) == T_STRING) { int h; char *name = svalue(ref); for (h=0; *name!=0; name++) /* interpret name as binary number */ { h <<= 8; h += (unsigned char) *name; /* avoid sign extension */ h %= nhash(t); /* make it a valid index */ } return h; } else { lua_reportbug ("unexpected type to index table"); return -1; } } static Node *present(Hash *t, Object *ref, int h) { Node *n=NULL, *p; if (tag(ref) == T_NUMBER) { for (p=NULL,n=list(t,h); n!=NULL; p=n, n=n->next) if (ref_tag(n) == T_NUMBER && nvalue(ref) == ref_nvalue(n)) break; } else if (tag(ref) == T_STRING) { for (p=NULL,n=list(t,h); n!=NULL; p=n, n=n->next) if (ref_tag(n) == T_STRING && streq(svalue(ref),ref_svalue(n))) break; } if (n==NULL) /* name not present */ return NULL; #if 0 if (p!=NULL) /* name present but not first */ { p->next=n->next; /* move-to-front self-organization */ n->next=list(t,h); list(t,h)=n; } #endif return n; } static void freelist (Node *n) { while (n) { Node *next = n->next; free (n); n = next; } } /* ** Create a new hash. Return the hash pointer or NULL on error. */ static Hash *hashcreate (unsigned int nhash) { Hash *t = new (Hash); if (t == NULL) { lua_error ("not enough memory"); return NULL; } nhash(t) = nhash; markarray(t) = 0; nodelist(t) = newvector (nhash, Node*); if (nodelist(t) == NULL) { lua_error ("not enough memory"); return NULL; } return t; } /* ** Delete a hash */ static void hashdelete (Hash *h) { int i; for (i=0; inext) { lua_markobject(&n->ref); lua_markobject(&n->val); } } } } /* ** Garbage collection to arrays ** Delete all unmarked arrays. */ void lua_hashcollector (void) { ArrayList *curr = listhead, *prev = NULL; while (curr != NULL) { ArrayList *next = curr->next; if (markarray(curr->array) != 1) { if (prev == NULL) listhead = next; else prev->next = next; hashdelete(curr->array); free(curr); } else { markarray(curr->array) = 0; prev = curr; } curr = next; } } /* ** Create a new array ** This function insert the new array at array list. It also ** execute garbage collection if the number of array created ** exceed a pre-defined range. */ Hash *lua_createarray (int nhash) { ArrayList *new = new(ArrayList); if (new == NULL) { lua_error ("not enough memory"); return NULL; } new->array = hashcreate(nhash); if (new->array == NULL) { lua_error ("not enough memory"); return NULL; } if (lua_nentity == lua_block) lua_pack(); lua_nentity++; new->next = listhead; listhead = new; return new->array; } /* ** If the hash node is present, return its pointer, otherwise create a new ** node for the given reference and also return its pointer. ** On error, return NULL. */ Object *lua_hashdefine (Hash *t, Object *ref) { int h; Node *n; h = head (t, ref); if (h < 0) return NULL; n = present(t, ref, h); if (n == NULL) { n = new(Node); if (n == NULL) { lua_error ("not enough memory"); return NULL; } n->ref = *ref; tag(&n->val) = T_NIL; n->next = list(t,h); /* link node to head of list */ list(t,h) = n; } return (&n->val); } /* ** Internal function to manipulate arrays. ** Given an array object and a reference value, return the next element ** in the hash. ** This function pushs the element value and its reference to the stack. */ static void firstnode (Hash *a, int h) { if (h < nhash(a)) { int i; for (i=h; ival) != T_NIL) { lua_pushobject (&list(a,i)->ref); lua_pushobject (&list(a,i)->val); return; } else { Node *next = list(a,i)->next; while (next != NULL && tag(&next->val) == T_NIL) next = next->next; if (next != NULL) { lua_pushobject (&next->ref); lua_pushobject (&next->val); return; } } } } } lua_pushnil(); lua_pushnil(); } void lua_next (void) { Hash *a; Object *o = lua_getparam (1); Object *r = lua_getparam (2); if (o == NULL || r == NULL) { lua_error ("too few arguments to function `next'"); return; } if (lua_getparam (3) != NULL) { lua_error ("too many arguments to function `next'"); return; } if (tag(o) != T_ARRAY) { lua_error ("first argument of function `next' is not a table"); return; } a = avalue(o); if (tag(r) == T_NIL) { firstnode (a, 0); return; } else { int h = head (a, r); if (h >= 0) { Node *n = list(a,h); while (n) { if (memcmp(&n->ref,r,sizeof(Object)) == 0) { if (n->next == NULL) { firstnode (a, h+1); return; } else if (tag(&n->next->val) != T_NIL) { lua_pushobject (&n->next->ref); lua_pushobject (&n->next->val); return; } else { Node *next = n->next->next; while (next != NULL && tag(&next->val) == T_NIL) next = next->next; if (next == NULL) { firstnode (a, h+1); return; } else { lua_pushobject (&next->ref); lua_pushobject (&next->val); } return; } } n = n->next; } if (n == NULL) lua_error ("error in function 'next': reference not found"); } } }