HXRET hxstat(HXFILE * hp, HXSTAT * sp) { HXLOCAL loc, *locp = &loc; PAGENO pg; int i, nchains; if (!hp || !sp) return HXERR_BAD_REQUEST; ENTER(locp, hp, 0, 1); HXBUF *bufp = &locp->buf[0]; locp->mode = F_RDLCK; _hxlock(locp, 0, 0); _hxsize(locp); memset(sp, 0, sizeof *sp); sp->npages = locp->npages; _hxinitRefs(locp); for (pg = 1; pg < (unsigned)locp->npages; ++pg) { _hxload(locp, bufp, pg); if (IS_MAP(hp, pg)) continue; _hxsetRef(locp, pg, bufp->next); if (IS_HEAD(bufp->pgno)) { sp->head_bytes += bufp->used; } else if (bufp->used) { ++sp->ovfl_pages; sp->ovfl_bytes += bufp->used; } char *recp, *endp; FOR_EACH_REC(recp, bufp, endp) { ++sp->nrecs; sp->hash ^= RECHASH(recp); } if (!IS_HEAD(pg)) { COUNT nheads = 0; _hxfindHeads(locp, bufp); while (locp->vprev[nheads]) ++nheads; if (nheads > HX_MAX_SHARE) nheads = HX_MAX_SHARE; ++sp->share_hist[nheads]; } }
void do_fixhead( CHAR_DATA *ch, char *argument ) { char arg1[MSL]; CHAR_DATA *victim; argument = one_argument( argument, arg1 ); victim = get_char_world(ch, arg1); if ( arg1[0] == '\0' ) { send_to_char("Syntax: fixhead <player name>\n\r",ch); return; } if (victim == NULL) { send_to_char("They must be playing.\n\r", ch); return; } if (IS_NPC(victim)) { send_to_char("Not on NPC's\n\r", ch); return; } if ( !IS_HEAD(victim,LOST_HEAD) ) return stcf(ch,"They're not a head!\n\r"); REMOVE_BIT(victim->loc_hp[0],LOST_HEAD); REMOVE_BIT(victim->affected_by,AFF_POLYMORPH); if ( victim->morph ) free_string(victim->morph); stcf(ch,"%s has been fixed.\n\r",victim->name); stcf(victim,"You have been fixed!\n\r"); }
//--------------|--------------------------------------------- HXRET hxput(HXFILE * hp, char const *recp, int leng) { HXLOCAL loc, *locp = &loc; if (!hp || leng < 0 || !recp || leng > hxmaxrec(hp) || !(hp->mode & HX_UPDATE) || !hp->test) return HXERR_BAD_REQUEST; if (leng && !hx_test(hp, recp, leng)) return HXERR_BAD_RECORD; if (SCANNING(hp) && hx_diff(hp, recp, RECDATA(_hxcurrec(hp)))) return HXERR_BAD_REQUEST; ENTER(locp, hp, recp, 3); _hxlockset(locp, leng ? HIGH_LOCK : HEAD_LOCK); if (IS_MMAP(hp)) _hxremap(locp); int may_find = 1, loops = HX_MAX_CHAIN; int newsize = leng ? leng + sizeof(HXREC) : 0; HXBUF *currp = &locp->buf[0], *prevp = &locp->buf[1]; // If scanning is on an overflow page, and hxdel might // empty the page, hxput after hxnext can't just jump to // the right page, because (prevp) is not loaded, // so deleting currp would hard. _hxload(locp, currp, SCANNING(hp) && (leng || IS_HEAD(hp->buffer.pgno)) ? hp->buffer.pgno : locp->head); while (1) { int pos, hindpos, skip = 0; PAGENO nextpg = currp->next; if (!--loops) LEAVE(locp, HXERR_BAD_FILE); // Search for the key (an old record to be deleted). // If SCANNING: the file is locked, and the matching // record must be there. pos = !may_find ? -1 : !SCANNING(hp) ? _hxfind(locp, currp, locp->hash, recp, &hindpos) : currp->pgno == hp->buffer.pgno ? hp->currpos : -1; if (pos >= 0) { char *oldp = currp->data + pos; COUNT oldsize = RECSIZE(oldp); int delta = newsize - oldsize; locp->ret = RECLENG(oldp); may_find = 0; assert(!currp->delta); currp->delpos = pos; currp->delta = delta; if (!newsize) { // hxdel or remove after inserted previously. _hxremove(currp, pos, oldsize); currp->recs--; if (SCANNING(hp)) hp->recsize = 0; } else if (FITS(hp, currp, delta, 0)) { // replace if (delta) { memmove(oldp + newsize, oldp + oldsize, currp->used - pos - oldsize); currp->used += delta; STSH(leng, oldp + sizeof(PAGENO)); if (SCANNING(hp)) hp->recsize = newsize; DEINDEX(currp); // force indexify } memcpy(oldp + sizeof(HXREC), recp, leng); STAIN(currp); newsize = 0; } else if (SCANNING(hp)) { // At this point we are stuck: if we delete the old copy of // the record, we are committed to inserting the new copy // somewhere else, but that might require changing links // or even growing the file: a NO-NO during a hxnext scan. LEAVE(locp, HXERR_BAD_REQUEST); } else { // Delete old version and continue (insert elsewhere). _hxremove(currp, pos, oldsize); currp->recs--; } } if (currp->used && !IS_HEAD(currp->pgno) && SHRUNK(prevp)) skip = !_hxshift(locp, locp->head, 0, currp, prevp, NULL); // Insert the new record if it fits. if (newsize && FITS(hp, currp, newsize, 1)) { HXREC hdr; STLG(locp->hash, &hdr.hash); STSH(leng, &hdr.leng); _hxappend(currp, (char *)&hdr, sizeof hdr); _hxappend(currp, recp, leng); currp->recs++; newsize = 0; } // If the current page contains only data of OTHER heads // -- and hence, must be at the END of a chain -- // unlink it from this chain. If the page is empty, // unlink it AND put it in the freemap. if (IS_HEAD(currp->pgno)) { skip = 0; } else if (!currp->used) { skip = 1; _hxputfreed(locp, currp); if (SCANNING(hp) && hp->buffer.pgno == currp->pgno) hp->buffer.used = 0; } else if (currp->next || !SHRUNK(currp)) { skip = 0; } else if (!skip) { // If skip not set by _hxshift above... char const *rp, *ep; FOR_EACH_REC(rp, currp, ep) if (locp->head == _hxhead(locp, RECHASH(rp))) break; skip = rp == ep; // No recs for locp->head in this tail. } if (skip) LINK(prevp, nextpg); else SWAP(prevp, currp); sync_save(locp, currp); if (!newsize && !prevp->next) break; if (!newsize && !may_find && !SHRUNK(prevp)) break; if (prevp->next) { _hxload(locp, currp, prevp->next); continue; } // We are at the end of the chain, and rec not yet inserted. // Unlocking is necessary even if tail is not shared; // it may be hp->tail.pgno in some other process. if (!FILE_HELD(hp) && !IS_HEAD(prevp->pgno)) _hxunlock(locp, prevp->pgno, 1); // _hxshare/_hxfindfree may update the map (root etc). // Split MUST be locked before root, else risk deadlock. _hxlockset(locp, BOTH_LOCK); if (IS_MMAP(hp)) _hxremap(locp); // At this point assert: // - head is locked, split is locked, // - head matches hash, npages matches filesize. // After locking the split, no other process can change // the file size. may_find = 0; COUNT need = IS_HEAD(prevp->pgno) ? newsize : 0; if (!_hxshare(locp, currp, need) && !_hxgetfreed(locp, currp) && !_hxfindfree(locp, currp)) { // _hxgrow will zero samehead if it splits locp->head. PAGENO samehead = locp->head; // _hxgrow will change the file length. A concurrent // hxget/hxdel could miscalculate locp->head as // being the newly-added page. _hxlock(locp, locp->npages, 0); _hxgrow(locp, currp, need, &samehead); DEBUG3("head=%u samehead=%u", locp->head, samehead); if (!samehead) { _hxputfreed(locp, currp); _hxpoint(locp); _hxload(locp, currp, locp->head); loops = HX_MAX_CHAIN; continue; } } // _hxgrow may clobber prevp, so we reload it. Even if // prevp->pgno == locp->head, prevp may contain an // obsolete copy of the head page. The empty page is // always appended to head. _hxshare only returns true // if currp is head and currp->next is 0, so it can't // clobber it. _hxsave(locp, prevp); _hxload(locp, prevp, locp->head); LINK(currp, prevp->next); LINK(prevp, currp->pgno); currp->orig = DATASIZE(hp); // make SHRUNK be true }
//--------------|--------------------------------------------- HXRET hxshape(HXFILE * hp, double overload) { HXLOCAL loc, *locp = &loc; HXBUF *srcp, *dstp, *oldp; int pos, bitpos; PGINFO *atail, *ztail; PAGENO pg, pm, *aprev, *afree, *zfree; double totbytes = 0, fullbytes = 0, fullpages = 0; if (!hp || hp->buffer.pgno || hp->mode & HX_MMAP || !(hp->mode & HX_UPDATE)) return HXERR_BAD_REQUEST; ENTER(locp, hp, NULL, 3); _hxlock(locp, 0, 0); _hxsize(locp); srcp = &locp->buf[0]; dstp = &locp->buf[1]; oldp = &locp->buf[2]; _hxinitRefs(locp); // Populate vnext,vrefs,vtail for tail-merging: ztail = calloc(locp->npages / HXPGRATE + 1, sizeof(PGINFO)); locp->vtail = ztail; for (pg = 1; pg < locp->npages; ++pg) { PGINFO x = _hxpginfo(locp, pg); _hxsetRef(locp, pg, x.pgno); totbytes += x.used; if (x.pgno) // i.e. page.next != 0 ++fullpages, fullbytes += x.used; else if (x.used && !IS_HEAD(pg)) x.pgno = pg, *ztail++ = x; } // Sort vtail by (used), so that smallest+largest (used) // counts can be matched up; simple greedy-fill algorithm. qsort(locp->vtail, ztail - locp->vtail, sizeof *locp->vtail, (cmpfn_t) cmpused); // Combine tail pages where possible: for (atail = locp->vtail, --ztail; atail < ztail;) { if (!_FITS(hp, atail->used, atail->recs, ztail->used, ztail->recs)) { --ztail; continue; } // Merge is always from [atail] to [ztail], to maintain // ([ztail].used >= [ztail-1].used). if (atail->pgno < ztail->pgno) { PGINFO tmp = *atail; *atail = *ztail; *ztail = tmp; } _hxload(locp, srcp, atail->pgno); _hxload(locp, dstp, ztail->pgno); _hxappend(dstp, srcp->data, srcp->used); dstp->recs += srcp->recs; _hxsave(locp, dstp); _hxfindRefs(locp, srcp, srcp->pgno); for (aprev = locp->vprev; *aprev; ++aprev) PUTLINK(locp, *aprev, dstp->pgno); ztail->used += srcp->used; srcp->used = srcp->recs = 0; BUFLINK(locp, srcp, 0); _hxsave(locp, srcp); _hxalloc(locp, srcp->pgno, 0); ++atail; } // Now decide whether to grow or shrink the file. PAGENO overflows = 0; for (pg = 1; pg < locp->npages; ++pg) { if (IS_HEAD(pg)) { int loops = HX_MAX_CHAIN; for (pm = pg; (pm = locp->vnext[pm]); ++overflows) if (!--loops) LEAVE(locp, HXERR_BAD_FILE); } } PAGENO dpages = locp->dpages + overflows; PAGENO goodsize = dpages / (1.0 + overload); DEBUG("%.0f/%.0f=%0.f %.0f %lu/%.2f=%lu => %lu", fullbytes, fullpages, fullbytes / fullpages, totbytes, dpages, overload + 1, goodsize, _hxd2f(goodsize)); // "+1" for the root page goodsize = goodsize ? _hxd2f(goodsize) + 1 : 2; if (locp->npages <= goodsize) { // Increase dpages. // Note that _hxgrow always returns an ALLOCATED // overflow. It would be smarter to clear all the // map bits in one step at the end, the way // hxbuild sets all the map bits in one load/save. PAGENO junk = 0; while (locp->npages < goodsize) { _hxgrow(locp, dstp, DATASIZE(hp), &junk); _hxsave(locp, dstp); _hxalloc(locp, dstp->pgno, 0); } _hxflushfreed(locp, dstp); LEAVE(locp, HXNOTE); } // Build a list of free pgnos assert(sizeof *afree <= sizeof *locp->vtail); afree = zfree = (PAGENO *) locp->vtail; for (pg = pm = 0; pg < locp->npages; pg += HXPGRATE) { if (!VREF(locp, pg) && !IS_MAP(hp, pg)) *zfree++ = pg; } // Since we are decrementing npages BEFORE reading last page, // set locked such that _hxislocked gives correct answer. hp->locked |= LOCKED_BEYOND; // Work backward from end of file, trimming pages. for (; locp->npages > goodsize; --locp->npages) { PAGENO srchead = locp->npages - 1; PAGENO srctail, dsthead, dstneck, dsttail; int loops = HX_MAX_CHAIN; // EASIEST CASE: a map or unreferenced oveflow page if (srchead == zfree[-1] || IS_MAP(hp, srchead)) { assert(!VREF(locp, srchead) && !IS_HEAD(srchead)); --zfree; continue; } // EASIER CASE: an empty head page _hxload(locp, srcp, srchead); if (!srcp->used) continue; // Anything from here on might need 2 free pages if (zfree - afree < 2) break; --VREF(locp, srcp->next); STAIN(srcp); srcp->pgno = *afree++; # if 0 // hxshape does not work with MMAP as yet. if (hp->mmap) memcpy(&hp->mmap[(off_t) srcp->pgno * hp->pgsize], srcp->page, hp->pgsize); # endif _hxalloc(locp, srcp->pgno, 1); _hxsetRef(locp, srcp->pgno, srcp->next); // EASY CASE: an overflow page to relocate: if (!IS_HEAD(srchead)) { _hxsave(locp, srcp); _hxfindRefs(locp, srcp, srchead); for (aprev = locp->vprev; *aprev; ++aprev) PUTLINK(locp, *aprev, srcp->pgno); continue; } // HARD CASE: a head page to desplit: locp->hash = RECHASH(srcp->data); _hxpoint(locp); // recalc (dpages,head) dsthead = locp->head; dstneck = locp->vnext[dsthead]; dsttail = _hxgetRef(locp, dsthead, 0); srctail = _hxgetRef(locp, srchead, 0); // Append srchead to dsttail, or insert chain between // dsthead and vnext[dsthead]. If srctail is shared, // make a copy of * it first. if (dsttail == dsthead || VREF(locp, dsttail) == 1) { dsthead = dsttail; } else if (srctail == srchead) { BUFLINK(locp, srcp, dstneck); } else if (VREF(locp, srctail) == 1) { PUTLINK(locp, srctail, dstneck); } else if (srctail == dsttail) { if (dsttail != dstneck) { srctail = _hxgetRef(locp, srcp->pgno, srctail); if (srctail == srcp->pgno) { BUFLINK(locp, srcp, dstneck); } else { PUTLINK(locp, srctail, dstneck); } } } else { _hxload(locp, oldp, srctail); if (!oldp->used) LEAVE(locp, HXERR_BAD_FILE); _hxfresh(locp, dstp, *afree++); _hxalloc(locp, dstp->pgno, 1); _hxshift(locp, locp->head, srchead, oldp, dstp, dstp); _hxsave(locp, oldp); // This hack prevents the CHECK in _hxlink from // aborting when oldp contains a page that the // next iteration wants to PUTLINK. This ONLY occurs // in this code (I think). // TODO: can this happen in (hxfix,hxshape)?? oldp->pgno = -1; BUFLINK(locp, dstp, dstneck); _hxsave(locp, dstp); if (srcp->next == srctail) { BUFLINK(locp, srcp, dstp->pgno); } else { pg = _hxgetRef(locp, srchead, srctail); PUTLINK(locp, pg, dstp->pgno); } } _hxload(locp, dstp, dsthead); BUFLINK(locp, dstp, srcp->pgno); // Cannot early-out on !SHRUNK here (as "hxput" does); // new chain may have vacancies in two places. while (1) { if (!--loops) LEAVE(locp, HXERR_BAD_FILE); if (_hxshift(locp, locp->head, srchead, srcp, dstp, dstp)) { SWAP(srcp, dstp); } else { BUFLINK(locp, dstp, srcp->next); if (!srcp->used != !VREF(locp, srcp->pgno)) LEAVE(locp, HXERR_BAD_FILE); if (!srcp->used) { BUFLINK(locp, srcp, 0); *--afree = srcp->pgno; _hxalloc(locp, srcp->pgno, 0); } } _hxsave(locp, srcp); if (!dstp->next) break; _hxload(locp, srcp, dstp->next); } _hxsave(locp, dstp); } // npages was overdecremented by one in loop _hxresize(locp, locp->npages + 1); // Zero the freemap for all truncated overflow pages: pg = _hxmap(hp, locp->npages + HXPGRATE - locp->npages % HXPGRATE, &bitpos); _hxload(locp, dstp, pg); DEBUG2("clear map %lu from bit %d onward", pg, bitpos); pos = bitpos >> 3; dstp->data[pos++] &= ~(-1 << (bitpos & 7)); memset(dstp->data + pos, 0, DATASIZE(hp) - pos); STAIN(dstp); _hxsave(locp, dstp); LEAVE(locp, HXOKAY); }