home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: rstruct.c
- * Contents: addmem, cplist, cpset, hmake, hchain, hgener, hgrow, hshrink, memb
- */
-
- #include "../h/config.h"
- #include "../h/rt.h"
- #include "rproto.h"
-
- /*
- * addmem - add a new set element block in the correct spot in
- * the bucket chain.
- */
-
- novalue addmem(ps,pe,pl)
- union block **pl;
- struct b_set *ps;
- struct b_selem *pe;
- {
- ps->size++;
- if (*pl != NULL )
- pe->clink = *pl;
- *pl = (union block *) pe;
- }
-
- /*
- * cplist(dp1,dp2,i,j) - copy sublist dp1[i:j] into dp2.
- */
-
- int cplist(dp1, dp2, i, j)
- dptr dp1, dp2;
- word i, j;
- {
- register dptr dp;
- word size, nslots;
- struct b_list *lp1, *lp2;
- struct b_lelem *bp1, *bp2;
-
- /*
- * Calculate the size of the sublist and fail if it's less than 0.
- * Also round nslots up to the minimum list block size.
- */
- size = nslots = j - i;
-
- /*
- * Get pointers to the list and list elements for the source list
- * (bp1, lp1) and the sublist (bp2, lp2).
- */
- if (blkreq(sizeof(struct b_list) + sizeof(struct b_lelem) +
- nslots * sizeof(struct descrip)) == Error)
- return Error;
- lp1 = (struct b_list *) BlkLoc(*dp1);
- bp1 = (struct b_lelem *) lp1->listhead;
- lp2 = (struct b_list *) alclist(size);
- bp2 = (struct b_lelem *) alclstb(nslots, (word)0, size);
- lp2->listhead = lp2->listtail = (union block *) bp2;
- dp = bp2->lslots;
-
- /*
- * Locate the block containing element i in the source list.
- */
- if (size > 0) {
- while (i > bp1->nused) {
- i -= bp1->nused;
- bp1 = (struct b_lelem *) bp1->listnext;
- }
- }
-
- /*
- * Copy elements from the source list into the sublist, moving to
- * the next list block in the source list when all elements in a
- * block have been copied.
- */
- while (size > 0) {
- j = bp1->first + i - 1;
- if (j >= bp1->nslots)
- j -= bp1->nslots;
- *dp++ = bp1->lslots[j];
- if (++i > bp1->nused) {
- i = 1;
- bp1 = (struct b_lelem *) bp1->listnext;
- }
- size--;
- }
-
- /*
- * Fix type and location fields for the new list.
- */
- dp2->dword = D_List;
- BlkLoc(*dp2) = (union block *) lp2;
- return Success;
- }
-
- /*
- * cpset(dp1,dp2,n) - copy set dp1 to dp2, reserving memory for n entries.
- */
- int cpset(dp1, dp2, n)
- dptr dp1, dp2;
- word n;
- {
- register union block **tp, *ep, *old, *new;
- register struct b_slots *seg;
- register word i, slotnum;
-
- /*
- * Make a new set organized like dp1, with room for n elements.
- */
- new = hmake(T_Set, BlkLoc(*dp1)->set.mask + 1, n);
- if (new == NULL)
- return Error;
- /*
- * Copy the header and slot blocks.
- */
- old = BlkLoc(*dp1);
- new->set.size = old->set.size; /* actual set size */
- new->set.mask = old->set.mask; /* hash mask */
- for (i = 0; i < HSegs && old->set.hdir[i] != NULL; i++)
- memcopy((char *)new->set.hdir[i], (char *)old->set.hdir[i],
- old->set.hdir[i]->blksize);
- /*
- * Work down the chain of element blocks in each bucket
- * and create identical chains in new set.
- */
- for (i = 0; i < HSegs && (seg = new->set.hdir[i]) != NULL; i++)
- for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
- tp = &seg->hslots[slotnum];
- for (ep = *tp; ep != NULL; ep = *tp) {
- *tp = (union block *)alcselem(&ep->selem.setmem, ep->selem.hashnum);
- (*tp)->selem.clink = ep->selem.clink;
- tp = &(*tp)->selem.clink;
- }
- }
- dp2->dword = D_Set;
- BlkLoc(*dp2) = new;
- if (TooSparse(new))
- hshrink(dp2);
- return Success;
- }
-
- /*
- * hmake - make a hash structure (Set or Table) with a given number of slots.
- * hmake also ensures adequate storage for *nelem* elements, but does not
- * allocate then. If *nslots* is zero, a value appropriate for *nelem*
- * elements is chosen.
- */
- union block *hmake(tcode, nslots, nelem)
- int tcode;
- word nslots, nelem;
- {
- word seg, t, nbytes, blksize, elemsize;
- union block *blk;
-
- if (nslots == 0)
- nslots = (nelem + MaxHLoad - 1) / MaxHLoad;
- for (seg = t = 0; seg < (HSegs - 1) && (t += segsize[seg]) < nslots; seg++)
- ;
- nslots = ((word)HSlots) << seg; /* ensure legal power of 2 */
- if (tcode == T_Table) {
- blksize = sizeof(struct b_table);
- elemsize = sizeof(struct b_telem);
- }
- else { /* T_Set */
- blksize = sizeof(struct b_set);
- elemsize = sizeof(struct b_selem);
- }
- nbytes = blksize + (seg + 1) * (sizeof(struct b_slots) - (HSlots*WordSize)) +
- nslots * WordSize + nelem * elemsize;
- if (blkreq(nbytes) == Error)
- return NULL; /* sorry, no memory */
- blk = alchash(tcode);
- for (; seg >= 0; seg--)
- blk->set.hdir[seg] = alcsegment(segsize[seg]);
- blk->set.mask = nslots - 1;
- return blk;
- }
-
- /*
- * hchain - return a pointer to the word that points to the head of the hash
- * chain for hash number hn in hashed structure s.
- */
-
- /*
- * lookup table for log to base 2; must have powers of 2 through (HSegs-1)/2.
- */
- static unsigned char log2[] = {
- 0,1,2,2, 3,3,3,3, 4,4,4,4, 4,4,4,4, 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5,
- };
-
- union block **hchain(pb, hn)
- union block *pb;
- register uword hn;
- {
- register struct b_set *ps;
- register word slotnum, segnum, segslot;
-
- ps = (struct b_set *)pb;
- slotnum = hn & ps->mask;
- if (slotnum >= HSlots * sizeof(log2))
- segnum = log2[slotnum >> (LogHSlots + HSegs/2)] + HSegs/2;
- else
- segnum = log2[slotnum >> LogHSlots];
- segslot = hn & (segsize[segnum] - 1);
- return &ps->hdir[segnum]->hslots[segslot];
- }
-
- /*
- * hgener - agent function to generate the elements of a hashed structure.
- *
- * Arg1 = set or table to enumerate
- * Arg2 = integer value indicating desired action:
- * 0 generate set elements
- * 1 generate table keys
- * 2 generate table values
- *
- * Carefully generate each element exactly once, even if the hash chains
- * split while suspended. Do this by recording the state of things at the
- * time of the split and checking past history when starting to process a
- * new chain.
- *
- * Elements inserted or deleted while the generator is suspended may or
- * may not be generated.
- *
- * We assume that no structure *shrinks* after its initial creation; they
- * only *grow*.
- */
-
- AgtDcl(hgener)
- {
- int i, segnum;
- word d, m, func, slotnum;
- uword hn;
- union block *ep;
-
- word tmask; /* structure mask before suspension */
- word sgmask[HSegs]; /* mask being used when the segment was created */
- uword sghash[HSegs]; /* hashnum in process when the segment was created */
-
- for (i = 0; i < HSegs; i++)
- sghash[i] = sgmask[i] = 0; /* set initial state */
- tmask = BlkLoc(Arg1)->table.mask;
-
- func = IntVal(Arg2); /* save function code */
- Arg2.dword = D_Telem; /* use Arg2 to tend address */
-
- for (segnum = 0; segnum < HSegs; segnum++) {
- if (BlkLoc(Arg1)->table.hdir[segnum] == NULL)
- break;
- for (slotnum = 0; slotnum < segsize[segnum]; slotnum++) {
- ep = BlkLoc(Arg1)->table.hdir[segnum]->hslots[slotnum];
- /*
- * Check to see if parts of this hash chain were already processed.
- * This could happen if the elements were in a different chain,
- * but a split occurred while we were suspended.
- */
- for (i = segnum; (m = sgmask[i]) != 0; i--) {
- d = (word)(m & slotnum) - (word)(m & sghash[i]);
- if (d < 0) /* if all elements processed earlier */
- ep = NULL; /* skip this slot */
- else if (d == 0) {
- /*
- * This chain was split from its parent while the parent was
- * being processed. Skip past elements already processed.
- */
- while (ep != NULL && ep->telem.hashnum <= sghash[i])
- ep = ep->telem.clink;
- }
- }
- /*
- * Process the elements of the hash chain, in turn.
- */
- while (ep != NULL) {
- switch ((int)func) {
- case 0: Arg0 = ep->selem.setmem; break;
- case 1: Arg0 = ep->telem.tref; break;
- case 2: Arg0 = ep->telem.tval; break;
- }
- BlkLoc(Arg2) = ep; /* save pointer, so it gets tended */
- Suspend; /* suspend, returning current element */
- ep = BlkLoc(Arg2); /* restore pointer */
-
- if (BlkLoc(Arg1)->table.mask != tmask &&
- (ep->telem.clink == NULL ||
- ep->telem.clink->telem.hashnum != ep->telem.hashnum)) {
- /*
- * The set or table's hash buckets split, once or more, while
- * we were suspended. (We notice this unless the next entry
- * has same hash value as the current one. In that case we
- * ignore it for now and will pick it up on the next pass.)
- *
- * Make a note of the current state.
- */
- hn = ep->telem.hashnum;
- for (i = 1; i < HSegs; i++)
- if ((((word)HSlots) << (i - 1)) > tmask) {
- /*
- * For the newly created segments only, save the mask and
- * hash number being processed at time of creation.
- */
- sgmask[i] = tmask;
- sghash[i] = hn;
- }
- tmask = BlkLoc(Arg1)->table.mask;
- /*
- * Find the next element in our original segment by starting
- * from the beginning and skipping through the current hash
- * number. We can't just follow the link from the current
- * element, because it may have moved to a new segment.
- */
- ep = BlkLoc(Arg1)->table.hdir[segnum]->hslots[slotnum];
- while (ep != NULL && ep->telem.hashnum <= hn)
- ep = ep->telem.clink;
- }
-
- else {
- /*
- * Nothing happened during the suspend, or else if it did we're
- * between items with identical hash numbers. Just move on.
- */
- ep = ep->telem.clink;
- }
- }
- }
- }
- Fail;
- }
-
- /*
- * hgrow - split a hashed structure (doubling the buckets) for faster access.
- */
-
- novalue hgrow(dp)
- dptr dp;
- {
- register union block **tp0, **tp1, *ep;
- register word newslots, slotnum, segnum;
- struct b_set *ps;
- struct b_slots *seg, *newseg;
- union block **curslot;
-
- ps = (struct b_set *)BlkLoc(*dp);
- if (ps->hdir[HSegs-1] != NULL)
- return; /* can't split further */
- newslots = ps->mask + 1;
- if (blkreq(sizeof(struct b_slots) + (newslots - HSlots) * WordSize) == Error)
- return; /* sorry, no memory */
- ps = (struct b_set *)BlkLoc(*dp); /* refresh address -- may have moved */
- newseg = alcsegment(newslots);
-
- curslot = newseg->hslots;
- for (segnum = 0; (seg = ps->hdir[segnum]) != NULL; segnum++)
- for (slotnum = 0; slotnum < segsize[segnum]; slotnum++) {
- tp0 = &seg->hslots[slotnum]; /* ptr to tail of old slot */
- tp1 = curslot++; /* ptr to tail of new slot */
- for (ep = *tp0; ep != NULL; ep = ep->selem.clink) {
- if ((ep->selem.hashnum & newslots) == 0) {
- *tp0 = ep; /* element does not move */
- tp0 = &ep->selem.clink;
- }
- else {
- *tp1 = ep; /* element moves to new slot */
- tp1 = &ep->selem.clink;
- }
- }
- *tp0 = *tp1 = NULL;
- }
- ps->hdir[segnum] = newseg;
- ps->mask = (ps->mask << 1) | 1;
- }
-
- /*
- * hshrink - combine buckets in a set or table that is too sparse.
- *
- * Call this only for newly created structures. Shrinking an active structure
- * can wreak havoc on suspended generators.
- */
- novalue hshrink(dp)
- dptr dp;
- {
- register union block **tp, *ep0, *ep1;
- int topseg, curseg;
- word slotnum;
- struct b_set *ps;
- struct b_slots *seg;
- union block **uppslot;
-
- ps = (struct b_set *)BlkLoc(*dp);
- topseg = 0;
- for (topseg = 1; topseg < HSegs && ps->hdir[topseg] != NULL; topseg++)
- ;
- topseg--;
- while (TooSparse(ps)) {
- uppslot = ps->hdir[topseg]->hslots;
- ps->hdir[topseg--] = NULL;
- for (curseg = 0; (seg = ps->hdir[curseg]) != NULL; curseg++)
- for (slotnum = 0; slotnum < segsize[curseg]; slotnum++) {
- tp = &seg->hslots[slotnum]; /* tail pointer */
- ep0 = seg->hslots[slotnum]; /* lower slot entry pointer */
- ep1 = *uppslot++; /* upper slot entry pointer */
- while (ep0 != NULL && ep1 != NULL)
- if (ep0->selem.hashnum < ep1->selem.hashnum) {
- *tp = ep0;
- tp = &ep0->selem.clink;
- ep0 = ep0->selem.clink;
- }
- else {
- *tp = ep1;
- tp = &ep1->selem.clink;
- ep1 = ep1->selem.clink;
- }
- while (ep0 != NULL) {
- *tp = ep0;
- tp = &ep0->selem.clink;
- ep0 = ep0->selem.clink;
- }
- while (ep1 != NULL) {
- *tp = ep1;
- tp = &ep1->selem.clink;
- ep1 = ep1->selem.clink;
- }
- }
- ps->mask >>= 1;
- }
- }
-
- /*
- * memb - sets res flag to 1 if x is a member of a set or table, or to 0 if not.
- * Returns a pointer to the word which points to the element, or which
- * would point to it if it were there.
- */
-
- union block **memb(pb, x, hn, res)
- union block *pb;
- dptr x;
- register uword hn;
- int *res; /* pointer to integer result flag */
- {
- struct b_set *ps;
- register union block **lp;
- register struct b_selem *pe;
- register uword eh;
-
- ps = (struct b_set *)pb;
- lp = hchain(pb, hn);
- /*
- * Look for x in the hash chain.
- */
- *res = 0;
- while ((pe = (struct b_selem *)*lp) != NULL) {
- eh = pe->hashnum;
- if (eh > hn) /* too far - it isn't there */
- return lp;
- else if ((eh == hn) && (equiv(&pe->setmem, x))) {
- *res = 1;
- return lp;
- }
- /*
- * We haven't reached the right hashnumber yet or
- * the element isn't the right one so keep looking.
- */
- lp = &(pe->clink);
- }
- /*
- * At end of chain - not there.
- */
- return lp;
- }
-