xref: /csrg-svn/lib/libc/db/hash/hash_bigkey.c (revision 46364)
1*46364Sbostic /*-
2*46364Sbostic  * Copyright (c) 1990 The Regents of the University of California.
3*46364Sbostic  * All rights reserved.
4*46364Sbostic  *
5*46364Sbostic  * This code is derived from software contributed to Berkeley by
6*46364Sbostic  * Margo Seltzer.
7*46364Sbostic  *
8*46364Sbostic  * %sccs.include.redist.c%
9*46364Sbostic  */
10*46364Sbostic 
11*46364Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46364Sbostic static char sccsid[] = "@(#)hash_bigkey.c	5.1 (Berkeley) 02/12/91";
13*46364Sbostic #endif /* LIBC_SCCS and not lint */
14*46364Sbostic 
15*46364Sbostic /******************************************************************************
16*46364Sbostic 
17*46364Sbostic PACKAGE: hash
18*46364Sbostic 
19*46364Sbostic DESCRIPTION:
20*46364Sbostic 	Big key/data handling for the hashing package.
21*46364Sbostic 
22*46364Sbostic ROUTINES:
23*46364Sbostic     External
24*46364Sbostic 	__big_keydata
25*46364Sbostic 	__big_split
26*46364Sbostic 	__big_insert
27*46364Sbostic 	__big_return
28*46364Sbostic 	__big_delete
29*46364Sbostic 	__find_last_page
30*46364Sbostic     Internal
31*46364Sbostic 	collect_key
32*46364Sbostic 	collect_data
33*46364Sbostic ******************************************************************************/
34*46364Sbostic /* Includes */
35*46364Sbostic #include <sys/param.h>
36*46364Sbostic #include <sys/file.h>
37*46364Sbostic #include <assert.h>
38*46364Sbostic #include <errno.h>
39*46364Sbostic #include <db.h>
40*46364Sbostic #include <stdio.h>
41*46364Sbostic #include "hash.h"
42*46364Sbostic #include "page.h"
43*46364Sbostic 
44*46364Sbostic /* Externals */
45*46364Sbostic /* buf.c */
46*46364Sbostic extern BUFHEAD *__get_buf();
47*46364Sbostic 
48*46364Sbostic /* page.c */
49*46364Sbostic extern BUFHEAD *__add_ovflpage();
50*46364Sbostic 
51*46364Sbostic /* My externals */
52*46364Sbostic extern int __big_keydata();
53*46364Sbostic extern int __big_split();
54*46364Sbostic extern int __big_insert();
55*46364Sbostic extern int __big_return();
56*46364Sbostic extern int __big_delete();
57*46364Sbostic extern u_short __find_last_page();
58*46364Sbostic extern int __find_bigpair();
59*46364Sbostic 
60*46364Sbostic /* My internals */
61*46364Sbostic static int collect_key();
62*46364Sbostic static int collect_data();
63*46364Sbostic 
64*46364Sbostic #ifdef HASH_STATISTICS
65*46364Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
66*46364Sbostic #endif
67*46364Sbostic /*
68*46364Sbostic Big_insert
69*46364Sbostic 
70*46364Sbostic You need to do an insert and the key/data pair is too big
71*46364Sbostic 0 ==> OK
72*46364Sbostic -1 ==> ERROR
73*46364Sbostic */
74*46364Sbostic extern int
75*46364Sbostic __big_insert ( bufp, key, val )
76*46364Sbostic BUFHEAD *bufp;
77*46364Sbostic DBT	*key, *val;
78*46364Sbostic {
79*46364Sbostic     char	*cp = bufp->page;	/* Character pointer of p */
80*46364Sbostic     register u_short	*p = (u_short *)cp;
81*46364Sbostic     char	*key_data, *val_data;
82*46364Sbostic     int		key_size, val_size;
83*46364Sbostic     int		n;
84*46364Sbostic     u_short	space, move_bytes, off;
85*46364Sbostic 
86*46364Sbostic     key_data = key->data;
87*46364Sbostic     key_size = key->size;
88*46364Sbostic     val_data = val->data;
89*46364Sbostic     val_size = val->size;
90*46364Sbostic 
91*46364Sbostic     /* First move the Key */
92*46364Sbostic     for ( space = FREESPACE(p) - BIGOVERHEAD;
93*46364Sbostic 	  key_size;
94*46364Sbostic 	  space = FREESPACE(p) - BIGOVERHEAD ) {
95*46364Sbostic 	move_bytes = MIN(space, key_size);
96*46364Sbostic 	off = OFFSET(p) - move_bytes;
97*46364Sbostic 	bcopy (key_data, cp+off, move_bytes );
98*46364Sbostic 	key_size -= move_bytes;
99*46364Sbostic 	key_data += move_bytes;
100*46364Sbostic 	n = p[0];
101*46364Sbostic 	p[++n] = off;
102*46364Sbostic 	p[0] = ++n;
103*46364Sbostic 	FREESPACE(p) = off - PAGE_META(n);
104*46364Sbostic 	OFFSET(p) = off;
105*46364Sbostic 	p[n] = PARTIAL_KEY;
106*46364Sbostic 	bufp = __add_ovflpage(bufp);
107*46364Sbostic 	if ( !bufp ) {
108*46364Sbostic 	    return(-1);
109*46364Sbostic 	}
110*46364Sbostic 	n = p[0];
111*46364Sbostic 	if ( !key_size ) {
112*46364Sbostic 	    if ( FREESPACE(p) ) {
113*46364Sbostic 		move_bytes = MIN (FREESPACE(p), val_size);
114*46364Sbostic 		off = OFFSET(p) - move_bytes;
115*46364Sbostic 		p[n] = off;
116*46364Sbostic 		bcopy ( val_data, cp + off, move_bytes );
117*46364Sbostic 		val_data += move_bytes;
118*46364Sbostic 		val_size -= move_bytes;
119*46364Sbostic 		p[n-2] = FULL_KEY_DATA;
120*46364Sbostic 		FREESPACE(p) = FREESPACE(p) - move_bytes;
121*46364Sbostic 		OFFSET(p) = off;
122*46364Sbostic 	    }
123*46364Sbostic 	    else p[n-2] = FULL_KEY;
124*46364Sbostic 	}
125*46364Sbostic 	p = (u_short *)bufp->page;
126*46364Sbostic 	cp = bufp->page;
127*46364Sbostic 	bufp->flags |= BUF_MOD;
128*46364Sbostic     }
129*46364Sbostic 
130*46364Sbostic     /* Now move the data */
131*46364Sbostic     for ( space = FREESPACE(p) - BIGOVERHEAD;
132*46364Sbostic 	  val_size;
133*46364Sbostic 	  space = FREESPACE(p) - BIGOVERHEAD ) {
134*46364Sbostic 	move_bytes = MIN(space, val_size);
135*46364Sbostic 	/*
136*46364Sbostic 	    Here's the hack to make sure that if the data ends
137*46364Sbostic 	    on the same page as the key ends, FREESPACE is
138*46364Sbostic 	    at least one
139*46364Sbostic 	*/
140*46364Sbostic 	if ( space == val_size && val_size == val->size ) {
141*46364Sbostic 	    move_bytes--;
142*46364Sbostic 	}
143*46364Sbostic 	off = OFFSET(p) - move_bytes;
144*46364Sbostic 	bcopy (val_data, cp+off, move_bytes );
145*46364Sbostic 	val_size -= move_bytes;
146*46364Sbostic 	val_data += move_bytes;
147*46364Sbostic 	n = p[0];
148*46364Sbostic 	p[++n] = off;
149*46364Sbostic 	p[0] = ++n;
150*46364Sbostic 	FREESPACE(p) = off - PAGE_META(n);
151*46364Sbostic 	OFFSET(p) = off;
152*46364Sbostic 	if ( val_size ) {
153*46364Sbostic 	    p[n] = FULL_KEY;
154*46364Sbostic 	    bufp = __add_ovflpage (bufp);
155*46364Sbostic 	    if ( !bufp ) {
156*46364Sbostic 		return(-1);
157*46364Sbostic 	    }
158*46364Sbostic 	    cp = bufp->page;
159*46364Sbostic 	    p = (u_short *)cp;
160*46364Sbostic 	} else {
161*46364Sbostic 	    p[n] = FULL_KEY_DATA;
162*46364Sbostic 	}
163*46364Sbostic 	bufp->flags |= BUF_MOD;
164*46364Sbostic     }
165*46364Sbostic     return(0);
166*46364Sbostic }
167*46364Sbostic 
168*46364Sbostic /*
169*46364Sbostic     Called when bufp's page  contains a partial key (index should be 1)
170*46364Sbostic 
171*46364Sbostic     All pages in the big key/data pair except bufp are freed.  We cannot
172*46364Sbostic     free bufp because the page pointing to it is lost and we can't
173*46364Sbostic     get rid of its pointer.
174*46364Sbostic 
175*46364Sbostic     Returns 0 => OK
176*46364Sbostic 	    -1 => ERROR
177*46364Sbostic */
178*46364Sbostic extern int
179*46364Sbostic __big_delete (bufp, ndx)
180*46364Sbostic BUFHEAD	*bufp;
181*46364Sbostic int	ndx;
182*46364Sbostic {
183*46364Sbostic 	register	BUFHEAD		*rbufp = bufp;
184*46364Sbostic 	register	BUFHEAD		*last_bfp = NULL;
185*46364Sbostic 	char	*cp;
186*46364Sbostic 	u_short	*bp = (u_short *)bufp->page;
187*46364Sbostic 	u_short	*xbp;
188*46364Sbostic 	u_short	pageno = 0;
189*46364Sbostic 	u_short	off, free_sp;
190*46364Sbostic 	int	key_done = 0;
191*46364Sbostic 	int	n;
192*46364Sbostic 
193*46364Sbostic 	while (!key_done || (bp[2] != FULL_KEY_DATA)) {
194*46364Sbostic 	    if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) key_done = 1;
195*46364Sbostic 
196*46364Sbostic 	    /*
197*46364Sbostic 		If there is freespace left on a FULL_KEY_DATA page,
198*46364Sbostic 		then the data is short and fits entirely on this
199*46364Sbostic 		page, and this is the last page.
200*46364Sbostic 	    */
201*46364Sbostic 	    if ( bp[2] == FULL_KEY_DATA && FREESPACE(bp) ) break;
202*46364Sbostic 	    pageno = bp[bp[0]-1];
203*46364Sbostic 	    rbufp->flags |= BUF_MOD;
204*46364Sbostic 	    rbufp = __get_buf ( pageno, rbufp, 0 );
205*46364Sbostic 	    if ( last_bfp ) __free_ovflpage(last_bfp);
206*46364Sbostic 	    last_bfp = rbufp;
207*46364Sbostic 	    if ( !rbufp ) return(-1);			/* Error */
208*46364Sbostic 	    bp = (u_short *)rbufp->page;
209*46364Sbostic 	}
210*46364Sbostic 
211*46364Sbostic 	/*
212*46364Sbostic 	    If we get here then rbufp points to the last page of
213*46364Sbostic 	    the big key/data pair.  Bufp points to the first
214*46364Sbostic 	    one -- it should now be empty pointing to the next
215*46364Sbostic 	    page after this pair.  Can't free it because we don't
216*46364Sbostic 	    have the page pointing to it.
217*46364Sbostic 	*/
218*46364Sbostic 
219*46364Sbostic 	/* This is information from the last page of the pair */
220*46364Sbostic 	n = bp[0];
221*46364Sbostic 	pageno = bp[n-1];
222*46364Sbostic 
223*46364Sbostic 	/* Now, bp is the first page of the pair */
224*46364Sbostic 	bp = (u_short *)bufp->page;
225*46364Sbostic 	if ( n > 2 ) {
226*46364Sbostic 	    /* There is an overflow page */
227*46364Sbostic 	    bp[1] = pageno;
228*46364Sbostic 	    bp[2] = OVFLPAGE;
229*46364Sbostic 	    bufp->ovfl = rbufp->ovfl;
230*46364Sbostic 	} else {
231*46364Sbostic 	    /* This is the last page */
232*46364Sbostic 	    bufp->ovfl = NULL;
233*46364Sbostic 	}
234*46364Sbostic 	n -= 2;
235*46364Sbostic 	bp[0] = n;
236*46364Sbostic 	FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
237*46364Sbostic 	OFFSET(bp) = hashp->BSIZE - 1;
238*46364Sbostic 
239*46364Sbostic 	bufp->flags |= BUF_MOD;
240*46364Sbostic 	if ( rbufp ) __free_ovflpage(rbufp);
241*46364Sbostic 	if ( last_bfp != rbufp ) __free_ovflpage(last_bfp);
242*46364Sbostic 
243*46364Sbostic 	hashp->NKEYS--;
244*46364Sbostic 	return(0);
245*46364Sbostic }
246*46364Sbostic 
247*46364Sbostic /*
248*46364Sbostic     0 = key not found
249*46364Sbostic     -1 = get next overflow page
250*46364Sbostic     -2 means key not found and this is big key/data
251*46364Sbostic     -3 error
252*46364Sbostic */
253*46364Sbostic extern int
254*46364Sbostic __find_bigpair(bufp, ndx, key, size )
255*46364Sbostic BUFHEAD	*bufp;
256*46364Sbostic int	ndx;
257*46364Sbostic char	*key;
258*46364Sbostic int	size;
259*46364Sbostic {
260*46364Sbostic     register	u_short	*bp = (u_short *)bufp->page;
261*46364Sbostic     register	char	*p = bufp->page;
262*46364Sbostic     int		ksize = size;
263*46364Sbostic     char	*kkey = key;
264*46364Sbostic     u_short	bytes;
265*46364Sbostic 
266*46364Sbostic 
267*46364Sbostic     for ( bytes = hashp->BSIZE - bp[ndx];
268*46364Sbostic 	  bytes <= size && bp[ndx+1] == PARTIAL_KEY;
269*46364Sbostic 	  bytes = hashp->BSIZE - bp[ndx] ) {
270*46364Sbostic 
271*46364Sbostic 	if ( bcmp ( p+bp[ndx], kkey, bytes ))return(-2);
272*46364Sbostic 	kkey += bytes;
273*46364Sbostic 	ksize -= bytes;
274*46364Sbostic 	bufp = __get_buf ( bp[ndx+2], bufp, 0 );
275*46364Sbostic 	if ( !bufp ) {
276*46364Sbostic 	    return(-3);
277*46364Sbostic 	}
278*46364Sbostic 	p = bufp->page;
279*46364Sbostic 	bp = (u_short *)p;
280*46364Sbostic 	ndx = 1;
281*46364Sbostic     }
282*46364Sbostic 
283*46364Sbostic     if ( (bytes != ksize) || bcmp ( p+bp[ndx], kkey, bytes )) {
284*46364Sbostic #ifdef HASH_STATISTICS
285*46364Sbostic 	hash_collisions++;
286*46364Sbostic #endif
287*46364Sbostic 	return(-2);
288*46364Sbostic     }
289*46364Sbostic     else return (ndx);
290*46364Sbostic }
291*46364Sbostic 
292*46364Sbostic 
293*46364Sbostic /*
294*46364Sbostic     Given the buffer pointer of the first overflow page of a big pair,
295*46364Sbostic     find the end of the big pair
296*46364Sbostic 
297*46364Sbostic     This will set bpp to the buffer header of the last page of the big pair.
298*46364Sbostic     It will return the pageno of the overflow page following the last page of
299*46364Sbostic     the pair; 0 if there isn't any (i.e. big pair is the last key in the
300*46364Sbostic     bucket)
301*46364Sbostic */
302*46364Sbostic extern u_short
303*46364Sbostic __find_last_page ( bpp )
304*46364Sbostic BUFHEAD	**bpp;
305*46364Sbostic {
306*46364Sbostic 	int	n;
307*46364Sbostic 	u_short	pageno;
308*46364Sbostic 	BUFHEAD	*bufp = *bpp;
309*46364Sbostic 	u_short	*bp = (u_short *)bufp->page;
310*46364Sbostic 
311*46364Sbostic 	while ( 1 ) {
312*46364Sbostic 	    n = bp[0];
313*46364Sbostic 
314*46364Sbostic 	    /*
315*46364Sbostic 		This is the last page if:
316*46364Sbostic 			the tag is FULL_KEY_DATA and either
317*46364Sbostic 				only 2 entries
318*46364Sbostic 				OVFLPAGE marker is explicit
319*46364Sbostic 				there is freespace on the page
320*46364Sbostic 	    */
321*46364Sbostic 	    if ( bp[2] == FULL_KEY_DATA &&
322*46364Sbostic 		 ((n == 2) ||  (bp[n] == OVFLPAGE) || (FREESPACE(bp)) ) ) break;
323*46364Sbostic 
324*46364Sbostic 	    pageno = bp[n-1];
325*46364Sbostic 	    bufp = __get_buf ( pageno, bufp, 0 );
326*46364Sbostic 	    if ( !bufp ) return (0);		/* Need to indicate an error! */
327*46364Sbostic 	    bp = (u_short *)bufp->page;
328*46364Sbostic 	}
329*46364Sbostic 
330*46364Sbostic 	*bpp = bufp;
331*46364Sbostic 	if ( bp[0] > 2 ) return ( bp[3] );
332*46364Sbostic 	else return(0);
333*46364Sbostic }
334*46364Sbostic 
335*46364Sbostic 
336*46364Sbostic /*
337*46364Sbostic     Return the data for the key/data pair
338*46364Sbostic     that begins on this page at this index
339*46364Sbostic     (index should always be 1)
340*46364Sbostic */
341*46364Sbostic extern int
342*46364Sbostic __big_return ( bufp, ndx, val, set_current )
343*46364Sbostic BUFHEAD	*bufp;
344*46364Sbostic int	ndx;
345*46364Sbostic DBT	*val;
346*46364Sbostic int	set_current;
347*46364Sbostic {
348*46364Sbostic     BUFHEAD	*save_p;
349*46364Sbostic     u_short	save_addr;
350*46364Sbostic     u_short	*bp = (u_short *)bufp->page;
351*46364Sbostic     u_short	off, len;
352*46364Sbostic     char	*cp, *tp;
353*46364Sbostic 
354*46364Sbostic     while ( bp[ndx+1] == PARTIAL_KEY ) {
355*46364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
356*46364Sbostic 	if ( !bufp ) return(-1);
357*46364Sbostic 	bp = (u_short *)bufp->page;
358*46364Sbostic 	ndx = 1;
359*46364Sbostic     }
360*46364Sbostic 
361*46364Sbostic     if ( bp[ndx+1] == FULL_KEY ) {
362*46364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
363*46364Sbostic 	if ( !bufp ) return(-1);
364*46364Sbostic 	bp = (u_short *)bufp->page;
365*46364Sbostic 	save_p = bufp;
366*46364Sbostic 	save_addr = save_p->addr;
367*46364Sbostic 	off = bp[1];
368*46364Sbostic 	len = 0;
369*46364Sbostic     } else if (!FREESPACE(bp)) {
370*46364Sbostic 	/*
371*46364Sbostic 	    This is a hack.  We can't distinguish between
372*46364Sbostic 	    FULL_KEY_DATA that contains complete data or
373*46364Sbostic 	    incomplete data, so we require that if the
374*46364Sbostic 	    data  is complete, there is at least 1 byte
375*46364Sbostic 	    of free space left.
376*46364Sbostic 	*/
377*46364Sbostic 	off = bp[bp[0]];
378*46364Sbostic 	len = bp[1] - off;
379*46364Sbostic 	save_p = bufp;
380*46364Sbostic 	save_addr = bufp->addr;
381*46364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
382*46364Sbostic 	if ( !bufp ) return(-1);
383*46364Sbostic 	bp = (u_short *)bufp->page;
384*46364Sbostic     } else {
385*46364Sbostic 	/* The data is all on one page */
386*46364Sbostic 	tp = (char *)bp;
387*46364Sbostic 	off = bp[bp[0]];
388*46364Sbostic 	val->data = tp + off;
389*46364Sbostic 	val->size = bp[1] - off;
390*46364Sbostic 	if ( set_current ) {
391*46364Sbostic 	    if ( bp[0] == 2 ) {		/* No more buckets in chain */
392*46364Sbostic 		hashp->cpage = NULL;
393*46364Sbostic 		hashp->cbucket++;
394*46364Sbostic 		hashp->cndx=1;
395*46364Sbostic 	    } else  {
396*46364Sbostic 		hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 );
397*46364Sbostic 		if ( !hashp->cpage )return(-1);
398*46364Sbostic 		hashp->cndx = 1;
399*46364Sbostic 		if ( !((u_short *)hashp->cpage->page)[0] ) {
400*46364Sbostic 		    hashp->cbucket++;
401*46364Sbostic 		    hashp->cpage = NULL;
402*46364Sbostic 		}
403*46364Sbostic 	    }
404*46364Sbostic 	}
405*46364Sbostic 	return(0);
406*46364Sbostic     }
407*46364Sbostic 
408*46364Sbostic     val->size = collect_data ( bufp, len, set_current );
409*46364Sbostic     if ( val->size == -1 ) {
410*46364Sbostic 	return(-1);
411*46364Sbostic     }
412*46364Sbostic     if ( save_p->addr != save_addr ) {
413*46364Sbostic 	/* We are pretty short on buffers */
414*46364Sbostic 	errno = EINVAL;		/* OUT OF BUFFERS */
415*46364Sbostic 	return(-1);
416*46364Sbostic     }
417*46364Sbostic     bcopy ( (save_p->page)+off, hashp->tmp_buf, len );
418*46364Sbostic     val->data = hashp->tmp_buf;
419*46364Sbostic     return(0);
420*46364Sbostic }
421*46364Sbostic 
422*46364Sbostic /*
423*46364Sbostic     Count how big the total datasize is by
424*46364Sbostic     recursing through the pages.  Then allocate
425*46364Sbostic     a buffer and copy the data as you recurse up.
426*46364Sbostic */
427*46364Sbostic static int
428*46364Sbostic collect_data ( bufp, len, set )
429*46364Sbostic BUFHEAD	*bufp;
430*46364Sbostic int	len;
431*46364Sbostic int	set;
432*46364Sbostic {
433*46364Sbostic     register	char	*p = bufp->page;
434*46364Sbostic     register	u_short	*bp = (u_short *)p;
435*46364Sbostic     u_short	save_addr;
436*46364Sbostic     int	mylen, totlen;
437*46364Sbostic     BUFHEAD	*xbp;
438*46364Sbostic 
439*46364Sbostic     mylen = hashp->BSIZE - bp[1];
440*46364Sbostic     save_addr = bufp->addr;
441*46364Sbostic 
442*46364Sbostic     if ( bp[2] == FULL_KEY_DATA ) {	/* End of Data */
443*46364Sbostic 	totlen = len + mylen;
444*46364Sbostic 	if ( hashp->tmp_buf ) free (hashp->tmp_buf);
445*46364Sbostic 	hashp->tmp_buf = (char *)malloc ( totlen );
446*46364Sbostic 	if ( !hashp->tmp_buf ) {
447*46364Sbostic 	    return(-1);
448*46364Sbostic 	}
449*46364Sbostic 	if ( set ) {
450*46364Sbostic 	    hashp->cndx = 1;
451*46364Sbostic 	    if ( bp[0] == 2 ) {		/* No more buckets in chain */
452*46364Sbostic 		hashp->cpage = NULL;
453*46364Sbostic 		hashp->cbucket++;
454*46364Sbostic 	    } else  {
455*46364Sbostic 		hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 );
456*46364Sbostic 		if (!hashp->cpage) {
457*46364Sbostic 		    return(-1);
458*46364Sbostic 		} else if ( !((u_short *)hashp->cpage->page)[0] ) {
459*46364Sbostic 		    hashp->cbucket++;
460*46364Sbostic 		    hashp->cpage = NULL;
461*46364Sbostic 		}
462*46364Sbostic 	    }
463*46364Sbostic 	}
464*46364Sbostic     } else {
465*46364Sbostic 	xbp = __get_buf ( bp[bp[0]-1], bufp, 0 );
466*46364Sbostic 	if ( !xbp || ((totlen = collect_data ( xbp, len + mylen, set )) < 1) ) {
467*46364Sbostic 	    return(-1);
468*46364Sbostic 	}
469*46364Sbostic     }
470*46364Sbostic     if ( bufp->addr != save_addr ) {
471*46364Sbostic 	errno = EINVAL;		/* Out of buffers */
472*46364Sbostic 	return(-1);
473*46364Sbostic     }
474*46364Sbostic     bcopy ( (bufp->page) + bp[1], &hashp->tmp_buf[len], mylen );
475*46364Sbostic     return ( totlen );
476*46364Sbostic }
477*46364Sbostic 
478*46364Sbostic /*
479*46364Sbostic 	Fill in the key and data
480*46364Sbostic 	for this big pair
481*46364Sbostic */
482*46364Sbostic extern int
483*46364Sbostic __big_keydata ( bufp, ndx, key, val, set )
484*46364Sbostic BUFHEAD	*bufp;
485*46364Sbostic int	ndx;
486*46364Sbostic DBT	*key, *val;
487*46364Sbostic int	set;
488*46364Sbostic {
489*46364Sbostic     key->size = collect_key ( bufp, 0, val, set );
490*46364Sbostic     if ( key->size == -1 ) {
491*46364Sbostic 	return (-1);
492*46364Sbostic     }
493*46364Sbostic     key->data = hashp->tmp_key;
494*46364Sbostic     return(0);
495*46364Sbostic }
496*46364Sbostic 
497*46364Sbostic /*
498*46364Sbostic     Count how big the total key size is by
499*46364Sbostic     recursing through the pages.  Then collect
500*46364Sbostic     the data, allocate a buffer and copy the key as
501*46364Sbostic     you recurse up.
502*46364Sbostic */
503*46364Sbostic static int
504*46364Sbostic collect_key ( bufp, len, val, set )
505*46364Sbostic BUFHEAD	*bufp;
506*46364Sbostic int	len;
507*46364Sbostic DBT	*val;
508*46364Sbostic int	set;
509*46364Sbostic {
510*46364Sbostic     char	*p = bufp->page;
511*46364Sbostic     u_short	*bp = (u_short *)p;
512*46364Sbostic     u_short	save_addr;
513*46364Sbostic     int	mylen, totlen;
514*46364Sbostic     BUFHEAD	*xbp;
515*46364Sbostic 
516*46364Sbostic     mylen = hashp->BSIZE - bp[1];
517*46364Sbostic 
518*46364Sbostic     save_addr = bufp->addr;
519*46364Sbostic     totlen = len + mylen;
520*46364Sbostic     if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) {/* End of Key */
521*46364Sbostic 	if ( hashp->tmp_key ) free (hashp->tmp_key);
522*46364Sbostic 	hashp->tmp_key = (char *)malloc ( totlen );
523*46364Sbostic 	if ( !hashp->tmp_key ) {
524*46364Sbostic 	    return(-1);
525*46364Sbostic 	}
526*46364Sbostic 	__big_return ( bufp, 1, val, set );
527*46364Sbostic     } else {
528*46364Sbostic 	xbp = __get_buf (bp[bp[0]-1], bufp, 0);
529*46364Sbostic 	if ( !xbp || ((totlen = collect_key (xbp, totlen, val, set)) < 1 ) ) {
530*46364Sbostic 	    return(-1);
531*46364Sbostic 	}
532*46364Sbostic     }
533*46364Sbostic     if ( bufp->addr != save_addr ) {
534*46364Sbostic 	errno = EINVAL;		/* MIS -- OUT OF BUFFERS */
535*46364Sbostic 	return (-1);
536*46364Sbostic     }
537*46364Sbostic     bcopy ( (bufp->page) + bp[1], &hashp->tmp_key[len], mylen );
538*46364Sbostic     return ( totlen );
539*46364Sbostic }
540*46364Sbostic 
541*46364Sbostic 
542*46364Sbostic /*
543*46364Sbostic     return 0 => OK
544*46364Sbostic 	   -1 => error
545*46364Sbostic */
546*46364Sbostic extern int
547*46364Sbostic __big_split ( op, np, big_keyp, addr, obucket, ret )
548*46364Sbostic BUFHEAD	*op;		/* Pointer to where to put keys that go in old bucket */
549*46364Sbostic BUFHEAD	*np;		/* Pointer to new bucket page */
550*46364Sbostic BUFHEAD	*big_keyp;	/* Pointer to first page containing the big key/data */
551*46364Sbostic u_short	addr;		/* Address of big_keyp */
552*46364Sbostic int	obucket;	/* Old Bucket */
553*46364Sbostic SPLIT_RETURN	*ret;
554*46364Sbostic {
555*46364Sbostic     register	u_short	*prev_pagep;
556*46364Sbostic     register	BUFHEAD	*tmpp;
557*46364Sbostic     register	u_short 	*tp;
558*46364Sbostic     BUFHEAD	*bp = big_keyp;
559*46364Sbostic     u_short	off, free_space;
560*46364Sbostic     u_short	n;
561*46364Sbostic 
562*46364Sbostic     DBT		key, val;
563*46364Sbostic 
564*46364Sbostic     int		change;
565*46364Sbostic 
566*46364Sbostic     /* Now figure out where the big key/data goes */
567*46364Sbostic     if (__big_keydata ( big_keyp, 1, &key, &val, 0 )) {
568*46364Sbostic 	return(-1);
569*46364Sbostic     }
570*46364Sbostic     change = (__call_hash ( key.data, key.size ) != obucket );
571*46364Sbostic 
572*46364Sbostic     if ( ret->next_addr = __find_last_page ( &big_keyp ) ) {
573*46364Sbostic 	if (!(ret->nextp = __get_buf ( ret->next_addr, big_keyp, 0 ))) {
574*46364Sbostic 	    return(-1);;
575*46364Sbostic 	}
576*46364Sbostic     } else {
577*46364Sbostic 	ret->nextp = NULL;
578*46364Sbostic     }
579*46364Sbostic 
580*46364Sbostic     /* Now make one of np/op point to the big key/data pair */
581*46364Sbostic     assert(np->ovfl == NULL);
582*46364Sbostic     if ( change ) tmpp = np;
583*46364Sbostic     else tmpp = op;
584*46364Sbostic 
585*46364Sbostic     tmpp->flags |= BUF_MOD;
586*46364Sbostic #ifdef DEBUG1
587*46364Sbostic     fprintf ( stderr, "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
588*46364Sbostic 		(tmpp->ovfl?tmpp->ovfl->addr:0),
589*46364Sbostic 		(bp?bp->addr:0) );
590*46364Sbostic #endif
591*46364Sbostic     tmpp->ovfl = bp;		/* one of op/np point to big_keyp */
592*46364Sbostic     tp = (u_short *)tmpp->page;
593*46364Sbostic     assert ( FREESPACE(tp) >= OVFLSIZE);
594*46364Sbostic     n = tp[0];
595*46364Sbostic     off = OFFSET(tp);
596*46364Sbostic     free_space = FREESPACE(tp);
597*46364Sbostic     tp[++n] = addr;
598*46364Sbostic     tp[++n] = OVFLPAGE;
599*46364Sbostic     tp[0] = n;
600*46364Sbostic     OFFSET(tp) = off;
601*46364Sbostic     FREESPACE(tp) = free_space - OVFLSIZE;
602*46364Sbostic 
603*46364Sbostic     /*
604*46364Sbostic 	Finally, set the new and old return values.
605*46364Sbostic 	BIG_KEYP contains a pointer to the last page of the big key_data pair.
606*46364Sbostic 	Make sure that big_keyp has no following page (2 elements) or create
607*46364Sbostic 	an empty following page.
608*46364Sbostic     */
609*46364Sbostic 
610*46364Sbostic     ret->newp = np;
611*46364Sbostic     ret->oldp = op;
612*46364Sbostic 
613*46364Sbostic     tp = (u_short *)big_keyp->page;
614*46364Sbostic     big_keyp->flags |= BUF_MOD;
615*46364Sbostic     if ( tp[0] > 2 ) {
616*46364Sbostic 	/*
617*46364Sbostic 	    There may be either one or two offsets on this page
618*46364Sbostic 	    If there is one, then the overflow page is linked on
619*46364Sbostic 	    normally and tp[4] is OVFLPAGE.  If there are two, tp[4]
620*46364Sbostic 	    contains the second offset and needs to get stuffed in
621*46364Sbostic 	    after the next overflow page is added
622*46364Sbostic 	*/
623*46364Sbostic 	n = tp[4];
624*46364Sbostic 	free_space = FREESPACE(tp);
625*46364Sbostic 	off = OFFSET(tp);
626*46364Sbostic 	tp[0] -= 2;
627*46364Sbostic 	FREESPACE(tp) = free_space + OVFLSIZE;
628*46364Sbostic 	OFFSET(tp) = off;
629*46364Sbostic 	tmpp = __add_ovflpage ( big_keyp );
630*46364Sbostic 	if ( !tmpp ) {
631*46364Sbostic 	    return(-1);
632*46364Sbostic 	}
633*46364Sbostic 	tp[4] = n;
634*46364Sbostic     } else {
635*46364Sbostic 	tmpp = big_keyp;
636*46364Sbostic     }
637*46364Sbostic 
638*46364Sbostic     if ( change ) ret->newp = tmpp;
639*46364Sbostic     else ret->oldp = tmpp;
640*46364Sbostic 
641*46364Sbostic     return(0);
642*46364Sbostic }
643