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*53403Sbostic static char sccsid[] = "@(#)hash_page.c 5.18 (Berkeley) 05/11/92"; 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 4751055Sbostic static u_long *fetch_bitmap __P((int)); 4851055Sbostic static u_long first_free __P((u_long)); 4951055Sbostic static int open_temp __P((void)); 5051055Sbostic static u_short overflow_page __P((void)); 5151055Sbostic static void putpair __P((char *, const DBT *, const DBT *)); 5251055Sbostic static void squeeze_key __P((u_short *, const DBT *, const DBT *)); 5351055Sbostic 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) 11151055Sbostic 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, 17851055Sbostic (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; 25651055Sbostic 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) { 26951055Sbostic /* 27051055Sbostic * Ov_addr gets set before reaching this point; there's 27151055Sbostic * always an overflow page before a big key/data page. 27251055Sbostic */ 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 } 503*53403Sbostic if (!is_bitmap && !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()) 54151073Sbostic 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 */ 57851055Sbostic 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))) 58651055Sbostic return (1); 58750997Sbostic hashp->nmaps++; 58850997Sbostic clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; 58950997Sbostic clearbytes = clearints << INT_TO_BYTE; 59051055Sbostic (void)memset((char *)ip, 0, clearbytes); 59151055Sbostic (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; 59751055Sbostic return (0); 59846372Sbostic } 59950997Sbostic 60051055Sbostic 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; 62151061Sbostic int bit, first_page, free_bit, free_page, i, in_use_bits, j; 62246372Sbostic #ifdef DEBUG2 62350997Sbostic int tmp1, tmp2; 62446372Sbostic #endif 62551061Sbostic splitnum = hashp->OVFL_POINT; 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 */ 63251061Sbostic first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); 63351061Sbostic for ( i = first_page; i <= free_page; i++ ) { 63450997Sbostic if (!(freep = (u_long *)hashp->mapp[i]) && 63550997Sbostic !(freep = fetch_bitmap(i))) 63650997Sbostic return (NULL); 63750997Sbostic if (i == free_page) 63850997Sbostic in_use_bits = free_bit; 63950997Sbostic else 64050997Sbostic in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; 64151061Sbostic 64251061Sbostic if (i == first_page) { 64351061Sbostic bit = hashp->LAST_FREED & 64451061Sbostic ((hashp->BSIZE << BYTE_SHIFT) - 1); 64551061Sbostic j = bit / BITS_PER_MAP; 64651061Sbostic bit = bit & ~(BITS_PER_MAP - 1); 64751061Sbostic } else { 64851061Sbostic bit = 0; 64951061Sbostic j = 0; 65051061Sbostic } 65151061Sbostic for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) 65250997Sbostic if (freep[j] != ALL_SET) 65350997Sbostic goto found; 65447250Sbostic } 65546372Sbostic 65650997Sbostic /* No Free Page Found */ 65751061Sbostic hashp->LAST_FREED = hashp->SPARES[splitnum]; 65850997Sbostic hashp->SPARES[splitnum]++; 65950997Sbostic offset = hashp->SPARES[splitnum] - 66050997Sbostic (splitnum ? hashp->SPARES[splitnum - 1] : 0); 66146372Sbostic 66251061Sbostic #define OVMSG "HASH: Out of overflow pages. Increase page size\n" 66351061Sbostic if (offset > SPLITMASK) { 66451061Sbostic if (++splitnum >= NCACHED) { 66551061Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 66651061Sbostic return (NULL); 66751061Sbostic } 66851061Sbostic hashp->OVFL_POINT = splitnum; 66951061Sbostic hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; 67051061Sbostic hashp->SPARES[splitnum-1]--; 67151061Sbostic offset = 0; 67251061Sbostic } 67351061Sbostic 67450997Sbostic /* Check if we need to allocate a new bitmap page */ 67550997Sbostic if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { 67650997Sbostic free_page++; 67750997Sbostic if (free_page >= NCACHED) { 67850997Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 67950997Sbostic return (NULL); 68050997Sbostic } 68150997Sbostic /* 68250997Sbostic * This is tricky. The 1 indicates that you want the new page 68350997Sbostic * allocated with 1 clear bit. Actually, you are going to 68450997Sbostic * allocate 2 pages from this map. The first is going to be 68550997Sbostic * the map page, the second is the overflow page we were 68650997Sbostic * looking for. The init_bitmap routine automatically, sets 68750997Sbostic * the first bit of itself to indicate that the bitmap itself 68850997Sbostic * is in use. We would explicitly set the second bit, but 68950997Sbostic * don't have to if we tell init_bitmap not to leave it clear 69050997Sbostic * in the first place. 69150997Sbostic */ 69251055Sbostic if (__init_bitmap((int)OADDR_OF(splitnum, offset), 69351055Sbostic 1, free_page)) 69451055Sbostic return (NULL); 69550997Sbostic hashp->SPARES[splitnum]++; 69646372Sbostic #ifdef DEBUG2 69750997Sbostic free_bit = 2; 69846372Sbostic #endif 69950997Sbostic offset++; 70051061Sbostic if (offset > SPLITMASK) { 70151061Sbostic if (++splitnum >= NCACHED) { 70251061Sbostic (void)write(STDERR_FILENO, OVMSG, 70351061Sbostic sizeof(OVMSG) - 1); 70451061Sbostic return (NULL); 70551061Sbostic } 70651061Sbostic hashp->OVFL_POINT = splitnum; 70751061Sbostic hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; 70851061Sbostic hashp->SPARES[splitnum-1]--; 70951061Sbostic offset = 0; 71051061Sbostic } 71150997Sbostic } else { 71250997Sbostic /* 71350997Sbostic * Free_bit addresses the last used bit. Bump it to address 71450997Sbostic * the first available bit. 71550997Sbostic */ 71650997Sbostic free_bit++; 71750997Sbostic SETBIT(freep, free_bit); 71850997Sbostic } 71946372Sbostic 72050997Sbostic /* Calculate address of the new overflow page */ 72150997Sbostic addr = OADDR_OF(splitnum, offset); 72246372Sbostic #ifdef DEBUG2 72350997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 72450997Sbostic addr, free_bit, free_page); 72546372Sbostic #endif 72650997Sbostic return (addr); 72746372Sbostic 72846372Sbostic found: 72950997Sbostic bit = bit + first_free(freep[j]); 73050997Sbostic SETBIT(freep, bit); 73146372Sbostic #ifdef DEBUG2 73250997Sbostic tmp1 = bit; 73350997Sbostic tmp2 = i; 73446372Sbostic #endif 73550997Sbostic /* 73650997Sbostic * Bits are addressed starting with 0, but overflow pages are addressed 73750997Sbostic * beginning at 1. Bit is a bit addressnumber, so we need to increment 73850997Sbostic * it to convert it to a page number. 73950997Sbostic */ 74050997Sbostic bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); 74151061Sbostic if (bit >= hashp->LAST_FREED) 74251061Sbostic hashp->LAST_FREED = bit - 1; 74346372Sbostic 74450997Sbostic /* Calculate the split number for this page */ 74550997Sbostic for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); 74650997Sbostic offset = (i ? bit - hashp->SPARES[i - 1] : bit); 74750997Sbostic if (offset >= SPLITMASK) 74850997Sbostic return (NULL); /* Out of overflow pages */ 74950997Sbostic addr = OADDR_OF(i, offset); 75046372Sbostic #ifdef DEBUG2 75150997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 75250997Sbostic addr, tmp1, tmp2); 75346372Sbostic #endif 75446372Sbostic 75550997Sbostic /* Allocate and return the overflow page */ 75650997Sbostic return (addr); 75746372Sbostic } 75846372Sbostic 75946372Sbostic /* 76050997Sbostic * Mark this overflow page as free. 76150997Sbostic */ 76250997Sbostic extern void 76350997Sbostic __free_ovflpage(obufp) 76450997Sbostic BUFHEAD *obufp; 76546372Sbostic { 76650997Sbostic register u_short addr; 76750997Sbostic u_long *freep; 76850997Sbostic int bit_address, free_page, free_bit; 76950997Sbostic u_short ndx; 77046372Sbostic 77150997Sbostic addr = obufp->addr; 77246372Sbostic #ifdef DEBUG1 77350997Sbostic (void)fprintf(stderr, "Freeing %d\n", addr); 77446372Sbostic #endif 77550997Sbostic ndx = (((u_short)addr) >> SPLITSHIFT); 77650997Sbostic bit_address = 77750997Sbostic (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; 77851061Sbostic if (bit_address < hashp->LAST_FREED) 77951061Sbostic hashp->LAST_FREED = bit_address; 78050997Sbostic free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); 78150997Sbostic free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); 78246372Sbostic 78350997Sbostic if (!(freep = hashp->mapp[free_page])) 78450997Sbostic freep = fetch_bitmap(free_page); 78550997Sbostic #ifdef DEBUG 78650997Sbostic /* 78750997Sbostic * This had better never happen. It means we tried to read a bitmap 78850997Sbostic * that has already had overflow pages allocated off it, and we 78950997Sbostic * failed to read it from the file. 79050997Sbostic */ 79150997Sbostic if (!freep) 79250997Sbostic assert(0); 79350997Sbostic #endif 79450997Sbostic CLRBIT(freep, free_bit); 79546372Sbostic #ifdef DEBUG2 79650997Sbostic (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", 79750997Sbostic obufp->addr, free_bit, free_page); 79846372Sbostic #endif 79950997Sbostic __reclaim_buf(obufp); 80046372Sbostic } 80146372Sbostic 80246372Sbostic /* 80350997Sbostic * Returns: 80450997Sbostic * 0 success 80550997Sbostic * -1 failure 80650997Sbostic */ 80746372Sbostic static int 80846372Sbostic open_temp() 80946372Sbostic { 81050997Sbostic sigset_t set, oset; 81150997Sbostic static char namestr[] = "_hashXXXXXX"; 81246372Sbostic 81350997Sbostic /* Block signals; make sure file goes away at process exit. */ 81450997Sbostic sigfillset(&set); 81550997Sbostic (void)sigprocmask(SIG_BLOCK, &set, &oset); 81650997Sbostic if ((hashp->fp = mkstemp(namestr)) != -1) { 81750997Sbostic (void)unlink(namestr); 81850997Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 81950997Sbostic } 82050997Sbostic (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); 82150997Sbostic return (hashp->fp != -1 ? 0 : -1); 82246372Sbostic } 82346372Sbostic 82450997Sbostic /* 82550997Sbostic * We have to know that the key will fit, but the last entry on the page is 82650997Sbostic * an overflow pair, so we need to shift things. 82750997Sbostic */ 82846372Sbostic static void 82950997Sbostic squeeze_key(sp, key, val) 83050997Sbostic u_short *sp; 83150997Sbostic const DBT *key, *val; 83246372Sbostic { 83350997Sbostic register char *p; 83450997Sbostic u_short free_space, n, off, pageno; 83546372Sbostic 83650997Sbostic p = (char *)sp; 83750997Sbostic n = sp[0]; 83850997Sbostic free_space = FREESPACE(sp); 83950997Sbostic off = OFFSET(sp); 84046372Sbostic 84150997Sbostic pageno = sp[n - 1]; 84250997Sbostic off -= key->size; 84350997Sbostic sp[n - 1] = off; 84450997Sbostic bcopy(key->data, p + off, key->size); 84550997Sbostic off -= val->size; 84650997Sbostic sp[n] = off; 84750997Sbostic bcopy(val->data, p + off, val->size); 84850997Sbostic sp[0] = n + 2; 84950997Sbostic sp[n + 1] = pageno; 85050997Sbostic sp[n + 2] = OVFLPAGE; 85150997Sbostic FREESPACE(sp) = free_space - PAIRSIZE(key, val); 85250997Sbostic OFFSET(sp) = off; 85346372Sbostic } 85446372Sbostic 85547250Sbostic static u_long * 85650997Sbostic fetch_bitmap(ndx) 85750997Sbostic int ndx; 85847250Sbostic { 85950997Sbostic if (ndx >= hashp->nmaps || 86050997Sbostic !(hashp->mapp[ndx] = malloc(hashp->BSIZE)) || 86150997Sbostic __get_page((char *)hashp->mapp[ndx], 86250997Sbostic hashp->BITMAPS[ndx], 0, 1, 1)) 86350997Sbostic return (NULL); 86450997Sbostic return (hashp->mapp[ndx]); 86550997Sbostic } 86647250Sbostic 86746372Sbostic #ifdef DEBUG4 86850997Sbostic int 86950997Sbostic print_chain(addr) 87050997Sbostic int addr; 87146372Sbostic { 87250997Sbostic BUFHEAD *bufp; 87350997Sbostic short *bp, oaddr; 87446372Sbostic 87550997Sbostic (void)fprintf(stderr, "%d ", addr); 87650997Sbostic bufp = __get_buf(addr, NULL, 0); 87746372Sbostic bp = (short *)bufp->page; 87850997Sbostic while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || 87950997Sbostic ((bp[0] > 2) && bp[2] < REAL_KEY))) { 88050997Sbostic oaddr = bp[bp[0] - 1]; 88150997Sbostic (void)fprintf(stderr, "%d ", (int)oaddr); 88250997Sbostic bufp = __get_buf((int)oaddr, bufp, 0); 88346372Sbostic bp = (short *)bufp->page; 88446372Sbostic } 88550997Sbostic (void)fprintf(stderr, "\n"); 88646372Sbostic } 88746372Sbostic #endif 888