146372Sbostic /*- 246372Sbostic * Copyright (c) 1990 The Regents of the University of California. 346372Sbostic * All rights reserved. 446372Sbostic * 546372Sbostic * This code is derived from software contributed to Berkeley by 646372Sbostic * Margo Seltzer. 746372Sbostic * 846372Sbostic * %sccs.include.redist.c% 946372Sbostic */ 1046372Sbostic 1146372Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*51055Sbostic static char sccsid[] = "@(#)hash_page.c 5.15 (Berkeley) 09/08/91"; 1346372Sbostic #endif /* LIBC_SCCS and not lint */ 1446372Sbostic 1550997Sbostic /* 1650997Sbostic * PACKAGE: hashing 1750997Sbostic * 1850997Sbostic * DESCRIPTION: 1950997Sbostic * Page manipulation for hashing package. 2050997Sbostic * 2150997Sbostic * ROUTINES: 2250997Sbostic * 2350997Sbostic * External 2450997Sbostic * __get_page 2550997Sbostic * __add_ovflpage 2650997Sbostic * Internal 2750997Sbostic * overflow_page 2850997Sbostic * open_temp 2950997Sbostic */ 3046372Sbostic 3146372Sbostic #include <sys/param.h> 3246562Sbostic #include <fcntl.h> 3346416Sbostic #include <signal.h> 3446372Sbostic #include <errno.h> 3546372Sbostic #include <db.h> 3646372Sbostic #include <stdio.h> 3746562Sbostic #include <stdlib.h> 3846562Sbostic #include <string.h> 3946500Sbostic #include <unistd.h> 4050997Sbostic #ifdef DEBUG 4150997Sbostic #include <assert.h> 4250997Sbostic #endif 4346372Sbostic #include "hash.h" 4446372Sbostic #include "page.h" 4550997Sbostic #include "extern.h" 4646372Sbostic 47*51055Sbostic static u_long *fetch_bitmap __P((int)); 48*51055Sbostic static u_long first_free __P((u_long)); 49*51055Sbostic static int open_temp __P((void)); 50*51055Sbostic static u_short overflow_page __P((void)); 51*51055Sbostic static void putpair __P((char *, const DBT *, const DBT *)); 52*51055Sbostic static void squeeze_key __P((u_short *, const DBT *, const DBT *)); 53*51055Sbostic static int ugly_split __P((u_int, BUFHEAD *, BUFHEAD *, int, int)); 5446372Sbostic 5550997Sbostic #define PAGE_INIT(P) { \ 5650997Sbostic ((u_short *)(P))[0] = 0; \ 5750997Sbostic ((u_short *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_short); \ 5850997Sbostic ((u_short *)(P))[2] = hashp->BSIZE; \ 5946372Sbostic } 6046372Sbostic 6146372Sbostic /* 6250997Sbostic * This is called AFTER we have verified that there is room on the page for 6350997Sbostic * the pair (PAIRFITS has returned true) so we go right ahead and start moving 6450997Sbostic * stuff on. 6550997Sbostic */ 6646372Sbostic static void 6746372Sbostic putpair(p, key, val) 6850997Sbostic char *p; 6950997Sbostic const DBT *key, *val; 7046372Sbostic { 7150997Sbostic register u_short *bp, n, off; 7246372Sbostic 7350997Sbostic bp = (u_short *)p; 7450997Sbostic 7550997Sbostic /* Enter the key first. */ 7646372Sbostic n = bp[0]; 7746372Sbostic 7846372Sbostic off = OFFSET(bp) - key->size; 7950997Sbostic bcopy(key->data, p + off, key->size); 8046372Sbostic bp[++n] = off; 8146372Sbostic 8250997Sbostic /* Now the data. */ 8346372Sbostic off -= val->size; 8450997Sbostic bcopy(val->data, p + off, val->size); 8546372Sbostic bp[++n] = off; 8646372Sbostic 8750997Sbostic /* Adjust page info. */ 8846372Sbostic bp[0] = n; 8950997Sbostic bp[n + 1] = off - ((n + 3) * sizeof(u_short)); 9050997Sbostic bp[n + 2] = off; 9146372Sbostic } 9250997Sbostic 9346372Sbostic /* 9450997Sbostic * Returns: 9550997Sbostic * 0 OK 9650997Sbostic * -1 error 9750997Sbostic */ 9846372Sbostic extern int 9946372Sbostic __delpair(bufp, ndx) 10050997Sbostic BUFHEAD *bufp; 10150997Sbostic register int ndx; 10246372Sbostic { 10350997Sbostic register u_short *bp, newoff; 10450997Sbostic register int n; 10546372Sbostic u_short pairlen; 10646372Sbostic 10750997Sbostic bp = (u_short *)bufp->page; 10850997Sbostic n = bp[0]; 10946372Sbostic 11050997Sbostic if (bp[ndx + 1] < REAL_KEY) 111*51055Sbostic return (__big_delete(bufp)); 11250997Sbostic if (ndx != 1) 11350997Sbostic newoff = bp[ndx - 1]; 11450997Sbostic else 11550997Sbostic newoff = hashp->BSIZE; 11650997Sbostic pairlen = newoff - bp[ndx + 1]; 11750997Sbostic 11850997Sbostic if (ndx != (n - 1)) { 11946372Sbostic /* Hard Case -- need to shuffle keys */ 12046372Sbostic register int i; 12146372Sbostic register char *src = bufp->page + (int)OFFSET(bp); 12246372Sbostic register char *dst = src + (int)pairlen; 12350997Sbostic bcopy(src, dst, bp[ndx + 1] - OFFSET(bp)); 12446372Sbostic 12546372Sbostic /* Now adjust the pointers */ 12650997Sbostic for (i = ndx + 2; i <= n; i += 2) { 12750997Sbostic if (bp[i + 1] == OVFLPAGE) { 12850997Sbostic bp[i - 2] = bp[i]; 12950997Sbostic bp[i - 1] = bp[i + 1]; 13050997Sbostic } else { 13150997Sbostic bp[i - 2] = bp[i] + pairlen; 13250997Sbostic bp[i - 1] = bp[i + 1] + pairlen; 13350997Sbostic } 13446372Sbostic } 13546372Sbostic } 13646372Sbostic /* Finally adjust the page data */ 13746372Sbostic bp[n] = OFFSET(bp) + pairlen; 13850997Sbostic bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_short); 13950997Sbostic bp[0] = n - 2; 14046372Sbostic hashp->NKEYS--; 14146372Sbostic 14246372Sbostic bufp->flags |= BUF_MOD; 14346372Sbostic return (0); 14446372Sbostic } 14546372Sbostic /* 14650997Sbostic * Returns: 14750997Sbostic * 0 ==> OK 14850997Sbostic * -1 ==> Error 14950997Sbostic */ 15046372Sbostic extern int 15146372Sbostic __split_page(obucket, nbucket) 15250997Sbostic u_int obucket, nbucket; 15346372Sbostic { 15450997Sbostic register BUFHEAD *new_bufp, *old_bufp; 15546372Sbostic register u_short *ino; 15650997Sbostic register char *np; 15750997Sbostic DBT key, val; 15850997Sbostic int n, ndx, retval; 15950997Sbostic u_short copyto, diff, off, moved; 16050997Sbostic char *op; 16146372Sbostic 16250997Sbostic copyto = (u_short)hashp->BSIZE; 16350997Sbostic off = (u_short)hashp->BSIZE; 16450997Sbostic old_bufp = __get_buf(obucket, NULL, 0); 16550997Sbostic new_bufp = __get_buf(nbucket, NULL, 0); 16646372Sbostic 16750997Sbostic old_bufp->flags |= (BUF_MOD | BUF_PIN); 16850997Sbostic new_bufp->flags |= (BUF_MOD | BUF_PIN); 16946372Sbostic 17046372Sbostic ino = (u_short *)(op = old_bufp->page); 17146372Sbostic np = new_bufp->page; 17246372Sbostic 17346372Sbostic moved = 0; 17446372Sbostic 17550997Sbostic for (n = 1, ndx = 1; n < ino[0]; n += 2) { 17650997Sbostic if (ino[n + 1] < REAL_KEY) { 17750997Sbostic retval = ugly_split(obucket, old_bufp, new_bufp, 178*51055Sbostic (int)copyto, (int)moved); 17950997Sbostic old_bufp->flags &= ~BUF_PIN; 18050997Sbostic new_bufp->flags &= ~BUF_PIN; 18150997Sbostic return (retval); 18250997Sbostic 18346372Sbostic } 18450997Sbostic key.data = (u_char *)op + ino[n]; 18546372Sbostic key.size = off - ino[n]; 18646372Sbostic 18750997Sbostic if (__call_hash(key.data, key.size) == obucket) { 18850997Sbostic /* Don't switch page */ 18950997Sbostic diff = copyto - off; 19050997Sbostic if (diff) { 19150997Sbostic copyto = ino[n + 1] + diff; 19250997Sbostic bcopy(op + ino[n + 1], op + copyto, 19350997Sbostic off - ino[n + 1]); 19450997Sbostic ino[ndx] = copyto + ino[n] - ino[n + 1]; 19550997Sbostic ino[ndx + 1] = copyto; 19650997Sbostic } else 19750997Sbostic copyto = ino[n + 1]; 19850997Sbostic ndx += 2; 19946372Sbostic } else { 20050997Sbostic /* Switch page */ 20150997Sbostic val.data = (u_char *)op + ino[n + 1]; 20250997Sbostic val.size = ino[n] - ino[n + 1]; 20350997Sbostic putpair(np, &key, &val); 20450997Sbostic moved += 2; 20546372Sbostic } 20646372Sbostic 20750997Sbostic off = ino[n + 1]; 20846372Sbostic } 20946372Sbostic 21046372Sbostic /* Now clean up the page */ 21146372Sbostic ino[0] -= moved; 21250997Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0] + 3); 21346372Sbostic OFFSET(ino) = copyto; 21446372Sbostic 21546372Sbostic #ifdef DEBUG3 21650997Sbostic (void)fprintf(stderr, "split %d/%d\n", 21750997Sbostic ((u_short *)np)[0] / 2, 21850997Sbostic ((u_short *)op)[0] / 2); 21946372Sbostic #endif 22046460Sbostic /* unpin both pages */ 22146460Sbostic old_bufp->flags &= ~BUF_PIN; 22246460Sbostic new_bufp->flags &= ~BUF_PIN; 22350997Sbostic return (0); 22446372Sbostic } 22550997Sbostic 22646372Sbostic /* 22750997Sbostic * Called when we encounter an overflow or big key/data page during split 22850997Sbostic * handling. This is special cased since we have to begin checking whether 22950997Sbostic * the key/data pairs fit on their respective pages and because we may need 23050997Sbostic * overflow pages for both the old and new pages. 23150997Sbostic * 23250997Sbostic * The first page might be a page with regular key/data pairs in which case 23350997Sbostic * we have a regular overflow condition and just need to go on to the next 23450997Sbostic * page or it might be a big key/data pair in which case we need to fix the 23550997Sbostic * big key/data pair. 23650997Sbostic * 23750997Sbostic * Returns: 23850997Sbostic * 0 ==> success 23950997Sbostic * -1 ==> failure 24050997Sbostic */ 24146372Sbostic static int 24250997Sbostic ugly_split(obucket, old_bufp, new_bufp, copyto, moved) 24350997Sbostic u_int obucket; /* Same as __split_page. */ 24450997Sbostic BUFHEAD *old_bufp, *new_bufp; 24550997Sbostic int copyto; /* First byte on page which contains key/data values. */ 24650997Sbostic int moved; /* Number of pairs moved to new page. */ 24746372Sbostic { 24850997Sbostic register BUFHEAD *bufp; /* Buffer header for ino */ 24950997Sbostic register u_short *ino; /* Page keys come off of */ 25050997Sbostic register u_short *np; /* New page */ 25150997Sbostic register u_short *op; /* Page keys go on to if they aren't moving */ 25246372Sbostic 25350997Sbostic BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ 25450997Sbostic DBT key, val; 25550997Sbostic SPLIT_RETURN ret; 256*51055Sbostic u_short n, off, ov_addr, scopyto; 25750997Sbostic char *cino; /* Character value of ino */ 25846372Sbostic 25950997Sbostic bufp = old_bufp; 26050997Sbostic ino = (u_short *)old_bufp->page; 26150997Sbostic np = (u_short *)new_bufp->page; 26250997Sbostic op = (u_short *)old_bufp->page; 26350997Sbostic last_bfp = NULL; 26450997Sbostic scopyto = (u_short)copyto; /* ANSI */ 26546372Sbostic 26650997Sbostic n = ino[0] - 1; 26750997Sbostic while (n < ino[0]) { 26850997Sbostic if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { 269*51055Sbostic /* 270*51055Sbostic * Ov_addr gets set before reaching this point; there's 271*51055Sbostic * always an overflow page before a big key/data page. 272*51055Sbostic */ 27350997Sbostic if (__big_split(old_bufp, 27450997Sbostic new_bufp, bufp, ov_addr, obucket, &ret)) 27550997Sbostic return (-1); 27650997Sbostic old_bufp = ret.oldp; 27750997Sbostic if (!old_bufp) 27850997Sbostic return (-1); 27950997Sbostic op = (u_short *)old_bufp->page; 28050997Sbostic new_bufp = ret.newp; 28150997Sbostic if (!new_bufp) 28250997Sbostic return (-1); 28350997Sbostic np = (u_short *)new_bufp->page; 28450997Sbostic bufp = ret.nextp; 28550997Sbostic if (!bufp) 28650997Sbostic return (0); 28750997Sbostic cino = (char *)bufp->page; 28850997Sbostic ino = (u_short *)cino; 28950997Sbostic last_bfp = ret.nextp; 29050997Sbostic } else if (ino[n + 1] == OVFLPAGE) { 29150997Sbostic ov_addr = ino[n]; 29250997Sbostic /* 29350997Sbostic * Fix up the old page -- the extra 2 are the fields 29450997Sbostic * which contained the overflow information. 29550997Sbostic */ 29650997Sbostic ino[0] -= (moved + 2); 29750997Sbostic FREESPACE(ino) = 29850997Sbostic scopyto - sizeof(u_short) * (ino[0] + 3); 29950997Sbostic OFFSET(ino) = scopyto; 30046372Sbostic 30150997Sbostic bufp = __get_buf(ov_addr, bufp, 0); 30250997Sbostic if (!bufp) 30350997Sbostic return (-1); 30446372Sbostic 30550997Sbostic ino = (u_short *)bufp->page; 30650997Sbostic n = 1; 30750997Sbostic scopyto = hashp->BSIZE; 30850997Sbostic moved = 0; 30946372Sbostic 31050997Sbostic if (last_bfp) 31150997Sbostic __free_ovflpage(last_bfp); 31250997Sbostic last_bfp = bufp; 31350997Sbostic } 31450997Sbostic /* Move regular sized pairs of there are any */ 31550997Sbostic off = hashp->BSIZE; 31650997Sbostic for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { 31750997Sbostic cino = (char *)ino; 31850997Sbostic key.data = (u_char *)cino + ino[n]; 31950997Sbostic key.size = off - ino[n]; 32050997Sbostic val.data = (u_char *)cino + ino[n + 1]; 32150997Sbostic val.size = ino[n] - ino[n + 1]; 32250997Sbostic off = ino[n + 1]; 32346372Sbostic 32450997Sbostic if (__call_hash(key.data, key.size) == obucket) { 32550997Sbostic /* Keep on old page */ 32650997Sbostic if (PAIRFITS(op, (&key), (&val))) 32750997Sbostic putpair((char *)op, &key, &val); 32850997Sbostic else { 32950997Sbostic old_bufp = __add_ovflpage(old_bufp); 33050997Sbostic if (!old_bufp) 33150997Sbostic return (-1); 33250997Sbostic op = (u_short *)old_bufp->page; 33350997Sbostic putpair((char *)op, &key, &val); 33450997Sbostic } 33550997Sbostic old_bufp->flags |= BUF_MOD; 33650997Sbostic } else { 33750997Sbostic /* Move to new page */ 33850997Sbostic if (PAIRFITS(np, (&key), (&val))) 33950997Sbostic putpair((char *)np, &key, &val); 34050997Sbostic else { 34150997Sbostic new_bufp = __add_ovflpage(new_bufp); 34250997Sbostic if (!new_bufp) 34350997Sbostic return (-1); 34450997Sbostic np = (u_short *)new_bufp->page; 34550997Sbostic putpair((char *)np, &key, &val); 34650997Sbostic } 34750997Sbostic new_bufp->flags |= BUF_MOD; 34850997Sbostic } 34946372Sbostic } 35046372Sbostic } 35150997Sbostic if (last_bfp) 35250997Sbostic __free_ovflpage(last_bfp); 35350997Sbostic return (0); 35450997Sbostic } 35546372Sbostic 35646372Sbostic /* 35750997Sbostic * Add the given pair to the page 35850997Sbostic * 35950997Sbostic * Returns: 36050997Sbostic * 0 ==> OK 36150997Sbostic * 1 ==> failure 36250997Sbostic */ 36346372Sbostic extern int 36446372Sbostic __addel(bufp, key, val) 36550997Sbostic BUFHEAD *bufp; 36650997Sbostic const DBT *key, *val; 36746372Sbostic { 36850997Sbostic register u_short *bp, *sop; 36950997Sbostic int do_expand; 37046372Sbostic 37150997Sbostic bp = (u_short *)bufp->page; 37250997Sbostic do_expand = 0; 37350997Sbostic while (bp[0] && (bp[bp[0]] < REAL_KEY)) 37450997Sbostic /* Exception case */ 37550997Sbostic if (bp[2] < REAL_KEY) { 37650997Sbostic /* This is a big-keydata pair */ 37750997Sbostic bufp = __add_ovflpage(bufp); 37850997Sbostic if (!bufp) 37950997Sbostic return (-1); 38050997Sbostic bp = (u_short *)bufp->page; 38150997Sbostic } else 38250997Sbostic /* Try to squeeze key on this page */ 38350997Sbostic if (FREESPACE(bp) > PAIRSIZE(key, val)) { 38450997Sbostic squeeze_key(bp, key, val); 38550997Sbostic return (0); 38650997Sbostic } else { 38750997Sbostic bufp = __get_buf(bp[bp[0] - 1], bufp, 0); 38850997Sbostic if (!bufp) 38950997Sbostic return (-1); 39050997Sbostic bp = (u_short *)bufp->page; 39150997Sbostic } 39246372Sbostic 39350997Sbostic if (PAIRFITS(bp, key, val)) 39450997Sbostic putpair(bufp->page, key, val); 39550997Sbostic else { 39650997Sbostic do_expand = 1; 39750997Sbostic bufp = __add_ovflpage(bufp); 39850997Sbostic if (!bufp) 39950997Sbostic return (-1); 40050997Sbostic sop = (u_short *)bufp->page; 40146372Sbostic 40250997Sbostic if (PAIRFITS(sop, key, val)) 40350997Sbostic putpair((char *)sop, key, val); 40450997Sbostic else 40550997Sbostic if (__big_insert(bufp, key, val)) 40650997Sbostic return (-1); 40746372Sbostic } 40850997Sbostic bufp->flags |= BUF_MOD; 40950997Sbostic /* 41050997Sbostic * If the average number of keys per bucket exceeds the fill factor, 41150997Sbostic * expand the table. 41250997Sbostic */ 41350997Sbostic hashp->NKEYS++; 41450997Sbostic if (do_expand || 41550997Sbostic (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) 41650997Sbostic return (__expand_table()); 41750997Sbostic return (0); 41846372Sbostic } 41946372Sbostic 42046372Sbostic /* 42150997Sbostic * 42250997Sbostic * Returns: 42350997Sbostic * pointer on success 42450997Sbostic * NULL on error 42550997Sbostic */ 42646372Sbostic extern BUFHEAD * 42750997Sbostic __add_ovflpage(bufp) 42850997Sbostic BUFHEAD *bufp; 42946372Sbostic { 43050997Sbostic register u_short *sp; 43150997Sbostic u_short ndx, ovfl_num; 43246372Sbostic #ifdef DEBUG1 43350997Sbostic int tmp1, tmp2; 43446372Sbostic #endif 43550997Sbostic sp = (u_short *)bufp->page; 43650997Sbostic bufp->flags |= BUF_MOD; 43750997Sbostic ovfl_num = overflow_page(); 43846372Sbostic #ifdef DEBUG1 43950997Sbostic tmp1 = bufp->addr; 44050997Sbostic tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; 44146372Sbostic #endif 44250997Sbostic if (!ovfl_num || !(bufp->ovfl = __get_buf(ovfl_num, bufp, 1))) 44350997Sbostic return (NULL); 44450997Sbostic bufp->ovfl->flags |= BUF_MOD; 44546372Sbostic #ifdef DEBUG1 44650997Sbostic (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", 44750997Sbostic tmp1, tmp2, bufp->ovfl->addr); 44846372Sbostic #endif 44950997Sbostic ndx = sp[0]; 45050997Sbostic /* 45150997Sbostic * Since a pair is allocated on a page only if there's room to add 45250997Sbostic * an overflow page, we know that the OVFL information will fit on 45350997Sbostic * the page. 45450997Sbostic */ 45550997Sbostic sp[ndx + 4] = OFFSET(sp); 45650997Sbostic sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; 45750997Sbostic sp[ndx + 1] = ovfl_num; 45850997Sbostic sp[ndx + 2] = OVFLPAGE; 45950997Sbostic sp[0] = ndx + 2; 46046372Sbostic #ifdef HASH_STATISTICS 46150997Sbostic hash_overflows++; 46246372Sbostic #endif 46350997Sbostic return (bufp->ovfl); 46446372Sbostic } 46546372Sbostic 46646372Sbostic /* 46750997Sbostic * Returns: 46850997Sbostic * 0 indicates SUCCESS 46950997Sbostic * -1 indicates FAILURE 47050997Sbostic */ 47150997Sbostic extern int 47250997Sbostic __get_page(p, bucket, is_bucket, is_disk, is_bitmap) 47350997Sbostic char *p; 47450997Sbostic u_int bucket; 47550997Sbostic int is_bucket, is_disk, is_bitmap; 47646372Sbostic { 47750997Sbostic register int fd, page, size; 47850997Sbostic int rsize; 47950997Sbostic u_short *bp; 48046372Sbostic 48150997Sbostic fd = hashp->fp; 48250997Sbostic size = hashp->BSIZE; 48346372Sbostic 48450997Sbostic if ((fd == -1) || !is_disk) { 48550997Sbostic PAGE_INIT(p); 48650997Sbostic return (0); 48750997Sbostic } 48850997Sbostic if (is_bucket) 48950997Sbostic page = BUCKET_TO_PAGE(bucket); 49050997Sbostic else 49150997Sbostic page = OADDR_TO_PAGE(bucket); 49250997Sbostic if ((lseek(fd, page << hashp->BSHIFT, SEEK_SET) == -1) || 49350997Sbostic ((rsize = read(fd, p, size)) == -1)) 49450997Sbostic return (-1); 49550997Sbostic bp = (u_short *)p; 49650997Sbostic if (!rsize) 49750997Sbostic bp[0] = 0; /* We hit the EOF, so initialize a new page */ 49850997Sbostic else 49950997Sbostic if (rsize != size) { 50050997Sbostic errno = EFTYPE; 50150997Sbostic return (-1); 50250997Sbostic } 50350997Sbostic if (!bp[0]) { 50450997Sbostic PAGE_INIT(p); 50550997Sbostic } else 50650997Sbostic if (hashp->LORDER != BYTE_ORDER) { 50750997Sbostic register int i, max; 50846372Sbostic 50950997Sbostic if (is_bitmap) { 51050997Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 51150997Sbostic for (i = 0; i < max; i++) 51250997Sbostic BLSWAP(((long *)p)[i]); 51350997Sbostic } else { 51450997Sbostic BSSWAP(bp[0]); 51550997Sbostic max = bp[0] + 2; 51650997Sbostic for (i = 1; i <= max; i++) 51750997Sbostic BSSWAP(bp[i]); 51850997Sbostic } 51950997Sbostic } 52050997Sbostic return (0); 52146372Sbostic } 52246372Sbostic 52350997Sbostic /* 52450997Sbostic * Write page p to disk 52550997Sbostic * 52650997Sbostic * Returns: 52750997Sbostic * 0 ==> OK 52850997Sbostic * -1 ==>failure 52950997Sbostic */ 53046372Sbostic extern int 53150997Sbostic __put_page(p, bucket, is_bucket, is_bitmap) 53250997Sbostic char *p; 53350997Sbostic u_int bucket; 53450997Sbostic int is_bucket, is_bitmap; 53546372Sbostic { 53650997Sbostic register int fd, page, size; 53750997Sbostic int wsize; 53846372Sbostic 53950997Sbostic size = hashp->BSIZE; 54050997Sbostic if ((hashp->fp == -1) && open_temp()) 54150997Sbostic return (1); 54250997Sbostic fd = hashp->fp; 54346372Sbostic 54450997Sbostic if (hashp->LORDER != BYTE_ORDER) { 54550997Sbostic register int i; 54650997Sbostic register int max; 54746372Sbostic 54850997Sbostic if (is_bitmap) { 54950997Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 55050997Sbostic for (i = 0; i < max; i++) 55150997Sbostic BLSWAP(((long *)p)[i]); 55250997Sbostic } else { 55350997Sbostic max = ((u_short *)p)[0] + 2; 55450997Sbostic for (i = 0; i <= max; i++) 55550997Sbostic BSSWAP(((u_short *)p)[i]); 55650997Sbostic } 55746372Sbostic } 55850997Sbostic if (is_bucket) 55950997Sbostic page = BUCKET_TO_PAGE(bucket); 56050997Sbostic else 56150997Sbostic page = OADDR_TO_PAGE(bucket); 56250997Sbostic if ((lseek(fd, page << hashp->BSHIFT, SEEK_SET) == -1) || 56350997Sbostic ((wsize = write(fd, p, size)) == -1)) 56450997Sbostic /* Errno is set */ 56550997Sbostic return (-1); 56650997Sbostic if (wsize != size) { 56750997Sbostic errno = EFTYPE; 56850997Sbostic return (-1); 56950997Sbostic } 57050997Sbostic return (0); 57146372Sbostic } 57250997Sbostic 57346372Sbostic #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) 57446372Sbostic /* 57550997Sbostic * Initialize a new bitmap page. Bitmap pages are left in memory 57650997Sbostic * once they are read in. 57750997Sbostic */ 578*51055Sbostic extern int 57946372Sbostic __init_bitmap(pnum, nbits, ndx) 58050997Sbostic int pnum, nbits, ndx; 58146372Sbostic { 58250997Sbostic u_long *ip; 58350997Sbostic int clearbytes, clearints; 58446372Sbostic 58550997Sbostic if (!(ip = malloc(hashp->BSIZE))) 586*51055Sbostic return (1); 58750997Sbostic hashp->nmaps++; 58850997Sbostic clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; 58950997Sbostic clearbytes = clearints << INT_TO_BYTE; 590*51055Sbostic (void)memset((char *)ip, 0, clearbytes); 591*51055Sbostic (void)memset(((char *)ip) + clearbytes, 0xFF, 59250997Sbostic hashp->BSIZE - clearbytes); 59350997Sbostic ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); 59450997Sbostic SETBIT(ip, 0); 59550997Sbostic hashp->BITMAPS[ndx] = (u_short)pnum; 59650997Sbostic hashp->mapp[ndx] = ip; 597*51055Sbostic return (0); 59846372Sbostic } 59950997Sbostic 600*51055Sbostic static u_long 60150997Sbostic first_free(map) 60250997Sbostic u_long map; 60346372Sbostic { 60450997Sbostic register u_long i, mask; 60546372Sbostic 60650997Sbostic mask = 0x1; 60750997Sbostic for (i = 0; i < BITS_PER_MAP; i++) { 60850997Sbostic if (!(mask & map)) 60950997Sbostic return (i); 61050997Sbostic mask = mask << 1; 61150997Sbostic } 61250997Sbostic return (i); 61346372Sbostic } 61446372Sbostic 61550997Sbostic static u_short 61650997Sbostic overflow_page() 61746372Sbostic { 61850997Sbostic register u_long *freep; 61950997Sbostic register int max_free, offset, splitnum; 62050997Sbostic u_short addr; 62150997Sbostic int bit, free_bit, free_page, i, in_use_bits, j; 62246372Sbostic #ifdef DEBUG2 62350997Sbostic int tmp1, tmp2; 62446372Sbostic #endif 62550997Sbostic splitnum = __log2(hashp->MAX_BUCKET); 62650997Sbostic max_free = hashp->SPARES[splitnum]; 62746372Sbostic 62850997Sbostic free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); 62950997Sbostic free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); 63046372Sbostic 63150997Sbostic /* Look through all the free maps to find the first free block */ 63250997Sbostic for (i = 0; i <= free_page; i++) { 63350997Sbostic if (!(freep = (u_long *)hashp->mapp[i]) && 63450997Sbostic !(freep = fetch_bitmap(i))) 63550997Sbostic return (NULL); 63650997Sbostic if (i == free_page) 63750997Sbostic in_use_bits = free_bit; 63850997Sbostic else 63950997Sbostic in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; 64046372Sbostic 64150997Sbostic for (j = 0, bit = 0; bit <= in_use_bits; 64250997Sbostic j++, bit += BITS_PER_MAP) 64350997Sbostic if (freep[j] != ALL_SET) 64450997Sbostic goto found; 64547250Sbostic } 64646372Sbostic 64750997Sbostic /* No Free Page Found */ 64850997Sbostic hashp->SPARES[splitnum]++; 64950997Sbostic offset = hashp->SPARES[splitnum] - 65050997Sbostic (splitnum ? hashp->SPARES[splitnum - 1] : 0); 65146372Sbostic 65250997Sbostic /* Check if we need to allocate a new bitmap page */ 65350997Sbostic if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { 65450997Sbostic free_page++; 65550058Sbostic #define OVMSG "hash: out of overflow pages; increase page size\n" 65650997Sbostic if (free_page >= NCACHED) { 65750997Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 65850997Sbostic return (NULL); 65950997Sbostic } 66050997Sbostic /* 66150997Sbostic * This is tricky. The 1 indicates that you want the new page 66250997Sbostic * allocated with 1 clear bit. Actually, you are going to 66350997Sbostic * allocate 2 pages from this map. The first is going to be 66450997Sbostic * the map page, the second is the overflow page we were 66550997Sbostic * looking for. The init_bitmap routine automatically, sets 66650997Sbostic * the first bit of itself to indicate that the bitmap itself 66750997Sbostic * is in use. We would explicitly set the second bit, but 66850997Sbostic * don't have to if we tell init_bitmap not to leave it clear 66950997Sbostic * in the first place. 67050997Sbostic */ 671*51055Sbostic if (__init_bitmap((int)OADDR_OF(splitnum, offset), 672*51055Sbostic 1, free_page)) 673*51055Sbostic return (NULL); 67450997Sbostic hashp->SPARES[splitnum]++; 67546372Sbostic #ifdef DEBUG2 67650997Sbostic free_bit = 2; 67746372Sbostic #endif 67850997Sbostic offset++; 67950997Sbostic } else { 68050997Sbostic /* 68150997Sbostic * Free_bit addresses the last used bit. Bump it to address 68250997Sbostic * the first available bit. 68350997Sbostic */ 68450997Sbostic free_bit++; 68550997Sbostic SETBIT(freep, free_bit); 68650997Sbostic } 68746372Sbostic 68850997Sbostic /* Calculate address of the new overflow page */ 68950997Sbostic if (offset > SPLITMASK) { 69050997Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 69150997Sbostic return (NULL); 69250997Sbostic } 69350997Sbostic addr = OADDR_OF(splitnum, offset); 69446372Sbostic #ifdef DEBUG2 69550997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 69650997Sbostic addr, free_bit, free_page); 69746372Sbostic #endif 69850997Sbostic return (addr); 69946372Sbostic 70046372Sbostic found: 70150997Sbostic bit = bit + first_free(freep[j]); 70250997Sbostic SETBIT(freep, bit); 70346372Sbostic #ifdef DEBUG2 70450997Sbostic tmp1 = bit; 70550997Sbostic tmp2 = i; 70646372Sbostic #endif 70750997Sbostic /* 70850997Sbostic * Bits are addressed starting with 0, but overflow pages are addressed 70950997Sbostic * beginning at 1. Bit is a bit addressnumber, so we need to increment 71050997Sbostic * it to convert it to a page number. 71150997Sbostic */ 71250997Sbostic bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); 71346372Sbostic 71450997Sbostic /* Calculate the split number for this page */ 71550997Sbostic for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); 71650997Sbostic offset = (i ? bit - hashp->SPARES[i - 1] : bit); 71750997Sbostic if (offset >= SPLITMASK) 71850997Sbostic return (NULL); /* Out of overflow pages */ 71950997Sbostic addr = OADDR_OF(i, offset); 72046372Sbostic #ifdef DEBUG2 72150997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 72250997Sbostic addr, tmp1, tmp2); 72346372Sbostic #endif 72446372Sbostic 72550997Sbostic /* Allocate and return the overflow page */ 72650997Sbostic return (addr); 72746372Sbostic } 72846372Sbostic 72946372Sbostic /* 73050997Sbostic * Mark this overflow page as free. 73150997Sbostic */ 73250997Sbostic extern void 73350997Sbostic __free_ovflpage(obufp) 73450997Sbostic BUFHEAD *obufp; 73546372Sbostic { 73650997Sbostic register u_short addr; 73750997Sbostic u_long *freep; 73850997Sbostic int bit_address, free_page, free_bit; 73950997Sbostic u_short ndx; 74046372Sbostic 74150997Sbostic addr = obufp->addr; 74246372Sbostic #ifdef DEBUG1 74350997Sbostic (void)fprintf(stderr, "Freeing %d\n", addr); 74446372Sbostic #endif 74550997Sbostic ndx = (((u_short)addr) >> SPLITSHIFT); 74650997Sbostic bit_address = 74750997Sbostic (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; 74850997Sbostic free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); 74950997Sbostic free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); 75046372Sbostic 75150997Sbostic if (!(freep = hashp->mapp[free_page])) 75250997Sbostic freep = fetch_bitmap(free_page); 75350997Sbostic #ifdef DEBUG 75450997Sbostic /* 75550997Sbostic * This had better never happen. It means we tried to read a bitmap 75650997Sbostic * that has already had overflow pages allocated off it, and we 75750997Sbostic * failed to read it from the file. 75850997Sbostic */ 75950997Sbostic if (!freep) 76050997Sbostic assert(0); 76150997Sbostic #endif 76250997Sbostic CLRBIT(freep, free_bit); 76346372Sbostic #ifdef DEBUG2 76450997Sbostic (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", 76550997Sbostic obufp->addr, free_bit, free_page); 76646372Sbostic #endif 76750997Sbostic __reclaim_buf(obufp); 76846372Sbostic } 76946372Sbostic 77046372Sbostic /* 77150997Sbostic * Returns: 77250997Sbostic * 0 success 77350997Sbostic * -1 failure 77450997Sbostic */ 77546372Sbostic static int 77646372Sbostic open_temp() 77746372Sbostic { 77850997Sbostic sigset_t set, oset; 77950997Sbostic static char namestr[] = "_hashXXXXXX"; 78046372Sbostic 78150997Sbostic /* Block signals; make sure file goes away at process exit. */ 78250997Sbostic sigfillset(&set); 78350997Sbostic (void)sigprocmask(SIG_BLOCK, &set, &oset); 78450997Sbostic if ((hashp->fp = mkstemp(namestr)) != -1) { 78550997Sbostic (void)unlink(namestr); 78650997Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 78750997Sbostic } 78850997Sbostic (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); 78950997Sbostic return (hashp->fp != -1 ? 0 : -1); 79046372Sbostic } 79146372Sbostic 79250997Sbostic /* 79350997Sbostic * We have to know that the key will fit, but the last entry on the page is 79450997Sbostic * an overflow pair, so we need to shift things. 79550997Sbostic */ 79646372Sbostic static void 79750997Sbostic squeeze_key(sp, key, val) 79850997Sbostic u_short *sp; 79950997Sbostic const DBT *key, *val; 80046372Sbostic { 80150997Sbostic register char *p; 80250997Sbostic u_short free_space, n, off, pageno; 80346372Sbostic 80450997Sbostic p = (char *)sp; 80550997Sbostic n = sp[0]; 80650997Sbostic free_space = FREESPACE(sp); 80750997Sbostic off = OFFSET(sp); 80846372Sbostic 80950997Sbostic pageno = sp[n - 1]; 81050997Sbostic off -= key->size; 81150997Sbostic sp[n - 1] = off; 81250997Sbostic bcopy(key->data, p + off, key->size); 81350997Sbostic off -= val->size; 81450997Sbostic sp[n] = off; 81550997Sbostic bcopy(val->data, p + off, val->size); 81650997Sbostic sp[0] = n + 2; 81750997Sbostic sp[n + 1] = pageno; 81850997Sbostic sp[n + 2] = OVFLPAGE; 81950997Sbostic FREESPACE(sp) = free_space - PAIRSIZE(key, val); 82050997Sbostic OFFSET(sp) = off; 82146372Sbostic } 82246372Sbostic 82347250Sbostic static u_long * 82450997Sbostic fetch_bitmap(ndx) 82550997Sbostic int ndx; 82647250Sbostic { 82750997Sbostic if (ndx >= hashp->nmaps || 82850997Sbostic !(hashp->mapp[ndx] = malloc(hashp->BSIZE)) || 82950997Sbostic __get_page((char *)hashp->mapp[ndx], 83050997Sbostic hashp->BITMAPS[ndx], 0, 1, 1)) 83150997Sbostic return (NULL); 83250997Sbostic return (hashp->mapp[ndx]); 83350997Sbostic } 83447250Sbostic 83546372Sbostic #ifdef DEBUG4 83650997Sbostic int 83750997Sbostic print_chain(addr) 83850997Sbostic int addr; 83946372Sbostic { 84050997Sbostic BUFHEAD *bufp; 84150997Sbostic short *bp, oaddr; 84246372Sbostic 84350997Sbostic (void)fprintf(stderr, "%d ", addr); 84450997Sbostic bufp = __get_buf(addr, NULL, 0); 84546372Sbostic bp = (short *)bufp->page; 84650997Sbostic while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || 84750997Sbostic ((bp[0] > 2) && bp[2] < REAL_KEY))) { 84850997Sbostic oaddr = bp[bp[0] - 1]; 84950997Sbostic (void)fprintf(stderr, "%d ", (int)oaddr); 85050997Sbostic bufp = __get_buf((int)oaddr, bufp, 0); 85146372Sbostic bp = (short *)bufp->page; 85246372Sbostic } 85350997Sbostic (void)fprintf(stderr, "\n"); 85446372Sbostic } 85546372Sbostic #endif 856