2 * Copyright (c) 1990 The Regents of the University of California.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #if defined(LIBC_SCCS) && !defined(lint)
38 static char sccsid
[] = "@(#)bigkey.c 5.5 (Berkeley) 3/12/91";
39 #endif /* LIBC_SCCS and not lint */
41 /******************************************************************************
46 Big key/data handling for the hashing package.
59 ******************************************************************************/
61 #include <sys/param.h>
73 extern BUFHEAD
*__get_buf();
76 extern u_int
call_hash();
79 extern BUFHEAD
*__add_ovflpage();
82 extern int __big_keydata();
83 extern int __big_split();
84 extern int __big_insert();
85 extern int __big_return();
86 extern int __big_delete();
87 extern u_short
__find_last_page();
88 extern int __find_bigpair();
91 static int collect_key();
92 static int collect_data();
94 #ifdef HASH_STATISTICS
95 extern long hash_accesses
, hash_collisions
, hash_expansions
, hash_overflows
;
100 You need to do an insert and the key/data pair is too big
105 __big_insert ( bufp
, key
, val
)
109 char *cp
= bufp
->page
; /* Character pointer of p */
110 register u_short
*p
= (u_short
*)cp
;
111 char *key_data
, *val_data
;
112 int key_size
, val_size
;
114 u_short space
, move_bytes
, off
;
116 key_data
= (char *)key
->data
;
117 key_size
= key
->size
;
118 val_data
= (char *)val
->data
;
119 val_size
= val
->size
;
121 /* First move the Key */
122 for ( space
= FREESPACE(p
) - BIGOVERHEAD
;
124 space
= FREESPACE(p
) - BIGOVERHEAD
) {
125 move_bytes
= MIN(space
, key_size
);
126 off
= OFFSET(p
) - move_bytes
;
127 bcopy (key_data
, cp
+off
, move_bytes
);
128 key_size
-= move_bytes
;
129 key_data
+= move_bytes
;
133 FREESPACE(p
) = off
- PAGE_META(n
);
136 bufp
= __add_ovflpage(bufp
);
142 if ( FREESPACE(p
) ) {
143 move_bytes
= MIN (FREESPACE(p
), val_size
);
144 off
= OFFSET(p
) - move_bytes
;
146 bcopy ( val_data
, cp
+ off
, move_bytes
);
147 val_data
+= move_bytes
;
148 val_size
-= move_bytes
;
149 p
[n
-2] = FULL_KEY_DATA
;
150 FREESPACE(p
) = FREESPACE(p
) - move_bytes
;
153 else p
[n
-2] = FULL_KEY
;
155 p
= (u_short
*)bufp
->page
;
157 bufp
->flags
|= BUF_MOD
;
160 /* Now move the data */
161 for ( space
= FREESPACE(p
) - BIGOVERHEAD
;
163 space
= FREESPACE(p
) - BIGOVERHEAD
) {
164 move_bytes
= MIN(space
, val_size
);
166 Here's the hack to make sure that if the data ends
167 on the same page as the key ends, FREESPACE is
170 if ( space
== val_size
&& val_size
== val
->size
) {
173 off
= OFFSET(p
) - move_bytes
;
174 bcopy (val_data
, cp
+off
, move_bytes
);
175 val_size
-= move_bytes
;
176 val_data
+= move_bytes
;
180 FREESPACE(p
) = off
- PAGE_META(n
);
184 bufp
= __add_ovflpage (bufp
);
191 p
[n
] = FULL_KEY_DATA
;
193 bufp
->flags
|= BUF_MOD
;
199 Called when bufp's page contains a partial key (index should be 1)
201 All pages in the big key/data pair except bufp are freed. We cannot
202 free bufp because the page pointing to it is lost and we can't
203 get rid of its pointer.
209 __big_delete (bufp
, ndx
)
213 register BUFHEAD
*rbufp
= bufp
;
214 register BUFHEAD
*last_bfp
= NULL
;
216 u_short
*bp
= (u_short
*)bufp
->page
;
219 u_short off
, free_sp
;
223 while (!key_done
|| (bp
[2] != FULL_KEY_DATA
)) {
224 if ( bp
[2] == FULL_KEY
|| bp
[2] == FULL_KEY_DATA
) key_done
= 1;
227 If there is freespace left on a FULL_KEY_DATA page,
228 then the data is short and fits entirely on this
229 page, and this is the last page.
231 if ( bp
[2] == FULL_KEY_DATA
&& FREESPACE(bp
) ) break;
232 pageno
= bp
[bp
[0]-1];
233 rbufp
->flags
|= BUF_MOD
;
234 rbufp
= __get_buf ( pageno
, rbufp
, 0 );
235 if ( last_bfp
) __free_ovflpage(last_bfp
);
237 if ( !rbufp
) return(-1); /* Error */
238 bp
= (u_short
*)rbufp
->page
;
242 If we get here then rbufp points to the last page of
243 the big key/data pair. Bufp points to the first
244 one -- it should now be empty pointing to the next
245 page after this pair. Can't free it because we don't
246 have the page pointing to it.
249 /* This is information from the last page of the pair */
253 /* Now, bp is the first page of the pair */
254 bp
= (u_short
*)bufp
->page
;
256 /* There is an overflow page */
259 bufp
->ovfl
= rbufp
->ovfl
;
261 /* This is the last page */
266 FREESPACE(bp
) = hashp
->BSIZE
- PAGE_META(n
);
267 OFFSET(bp
) = hashp
->BSIZE
- 1;
269 bufp
->flags
|= BUF_MOD
;
270 if ( rbufp
) __free_ovflpage(rbufp
);
271 if ( last_bfp
!= rbufp
) __free_ovflpage(last_bfp
);
279 -1 = get next overflow page
280 -2 means key not found and this is big key/data
284 __find_bigpair(bufp
, ndx
, key
, size
)
290 register u_short
*bp
= (u_short
*)bufp
->page
;
291 register char *p
= bufp
->page
;
297 for ( bytes
= hashp
->BSIZE
- bp
[ndx
];
298 bytes
<= size
&& bp
[ndx
+1] == PARTIAL_KEY
;
299 bytes
= hashp
->BSIZE
- bp
[ndx
] ) {
301 if ( bcmp ( p
+bp
[ndx
], kkey
, bytes
))return(-2);
304 bufp
= __get_buf ( bp
[ndx
+2], bufp
, 0 );
313 if ( (bytes
!= ksize
) || bcmp ( p
+bp
[ndx
], kkey
, bytes
)) {
314 #ifdef HASH_STATISTICS
324 Given the buffer pointer of the first overflow page of a big pair,
325 find the end of the big pair
327 This will set bpp to the buffer header of the last page of the big pair.
328 It will return the pageno of the overflow page following the last page of
329 the pair; 0 if there isn't any (i.e. big pair is the last key in the
333 __find_last_page ( bpp
)
338 BUFHEAD
*bufp
= *bpp
;
339 u_short
*bp
= (u_short
*)bufp
->page
;
345 This is the last page if:
346 the tag is FULL_KEY_DATA and either
348 OVFLPAGE marker is explicit
349 there is freespace on the page
351 if ( bp
[2] == FULL_KEY_DATA
&&
352 ((n
== 2) || (bp
[n
] == OVFLPAGE
) || (FREESPACE(bp
)) ) ) break;
355 bufp
= __get_buf ( pageno
, bufp
, 0 );
356 if ( !bufp
) return (0); /* Need to indicate an error! */
357 bp
= (u_short
*)bufp
->page
;
361 if ( bp
[0] > 2 ) return ( bp
[3] );
367 Return the data for the key/data pair
368 that begins on this page at this index
369 (index should always be 1)
372 __big_return ( bufp
, ndx
, val
, set_current
)
380 u_short
*bp
= (u_short
*)bufp
->page
;
384 while ( bp
[ndx
+1] == PARTIAL_KEY
) {
385 bufp
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
386 if ( !bufp
) return(-1);
387 bp
= (u_short
*)bufp
->page
;
391 if ( bp
[ndx
+1] == FULL_KEY
) {
392 bufp
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
393 if ( !bufp
) return(-1);
394 bp
= (u_short
*)bufp
->page
;
396 save_addr
= save_p
->addr
;
399 } else if (!FREESPACE(bp
)) {
401 This is a hack. We can't distinguish between
402 FULL_KEY_DATA that contains complete data or
403 incomplete data, so we require that if the
404 data is complete, there is at least 1 byte
410 save_addr
= bufp
->addr
;
411 bufp
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
412 if ( !bufp
) return(-1);
413 bp
= (u_short
*)bufp
->page
;
415 /* The data is all on one page */
418 val
->data
= (u_char
*)tp
+ off
;
419 val
->size
= bp
[1] - off
;
421 if ( bp
[0] == 2 ) { /* No more buckets in chain */
426 hashp
->cpage
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
427 if ( !hashp
->cpage
)return(-1);
429 if ( !((u_short
*)hashp
->cpage
->page
)[0] ) {
438 val
->size
= collect_data ( bufp
, len
, set_current
);
439 if ( val
->size
== -1 ) {
442 if ( save_p
->addr
!= save_addr
) {
443 /* We are pretty short on buffers */
444 errno
= EINVAL
; /* OUT OF BUFFERS */
447 bcopy ( (save_p
->page
)+off
, hashp
->tmp_buf
, len
);
448 val
->data
= (u_char
*)hashp
->tmp_buf
;
453 Count how big the total datasize is by
454 recursing through the pages. Then allocate
455 a buffer and copy the data as you recurse up.
458 collect_data ( bufp
, len
, set
)
463 register char *p
= bufp
->page
;
464 register u_short
*bp
= (u_short
*)p
;
469 mylen
= hashp
->BSIZE
- bp
[1];
470 save_addr
= bufp
->addr
;
472 if ( bp
[2] == FULL_KEY_DATA
) { /* End of Data */
473 totlen
= len
+ mylen
;
474 if ( hashp
->tmp_buf
) free (hashp
->tmp_buf
);
475 hashp
->tmp_buf
= (char *)malloc ( totlen
);
476 if ( !hashp
->tmp_buf
) {
481 if ( bp
[0] == 2 ) { /* No more buckets in chain */
485 hashp
->cpage
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
488 } else if ( !((u_short
*)hashp
->cpage
->page
)[0] ) {
495 xbp
= __get_buf ( bp
[bp
[0]-1], bufp
, 0 );
496 if ( !xbp
|| ((totlen
= collect_data ( xbp
, len
+ mylen
, set
)) < 1) ) {
500 if ( bufp
->addr
!= save_addr
) {
501 errno
= EINVAL
; /* Out of buffers */
504 bcopy ( (bufp
->page
) + bp
[1], &hashp
->tmp_buf
[len
], mylen
);
509 Fill in the key and data
513 __big_keydata ( bufp
, ndx
, key
, val
, set
)
519 key
->size
= collect_key ( bufp
, 0, val
, set
);
520 if ( key
->size
== -1 ) {
523 key
->data
= (u_char
*)hashp
->tmp_key
;
528 Count how big the total key size is by
529 recursing through the pages. Then collect
530 the data, allocate a buffer and copy the key as
534 collect_key ( bufp
, len
, val
, set
)
540 char *p
= bufp
->page
;
541 u_short
*bp
= (u_short
*)p
;
546 mylen
= hashp
->BSIZE
- bp
[1];
548 save_addr
= bufp
->addr
;
549 totlen
= len
+ mylen
;
550 if ( bp
[2] == FULL_KEY
|| bp
[2] == FULL_KEY_DATA
) {/* End of Key */
551 if ( hashp
->tmp_key
) free (hashp
->tmp_key
);
552 hashp
->tmp_key
= (char *)malloc ( totlen
);
553 if ( !hashp
->tmp_key
) {
556 __big_return ( bufp
, 1, val
, set
);
558 xbp
= __get_buf (bp
[bp
[0]-1], bufp
, 0);
559 if ( !xbp
|| ((totlen
= collect_key (xbp
, totlen
, val
, set
)) < 1 ) ) {
563 if ( bufp
->addr
!= save_addr
) {
564 errno
= EINVAL
; /* MIS -- OUT OF BUFFERS */
567 bcopy ( (bufp
->page
) + bp
[1], &hashp
->tmp_key
[len
], mylen
);
577 __big_split ( op
, np
, big_keyp
, addr
, obucket
, ret
)
578 BUFHEAD
*op
; /* Pointer to where to put keys that go in old bucket */
579 BUFHEAD
*np
; /* Pointer to new bucket page */
580 BUFHEAD
*big_keyp
; /* Pointer to first page containing the big key/data */
581 u_short addr
; /* Address of big_keyp */
582 u_int obucket
; /* Old Bucket */
585 register u_short
*prev_pagep
;
586 register BUFHEAD
*tmpp
;
587 register u_short
*tp
;
588 BUFHEAD
*bp
= big_keyp
;
589 u_short off
, free_space
;
596 /* Now figure out where the big key/data goes */
597 if (__big_keydata ( big_keyp
, 1, &key
, &val
, 0 )) {
600 change
= (__call_hash ( key
.data
, key
.size
) != obucket
);
602 if ( ret
->next_addr
= __find_last_page ( &big_keyp
) ) {
603 if (!(ret
->nextp
= __get_buf ( ret
->next_addr
, big_keyp
, 0 ))) {
610 /* Now make one of np/op point to the big key/data pair */
611 assert(np
->ovfl
== NULL
);
612 if ( change
) tmpp
= np
;
615 tmpp
->flags
|= BUF_MOD
;
617 fprintf ( stderr
, "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp
->addr
,
618 (tmpp
->ovfl
?tmpp
->ovfl
->addr
:0),
621 tmpp
->ovfl
= bp
; /* one of op/np point to big_keyp */
622 tp
= (u_short
*)tmpp
->page
;
623 assert ( FREESPACE(tp
) >= OVFLSIZE
);
626 free_space
= FREESPACE(tp
);
631 FREESPACE(tp
) = free_space
- OVFLSIZE
;
634 Finally, set the new and old return values.
635 BIG_KEYP contains a pointer to the last page of the big key_data pair.
636 Make sure that big_keyp has no following page (2 elements) or create
637 an empty following page.
643 tp
= (u_short
*)big_keyp
->page
;
644 big_keyp
->flags
|= BUF_MOD
;
647 There may be either one or two offsets on this page
648 If there is one, then the overflow page is linked on
649 normally and tp[4] is OVFLPAGE. If there are two, tp[4]
650 contains the second offset and needs to get stuffed in
651 after the next overflow page is added
654 free_space
= FREESPACE(tp
);
657 FREESPACE(tp
) = free_space
+ OVFLSIZE
;
659 tmpp
= __add_ovflpage ( big_keyp
);
668 if ( change
) ret
->newp
= tmpp
;
669 else ret
->oldp
= tmpp
;