michael@0: /*- michael@0: * Copyright (c) 1990, 1993, 1994 michael@0: * The Regents of the University of California. All rights reserved. michael@0: * michael@0: * This code is derived from software contributed to Berkeley by michael@0: * Margo Seltzer. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. ***REMOVED*** - see michael@0: * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change michael@0: * 4. Neither the name of the University nor the names of its contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND michael@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE michael@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE michael@0: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL michael@0: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS michael@0: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) michael@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT michael@0: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY michael@0: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@0: * SUCH DAMAGE. michael@0: */ michael@0: michael@0: #if defined(unix) michael@0: #define MY_LSEEK lseek michael@0: #else michael@0: #define MY_LSEEK new_lseek michael@0: extern long new_lseek(int fd, long pos, int start); michael@0: #endif michael@0: michael@0: #if defined(LIBC_SCCS) && !defined(lint) michael@0: static char sccsid[] = "@(#)hash_page.c 8.7 (Berkeley) 8/16/94"; michael@0: #endif /* LIBC_SCCS and not lint */ michael@0: michael@0: /* michael@0: * PACKAGE: hashing michael@0: * michael@0: * DESCRIPTION: michael@0: * Page manipulation for hashing package. michael@0: * michael@0: * ROUTINES: michael@0: * michael@0: * External michael@0: * __get_page michael@0: * __add_ovflpage michael@0: * Internal michael@0: * overflow_page michael@0: * open_temp michael@0: */ michael@0: #ifndef macintosh michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(macintosh) michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #if defined(_WIN32) || defined(_WINDOWS) michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include "mcom_db.h" michael@0: #include "hash.h" michael@0: #include "page.h" michael@0: /* #include "extern.h" */ michael@0: michael@0: extern int mkstempflags(char *path, int extraFlags); michael@0: michael@0: static uint32 *fetch_bitmap __P((HTAB *, uint32)); michael@0: static uint32 first_free __P((uint32)); michael@0: static int open_temp __P((HTAB *)); michael@0: static uint16 overflow_page __P((HTAB *)); michael@0: static void squeeze_key __P((uint16 *, const DBT *, const DBT *)); michael@0: static int ugly_split michael@0: __P((HTAB *, uint32, BUFHEAD *, BUFHEAD *, int, int)); michael@0: michael@0: #define PAGE_INIT(P) { \ michael@0: ((uint16 *)(P))[0] = 0; \ michael@0: ((uint16 *)(P))[1] = hashp->BSIZE - 3 * sizeof(uint16); \ michael@0: ((uint16 *)(P))[2] = hashp->BSIZE; \ michael@0: } michael@0: michael@0: /* implement a new lseek using lseek that michael@0: * writes zero's when extending a file michael@0: * beyond the end. michael@0: */ michael@0: long new_lseek(int fd, long offset, int origin) michael@0: { michael@0: long cur_pos=0; michael@0: long end_pos=0; michael@0: long seek_pos=0; michael@0: michael@0: if(origin == SEEK_CUR) michael@0: { michael@0: if(offset < 1) michael@0: return(lseek(fd, offset, SEEK_CUR)); michael@0: michael@0: cur_pos = lseek(fd, 0, SEEK_CUR); michael@0: michael@0: if(cur_pos < 0) michael@0: return(cur_pos); michael@0: } michael@0: michael@0: end_pos = lseek(fd, 0, SEEK_END); michael@0: if(end_pos < 0) michael@0: return(end_pos); michael@0: michael@0: if(origin == SEEK_SET) michael@0: seek_pos = offset; michael@0: else if(origin == SEEK_CUR) michael@0: seek_pos = cur_pos + offset; michael@0: else if(origin == SEEK_END) michael@0: seek_pos = end_pos + offset; michael@0: else michael@0: { michael@0: assert(0); michael@0: return(-1); michael@0: } michael@0: michael@0: /* the seek position desired is before the michael@0: * end of the file. We don't need michael@0: * to do anything special except the seek. michael@0: */ michael@0: if(seek_pos <= end_pos) michael@0: return(lseek(fd, seek_pos, SEEK_SET)); michael@0: michael@0: /* the seek position is beyond the end of the michael@0: * file. Write zero's to the end. michael@0: * michael@0: * we are already at the end of the file so michael@0: * we just need to "write()" zeros for the michael@0: * difference between seek_pos-end_pos and michael@0: * then seek to the position to finish michael@0: * the call michael@0: */ michael@0: { michael@0: char buffer[1024]; michael@0: long len = seek_pos-end_pos; michael@0: memset(&buffer, 0, 1024); michael@0: while(len > 0) michael@0: { michael@0: write(fd, (char*)&buffer, (size_t)(1024 > len ? len : 1024)); michael@0: len -= 1024; michael@0: } michael@0: return(lseek(fd, seek_pos, SEEK_SET)); michael@0: } michael@0: michael@0: } michael@0: michael@0: /* michael@0: * This is called AFTER we have verified that there is room on the page for michael@0: * the pair (PAIRFITS has returned true) so we go right ahead and start moving michael@0: * stuff on. michael@0: */ michael@0: static void michael@0: putpair(char *p, const DBT *key, DBT * val) michael@0: { michael@0: register uint16 *bp, n, off; michael@0: michael@0: bp = (uint16 *)p; michael@0: michael@0: /* Enter the key first. */ michael@0: n = bp[0]; michael@0: michael@0: off = OFFSET(bp) - key->size; michael@0: memmove(p + off, key->data, key->size); michael@0: bp[++n] = off; michael@0: michael@0: /* Now the data. */ michael@0: off -= val->size; michael@0: memmove(p + off, val->data, val->size); michael@0: bp[++n] = off; michael@0: michael@0: /* Adjust page info. */ michael@0: bp[0] = n; michael@0: bp[n + 1] = off - ((n + 3) * sizeof(uint16)); michael@0: bp[n + 2] = off; michael@0: } michael@0: michael@0: /* michael@0: * Returns: michael@0: * 0 OK michael@0: * -1 error michael@0: */ michael@0: extern int michael@0: __delpair(HTAB *hashp, BUFHEAD *bufp, int ndx) michael@0: { michael@0: register uint16 *bp, newoff; michael@0: register int n; michael@0: uint16 pairlen; michael@0: michael@0: bp = (uint16 *)bufp->page; michael@0: n = bp[0]; michael@0: michael@0: if (bp[ndx + 1] < REAL_KEY) michael@0: return (__big_delete(hashp, bufp)); michael@0: if (ndx != 1) michael@0: newoff = bp[ndx - 1]; michael@0: else michael@0: newoff = hashp->BSIZE; michael@0: pairlen = newoff - bp[ndx + 1]; michael@0: michael@0: if (ndx != (n - 1)) { michael@0: /* Hard Case -- need to shuffle keys */ michael@0: register int i; michael@0: register char *src = bufp->page + (int)OFFSET(bp); michael@0: uint32 dst_offset = (uint32)OFFSET(bp) + (uint32)pairlen; michael@0: register char *dst = bufp->page + dst_offset; michael@0: uint32 length = bp[ndx + 1] - OFFSET(bp); michael@0: michael@0: /* michael@0: * +-----------+XXX+---------+XXX+---------+---------> +infinity michael@0: * | | | | michael@0: * 0 src_offset dst_offset BSIZE michael@0: * michael@0: * Dst_offset is > src_offset, so if src_offset were bad, dst_offset michael@0: * would be too, therefore we check only dst_offset. michael@0: * michael@0: * If dst_offset is >= BSIZE, either OFFSET(bp), or pairlen, or both michael@0: * is corrupted. michael@0: * michael@0: * Once we know dst_offset is < BSIZE, we can subtract it from BSIZE michael@0: * to get an upper bound on length. michael@0: */ michael@0: if(dst_offset > (uint32)hashp->BSIZE) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: if(length > (uint32)(hashp->BSIZE - dst_offset)) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: memmove(dst, src, length); michael@0: michael@0: /* Now adjust the pointers */ michael@0: for (i = ndx + 2; i <= n; i += 2) { michael@0: if (bp[i + 1] == OVFLPAGE) { michael@0: bp[i - 2] = bp[i]; michael@0: bp[i - 1] = bp[i + 1]; michael@0: } else { michael@0: bp[i - 2] = bp[i] + pairlen; michael@0: bp[i - 1] = bp[i + 1] + pairlen; michael@0: } michael@0: } michael@0: } michael@0: /* Finally adjust the page data */ michael@0: bp[n] = OFFSET(bp) + pairlen; michael@0: bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(uint16); michael@0: bp[0] = n - 2; michael@0: hashp->NKEYS--; michael@0: michael@0: bufp->flags |= BUF_MOD; michael@0: return (0); michael@0: } michael@0: /* michael@0: * Returns: michael@0: * 0 ==> OK michael@0: * -1 ==> Error michael@0: */ michael@0: extern int michael@0: __split_page(HTAB *hashp, uint32 obucket, uint32 nbucket) michael@0: { michael@0: register BUFHEAD *new_bufp, *old_bufp; michael@0: register uint16 *ino; michael@0: register uint16 *tmp_uint16_array; michael@0: register char *np; michael@0: DBT key, val; michael@0: uint16 n, ndx; michael@0: int retval; michael@0: uint16 copyto, diff, moved; michael@0: size_t off; michael@0: char *op; michael@0: michael@0: copyto = (uint16)hashp->BSIZE; michael@0: off = (uint16)hashp->BSIZE; michael@0: old_bufp = __get_buf(hashp, obucket, NULL, 0); michael@0: if (old_bufp == NULL) michael@0: return (-1); michael@0: new_bufp = __get_buf(hashp, nbucket, NULL, 0); michael@0: if (new_bufp == NULL) michael@0: return (-1); michael@0: michael@0: old_bufp->flags |= (BUF_MOD | BUF_PIN); michael@0: new_bufp->flags |= (BUF_MOD | BUF_PIN); michael@0: michael@0: ino = (uint16 *)(op = old_bufp->page); michael@0: np = new_bufp->page; michael@0: michael@0: moved = 0; michael@0: michael@0: for (n = 1, ndx = 1; n < ino[0]; n += 2) { michael@0: if (ino[n + 1] < REAL_KEY) { michael@0: retval = ugly_split(hashp, obucket, old_bufp, new_bufp, michael@0: (int)copyto, (int)moved); michael@0: old_bufp->flags &= ~BUF_PIN; michael@0: new_bufp->flags &= ~BUF_PIN; michael@0: return (retval); michael@0: michael@0: } michael@0: key.data = (uint8 *)op + ino[n]; michael@0: michael@0: /* check here for ino[n] being greater than michael@0: * off. If it is then the database has michael@0: * been corrupted. michael@0: */ michael@0: if(ino[n] > off) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: key.size = off - ino[n]; michael@0: michael@0: #ifdef DEBUG michael@0: /* make sure the size is positive */ michael@0: assert(((int)key.size) > -1); michael@0: #endif michael@0: michael@0: if (__call_hash(hashp, (char *)key.data, key.size) == obucket) { michael@0: /* Don't switch page */ michael@0: diff = copyto - off; michael@0: if (diff) { michael@0: copyto = ino[n + 1] + diff; michael@0: memmove(op + copyto, op + ino[n + 1], michael@0: off - ino[n + 1]); michael@0: ino[ndx] = copyto + ino[n] - ino[n + 1]; michael@0: ino[ndx + 1] = copyto; michael@0: } else michael@0: copyto = ino[n + 1]; michael@0: ndx += 2; michael@0: } else { michael@0: /* Switch page */ michael@0: val.data = (uint8 *)op + ino[n + 1]; michael@0: val.size = ino[n] - ino[n + 1]; michael@0: michael@0: /* if the pair doesn't fit something is horribly michael@0: * wrong. LJM michael@0: */ michael@0: tmp_uint16_array = (uint16*)np; michael@0: if(!PAIRFITS(tmp_uint16_array, &key, &val)) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: putpair(np, &key, &val); michael@0: moved += 2; michael@0: } michael@0: michael@0: off = ino[n + 1]; michael@0: } michael@0: michael@0: /* Now clean up the page */ michael@0: ino[0] -= moved; michael@0: FREESPACE(ino) = copyto - sizeof(uint16) * (ino[0] + 3); michael@0: OFFSET(ino) = copyto; michael@0: michael@0: #ifdef DEBUG3 michael@0: (void)fprintf(stderr, "split %d/%d\n", michael@0: ((uint16 *)np)[0] / 2, michael@0: ((uint16 *)op)[0] / 2); michael@0: #endif michael@0: /* unpin both pages */ michael@0: old_bufp->flags &= ~BUF_PIN; michael@0: new_bufp->flags &= ~BUF_PIN; michael@0: return (0); michael@0: } michael@0: michael@0: /* michael@0: * Called when we encounter an overflow or big key/data page during split michael@0: * handling. This is special cased since we have to begin checking whether michael@0: * the key/data pairs fit on their respective pages and because we may need michael@0: * overflow pages for both the old and new pages. michael@0: * michael@0: * The first page might be a page with regular key/data pairs in which case michael@0: * we have a regular overflow condition and just need to go on to the next michael@0: * page or it might be a big key/data pair in which case we need to fix the michael@0: * big key/data pair. michael@0: * michael@0: * Returns: michael@0: * 0 ==> success michael@0: * -1 ==> failure michael@0: */ michael@0: michael@0: /* the maximum number of loops we will allow UGLY split to chew michael@0: * on before we assume the database is corrupted and throw it michael@0: * away. michael@0: */ michael@0: #define MAX_UGLY_SPLIT_LOOPS 10000 michael@0: michael@0: static int michael@0: ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp, michael@0: BUFHEAD *new_bufp,/* Same as __split_page. */ int copyto, int moved) michael@0: /* int copyto; First byte on page which contains key/data values. */ michael@0: /* int moved; Number of pairs moved to new page. */ michael@0: { michael@0: register BUFHEAD *bufp; /* Buffer header for ino */ michael@0: register uint16 *ino; /* Page keys come off of */ michael@0: register uint16 *np; /* New page */ michael@0: register uint16 *op; /* Page keys go on to if they aren't moving */ michael@0: uint32 loop_detection=0; michael@0: michael@0: BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ michael@0: DBT key, val; michael@0: SPLIT_RETURN ret; michael@0: uint16 n, off, ov_addr, scopyto; michael@0: char *cino; /* Character value of ino */ michael@0: int status; michael@0: michael@0: bufp = old_bufp; michael@0: ino = (uint16 *)old_bufp->page; michael@0: np = (uint16 *)new_bufp->page; michael@0: op = (uint16 *)old_bufp->page; michael@0: last_bfp = NULL; michael@0: scopyto = (uint16)copyto; /* ANSI */ michael@0: michael@0: n = ino[0] - 1; michael@0: while (n < ino[0]) { michael@0: michael@0: michael@0: /* this function goes nuts sometimes and never returns. michael@0: * I havent found the problem yet but I need a solution michael@0: * so if we loop too often we assume a database curruption error michael@0: * :LJM michael@0: */ michael@0: loop_detection++; michael@0: michael@0: if(loop_detection > MAX_UGLY_SPLIT_LOOPS) michael@0: return DATABASE_CORRUPTED_ERROR; michael@0: michael@0: if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { michael@0: if ((status = __big_split(hashp, old_bufp, michael@0: new_bufp, bufp, bufp->addr, obucket, &ret))) michael@0: return (status); michael@0: old_bufp = ret.oldp; michael@0: if (!old_bufp) michael@0: return (-1); michael@0: op = (uint16 *)old_bufp->page; michael@0: new_bufp = ret.newp; michael@0: if (!new_bufp) michael@0: return (-1); michael@0: np = (uint16 *)new_bufp->page; michael@0: bufp = ret.nextp; michael@0: if (!bufp) michael@0: return (0); michael@0: cino = (char *)bufp->page; michael@0: ino = (uint16 *)cino; michael@0: last_bfp = ret.nextp; michael@0: } else if (ino[n + 1] == OVFLPAGE) { michael@0: ov_addr = ino[n]; michael@0: /* michael@0: * Fix up the old page -- the extra 2 are the fields michael@0: * which contained the overflow information. michael@0: */ michael@0: ino[0] -= (moved + 2); michael@0: FREESPACE(ino) = michael@0: scopyto - sizeof(uint16) * (ino[0] + 3); michael@0: OFFSET(ino) = scopyto; michael@0: michael@0: bufp = __get_buf(hashp, ov_addr, bufp, 0); michael@0: if (!bufp) michael@0: return (-1); michael@0: michael@0: ino = (uint16 *)bufp->page; michael@0: n = 1; michael@0: scopyto = hashp->BSIZE; michael@0: moved = 0; michael@0: michael@0: if (last_bfp) michael@0: __free_ovflpage(hashp, last_bfp); michael@0: last_bfp = bufp; michael@0: } michael@0: /* Move regular sized pairs of there are any */ michael@0: off = hashp->BSIZE; michael@0: for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { michael@0: cino = (char *)ino; michael@0: key.data = (uint8 *)cino + ino[n]; michael@0: key.size = off - ino[n]; michael@0: val.data = (uint8 *)cino + ino[n + 1]; michael@0: val.size = ino[n] - ino[n + 1]; michael@0: off = ino[n + 1]; michael@0: michael@0: if (__call_hash(hashp, (char*)key.data, key.size) == obucket) { michael@0: /* Keep on old page */ michael@0: if (PAIRFITS(op, (&key), (&val))) michael@0: putpair((char *)op, &key, &val); michael@0: else { michael@0: old_bufp = michael@0: __add_ovflpage(hashp, old_bufp); michael@0: if (!old_bufp) michael@0: return (-1); michael@0: op = (uint16 *)old_bufp->page; michael@0: putpair((char *)op, &key, &val); michael@0: } michael@0: old_bufp->flags |= BUF_MOD; michael@0: } else { michael@0: /* Move to new page */ michael@0: if (PAIRFITS(np, (&key), (&val))) michael@0: putpair((char *)np, &key, &val); michael@0: else { michael@0: new_bufp = michael@0: __add_ovflpage(hashp, new_bufp); michael@0: if (!new_bufp) michael@0: return (-1); michael@0: np = (uint16 *)new_bufp->page; michael@0: putpair((char *)np, &key, &val); michael@0: } michael@0: new_bufp->flags |= BUF_MOD; michael@0: } michael@0: } michael@0: } michael@0: if (last_bfp) michael@0: __free_ovflpage(hashp, last_bfp); michael@0: return (0); michael@0: } michael@0: michael@0: /* michael@0: * Add the given pair to the page michael@0: * michael@0: * Returns: michael@0: * 0 ==> OK michael@0: * 1 ==> failure michael@0: */ michael@0: extern int michael@0: __addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT * val) michael@0: { michael@0: register uint16 *bp, *sop; michael@0: int do_expand; michael@0: michael@0: bp = (uint16 *)bufp->page; michael@0: do_expand = 0; michael@0: while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY)) michael@0: /* Exception case */ michael@0: if (bp[2] == FULL_KEY_DATA && bp[0] == 2) michael@0: /* This is the last page of a big key/data pair michael@0: and we need to add another page */ michael@0: break; michael@0: else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { michael@0: bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); michael@0: if (!bufp) michael@0: { michael@0: #ifdef DEBUG michael@0: assert(0); michael@0: #endif michael@0: return (-1); michael@0: } michael@0: bp = (uint16 *)bufp->page; michael@0: } else michael@0: /* Try to squeeze key on this page */ michael@0: if (FREESPACE(bp) > PAIRSIZE(key, val)) { michael@0: { michael@0: squeeze_key(bp, key, val); michael@0: michael@0: /* LJM: I added this because I think it was michael@0: * left out on accident. michael@0: * if this isn't incremented nkeys will not michael@0: * be the actual number of keys in the db. michael@0: */ michael@0: hashp->NKEYS++; michael@0: return (0); michael@0: } michael@0: } else { michael@0: bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); michael@0: if (!bufp) michael@0: { michael@0: #ifdef DEBUG michael@0: assert(0); michael@0: #endif michael@0: return (-1); michael@0: } michael@0: bp = (uint16 *)bufp->page; michael@0: } michael@0: michael@0: if (PAIRFITS(bp, key, val)) michael@0: putpair(bufp->page, key, (DBT *)val); michael@0: else { michael@0: do_expand = 1; michael@0: bufp = __add_ovflpage(hashp, bufp); michael@0: if (!bufp) michael@0: { michael@0: #ifdef DEBUG michael@0: assert(0); michael@0: #endif michael@0: return (-1); michael@0: } michael@0: sop = (uint16 *)bufp->page; michael@0: michael@0: if (PAIRFITS(sop, key, val)) michael@0: putpair((char *)sop, key, (DBT *)val); michael@0: else michael@0: if (__big_insert(hashp, bufp, key, val)) michael@0: { michael@0: #ifdef DEBUG michael@0: assert(0); michael@0: #endif michael@0: return (-1); michael@0: } michael@0: } michael@0: bufp->flags |= BUF_MOD; michael@0: /* michael@0: * If the average number of keys per bucket exceeds the fill factor, michael@0: * expand the table. michael@0: */ michael@0: hashp->NKEYS++; michael@0: if (do_expand || michael@0: (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) michael@0: return (__expand_table(hashp)); michael@0: return (0); michael@0: } michael@0: michael@0: /* michael@0: * michael@0: * Returns: michael@0: * pointer on success michael@0: * NULL on error michael@0: */ michael@0: extern BUFHEAD * michael@0: __add_ovflpage(HTAB *hashp, BUFHEAD *bufp) michael@0: { michael@0: register uint16 *sp; michael@0: uint16 ndx, ovfl_num; michael@0: #ifdef DEBUG1 michael@0: int tmp1, tmp2; michael@0: #endif michael@0: sp = (uint16 *)bufp->page; michael@0: michael@0: /* Check if we are dynamically determining the fill factor */ michael@0: if (hashp->FFACTOR == DEF_FFACTOR) { michael@0: hashp->FFACTOR = sp[0] >> 1; michael@0: if (hashp->FFACTOR < MIN_FFACTOR) michael@0: hashp->FFACTOR = MIN_FFACTOR; michael@0: } michael@0: bufp->flags |= BUF_MOD; michael@0: ovfl_num = overflow_page(hashp); michael@0: #ifdef DEBUG1 michael@0: tmp1 = bufp->addr; michael@0: tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; michael@0: #endif michael@0: if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1))) michael@0: return (NULL); michael@0: bufp->ovfl->flags |= BUF_MOD; michael@0: #ifdef DEBUG1 michael@0: (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", michael@0: tmp1, tmp2, bufp->ovfl->addr); michael@0: #endif michael@0: ndx = sp[0]; michael@0: /* michael@0: * Since a pair is allocated on a page only if there's room to add michael@0: * an overflow page, we know that the OVFL information will fit on michael@0: * the page. michael@0: */ michael@0: sp[ndx + 4] = OFFSET(sp); michael@0: sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; michael@0: sp[ndx + 1] = ovfl_num; michael@0: sp[ndx + 2] = OVFLPAGE; michael@0: sp[0] = ndx + 2; michael@0: #ifdef HASH_STATISTICS michael@0: hash_overflows++; michael@0: #endif michael@0: return (bufp->ovfl); michael@0: } michael@0: michael@0: /* michael@0: * Returns: michael@0: * 0 indicates SUCCESS michael@0: * -1 indicates FAILURE michael@0: */ michael@0: extern int michael@0: __get_page(HTAB *hashp, michael@0: char * p, michael@0: uint32 bucket, michael@0: int is_bucket, michael@0: int is_disk, michael@0: int is_bitmap) michael@0: { michael@0: register int fd, page; michael@0: size_t size; michael@0: int rsize; michael@0: uint16 *bp; michael@0: michael@0: fd = hashp->fp; michael@0: size = hashp->BSIZE; michael@0: michael@0: if ((fd == -1) || !is_disk) { michael@0: PAGE_INIT(p); michael@0: return (0); michael@0: } michael@0: if (is_bucket) michael@0: page = BUCKET_TO_PAGE(bucket); michael@0: else michael@0: page = OADDR_TO_PAGE(bucket); michael@0: if ((MY_LSEEK(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) || michael@0: ((rsize = read(fd, p, size)) == -1)) michael@0: return (-1); michael@0: michael@0: bp = (uint16 *)p; michael@0: if (!rsize) michael@0: bp[0] = 0; /* We hit the EOF, so initialize a new page */ michael@0: else michael@0: if ((unsigned)rsize != size) { michael@0: errno = EFTYPE; michael@0: return (-1); michael@0: } michael@0: michael@0: if (!is_bitmap && !bp[0]) { michael@0: PAGE_INIT(p); michael@0: } else { michael@0: michael@0: #ifdef DEBUG michael@0: if(BYTE_ORDER == LITTLE_ENDIAN) michael@0: { michael@0: int is_little_endian; michael@0: is_little_endian = BYTE_ORDER; michael@0: } michael@0: else if(BYTE_ORDER == BIG_ENDIAN) michael@0: { michael@0: int is_big_endian; michael@0: is_big_endian = BYTE_ORDER; michael@0: } michael@0: else michael@0: { michael@0: assert(0); michael@0: } michael@0: #endif michael@0: michael@0: if (hashp->LORDER != BYTE_ORDER) { michael@0: register int i, max; michael@0: michael@0: if (is_bitmap) { michael@0: max = hashp->BSIZE >> 2; /* divide by 4 */ michael@0: for (i = 0; i < max; i++) michael@0: M_32_SWAP(((int *)p)[i]); michael@0: } else { michael@0: M_16_SWAP(bp[0]); michael@0: max = bp[0] + 2; michael@0: michael@0: /* bound the size of max by michael@0: * the maximum number of entries michael@0: * in the array michael@0: */ michael@0: if((unsigned)max > (size / sizeof(uint16))) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: /* do the byte order swap michael@0: */ michael@0: for (i = 1; i <= max; i++) michael@0: M_16_SWAP(bp[i]); michael@0: } michael@0: } michael@0: michael@0: /* check the validity of the page here michael@0: * (after doing byte order swaping if necessary) michael@0: */ michael@0: if(!is_bitmap && bp[0] != 0) michael@0: { michael@0: uint16 num_keys = bp[0]; michael@0: uint16 offset; michael@0: uint16 i; michael@0: michael@0: /* bp[0] is supposed to be the number of michael@0: * entries currently in the page. If michael@0: * bp[0] is too large (larger than the whole michael@0: * page) then the page is corrupted michael@0: */ michael@0: if(bp[0] > (size / sizeof(uint16))) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: /* bound free space */ michael@0: if(FREESPACE(bp) > size) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: /* check each key and data offset to make michael@0: * sure they are all within bounds they michael@0: * should all be less than the previous michael@0: * offset as well. michael@0: */ michael@0: offset = size; michael@0: for(i=1 ; i <= num_keys; i+=2) michael@0: { michael@0: /* ignore overflow pages etc. */ michael@0: if(bp[i+1] >= REAL_KEY) michael@0: { michael@0: michael@0: if(bp[i] > offset || bp[i+1] > bp[i]) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: offset = bp[i+1]; michael@0: } michael@0: else michael@0: { michael@0: /* there are no other valid keys after michael@0: * seeing a non REAL_KEY michael@0: */ michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return (0); michael@0: } michael@0: michael@0: /* michael@0: * Write page p to disk michael@0: * michael@0: * Returns: michael@0: * 0 ==> OK michael@0: * -1 ==>failure michael@0: */ michael@0: extern int michael@0: __put_page(HTAB *hashp, char *p, uint32 bucket, int is_bucket, int is_bitmap) michael@0: { michael@0: register int fd, page; michael@0: size_t size; michael@0: int wsize; michael@0: off_t offset; michael@0: michael@0: size = hashp->BSIZE; michael@0: if ((hashp->fp == -1) && open_temp(hashp)) michael@0: return (-1); michael@0: fd = hashp->fp; michael@0: michael@0: if (hashp->LORDER != BYTE_ORDER) { michael@0: register int i; michael@0: register int max; michael@0: michael@0: if (is_bitmap) { michael@0: max = hashp->BSIZE >> 2; /* divide by 4 */ michael@0: for (i = 0; i < max; i++) michael@0: M_32_SWAP(((int *)p)[i]); michael@0: } else { michael@0: max = ((uint16 *)p)[0] + 2; michael@0: michael@0: /* bound the size of max by michael@0: * the maximum number of entries michael@0: * in the array michael@0: */ michael@0: if((unsigned)max > (size / sizeof(uint16))) michael@0: return(DATABASE_CORRUPTED_ERROR); michael@0: michael@0: for (i = 0; i <= max; i++) michael@0: M_16_SWAP(((uint16 *)p)[i]); michael@0: michael@0: } michael@0: } michael@0: michael@0: if (is_bucket) michael@0: page = BUCKET_TO_PAGE(bucket); michael@0: else michael@0: page = OADDR_TO_PAGE(bucket); michael@0: offset = (off_t)page << hashp->BSHIFT; michael@0: if ((MY_LSEEK(fd, offset, SEEK_SET) == -1) || michael@0: ((wsize = write(fd, p, size)) == -1)) michael@0: /* Errno is set */ michael@0: return (-1); michael@0: if ((unsigned)wsize != size) { michael@0: errno = EFTYPE; michael@0: return (-1); michael@0: } michael@0: #if defined(_WIN32) || defined(_WINDOWS) michael@0: if (offset + size > hashp->file_size) { michael@0: hashp->updateEOF = 1; michael@0: } michael@0: #endif michael@0: /* put the page back the way it was so that it isn't byteswapped michael@0: * if it remains in memory - LJM michael@0: */ michael@0: if (hashp->LORDER != BYTE_ORDER) { michael@0: register int i; michael@0: register int max; michael@0: michael@0: if (is_bitmap) { michael@0: max = hashp->BSIZE >> 2; /* divide by 4 */ michael@0: for (i = 0; i < max; i++) michael@0: M_32_SWAP(((int *)p)[i]); michael@0: } else { michael@0: uint16 *bp = (uint16 *)p; michael@0: michael@0: M_16_SWAP(bp[0]); michael@0: max = bp[0] + 2; michael@0: michael@0: /* no need to bound the size if max again michael@0: * since it was done already above michael@0: */ michael@0: michael@0: /* do the byte order re-swap michael@0: */ michael@0: for (i = 1; i <= max; i++) michael@0: M_16_SWAP(bp[i]); michael@0: } michael@0: } michael@0: michael@0: return (0); michael@0: } michael@0: michael@0: #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) michael@0: /* michael@0: * Initialize a new bitmap page. Bitmap pages are left in memory michael@0: * once they are read in. michael@0: */ michael@0: extern int michael@0: __ibitmap(HTAB *hashp, int pnum, int nbits, int ndx) michael@0: { michael@0: uint32 *ip; michael@0: size_t clearbytes, clearints; michael@0: michael@0: if ((ip = (uint32 *)malloc((size_t)hashp->BSIZE)) == NULL) michael@0: return (1); michael@0: hashp->nmaps++; michael@0: clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; michael@0: clearbytes = clearints << INT_TO_BYTE; michael@0: (void)memset((char *)ip, 0, clearbytes); michael@0: (void)memset(((char *)ip) + clearbytes, 0xFF, michael@0: hashp->BSIZE - clearbytes); michael@0: ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); michael@0: SETBIT(ip, 0); michael@0: hashp->BITMAPS[ndx] = (uint16)pnum; michael@0: hashp->mapp[ndx] = ip; michael@0: return (0); michael@0: } michael@0: michael@0: static uint32 michael@0: first_free(uint32 map) michael@0: { michael@0: register uint32 i, mask; michael@0: michael@0: mask = 0x1; michael@0: for (i = 0; i < BITS_PER_MAP; i++) { michael@0: if (!(mask & map)) michael@0: return (i); michael@0: mask = mask << 1; michael@0: } michael@0: return (i); michael@0: } michael@0: michael@0: static uint16 michael@0: overflow_page(HTAB *hashp) michael@0: { michael@0: register uint32 *freep=NULL; michael@0: register int max_free, offset, splitnum; michael@0: uint16 addr; michael@0: uint32 i; michael@0: int bit, first_page, free_bit, free_page, in_use_bits, j; michael@0: #ifdef DEBUG2 michael@0: int tmp1, tmp2; michael@0: #endif michael@0: splitnum = hashp->OVFL_POINT; michael@0: max_free = hashp->SPARES[splitnum]; michael@0: michael@0: free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); michael@0: free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); michael@0: michael@0: /* Look through all the free maps to find the first free block */ michael@0: first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); michael@0: for ( i = first_page; i <= (unsigned)free_page; i++ ) { michael@0: if (!(freep = (uint32 *)hashp->mapp[i]) && michael@0: !(freep = fetch_bitmap(hashp, i))) michael@0: return (0); michael@0: if (i == (unsigned)free_page) michael@0: in_use_bits = free_bit; michael@0: else michael@0: in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; michael@0: michael@0: if (i == (unsigned)first_page) { michael@0: bit = hashp->LAST_FREED & michael@0: ((hashp->BSIZE << BYTE_SHIFT) - 1); michael@0: j = bit / BITS_PER_MAP; michael@0: bit = bit & ~(BITS_PER_MAP - 1); michael@0: } else { michael@0: bit = 0; michael@0: j = 0; michael@0: } michael@0: for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) michael@0: if (freep[j] != ALL_SET) michael@0: goto found; michael@0: } michael@0: michael@0: /* No Free Page Found */ michael@0: hashp->LAST_FREED = hashp->SPARES[splitnum]; michael@0: hashp->SPARES[splitnum]++; michael@0: offset = hashp->SPARES[splitnum] - michael@0: (splitnum ? hashp->SPARES[splitnum - 1] : 0); michael@0: michael@0: #define OVMSG "HASH: Out of overflow pages. Increase page size\n" michael@0: if (offset > SPLITMASK) { michael@0: if (++splitnum >= NCACHED) { michael@0: #ifndef macintosh michael@0: (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); michael@0: #endif michael@0: return (0); michael@0: } michael@0: hashp->OVFL_POINT = splitnum; michael@0: hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; michael@0: hashp->SPARES[splitnum-1]--; michael@0: offset = 1; michael@0: } michael@0: michael@0: /* Check if we need to allocate a new bitmap page */ michael@0: if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { michael@0: free_page++; michael@0: if (free_page >= NCACHED) { michael@0: #ifndef macintosh michael@0: (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); michael@0: #endif michael@0: return (0); michael@0: } michael@0: /* michael@0: * This is tricky. The 1 indicates that you want the new page michael@0: * allocated with 1 clear bit. Actually, you are going to michael@0: * allocate 2 pages from this map. The first is going to be michael@0: * the map page, the second is the overflow page we were michael@0: * looking for. The init_bitmap routine automatically, sets michael@0: * the first bit of itself to indicate that the bitmap itself michael@0: * is in use. We would explicitly set the second bit, but michael@0: * don't have to if we tell init_bitmap not to leave it clear michael@0: * in the first place. michael@0: */ michael@0: if (__ibitmap(hashp, michael@0: (int)OADDR_OF(splitnum, offset), 1, free_page)) michael@0: return (0); michael@0: hashp->SPARES[splitnum]++; michael@0: #ifdef DEBUG2 michael@0: free_bit = 2; michael@0: #endif michael@0: offset++; michael@0: if (offset > SPLITMASK) { michael@0: if (++splitnum >= NCACHED) { michael@0: #ifndef macintosh michael@0: (void)write(STDERR_FILENO, OVMSG, michael@0: sizeof(OVMSG) - 1); michael@0: #endif michael@0: return (0); michael@0: } michael@0: hashp->OVFL_POINT = splitnum; michael@0: hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; michael@0: hashp->SPARES[splitnum-1]--; michael@0: offset = 0; michael@0: } michael@0: } else { michael@0: /* michael@0: * Free_bit addresses the last used bit. Bump it to address michael@0: * the first available bit. michael@0: */ michael@0: free_bit++; michael@0: SETBIT(freep, free_bit); michael@0: } michael@0: michael@0: /* Calculate address of the new overflow page */ michael@0: addr = OADDR_OF(splitnum, offset); michael@0: #ifdef DEBUG2 michael@0: (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", michael@0: addr, free_bit, free_page); michael@0: #endif michael@0: return (addr); michael@0: michael@0: found: michael@0: bit = bit + first_free(freep[j]); michael@0: SETBIT(freep, bit); michael@0: #ifdef DEBUG2 michael@0: tmp1 = bit; michael@0: tmp2 = i; michael@0: #endif michael@0: /* michael@0: * Bits are addressed starting with 0, but overflow pages are addressed michael@0: * beginning at 1. Bit is a bit addressnumber, so we need to increment michael@0: * it to convert it to a page number. michael@0: */ michael@0: bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); michael@0: if (bit >= hashp->LAST_FREED) michael@0: hashp->LAST_FREED = bit - 1; michael@0: michael@0: /* Calculate the split number for this page */ michael@0: for (i = 0; (i < (unsigned)splitnum) && (bit > hashp->SPARES[i]); i++) {} michael@0: offset = (i ? bit - hashp->SPARES[i - 1] : bit); michael@0: if (offset >= SPLITMASK) michael@0: return (0); /* Out of overflow pages */ michael@0: addr = OADDR_OF(i, offset); michael@0: #ifdef DEBUG2 michael@0: (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", michael@0: addr, tmp1, tmp2); michael@0: #endif michael@0: michael@0: /* Allocate and return the overflow page */ michael@0: return (addr); michael@0: } michael@0: michael@0: /* michael@0: * Mark this overflow page as free. michael@0: */ michael@0: extern void michael@0: __free_ovflpage(HTAB *hashp, BUFHEAD *obufp) michael@0: { michael@0: uint16 addr; michael@0: uint32 *freep; michael@0: uint32 bit_address, free_page, free_bit; michael@0: uint16 ndx; michael@0: michael@0: if(!obufp || !obufp->addr) michael@0: return; michael@0: michael@0: addr = obufp->addr; michael@0: #ifdef DEBUG1 michael@0: (void)fprintf(stderr, "Freeing %d\n", addr); michael@0: #endif michael@0: ndx = (((uint16)addr) >> SPLITSHIFT); michael@0: bit_address = michael@0: (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; michael@0: if (bit_address < (uint32)hashp->LAST_FREED) michael@0: hashp->LAST_FREED = bit_address; michael@0: free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); michael@0: free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); michael@0: michael@0: if (!(freep = hashp->mapp[free_page])) michael@0: freep = fetch_bitmap(hashp, free_page); michael@0: michael@0: #ifdef DEBUG michael@0: /* michael@0: * This had better never happen. It means we tried to read a bitmap michael@0: * that has already had overflow pages allocated off it, and we michael@0: * failed to read it from the file. michael@0: */ michael@0: if (!freep) michael@0: { michael@0: assert(0); michael@0: return; michael@0: } michael@0: #endif michael@0: CLRBIT(freep, free_bit); michael@0: #ifdef DEBUG2 michael@0: (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", michael@0: obufp->addr, free_bit, free_page); michael@0: #endif michael@0: __reclaim_buf(hashp, obufp); michael@0: } michael@0: michael@0: /* michael@0: * Returns: michael@0: * 0 success michael@0: * -1 failure michael@0: */ michael@0: static int michael@0: open_temp(HTAB *hashp) michael@0: { michael@0: #ifdef XP_OS2 michael@0: hashp->fp = mkstemp(NULL); michael@0: #else michael@0: #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) michael@0: sigset_t set, oset; michael@0: #endif michael@0: #if !defined(macintosh) michael@0: char * tmpdir; michael@0: size_t len; michael@0: char last; michael@0: #endif michael@0: static const char namestr[] = "/_hashXXXXXX"; michael@0: char filename[1024]; michael@0: michael@0: #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) michael@0: /* Block signals; make sure file goes away at process exit. */ michael@0: (void)sigfillset(&set); michael@0: (void)sigprocmask(SIG_BLOCK, &set, &oset); michael@0: #endif michael@0: michael@0: filename[0] = 0; michael@0: #if defined(macintosh) michael@0: strcat(filename, namestr + 1); michael@0: #else michael@0: tmpdir = getenv("TMP"); michael@0: if (!tmpdir) michael@0: tmpdir = getenv("TMPDIR"); michael@0: if (!tmpdir) michael@0: tmpdir = getenv("TEMP"); michael@0: if (!tmpdir) michael@0: tmpdir = "."; michael@0: len = strlen(tmpdir); michael@0: if (len && len < (sizeof filename - sizeof namestr)) { michael@0: strcpy(filename, tmpdir); michael@0: } michael@0: len = strlen(filename); michael@0: last = tmpdir[len - 1]; michael@0: strcat(filename, (last == '/' || last == '\\') ? namestr + 1 : namestr); michael@0: #endif michael@0: michael@0: #if defined(_WIN32) || defined(_WINDOWS) michael@0: if ((hashp->fp = mkstempflags(filename, _O_BINARY|_O_TEMPORARY)) != -1) { michael@0: if (hashp->filename) { michael@0: free(hashp->filename); michael@0: } michael@0: hashp->filename = strdup(filename); michael@0: hashp->is_temp = 1; michael@0: } michael@0: #else michael@0: if ((hashp->fp = mkstemp(filename)) != -1) { michael@0: (void)unlink(filename); michael@0: #if !defined(macintosh) michael@0: (void)fcntl(hashp->fp, F_SETFD, 1); michael@0: #endif michael@0: } michael@0: #endif michael@0: michael@0: #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) michael@0: (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); michael@0: #endif michael@0: #endif /* !OS2 */ michael@0: return (hashp->fp != -1 ? 0 : -1); michael@0: } michael@0: michael@0: /* michael@0: * We have to know that the key will fit, but the last entry on the page is michael@0: * an overflow pair, so we need to shift things. michael@0: */ michael@0: static void michael@0: squeeze_key(uint16 *sp, const DBT * key, const DBT * val) michael@0: { michael@0: register char *p; michael@0: uint16 free_space, n, off, pageno; michael@0: michael@0: p = (char *)sp; michael@0: n = sp[0]; michael@0: free_space = FREESPACE(sp); michael@0: off = OFFSET(sp); michael@0: michael@0: pageno = sp[n - 1]; michael@0: off -= key->size; michael@0: sp[n - 1] = off; michael@0: memmove(p + off, key->data, key->size); michael@0: off -= val->size; michael@0: sp[n] = off; michael@0: memmove(p + off, val->data, val->size); michael@0: sp[0] = n + 2; michael@0: sp[n + 1] = pageno; michael@0: sp[n + 2] = OVFLPAGE; michael@0: FREESPACE(sp) = free_space - PAIRSIZE(key, val); michael@0: OFFSET(sp) = off; michael@0: } michael@0: michael@0: static uint32 * michael@0: fetch_bitmap(HTAB *hashp, uint32 ndx) michael@0: { michael@0: if (ndx >= (unsigned)hashp->nmaps) michael@0: return (NULL); michael@0: if ((hashp->mapp[ndx] = (uint32 *)malloc((size_t)hashp->BSIZE)) == NULL) michael@0: return (NULL); michael@0: if (__get_page(hashp, michael@0: (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) { michael@0: free(hashp->mapp[ndx]); michael@0: hashp->mapp[ndx] = NULL; /* NEW: 9-11-95 */ michael@0: return (NULL); michael@0: } michael@0: return (hashp->mapp[ndx]); michael@0: } michael@0: michael@0: #ifdef DEBUG4 michael@0: int michael@0: print_chain(int addr) michael@0: { michael@0: BUFHEAD *bufp; michael@0: short *bp, oaddr; michael@0: michael@0: (void)fprintf(stderr, "%d ", addr); michael@0: bufp = __get_buf(hashp, addr, NULL, 0); michael@0: bp = (short *)bufp->page; michael@0: while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || michael@0: ((bp[0] > 2) && bp[2] < REAL_KEY))) { michael@0: oaddr = bp[bp[0] - 1]; michael@0: (void)fprintf(stderr, "%d ", (int)oaddr); michael@0: bufp = __get_buf(hashp, (int)oaddr, bufp, 0); michael@0: bp = (short *)bufp->page; michael@0: } michael@0: (void)fprintf(stderr, "\n"); michael@0: } michael@0: #endif