blob: 0b82309242bb15d51b02114a8219e97efdcfd17a [file] [log] [blame]
/*
** 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 <string.h>
#include <stdlib.h>
#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; i<nhash(h); i++)
freelist (list(h,i));
free (nodelist(h));
free(h);
}
/*
** Mark a hash and check its elements
*/
void lua_hashmark (Hash *h)
{
if (markarray(h) == 0)
{
int i;
markarray(h) = 1;
for (i=0; i<nhash(h); i++)
{
Node *n;
for (n = list(h,i); n != NULL; n = n->next)
{
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; i<nhash(a); i++)
{
if (list(a,i) != NULL)
{
if (tag(&list(a,i)->val) != 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");
}
}
}