xref: /csrg-svn/lib/libc/db/hash/hash_page.c (revision 46372)
1*46372Sbostic /*-
2*46372Sbostic  * Copyright (c) 1990 The Regents of the University of California.
3*46372Sbostic  * All rights reserved.
4*46372Sbostic  *
5*46372Sbostic  * This code is derived from software contributed to Berkeley by
6*46372Sbostic  * Margo Seltzer.
7*46372Sbostic  *
8*46372Sbostic  * %sccs.include.redist.c%
9*46372Sbostic  */
10*46372Sbostic 
11*46372Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46372Sbostic static char sccsid[] = "@(#)hash_page.c	5.1 (Berkeley) 02/12/91";
13*46372Sbostic #endif /* LIBC_SCCS and not lint */
14*46372Sbostic 
15*46372Sbostic /******************************************************************************
16*46372Sbostic PACKAGE:  hashing
17*46372Sbostic 
18*46372Sbostic DESCRIPTION:
19*46372Sbostic 	Page manipulation for hashing package.
20*46372Sbostic 
21*46372Sbostic ROUTINES:
22*46372Sbostic     External
23*46372Sbostic 	__get_page
24*46372Sbostic 	__add_ovflpage
25*46372Sbostic     Internal
26*46372Sbostic 	overflow_page
27*46372Sbostic 	open_temp
28*46372Sbostic ******************************************************************************/
29*46372Sbostic 
30*46372Sbostic #include <sys/param.h>
31*46372Sbostic #include <sys/file.h>
32*46372Sbostic #include <assert.h>
33*46372Sbostic #include <errno.h>
34*46372Sbostic #include <db.h>
35*46372Sbostic #include <stdio.h>
36*46372Sbostic #include "hash.h"
37*46372Sbostic #include "page.h"
38*46372Sbostic 
39*46372Sbostic /* Externals */
40*46372Sbostic /* buf.c */
41*46372Sbostic extern BUFHEAD	*__get_buf();
42*46372Sbostic extern void __reclaim_buf();
43*46372Sbostic 
44*46372Sbostic /* big.c */
45*46372Sbostic extern int __big_split();
46*46372Sbostic extern int __big_insert();
47*46372Sbostic extern int __big_delete();
48*46372Sbostic extern int __find_bigpair();
49*46372Sbostic 
50*46372Sbostic /* dynahash.c */
51*46372Sbostic extern	int	__call_hash();
52*46372Sbostic extern	int	__expand_table();
53*46372Sbostic 
54*46372Sbostic /* my externals */
55*46372Sbostic extern int __get_page();
56*46372Sbostic extern BUFHEAD *__add_ovflpage();
57*46372Sbostic extern int __split_page();
58*46372Sbostic extern int __addel();
59*46372Sbostic 
60*46372Sbostic /* my internals */
61*46372Sbostic static u_short overflow_page();
62*46372Sbostic static int open_temp();
63*46372Sbostic static void squeeze_key();
64*46372Sbostic static void putpair();
65*46372Sbostic 
66*46372Sbostic #ifdef HASH_STATISTICS
67*46372Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
68*46372Sbostic #endif
69*46372Sbostic #define	PAGE_INIT(P)					\
70*46372Sbostic {							\
71*46372Sbostic     ((u_short *)P)[0] = 0;				\
72*46372Sbostic     ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short);	\
73*46372Sbostic     ((u_short *)P)[2] = hashp->BSIZE;			\
74*46372Sbostic }
75*46372Sbostic 
76*46372Sbostic /*
77*46372Sbostic 	This is called AFTER we have verified that there is room on the
78*46372Sbostic 	page for the pair (PAIRFITS has returned true) so we go right
79*46372Sbostic 	ahead and start moving stuff on.
80*46372Sbostic */
81*46372Sbostic static void
82*46372Sbostic putpair(p, key, val)
83*46372Sbostic char *p;
84*46372Sbostic DBT *key;
85*46372Sbostic DBT *val;
86*46372Sbostic {
87*46372Sbostic 	register u_short n;
88*46372Sbostic 	register u_short off;
89*46372Sbostic 	register u_short *bp = (u_short *) p;
90*46372Sbostic 
91*46372Sbostic /* enter the key first */
92*46372Sbostic 	n = bp[0];
93*46372Sbostic 
94*46372Sbostic 	off = OFFSET(bp) - key->size;
95*46372Sbostic 	bcopy( key->data, p+off, key->size );
96*46372Sbostic 	bp[++n] = off;
97*46372Sbostic 
98*46372Sbostic /* now the data */
99*46372Sbostic 	off -= val->size;
100*46372Sbostic 	bcopy(val->data,  p + off, val->size);
101*46372Sbostic 	bp[++n] = off;
102*46372Sbostic 
103*46372Sbostic /* adjust page info */
104*46372Sbostic 	bp[0] = n;
105*46372Sbostic 	bp[n+1] = off - ((n+3)*sizeof(u_short));
106*46372Sbostic 	bp[n+2] = off;
107*46372Sbostic 	return;
108*46372Sbostic }
109*46372Sbostic /*
110*46372Sbostic     0 OK
111*46372Sbostic     -1 error
112*46372Sbostic */
113*46372Sbostic extern int
114*46372Sbostic __delpair(bufp, ndx)
115*46372Sbostic BUFHEAD *bufp;
116*46372Sbostic register int ndx;
117*46372Sbostic {
118*46372Sbostic 	register u_short *bp = (u_short *) bufp->page;
119*46372Sbostic 	register int n = bp[0];
120*46372Sbostic 	register u_short newoff;
121*46372Sbostic 	u_short pairlen;
122*46372Sbostic 
123*46372Sbostic 	if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) );
124*46372Sbostic 	if ( ndx != 1 ) newoff = bp[ndx-1];
125*46372Sbostic 	else newoff = hashp->BSIZE;
126*46372Sbostic 	pairlen = newoff - bp[ndx+1];
127*46372Sbostic 
128*46372Sbostic 	if ( ndx != (n-1) ) {
129*46372Sbostic 		/* Hard Case -- need to shuffle keys */
130*46372Sbostic 		register int i;
131*46372Sbostic 		register char *src = bufp->page + (int)OFFSET(bp);
132*46372Sbostic 		register char *dst = src + (int)pairlen;
133*46372Sbostic 		bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) );
134*46372Sbostic 
135*46372Sbostic 		/* Now adjust the pointers */
136*46372Sbostic 		for ( i = ndx+2; i <= n; i += 2 ) {
137*46372Sbostic 		    if ( bp[i+1]  == OVFLPAGE ) {
138*46372Sbostic 			bp[i-2] = bp[i];
139*46372Sbostic 			bp[i-1] = bp[i+1];
140*46372Sbostic 		    } else {
141*46372Sbostic 			bp[i-2] = bp[i] + pairlen;
142*46372Sbostic 			bp[i-1] = bp[i+1] + pairlen;
143*46372Sbostic 		    }
144*46372Sbostic 		}
145*46372Sbostic 	}
146*46372Sbostic 
147*46372Sbostic 	/* Finally adjust the page data */
148*46372Sbostic 	bp[n] = OFFSET(bp) + pairlen;
149*46372Sbostic 	bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short);
150*46372Sbostic 	bp[0] = n-2;
151*46372Sbostic 	hashp->NKEYS--;
152*46372Sbostic 
153*46372Sbostic 	bufp->flags |= BUF_MOD;
154*46372Sbostic 	return (0);
155*46372Sbostic }
156*46372Sbostic /*
157*46372Sbostic     -1 ==> Error
158*46372Sbostic     0 ==> OK
159*46372Sbostic */
160*46372Sbostic extern int
161*46372Sbostic __split_page(obucket, nbucket)
162*46372Sbostic int obucket;
163*46372Sbostic int nbucket;
164*46372Sbostic {
165*46372Sbostic 	DBT key;
166*46372Sbostic 	DBT val;
167*46372Sbostic 
168*46372Sbostic 	register BUFHEAD *new_bufp;
169*46372Sbostic 	register BUFHEAD *old_bufp;
170*46372Sbostic 	register u_short *ino;
171*46372Sbostic 	register char	*np;
172*46372Sbostic 	char	*op;
173*46372Sbostic 
174*46372Sbostic 	u_short copyto = (u_short)hashp->BSIZE;
175*46372Sbostic 	u_short off = (u_short)hashp->BSIZE;
176*46372Sbostic 	int n;
177*46372Sbostic 	u_short diff;
178*46372Sbostic 	u_short moved;
179*46372Sbostic 	int ndx;
180*46372Sbostic 
181*46372Sbostic 	old_bufp = __get_buf ( obucket, NULL, 0 );
182*46372Sbostic 	new_bufp = __get_buf ( nbucket, NULL, 0 );
183*46372Sbostic 
184*46372Sbostic 	old_bufp->flags |= BUF_MOD;
185*46372Sbostic 	new_bufp->flags |= BUF_MOD;
186*46372Sbostic 
187*46372Sbostic 	ino = (u_short *)(op = old_bufp->page);
188*46372Sbostic 	np = new_bufp->page;
189*46372Sbostic 
190*46372Sbostic 	moved = 0;
191*46372Sbostic 
192*46372Sbostic 	for (n = 1, ndx = 1; n < ino[0]; n+=2) {
193*46372Sbostic 		if ( ino[n+1] < REAL_KEY ) {
194*46372Sbostic 		    return ( ugly_split( obucket, old_bufp, new_bufp,
195*46372Sbostic 					 copyto, moved ) );
196*46372Sbostic 		}
197*46372Sbostic 		key.data = op + ino[n];
198*46372Sbostic 		key.size = off - ino[n];
199*46372Sbostic 
200*46372Sbostic 		if ( __call_hash ( key.data, key.size ) == obucket ) {
201*46372Sbostic 		    /* Don't switch page */
202*46372Sbostic 		    diff = copyto - off;
203*46372Sbostic 		    if ( diff ) {
204*46372Sbostic 			copyto = ino[n+1] + diff;
205*46372Sbostic 			bcopy ( op + ino[n+1], op + copyto,  off-ino[n+1]);
206*46372Sbostic 			ino[ndx] = copyto + ino[n] - ino[n+1];
207*46372Sbostic 			ino[ndx+1] = copyto;
208*46372Sbostic 		    } else copyto = ino[n+1];
209*46372Sbostic 		    ndx += 2;
210*46372Sbostic 		} else {
211*46372Sbostic 		    /* Switch page */
212*46372Sbostic 		    val.data = op + ino[n+1];
213*46372Sbostic 		    val.size = ino[n] - ino[n+1];
214*46372Sbostic 		    putpair( np, &key, &val);
215*46372Sbostic 		    moved +=2;
216*46372Sbostic 		}
217*46372Sbostic 
218*46372Sbostic 		off = ino[n+1];
219*46372Sbostic 	}
220*46372Sbostic 
221*46372Sbostic 	/* Now clean up the page */
222*46372Sbostic 	ino[0] -= moved;
223*46372Sbostic 	FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
224*46372Sbostic 	OFFSET(ino) = copyto;
225*46372Sbostic 
226*46372Sbostic #ifdef DEBUG3
227*46372Sbostic 	fprintf(stderr, "split %d/%d\n",
228*46372Sbostic 	       ((u_short *) np)[0] / 2,
229*46372Sbostic 	       ((u_short *) op)[0] / 2);
230*46372Sbostic #endif
231*46372Sbostic 	return(0);
232*46372Sbostic }
233*46372Sbostic /*
234*46372Sbostic     0 ==> success
235*46372Sbostic     -1 ==> failure
236*46372Sbostic 
237*46372Sbostic     Called when we encounter an overflow page during split handling.
238*46372Sbostic     this is special cased since we have to begin checking whether
239*46372Sbostic     the key/data pairs fit on their respective pages and because
240*46372Sbostic     we may need overflow pages for both the old and new pages
241*46372Sbostic */
242*46372Sbostic static int
243*46372Sbostic ugly_split( obucket, old_bufp, new_bufp, copyto, moved )
244*46372Sbostic int	obucket;	/* Same as __split_page */
245*46372Sbostic BUFHEAD	*old_bufp;
246*46372Sbostic BUFHEAD	*new_bufp;
247*46372Sbostic u_short	copyto;		/* First byte on page which contains key/data values */
248*46372Sbostic int	moved;		/* number of pairs moved to new page */
249*46372Sbostic {
250*46372Sbostic     register BUFHEAD *bufp = old_bufp;		/* Buffer header for ino */
251*46372Sbostic     register u_short	*ino = (u_short *)old_bufp->page;
252*46372Sbostic 						/* Page keys come off of */
253*46372Sbostic     register u_short	*np = (u_short *)new_bufp->page;	/* New page */
254*46372Sbostic     register u_short	*op = (u_short *)old_bufp->page;
255*46372Sbostic 						/* Page keys go on to if they
256*46372Sbostic 						   aren't moving */
257*46372Sbostic 
258*46372Sbostic     char	*cino;				/* Character value of ino */
259*46372Sbostic     BUFHEAD	*last_bfp = NULL;		/* Last buffer header OVFL which
260*46372Sbostic 						   needs to be freed */
261*46372Sbostic     u_short	ov_addr, last_addr = 0;
262*46372Sbostic     u_short	n;
263*46372Sbostic     u_short	off;
264*46372Sbostic 
265*46372Sbostic     DBT	key, val;
266*46372Sbostic     SPLIT_RETURN	ret;
267*46372Sbostic 
268*46372Sbostic     n = ino[0]-1;
269*46372Sbostic     while ( n < ino[0] ) {
270*46372Sbostic 	if ( ino[n+1] == OVFLPAGE ) {
271*46372Sbostic 	    ov_addr = ino[n];
272*46372Sbostic 	    /*
273*46372Sbostic 		Fix up the old page -- the extra 2 are the fields which
274*46372Sbostic 		contained the overflow information
275*46372Sbostic 	    */
276*46372Sbostic 	    ino[0] -= (moved + 2);
277*46372Sbostic 	    FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
278*46372Sbostic 	    OFFSET(ino) = copyto;
279*46372Sbostic 
280*46372Sbostic 	    bufp = __get_buf ( ov_addr, bufp, 0 );
281*46372Sbostic 	    if ( !bufp ) return(-1);
282*46372Sbostic 
283*46372Sbostic 	    ino = (u_short *)bufp->page;
284*46372Sbostic 	    n = 1;
285*46372Sbostic 	    copyto = hashp->BSIZE;
286*46372Sbostic 	    moved = 0;
287*46372Sbostic 
288*46372Sbostic 	    if ( last_bfp ) {
289*46372Sbostic 		__free_ovflpage( last_bfp);
290*46372Sbostic 	    }
291*46372Sbostic 	    last_bfp = bufp;
292*46372Sbostic 	}
293*46372Sbostic 
294*46372Sbostic 	if ( (ino[2] < REAL_KEY) && (ino[2] != OVFLPAGE) ) {
295*46372Sbostic 	    if (__big_split (old_bufp,
296*46372Sbostic 		new_bufp, bufp, ov_addr, obucket, &ret)) {
297*46372Sbostic 		    return(-1);
298*46372Sbostic 	    }
299*46372Sbostic 	    old_bufp = ret.oldp;
300*46372Sbostic 	    if ( !old_bufp ) return(-1);
301*46372Sbostic 	    op = (u_short *)old_bufp->page;
302*46372Sbostic 	    new_bufp = ret.newp;
303*46372Sbostic 	    if ( !new_bufp ) return(-1);
304*46372Sbostic 	    np = (u_short *)new_bufp->page;
305*46372Sbostic 	    bufp = ret.nextp;
306*46372Sbostic 	    if ( !bufp ) return(0);
307*46372Sbostic 	    cino = (char *)bufp->page;
308*46372Sbostic 	    ino = (u_short *)cino;
309*46372Sbostic 	    last_bfp = ret.nextp;
310*46372Sbostic 	}
311*46372Sbostic 
312*46372Sbostic 
313*46372Sbostic 	off = hashp->BSIZE;
314*46372Sbostic 	for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) {
315*46372Sbostic 	    cino = (char *)ino;
316*46372Sbostic 	    key.data = cino + ino[n];
317*46372Sbostic 	    key.size = off - ino[n];
318*46372Sbostic 	    val.data = cino + ino[n+1];
319*46372Sbostic 	    val.size = ino[n] - ino[n+1];
320*46372Sbostic 	    off = ino[n+1];
321*46372Sbostic 
322*46372Sbostic 	    if ( __call_hash ( key.data, key.size ) == obucket ) {
323*46372Sbostic 		/* Keep on old page */
324*46372Sbostic 		if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val);
325*46372Sbostic 		else {
326*46372Sbostic 		    old_bufp = __add_ovflpage ( old_bufp );
327*46372Sbostic 		    if ( !old_bufp ) return(-1);
328*46372Sbostic 		    op = (u_short *)old_bufp->page;
329*46372Sbostic 		    putpair ((char *)op, &key, &val);
330*46372Sbostic 		}
331*46372Sbostic 		old_bufp->flags |= BUF_MOD;
332*46372Sbostic 	    } else {
333*46372Sbostic 		/* Move to new page */
334*46372Sbostic 		if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val);
335*46372Sbostic 		else {
336*46372Sbostic 		    new_bufp = __add_ovflpage ( new_bufp );
337*46372Sbostic 		    if ( !new_bufp )return(-1);
338*46372Sbostic 		    np = (u_short *)new_bufp->page;
339*46372Sbostic 		    putpair ((char *)np, &key, &val);
340*46372Sbostic 		}
341*46372Sbostic 		new_bufp->flags |= BUF_MOD;
342*46372Sbostic 	    }
343*46372Sbostic 	}
344*46372Sbostic     }
345*46372Sbostic     if ( last_bfp ) {
346*46372Sbostic 	__free_ovflpage(last_bfp);
347*46372Sbostic     }
348*46372Sbostic 
349*46372Sbostic     return (0);
350*46372Sbostic }
351*46372Sbostic /*
352*46372Sbostic     Add the given pair to the page
353*46372Sbostic     1 ==> failure
354*46372Sbostic     0 ==> OK
355*46372Sbostic */
356*46372Sbostic extern int
357*46372Sbostic __addel(bufp, key, val)
358*46372Sbostic BUFHEAD	*bufp;
359*46372Sbostic DBT	*key;
360*46372Sbostic DBT	*val;
361*46372Sbostic {
362*46372Sbostic     register u_short *bp = (u_short *)bufp->page;
363*46372Sbostic     register u_short *sop;
364*46372Sbostic     int	do_expand;
365*46372Sbostic 
366*46372Sbostic     do_expand = 0;
367*46372Sbostic     while ( bp[0] &&  (bp[bp[0]] < REAL_KEY) ) {
368*46372Sbostic 	/* Exception case */
369*46372Sbostic 	if ( bp[2] < REAL_KEY ) {
370*46372Sbostic 	    /* This is a big-keydata pair */
371*46372Sbostic 	    bufp = __add_ovflpage(bufp);
372*46372Sbostic 	    if ( !bufp ) {
373*46372Sbostic 		return(-1);
374*46372Sbostic 	    }
375*46372Sbostic 	    bp = (u_short *)bufp->page;
376*46372Sbostic 	} else {
377*46372Sbostic 	    /* Try to squeeze key on this page */
378*46372Sbostic 	    if ( FREESPACE(bp) > PAIRSIZE(key,val) ) {
379*46372Sbostic 		squeeze_key ( bp, key, val );
380*46372Sbostic 		return(0);
381*46372Sbostic 	    } else {
382*46372Sbostic 		bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
383*46372Sbostic 		if (!bufp) {
384*46372Sbostic 		    return(-1);
385*46372Sbostic 		}
386*46372Sbostic 		bp = (u_short *)bufp->page;
387*46372Sbostic 	    }
388*46372Sbostic 	}
389*46372Sbostic     }
390*46372Sbostic 
391*46372Sbostic     if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val);
392*46372Sbostic     else {
393*46372Sbostic 	do_expand = 1;
394*46372Sbostic 	bufp = __add_ovflpage ( bufp );
395*46372Sbostic 	if (!bufp)return(-1);
396*46372Sbostic 	sop = (u_short *) bufp->page;
397*46372Sbostic 
398*46372Sbostic 	if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val );
399*46372Sbostic 	else if ( __big_insert ( bufp, key, val ) ) {
400*46372Sbostic 	    return(-1);
401*46372Sbostic 	}
402*46372Sbostic     }
403*46372Sbostic     bufp->flags |= BUF_MOD;
404*46372Sbostic     /*
405*46372Sbostic 	If the average number of keys per bucket exceeds the fill factor,
406*46372Sbostic 	expand the table
407*46372Sbostic     */
408*46372Sbostic     hashp->NKEYS++;
409*46372Sbostic     if (do_expand ||
410*46372Sbostic 	(hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) {
411*46372Sbostic 	 return(__expand_table());
412*46372Sbostic     }
413*46372Sbostic     return(0);
414*46372Sbostic }
415*46372Sbostic 
416*46372Sbostic /*
417*46372Sbostic     returns a pointer, NULL on error
418*46372Sbostic */
419*46372Sbostic extern BUFHEAD *
420*46372Sbostic __add_ovflpage ( bufp )
421*46372Sbostic BUFHEAD	*bufp;
422*46372Sbostic {
423*46372Sbostic     register	u_short	*sp = (u_short *)bufp->page;
424*46372Sbostic 
425*46372Sbostic     u_short	ovfl_num;
426*46372Sbostic     u_short	ndx, newoff;
427*46372Sbostic     char	*op;
428*46372Sbostic     DBT	okey, oval;
429*46372Sbostic #ifdef DEBUG1
430*46372Sbostic     int	tmp1, tmp2;
431*46372Sbostic #endif
432*46372Sbostic 
433*46372Sbostic     bufp->flags |= BUF_MOD;
434*46372Sbostic     ovfl_num = overflow_page ();
435*46372Sbostic #ifdef DEBUG1
436*46372Sbostic     tmp1 = bufp->addr;
437*46372Sbostic     tmp2 = bufp->ovfl?bufp->ovfl->addr:0;
438*46372Sbostic #endif
439*46372Sbostic     if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) {
440*46372Sbostic 	return(NULL);
441*46372Sbostic     }
442*46372Sbostic     bufp->ovfl->flags |= BUF_MOD;
443*46372Sbostic #ifdef DEBUG1
444*46372Sbostic     fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2,
445*46372Sbostic 		bufp->ovfl->addr );
446*46372Sbostic #endif
447*46372Sbostic     ndx = sp[0];
448*46372Sbostic     /*
449*46372Sbostic 	Since a pair is allocated on a page only if there's room
450*46372Sbostic 	to add an overflow page, we know that the OVFL information
451*46372Sbostic 	will fit on the page
452*46372Sbostic     */
453*46372Sbostic     sp[ndx+4] = OFFSET(sp);
454*46372Sbostic     sp[ndx+3] = FREESPACE(sp) - OVFLSIZE;
455*46372Sbostic     sp[ndx+1] = ovfl_num;
456*46372Sbostic     sp[ndx+2] = OVFLPAGE;
457*46372Sbostic     sp[0] = ndx+2;
458*46372Sbostic #ifdef HASH_STATISTICS
459*46372Sbostic 	    hash_overflows++;
460*46372Sbostic #endif
461*46372Sbostic     return(bufp->ovfl);
462*46372Sbostic }
463*46372Sbostic 
464*46372Sbostic /*
465*46372Sbostic     0 indicates SUCCESS
466*46372Sbostic     -1 indicates FAILURE
467*46372Sbostic */
468*46372Sbostic extern	int
469*46372Sbostic __get_page ( p, bucket, is_bucket, is_disk, is_bitmap )
470*46372Sbostic char	*p;
471*46372Sbostic int	bucket;
472*46372Sbostic int	is_bucket;
473*46372Sbostic int	is_disk;
474*46372Sbostic int	is_bitmap;
475*46372Sbostic {
476*46372Sbostic     register int size;
477*46372Sbostic     register int fd;
478*46372Sbostic     register int page;
479*46372Sbostic     u_short	*bp;
480*46372Sbostic     int		rsize;
481*46372Sbostic 
482*46372Sbostic     fd = hashp->fp;
483*46372Sbostic     size = hashp->BSIZE;
484*46372Sbostic 
485*46372Sbostic     if ( !fd || (hashp->new_file && !is_disk) ) {
486*46372Sbostic 	PAGE_INIT(p);
487*46372Sbostic 	return(0);
488*46372Sbostic     }
489*46372Sbostic 
490*46372Sbostic     if ( is_bucket) page = BUCKET_TO_PAGE (bucket);
491*46372Sbostic     else page = OADDR_TO_PAGE (bucket);
492*46372Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) ||
493*46372Sbostic 	((rsize = read ( fd, p, size )) == -1 )) {
494*46372Sbostic 	return(-1);
495*46372Sbostic     }
496*46372Sbostic     bp = (u_short *)p;
497*46372Sbostic     if ( !rsize ) {
498*46372Sbostic 	bp[0] = 0;		/* We hit the EOF, so initialize a new page */
499*46372Sbostic     } else if ( rsize != size ) {
500*46372Sbostic 	errno = EFTYPE;
501*46372Sbostic 	return(-1);
502*46372Sbostic     }
503*46372Sbostic     if (!bp[0]) {
504*46372Sbostic 	PAGE_INIT(p);
505*46372Sbostic     } else if ( hashp->LORDER != BYTE_ORDER ) {
506*46372Sbostic 	register int i;
507*46372Sbostic 	register int max;
508*46372Sbostic 
509*46372Sbostic 	if ( is_bitmap ) {
510*46372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
511*46372Sbostic 	    for ( i=0; i < max; i++ ) {
512*46372Sbostic 		BLSWAP(((long *)p)[i]);
513*46372Sbostic 	    }
514*46372Sbostic 	} else {
515*46372Sbostic 	    BSSWAP(bp[0]);
516*46372Sbostic 	    max = bp[0] + 2;
517*46372Sbostic 	    for ( i=1; i <= max; i++ ) {
518*46372Sbostic 		BSSWAP(bp[i]);
519*46372Sbostic 	    }
520*46372Sbostic 	}
521*46372Sbostic     }
522*46372Sbostic     return (0);
523*46372Sbostic }
524*46372Sbostic 
525*46372Sbostic /*
526*46372Sbostic     Write page p to disk
527*46372Sbostic     -1==>failure
528*46372Sbostic     0==> OK
529*46372Sbostic */
530*46372Sbostic extern int
531*46372Sbostic __put_page ( p, bucket, is_bucket, is_bitmap )
532*46372Sbostic char	*p;
533*46372Sbostic int	bucket;
534*46372Sbostic int	is_bucket;
535*46372Sbostic int	is_bitmap;
536*46372Sbostic {
537*46372Sbostic     register int size;
538*46372Sbostic     register int fd;
539*46372Sbostic     register int page;
540*46372Sbostic     int	wsize;
541*46372Sbostic 
542*46372Sbostic     size = hashp->BSIZE;
543*46372Sbostic     if ( !hashp->fp && open_temp() ) return (1);
544*46372Sbostic     fd = hashp->fp;
545*46372Sbostic 
546*46372Sbostic     if ( hashp->LORDER != BYTE_ORDER ) {
547*46372Sbostic 	register int i;
548*46372Sbostic 	register int max;
549*46372Sbostic 
550*46372Sbostic 	if ( is_bitmap ) {
551*46372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
552*46372Sbostic 	    for ( i=0; i < max; i++ ) {
553*46372Sbostic 		BLSWAP(((long *)p)[i]);
554*46372Sbostic 	    }
555*46372Sbostic 	} else {
556*46372Sbostic 	    max = ((u_short *)p)[0] + 2;
557*46372Sbostic 	    for ( i=0; i <= max; i++ ) {
558*46372Sbostic 		BSSWAP(((u_short *)p)[i]);
559*46372Sbostic 	    }
560*46372Sbostic 	}
561*46372Sbostic     }
562*46372Sbostic     if (is_bucket ) page = BUCKET_TO_PAGE (bucket);
563*46372Sbostic     else page = OADDR_TO_PAGE ( bucket );
564*46372Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) ||
565*46372Sbostic 	((wsize = write ( fd, p, size )) == -1 )) {
566*46372Sbostic 	/* Errno is set */
567*46372Sbostic 	return(-1);
568*46372Sbostic     }
569*46372Sbostic     if ( wsize != size ) {
570*46372Sbostic 	errno = EFTYPE;
571*46372Sbostic 	return(-1);
572*46372Sbostic     }
573*46372Sbostic     return(0);
574*46372Sbostic }
575*46372Sbostic #define BYTE_MASK	((1 << INT_BYTE_SHIFT) -1)
576*46372Sbostic /*
577*46372Sbostic     Initialize a new bitmap page.  Bitmap pages are left in memory
578*46372Sbostic     once they are read in.
579*46372Sbostic */
580*46372Sbostic extern u_long *
581*46372Sbostic __init_bitmap(pnum, nbits, ndx)
582*46372Sbostic u_short	pnum;
583*46372Sbostic int	nbits;
584*46372Sbostic int	ndx;
585*46372Sbostic {
586*46372Sbostic     u_long	*ip;
587*46372Sbostic     int		clearints;
588*46372Sbostic     int		clearbytes;
589*46372Sbostic 
590*46372Sbostic     if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL);
591*46372Sbostic     clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
592*46372Sbostic     clearbytes = clearints << INT_TO_BYTE;
593*46372Sbostic     memset ((char *)ip, 0, clearbytes );
594*46372Sbostic     memset ( ((char *) ip) + clearbytes, 0xFF,
595*46372Sbostic 		hashp->BSIZE-clearbytes );
596*46372Sbostic     ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK);
597*46372Sbostic     SETBIT(ip, 0);
598*46372Sbostic     hashp->BITMAPS[ndx] = pnum;
599*46372Sbostic     hashp->mapp[ndx] = ip;
600*46372Sbostic     return(ip);
601*46372Sbostic }
602*46372Sbostic static int
603*46372Sbostic first_free ( map )
604*46372Sbostic u_long map;
605*46372Sbostic {
606*46372Sbostic     register u_long      mask;
607*46372Sbostic     register u_long      i;
608*46372Sbostic 
609*46372Sbostic     mask = 0x1;
610*46372Sbostic     for ( i=0; i < BITS_PER_MAP; i++ ) {
611*46372Sbostic 	if ( !(mask & map) ) return(i);
612*46372Sbostic 	mask = mask << 1;
613*46372Sbostic     }
614*46372Sbostic     return ( i );
615*46372Sbostic }
616*46372Sbostic 
617*46372Sbostic static u_short
618*46372Sbostic overflow_page ( )
619*46372Sbostic {
620*46372Sbostic     register	int max_free;
621*46372Sbostic     register	int splitnum;
622*46372Sbostic     register	u_long *freep;
623*46372Sbostic     register	int offset;
624*46372Sbostic     u_short	addr;
625*46372Sbostic     int		in_use_bits;
626*46372Sbostic     int		free_page, free_bit;
627*46372Sbostic     int		i, j, bit;
628*46372Sbostic #ifdef DEBUG2
629*46372Sbostic     int	tmp1, tmp2;
630*46372Sbostic #endif
631*46372Sbostic 
632*46372Sbostic     splitnum = __log2(hashp->MAX_BUCKET);
633*46372Sbostic     max_free = hashp->SPARES[splitnum];
634*46372Sbostic 
635*46372Sbostic     free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT);
636*46372Sbostic     free_bit  = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
637*46372Sbostic 
638*46372Sbostic     /* Look through all the free maps to find the first free block */
639*46372Sbostic     for ( i = 0; i <= free_page; i++ ) {
640*46372Sbostic 	if (!(freep = (u_long *)hashp->mapp[i]) ) return(NULL);
641*46372Sbostic 	if ( i == free_page ) in_use_bits = free_bit;
642*46372Sbostic 	else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1;
643*46372Sbostic 
644*46372Sbostic 	for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) {
645*46372Sbostic 	     if ( freep[j] != ALL_SET ) goto found;
646*46372Sbostic 	}
647*46372Sbostic     }
648*46372Sbostic     /* No Free Page Found */
649*46372Sbostic     hashp->SPARES[splitnum]++;
650*46372Sbostic     offset = hashp->SPARES[splitnum] -
651*46372Sbostic 	     (splitnum ? hashp->SPARES[splitnum-1] : 0);
652*46372Sbostic 
653*46372Sbostic     /* Check if we need to allocate a new bitmap page */
654*46372Sbostic     if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) {
655*46372Sbostic 	free_page++;
656*46372Sbostic 	if ( free_page >= NCACHED ) {
657*46372Sbostic 	    fprintf ( stderr,
658*46372Sbostic 		"HASH: Out of overflow pages.  Increase page size\n" );
659*46372Sbostic 	    return(NULL);
660*46372Sbostic 	}
661*46372Sbostic 	/*
662*46372Sbostic 	    This is tricky.  The 1 indicates that you want the
663*46372Sbostic 	    new page allocated with 1 clear bit.  Actually, you
664*46372Sbostic 	    are going to allocate 2 pages from this map.  The first
665*46372Sbostic 	    is going to be the map page, the second is the overflow
666*46372Sbostic 	    page we were looking for.  The init_bitmap routine
667*46372Sbostic 	    automatically, sets the first bit of itself to indicate
668*46372Sbostic 	    that the bitmap itself is in use.  We would explicitly
669*46372Sbostic 	    set the second bit, but don't have to if we tell init_bitmap
670*46372Sbostic 	    not to leave it clear in the first place.
671*46372Sbostic 	*/
672*46372Sbostic 	__init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page );
673*46372Sbostic 	hashp->SPARES[splitnum]++;
674*46372Sbostic #ifdef DEBUG2
675*46372Sbostic 	free_bit = 2;
676*46372Sbostic #endif
677*46372Sbostic 	offset++;
678*46372Sbostic     } else {
679*46372Sbostic 	/*
680*46372Sbostic 		Free_bit addresses the last used bit.  Bump it to
681*46372Sbostic 		address the first available bit.
682*46372Sbostic 	*/
683*46372Sbostic 	free_bit++;
684*46372Sbostic 	SETBIT ( freep, free_bit );
685*46372Sbostic     }
686*46372Sbostic 
687*46372Sbostic     /* Calculate address of the new overflow page */
688*46372Sbostic     if ( offset > SPLITMASK ) {
689*46372Sbostic 	fprintf ( stderr,
690*46372Sbostic 	    "HASH: Out of overflow pages.  Increase page size\n" );
691*46372Sbostic 	return(NULL);
692*46372Sbostic     }
693*46372Sbostic     addr = OADDR_OF(splitnum, offset);
694*46372Sbostic #ifdef DEBUG2
695*46372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
696*46372Sbostic 		addr, free_bit, free_page );
697*46372Sbostic #endif
698*46372Sbostic     return(addr);
699*46372Sbostic 
700*46372Sbostic found:
701*46372Sbostic     bit = bit + first_free(freep[j]);
702*46372Sbostic     SETBIT(freep,bit);
703*46372Sbostic #ifdef DEBUG2
704*46372Sbostic     tmp1 = bit;
705*46372Sbostic     tmp2 = i;
706*46372Sbostic #endif
707*46372Sbostic     /*
708*46372Sbostic 	Bits are addressed starting with 0, but overflow pages are
709*46372Sbostic 	addressed beginning at 1. Bit is a bit addressnumber, so we
710*46372Sbostic 	need to increment it to convert it to a page number.
711*46372Sbostic     */
712*46372Sbostic     bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
713*46372Sbostic 
714*46372Sbostic     /* Calculate the split number for this page */
715*46372Sbostic     for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ );
716*46372Sbostic     offset =(i ? bit - hashp->SPARES[i-1] : bit );
717*46372Sbostic     if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */
718*46372Sbostic     addr = OADDR_OF(i, offset);
719*46372Sbostic #ifdef DEBUG2
720*46372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
721*46372Sbostic 		addr, tmp1, tmp2 );
722*46372Sbostic #endif
723*46372Sbostic 
724*46372Sbostic     /* Allocate and return the overflow page */
725*46372Sbostic     return (addr);
726*46372Sbostic }
727*46372Sbostic 
728*46372Sbostic /*
729*46372Sbostic     Mark this overflow page as free.
730*46372Sbostic */
731*46372Sbostic __free_ovflpage ( obufp )
732*46372Sbostic BUFHEAD	*obufp;
733*46372Sbostic {
734*46372Sbostic     register u_short addr = obufp->addr;
735*46372Sbostic     int	free_page, free_bit;
736*46372Sbostic     int bit_address;
737*46372Sbostic     u_short ndx;
738*46372Sbostic     u_long *freep;
739*46372Sbostic     int j;
740*46372Sbostic 
741*46372Sbostic #ifdef DEBUG1
742*46372Sbostic     fprintf ( stderr, "Freeing %d\n", addr );
743*46372Sbostic #endif
744*46372Sbostic     ndx = (((u_short)addr) >> SPLITSHIFT);
745*46372Sbostic     bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1;
746*46372Sbostic     free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
747*46372Sbostic     free_bit  = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
748*46372Sbostic 
749*46372Sbostic     freep = hashp->mapp[free_page];
750*46372Sbostic     assert(freep);
751*46372Sbostic     CLRBIT(freep, free_bit);
752*46372Sbostic #ifdef DEBUG2
753*46372Sbostic     fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
754*46372Sbostic 		obufp->addr, free_bit, free_page );
755*46372Sbostic #endif
756*46372Sbostic     __reclaim_buf ( obufp );
757*46372Sbostic     return;
758*46372Sbostic }
759*46372Sbostic 
760*46372Sbostic /*
761*46372Sbostic 0 success
762*46372Sbostic -1 failure
763*46372Sbostic */
764*46372Sbostic static int
765*46372Sbostic open_temp()
766*46372Sbostic {
767*46372Sbostic     char        *namestr = "_hashXXXXXX";
768*46372Sbostic 
769*46372Sbostic     if ((hashp->fp = mkstemp ( namestr )) == -1){
770*46372Sbostic 	return (-1);
771*46372Sbostic     }
772*46372Sbostic     unlink(namestr);    /* Make sure file goes away at process exit*/
773*46372Sbostic     return(0);
774*46372Sbostic }
775*46372Sbostic 
776*46372Sbostic /*
777*46372Sbostic     We have to know that the key will fit, but the
778*46372Sbostic     last entry on the page is an overflow pair, so we
779*46372Sbostic     need to shift things.
780*46372Sbostic */
781*46372Sbostic static void
782*46372Sbostic squeeze_key ( sp, key, val )
783*46372Sbostic u_short	*sp;
784*46372Sbostic DBT	*key;
785*46372Sbostic DBT	*val;
786*46372Sbostic {
787*46372Sbostic     register	char	*p = (char *)sp;
788*46372Sbostic     u_short	free_space, off;
789*46372Sbostic     u_short	pageno, n;
790*46372Sbostic 
791*46372Sbostic     n = sp[0];
792*46372Sbostic     free_space = FREESPACE(sp);
793*46372Sbostic     off = OFFSET(sp);
794*46372Sbostic 
795*46372Sbostic     pageno = sp[n-1];
796*46372Sbostic     off -= key->size;
797*46372Sbostic     sp[n-1] = off;
798*46372Sbostic     bcopy ( key->data, p + off, key->size );
799*46372Sbostic     off -= val->size;
800*46372Sbostic     sp[n] = off;
801*46372Sbostic     bcopy ( val->data, p + off, val->size );
802*46372Sbostic     sp[0] = n+2;
803*46372Sbostic     sp[n+1] = pageno;
804*46372Sbostic     sp[n+2] = OVFLPAGE;
805*46372Sbostic     FREESPACE(sp) = free_space - PAIRSIZE(key,val);
806*46372Sbostic     OFFSET(sp) = off;
807*46372Sbostic }
808*46372Sbostic 
809*46372Sbostic #ifdef DEBUG4
810*46372Sbostic print_chain ( addr )
811*46372Sbostic short	addr;
812*46372Sbostic {
813*46372Sbostic 	BUFHEAD	*bufp;
814*46372Sbostic 	short	*bp;
815*46372Sbostic 	short	oaddr;
816*46372Sbostic 
817*46372Sbostic 	fprintf ( stderr, "%d ", addr );
818*46372Sbostic 	bufp = __get_buf ( (int)addr, NULL, 0 );
819*46372Sbostic 	bp = (short *)bufp->page;
820*46372Sbostic 	while ( bp[0] &&
821*46372Sbostic 		((bp[bp[0]] == OVFLPAGE) ||
822*46372Sbostic 		 ((bp[0] > 2) && bp[2] < REAL_KEY))) {
823*46372Sbostic 		oaddr = bp[bp[0]-1];
824*46372Sbostic 		fprintf ( stderr, "%d ", (int)oaddr );
825*46372Sbostic 		bufp = __get_buf ( (int)oaddr, bufp, 0 );
826*46372Sbostic 		bp = (short *)bufp->page;
827*46372Sbostic 	}
828*46372Sbostic 	fprintf ( stderr, "\n" );
829*46372Sbostic }
830*46372Sbostic #endif
831