home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: fstruct.c
- * Contents: delete, get, key, insert, list, member, pop, pull, push, put, set,
- * table
- */
-
- #include "../h/config.h"
- #include "../h/rt.h"
- #include "rproto.h"
-
-
- /*
- * delete(X,x) - delete element x from set or table X if it is there
- * (always succeeds and returns X).
- */
-
- FncDcl(delete,2)
- {
- register union block **pd;
- register uword hn;
- int res;
-
- if (Qual(Arg1))
- RunErr(122, &Arg1);
-
- /*
- * The technique and philosophy here are the same
- * as used in insert - see comment there.
- */
- switch (Type(Arg1)) {
- case T_Set:
- case T_Table:
- hn = hash(&Arg2);
- pd = memb(BlkLoc(Arg1), &Arg2, hn, &res);
- if (res == 1) {
- /*
- * The element is there so delete it.
- */
- *pd = (*pd)->selem.clink;
- (BlkLoc(Arg1)->set.size)--;
- }
- break;
-
- default:
- RunErr(122, &Arg1);
- }
-
- Arg0 = Arg1;
- Return;
- }
-
-
- /*
- * get(x) - get an element from end of list x.
- * Identical to pop(x).
- */
-
- FncDcl(get,1)
- {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
-
- /*
- * Arg1 must be a list.
- */
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Fail if the list is empty.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
- if (hp->size <= 0)
- Fail;
-
- /*
- * Point bp at the first list block. If the first block has no
- * elements in use, point bp at the next list block.
- */
- bp = (struct b_lelem *) hp->listhead;
- if (bp->nused <= 0) {
- bp = (struct b_lelem *) bp->listnext;
- hp->listhead = (union block *) bp;
- bp->listprev = NULL;
- }
- /*
- * Locate first element and assign it to Arg0 for return.
- */
- i = bp->first;
- Arg0 = bp->lslots[i];
- /*
- * Set bp->first to new first element, or 0 if the block is now
- * empty. Decrement the usage count for the block and the size
- * of the list.
- */
- if (++i >= bp->nslots)
- i = 0;
- bp->first = i;
- bp->nused--;
- hp->size--;
- Return;
- }
-
- /*
- * key(t) - generate successive keys (entry values) from table t.
- */
-
- FncDcl(key,2)
- {
- if (Arg1.dword != D_Table)
- RunErr(124, &Arg1);
- MakeInt(1, &Arg2); /* indicate that we want the keys */
- Forward(hgener); /* go to the hash generator */
- }
-
- /*
- * insert(X,x) - insert element x into set or table X if not already there
- * (always succeeds and returns X).
- */
-
- FncDcl(insert,3)
- {
- register union block *bp;
- register union block **pd;
- register struct b_telem *pe;
- register uword hn;
- int res;
-
- if (Qual(Arg1))
- RunErr(122, &Arg1);
-
- switch (Type(Arg1)) {
- case T_Set:
-
- /*
- * We may need at most one new element.
- */
- if (blkreq((word)sizeof(struct b_selem)) == Error)
- RunErr(0, NULL);
- bp = BlkLoc(Arg1);
- hn = hash(&Arg2);
- /*
- * If Arg2 is a member of set Arg1 then res will have the
- * value 1 and pd will have a pointer to the pointer
- * that points to that member.
- * If Arg2 is not a member of the set then res will have
- * the value 0 and pd will point to the pointer
- * which should point to the member - thus we know where
- * to link in the new element without having to do any
- * repetitive looking.
- */
- pd = memb(bp, &Arg2, hn, &res);
- if (res == 0) {
- /*
- * The element is not in the set - insert it.
- */
- addmem((struct b_set *)bp, alcselem(&Arg2, hn), pd);
- if (TooCrowded(bp))
- hgrow(&Arg1);
- }
- break;
-
- case T_Table:
- if (blkreq((word)sizeof(struct b_telem)) == Error)
- RunErr(0, NULL);
- bp = BlkLoc(Arg1);
- hn = hash(&Arg2);
- pd = memb(bp, &Arg2, hn, &res);
- if (res == 0) {
- /*
- * The element is not in the table - insert it.
- */
- bp->table.size++;
- pe = alctelem();
- pe->clink = *pd;
- *pd = (union block *)pe;
- pe->hashnum = hn;
- pe->tref = Arg2;
- pe->tval = Arg3;
- if (TooCrowded(bp))
- hgrow(&Arg1);
- }
- else {
- pe = (struct b_telem *) *pd;
- pe->tval = Arg3;
- }
- break;
-
- default:
- RunErr(122, &Arg1);
- }
-
- Arg0 = Arg1;
- Return;
- }
-
- /*
- * list(n,x) - create a list of size n, with initial value x.
- */
-
- FncDcl(list,2)
- {
- register word i, size;
- word nslots;
- register struct b_list *hp;
- register struct b_lelem *bp;
-
- if (defshort(&Arg1, 0) == Error)
- RunErr(0, NULL);
-
- nslots = size = IntVal(Arg1);
-
-
- /*
- * Ensure that the size is positive and that the list-element block
- * has MinListSlots slots if its size is zero.
- */
- if (size < 0)
- RunErr(205, &Arg1);
- if (nslots == 0)
- nslots = MinListSlots;
-
- /*
- * Ensure space for a list-header block, and a list-element block
- * with nslots slots.
- */
- if (blkreq(sizeof(struct b_list) + sizeof(struct b_lelem) +
- (nslots - 1) * sizeof(struct descrip)) == Error)
- RunErr(0, NULL);
-
- /*
- * Allocate the list-header block and a list-element block.
- * Note that nslots is the number of slots in the list-element
- * block while size is the number of elements in the list.
- */
- hp = alclist(size);
- bp = alclstb(nslots, (word)0, size);
- hp->listhead = hp->listtail = (union block *) bp;
-
- /*
- * Initialize each slot.
- */
- for (i = 0; i < size; i++)
- bp->lslots[i] = Arg2;
-
- /*
- * Return the new list.
- */
- Arg0.dword = D_List;
- BlkLoc(Arg0) = (union block *) hp;
- Return;
- }
-
- /*
- * member(X,x) - returns x if x is a member of set or table X otherwise fails.
- */
-
- FncDcl(member,2)
- {
- int res;
- register uword hn;
-
- if (Qual(Arg1))
- RunErr(122, &Arg1);
-
- switch (Type(Arg1)) {
- case T_Set:
- case T_Table:
- hn = hash(&Arg2);
- memb(BlkLoc(Arg1), &Arg2, hn, &res);
- break;
-
- default:
- RunErr(122, &Arg1);
- }
-
- /* If Arg2 is a member of Arg1 then "res" will have the
- * value 1 otherwise it will have the value 0.
- */
- if (res == 1) { /* It is a member. */
- Arg0 = Arg2; /* Return the member if it is in Arg1. */
- Return;
- }
- Fail;
- }
-
-
- /*
- * pop(x) - pop an element from beginning of list x.
- */
-
- FncDcl(pop,1)
- {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
-
- /*
- * Arg1 must be a list.
- */
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Fail if the list is empty.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
- if (hp->size <= 0)
- Fail;
-
- /*
- * Point bp to the first list-element block. If the first block has
- * no slots in use, point bp at the next list-element block.
- */
- bp = (struct b_lelem *) hp->listhead;
- if (bp->nused <= 0) {
- bp = (struct b_lelem *) bp->listnext;
- hp->listhead = (union block *) bp;
- bp->listprev = NULL;
- }
- /*
- * Locate first element and assign it to Arg0 for return.
- */
- i = bp->first;
- Arg0 = bp->lslots[i];
-
- /*
- * Set bp->first to new first element, or 0 if the block is now
- * empty. Decrement the usage count for the block and the size
- * of the list.
- */
- if (++i >= bp->nslots)
- i = 0;
- bp->first = i;
- bp->nused--;
- hp->size--;
- Return;
- }
-
- /*
- * pull(x) - pull an element from end of list x.
- */
-
- FncDcl(pull,1)
- {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
-
- /*
- * Arg1 must be a list.
- */
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Point at list header block and fail if the list is empty.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
- if (hp->size <= 0)
- Fail;
- /*
- * Point bp at the last list element block. If the last block has no
- * elements in use, point bp at the previous list element block.
- */
- bp = (struct b_lelem *) hp->listtail;
- if (bp->nused <= 0) {
- bp = (struct b_lelem *) bp->listprev;
- hp->listtail = (union block *) bp;
- bp->listnext = NULL;
- }
- /*
- * Set i to position of last element and assign the element to
- * Arg0 for return. Decrement the usage count for the block
- * and the size of the list.
- */
- i = bp->first + bp->nused - 1;
- if (i >= bp->nslots)
- i -= bp->nslots;
- Arg0 = bp->lslots[i];
- bp->nused--;
- hp->size--;
- Return;
- }
-
-
- /*
- * push(x,val) - push val onto beginning of list x.
- */
- FncDcl(push,2)
- {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
- static two = 2; /* some compilers generat bad code for
- division by a constant that's a power of 2 */
-
-
- /*
- * Arg1 must be a list.
- */
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Point hp at the list-header block and bp at the first
- * list-element block.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
- bp = (struct b_lelem *) hp->listhead;
-
- /*
- * If the first list-element block is full, allocate a new
- * list-element block, make it the first list-element block,
- * and make it the previous block of the former first list-element
- * block.
- */
- if (bp->nused >= bp->nslots) {
- /*
- * Set i to the size of block to allocate.
- */
- i = hp->size / two;
- if (i < MinListSlots)
- i = MinListSlots;
-
- /*
- * Ensure space for a new list element block. If the block can't
- * be allocated, try smaller blocks.
- */
- while (blkreq((word)sizeof(struct b_lelem) +
- i * sizeof(struct descrip)) == Error) {
- i /= 4;
- if (i < MinListSlots)
- RunErr(0, NULL);
- }
- /*
- * Reset hp in case there was a garbage collection.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
-
- bp = alclstb(i, (word)0, (word)0);
- hp->listhead->lelem.listprev = (union block *) bp;
- bp->listnext = hp->listhead;
- hp->listhead = (union block *) bp;
- }
-
- /*
- * Set i to position of new first element and assign val (Arg2) to
- * that element.
- */
- i = bp->first - 1;
- if (i < 0)
- i = bp->nslots - 1;
- bp->lslots[i] = Arg2;
- /*
- * Adjust value of location of first element, block usage count,
- * and current list size.
- */
- bp->first = i;
- bp->nused++;
- hp->size++;
- /*
- * Return the list.
- */
- Arg0 = Arg1;
- Return;
- }
-
-
- /*
- * put(x,val) - put val onto end of list x.
- */
-
- FncDcl(put,2)
- {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
- static two = 2; /* some compilers generate bad code for
- division by a constant that's a power of 2 */
-
- /*
- * Arg1 must be a list.
- */
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Point hp at the list-header block and bp at the last
- * list-element block.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
- bp = (struct b_lelem *) hp->listtail;
-
- /*
- * If the last list-element block is full, allocate a new
- * list-element block, make it the first list-element block,
- * and make it the next block of the former last list-element
- * block.
- */
- if (bp->nused >= bp->nslots) {
- /*
- * Set i to the size of block to allocate.
- */
- i = hp->size / two;
- if (i < MinListSlots)
- i = MinListSlots;
-
- /*
- * Ensure space for a new list element block. If the block can't
- * be allocated, try smaller blocks.
- */
- while (blkreq((word)sizeof(struct b_lelem) +
- i * sizeof(struct descrip)) == Error) {
- i /= 4;
- if (i < MinListSlots)
- RunErr(0, NULL);
- }
- /*
- * Reset hp in case there was a garbage collection.
- */
- hp = (struct b_list *) BlkLoc(Arg1);
-
- bp = alclstb(i, (word)0, (word)0);
- hp->listtail->lelem.listnext = (union block *) bp;
- bp->listprev = hp->listtail;
- hp->listtail = (union block *) bp;
- }
-
- /*
- * Set i to position of new last element and assign Arg2 to
- * that element.
- */
- i = bp->first + bp->nused;
- if (i >= bp->nslots)
- i -= bp->nslots;
- bp->lslots[i] = Arg2;
-
- /*
- * Adjust block usage count and current list size.
- */
- bp->nused++;
- hp->size++;
-
- /*
- * Return the list.
- */
- Arg0 = Arg1;
- Return;
- }
-
- /*
- * set(list) - create a set with members in list.
- * The members are linked into hash chains which are
- * arranged in increasing order by hash number.
- */
- FncDcl(set,1)
- {
- register uword hn;
- register dptr pd;
- register union block *ps, *pb;
- struct b_selem *ne;
- union block **pe;
- int res;
- word i, j;
-
- if (ChkNull(Arg1)) { /* Create empty set */
- ps = hmake(T_Set, (word)0, (word)0);
- if (ps == NULL)
- RunErr(0,NULL);
- Arg0.dword = D_Set;
- BlkLoc(Arg0) = ps;
- Return;
- }
-
- if (Arg1.dword != D_List)
- RunErr(108, &Arg1);
-
- /*
- * Make a set of the appropriate size.
- */
- ps = hmake(T_Set, (word)0, BlkLoc(Arg1)->list.size);
- if (ps == NULL)
- RunErr(0, NULL);
-
- /*
- * Chain through each list block and for
- * each element contained in the block
- * insert the element into the set if not there.
- */
- for (pb = BlkLoc(Arg1)->list.listhead; pb != NULL; pb = pb->lelem.listnext) {
- for (i = 0; i < pb->lelem.nused; i++) {
- j = pb->lelem.first + i;
- if (j >= pb->lelem.nslots)
- j -= pb->lelem.nslots;
- pd = &pb->lelem.lslots[j];
- pe = memb(ps, pd, hn = hash(pd), &res);
- if (res == 0) {
- ne = alcselem(pd,hn);
- addmem((struct b_set *)ps, ne, pe);
- }
- }
- }
- Arg0.dword = D_Set;
- BlkLoc(Arg0) = ps;
- Return;
- }
-
- /*
- * table(x) - create a table with default value x.
- */
- FncDcl(table,1)
- {
- union block *bp;
-
- bp = hmake(T_Table, (word)0, (word)0);
- if (bp == NULL)
- RunErr(0, NULL);
- bp->table.defvalue = Arg1;
- Arg0.dword = D_Table;
- BlkLoc(Arg0) = bp;
- Return;
- }
-