]> git.saurik.com Git - apple/libc.git/blob - db/hash/hash_bigkey.c
fa8e8ddecd2b46166071c17f5a6c17bfc805a4c3
[apple/libc.git] / db / hash / hash_bigkey.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 1990, 1993
27 * The Regents of the University of California. All rights reserved.
28 *
29 * This code is derived from software contributed to Berkeley by
30 * Margo Seltzer.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61
62 /*
63 * PACKAGE: hash
64 * DESCRIPTION:
65 * Big key/data handling for the hashing package.
66 *
67 * ROUTINES:
68 * External
69 * __big_keydata
70 * __big_split
71 * __big_insert
72 * __big_return
73 * __big_delete
74 * __find_last_page
75 * Internal
76 * collect_key
77 * collect_data
78 */
79
80 #include <sys/param.h>
81
82 #include <errno.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86
87 #ifdef DEBUG
88 #include <assert.h>
89 #endif
90
91 #include <db.h>
92 #include "hash.h"
93 #include "page.h"
94 #include "extern.h"
95
96 static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int));
97 static int collect_data __P((HTAB *, BUFHEAD *, int, int));
98
99 /*
100 * Big_insert
101 *
102 * You need to do an insert and the key/data pair is too big
103 *
104 * Returns:
105 * 0 ==> OK
106 *-1 ==> ERROR
107 */
108 extern int
109 __big_insert(hashp, bufp, key, val)
110 HTAB *hashp;
111 BUFHEAD *bufp;
112 const DBT *key, *val;
113 {
114 register u_short *p;
115 int key_size, n, val_size;
116 u_short space, move_bytes, off;
117 char *cp, *key_data, *val_data;
118
119 cp = bufp->page; /* Character pointer of p. */
120 p = (u_short *)cp;
121
122 key_data = (char *)key->data;
123 key_size = key->size;
124 val_data = (char *)val->data;
125 val_size = val->size;
126
127 /* First move the Key */
128 for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
129 space = FREESPACE(p) - BIGOVERHEAD) {
130 move_bytes = MIN(space, key_size);
131 off = OFFSET(p) - move_bytes;
132 memmove(cp + off, key_data, move_bytes);
133 key_size -= move_bytes;
134 key_data += move_bytes;
135 n = p[0];
136 p[++n] = off;
137 p[0] = ++n;
138 FREESPACE(p) = off - PAGE_META(n);
139 OFFSET(p) = off;
140 p[n] = PARTIAL_KEY;
141 bufp = __add_ovflpage(hashp, bufp);
142 if (!bufp)
143 return (-1);
144 n = p[0];
145 if (!key_size)
146 if (FREESPACE(p)) {
147 move_bytes = MIN(FREESPACE(p), val_size);
148 off = OFFSET(p) - move_bytes;
149 p[n] = off;
150 memmove(cp + off, val_data, move_bytes);
151 val_data += move_bytes;
152 val_size -= move_bytes;
153 p[n - 2] = FULL_KEY_DATA;
154 FREESPACE(p) = FREESPACE(p) - move_bytes;
155 OFFSET(p) = off;
156 } else
157 p[n - 2] = FULL_KEY;
158 p = (u_short *)bufp->page;
159 cp = bufp->page;
160 bufp->flags |= BUF_MOD;
161 }
162
163 /* Now move the data */
164 for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
165 space = FREESPACE(p) - BIGOVERHEAD) {
166 move_bytes = MIN(space, val_size);
167 /*
168 * Here's the hack to make sure that if the data ends on the
169 * same page as the key ends, FREESPACE is at least one.
170 */
171 if (space == val_size && val_size == val->size)
172 move_bytes--;
173 off = OFFSET(p) - move_bytes;
174 memmove(cp + off, val_data, move_bytes);
175 val_size -= move_bytes;
176 val_data += move_bytes;
177 n = p[0];
178 p[++n] = off;
179 p[0] = ++n;
180 FREESPACE(p) = off - PAGE_META(n);
181 OFFSET(p) = off;
182 if (val_size) {
183 p[n] = FULL_KEY;
184 bufp = __add_ovflpage(hashp, bufp);
185 if (!bufp)
186 return (-1);
187 cp = bufp->page;
188 p = (u_short *)cp;
189 } else
190 p[n] = FULL_KEY_DATA;
191 bufp->flags |= BUF_MOD;
192 }
193 return (0);
194 }
195
196 /*
197 * Called when bufp's page contains a partial key (index should be 1)
198 *
199 * All pages in the big key/data pair except bufp are freed. We cannot
200 * free bufp because the page pointing to it is lost and we can't get rid
201 * of its pointer.
202 *
203 * Returns:
204 * 0 => OK
205 *-1 => ERROR
206 */
207 extern int
208 __big_delete(hashp, bufp)
209 HTAB *hashp;
210 BUFHEAD *bufp;
211 {
212 register BUFHEAD *last_bfp, *rbufp;
213 u_short *bp, pageno;
214 int key_done, n;
215
216 rbufp = bufp;
217 last_bfp = NULL;
218 bp = (u_short *)bufp->page;
219 pageno = 0;
220 key_done = 0;
221
222 while (!key_done || (bp[2] != FULL_KEY_DATA)) {
223 if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
224 key_done = 1;
225
226 /*
227 * If there is freespace left on a FULL_KEY_DATA page, then
228 * the data is short and fits entirely on this page, and this
229 * is the last page.
230 */
231 if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
232 break;
233 pageno = bp[bp[0] - 1];
234 rbufp->flags |= BUF_MOD;
235 rbufp = __get_buf(hashp, pageno, rbufp, 0);
236 if (last_bfp)
237 __free_ovflpage(hashp, last_bfp);
238 last_bfp = rbufp;
239 if (!rbufp)
240 return (-1); /* Error. */
241 bp = (u_short *)rbufp->page;
242 }
243
244 /*
245 * If we get here then rbufp points to the last page of the big
246 * key/data pair. Bufp points to the first one -- it should now be
247 * empty pointing to the next page after this pair. Can't free it
248 * because we don't have the page pointing to it.
249 */
250
251 /* This is information from the last page of the pair. */
252 n = bp[0];
253 pageno = bp[n - 1];
254
255 /* Now, bp is the first page of the pair. */
256 bp = (u_short *)bufp->page;
257 if (n > 2) {
258 /* There is an overflow page. */
259 bp[1] = pageno;
260 bp[2] = OVFLPAGE;
261 bufp->ovfl = rbufp->ovfl;
262 } else
263 /* This is the last page. */
264 bufp->ovfl = NULL;
265 n -= 2;
266 bp[0] = n;
267 FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
268 OFFSET(bp) = hashp->BSIZE - 1;
269
270 bufp->flags |= BUF_MOD;
271 if (rbufp)
272 __free_ovflpage(hashp, rbufp);
273 if (last_bfp != rbufp)
274 __free_ovflpage(hashp, last_bfp);
275
276 hashp->NKEYS--;
277 return (0);
278 }
279 /*
280 * Returns:
281 * 0 = key not found
282 * -1 = get next overflow page
283 * -2 means key not found and this is big key/data
284 * -3 error
285 */
286 extern int
287 __find_bigpair(hashp, bufp, ndx, key, size)
288 HTAB *hashp;
289 BUFHEAD *bufp;
290 int ndx;
291 char *key;
292 int size;
293 {
294 register u_short *bp;
295 register char *p;
296 int ksize;
297 u_short bytes;
298 char *kkey;
299
300 bp = (u_short *)bufp->page;
301 p = bufp->page;
302 ksize = size;
303 kkey = key;
304
305 for (bytes = hashp->BSIZE - bp[ndx];
306 bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
307 bytes = hashp->BSIZE - bp[ndx]) {
308 if (memcmp(p + bp[ndx], kkey, bytes))
309 return (-2);
310 kkey += bytes;
311 ksize -= bytes;
312 bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0);
313 if (!bufp)
314 return (-3);
315 p = bufp->page;
316 bp = (u_short *)p;
317 ndx = 1;
318 }
319
320 if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) {
321 #ifdef HASH_STATISTICS
322 ++hash_collisions;
323 #endif
324 return (-2);
325 } else
326 return (ndx);
327 }
328
329 /*
330 * Given the buffer pointer of the first overflow page of a big pair,
331 * find the end of the big pair
332 *
333 * This will set bpp to the buffer header of the last page of the big pair.
334 * It will return the pageno of the overflow page following the last page
335 * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
336 * bucket)
337 */
338 extern u_short
339 __find_last_page(hashp, bpp)
340 HTAB *hashp;
341 BUFHEAD **bpp;
342 {
343 BUFHEAD *bufp;
344 u_short *bp, pageno;
345 int n;
346
347 bufp = *bpp;
348 bp = (u_short *)bufp->page;
349 for (;;) {
350 n = bp[0];
351
352 /*
353 * This is the last page if: the tag is FULL_KEY_DATA and
354 * either only 2 entries OVFLPAGE marker is explicit there
355 * is freespace on the page.
356 */
357 if (bp[2] == FULL_KEY_DATA &&
358 ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
359 break;
360
361 pageno = bp[n - 1];
362 bufp = __get_buf(hashp, pageno, bufp, 0);
363 if (!bufp)
364 return (0); /* Need to indicate an error! */
365 bp = (u_short *)bufp->page;
366 }
367
368 *bpp = bufp;
369 if (bp[0] > 2)
370 return (bp[3]);
371 else
372 return (0);
373 }
374
375 /*
376 * Return the data for the key/data pair that begins on this page at this
377 * index (index should always be 1).
378 */
379 extern int
380 __big_return(hashp, bufp, ndx, val, set_current)
381 HTAB *hashp;
382 BUFHEAD *bufp;
383 int ndx;
384 DBT *val;
385 int set_current;
386 {
387 BUFHEAD *save_p;
388 u_short *bp, len, off, save_addr;
389 char *tp;
390
391 bp = (u_short *)bufp->page;
392 while (bp[ndx + 1] == PARTIAL_KEY) {
393 bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
394 if (!bufp)
395 return (-1);
396 bp = (u_short *)bufp->page;
397 ndx = 1;
398 }
399
400 if (bp[ndx + 1] == FULL_KEY) {
401 bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
402 if (!bufp)
403 return (-1);
404 bp = (u_short *)bufp->page;
405 save_p = bufp;
406 save_addr = save_p->addr;
407 off = bp[1];
408 len = 0;
409 } else
410 if (!FREESPACE(bp)) {
411 /*
412 * This is a hack. We can't distinguish between
413 * FULL_KEY_DATA that contains complete data or
414 * incomplete data, so we require that if the data
415 * is complete, there is at least 1 byte of free
416 * space left.
417 */
418 off = bp[bp[0]];
419 len = bp[1] - off;
420 save_p = bufp;
421 save_addr = bufp->addr;
422 bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
423 if (!bufp)
424 return (-1);
425 bp = (u_short *)bufp->page;
426 } else {
427 /* The data is all on one page. */
428 tp = (char *)bp;
429 off = bp[bp[0]];
430 val->data = (u_char *)tp + off;
431 val->size = bp[1] - off;
432 if (set_current) {
433 if (bp[0] == 2) { /* No more buckets in
434 * chain */
435 hashp->cpage = NULL;
436 hashp->cbucket++;
437 hashp->cndx = 1;
438 } else {
439 hashp->cpage = __get_buf(hashp,
440 bp[bp[0] - 1], bufp, 0);
441 if (!hashp->cpage)
442 return (-1);
443 hashp->cndx = 1;
444 if (!((u_short *)
445 hashp->cpage->page)[0]) {
446 hashp->cbucket++;
447 hashp->cpage = NULL;
448 }
449 }
450 }
451 return (0);
452 }
453
454 val->size = collect_data(hashp, bufp, (int)len, set_current);
455 if (val->size == -1)
456 return (-1);
457 if (save_p->addr != save_addr) {
458 /* We are pretty short on buffers. */
459 errno = EINVAL; /* OUT OF BUFFERS */
460 return (-1);
461 }
462 memmove(hashp->tmp_buf, (save_p->page) + off, len);
463 val->data = (u_char *)hashp->tmp_buf;
464 return (0);
465 }
466 /*
467 * Count how big the total datasize is by recursing through the pages. Then
468 * allocate a buffer and copy the data as you recurse up.
469 */
470 static int
471 collect_data(hashp, bufp, len, set)
472 HTAB *hashp;
473 BUFHEAD *bufp;
474 int len, set;
475 {
476 register u_short *bp;
477 register char *p;
478 BUFHEAD *xbp;
479 u_short save_addr;
480 int mylen, totlen;
481
482 p = bufp->page;
483 bp = (u_short *)p;
484 mylen = hashp->BSIZE - bp[1];
485 save_addr = bufp->addr;
486
487 if (bp[2] == FULL_KEY_DATA) { /* End of Data */
488 totlen = len + mylen;
489 if (hashp->tmp_buf)
490 free(hashp->tmp_buf);
491 if ((hashp->tmp_buf = (char *)malloc(totlen)) == NULL)
492 return (-1);
493 if (set) {
494 hashp->cndx = 1;
495 if (bp[0] == 2) { /* No more buckets in chain */
496 hashp->cpage = NULL;
497 hashp->cbucket++;
498 } else {
499 hashp->cpage =
500 __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
501 if (!hashp->cpage)
502 return (-1);
503 else if (!((u_short *)hashp->cpage->page)[0]) {
504 hashp->cbucket++;
505 hashp->cpage = NULL;
506 }
507 }
508 }
509 } else {
510 xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
511 if (!xbp || ((totlen =
512 collect_data(hashp, xbp, len + mylen, set)) < 1))
513 return (-1);
514 }
515 if (bufp->addr != save_addr) {
516 errno = EINVAL; /* Out of buffers. */
517 return (-1);
518 }
519 memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen);
520 return (totlen);
521 }
522
523 /*
524 * Fill in the key and data for this big pair.
525 */
526 extern int
527 __big_keydata(hashp, bufp, key, val, set)
528 HTAB *hashp;
529 BUFHEAD *bufp;
530 DBT *key, *val;
531 int set;
532 {
533 key->size = collect_key(hashp, bufp, 0, val, set);
534 if (key->size == -1)
535 return (-1);
536 key->data = (u_char *)hashp->tmp_key;
537 return (0);
538 }
539
540 /*
541 * Count how big the total key size is by recursing through the pages. Then
542 * collect the data, allocate a buffer and copy the key as you recurse up.
543 */
544 static int
545 collect_key(hashp, bufp, len, val, set)
546 HTAB *hashp;
547 BUFHEAD *bufp;
548 int len;
549 DBT *val;
550 int set;
551 {
552 BUFHEAD *xbp;
553 char *p;
554 int mylen, totlen;
555 u_short *bp, save_addr;
556
557 p = bufp->page;
558 bp = (u_short *)p;
559 mylen = hashp->BSIZE - bp[1];
560
561 save_addr = bufp->addr;
562 totlen = len + mylen;
563 if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) { /* End of Key. */
564 if (hashp->tmp_key != NULL)
565 free(hashp->tmp_key);
566 if ((hashp->tmp_key = (char *)malloc(totlen)) == NULL)
567 return (-1);
568 if (__big_return(hashp, bufp, 1, val, set))
569 return (-1);
570 } else {
571 xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
572 if (!xbp || ((totlen =
573 collect_key(hashp, xbp, totlen, val, set)) < 1))
574 return (-1);
575 }
576 if (bufp->addr != save_addr) {
577 errno = EINVAL; /* MIS -- OUT OF BUFFERS */
578 return (-1);
579 }
580 memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], mylen);
581 return (totlen);
582 }
583
584 /*
585 * Returns:
586 * 0 => OK
587 * -1 => error
588 */
589 extern int
590 __big_split(hashp, op, np, big_keyp, addr, obucket, ret)
591 HTAB *hashp;
592 BUFHEAD *op; /* Pointer to where to put keys that go in old bucket */
593 BUFHEAD *np; /* Pointer to new bucket page */
594 /* Pointer to first page containing the big key/data */
595 BUFHEAD *big_keyp;
596 int addr; /* Address of big_keyp */
597 u_int obucket;/* Old Bucket */
598 SPLIT_RETURN *ret;
599 {
600 register BUFHEAD *tmpp;
601 register u_short *tp;
602 BUFHEAD *bp;
603 DBT key, val;
604 u_int change;
605 u_short free_space, n, off;
606
607 bp = big_keyp;
608
609 /* Now figure out where the big key/data goes */
610 if (__big_keydata(hashp, big_keyp, &key, &val, 0))
611 return (-1);
612 change = (__call_hash(hashp, key.data, key.size) != obucket);
613
614 if (ret->next_addr = __find_last_page(hashp, &big_keyp)) {
615 if (!(ret->nextp =
616 __get_buf(hashp, ret->next_addr, big_keyp, 0)))
617 return (-1);;
618 } else
619 ret->nextp = NULL;
620
621 /* Now make one of np/op point to the big key/data pair */
622 #ifdef DEBUG
623 assert(np->ovfl == NULL);
624 #endif
625 if (change)
626 tmpp = np;
627 else
628 tmpp = op;
629
630 tmpp->flags |= BUF_MOD;
631 #ifdef DEBUG1
632 (void)fprintf(stderr,
633 "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
634 (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
635 #endif
636 tmpp->ovfl = bp; /* one of op/np point to big_keyp */
637 tp = (u_short *)tmpp->page;
638 #ifdef DEBUG
639 assert(FREESPACE(tp) >= OVFLSIZE);
640 #endif
641 n = tp[0];
642 off = OFFSET(tp);
643 free_space = FREESPACE(tp);
644 tp[++n] = (u_short)addr;
645 tp[++n] = OVFLPAGE;
646 tp[0] = n;
647 OFFSET(tp) = off;
648 FREESPACE(tp) = free_space - OVFLSIZE;
649
650 /*
651 * Finally, set the new and old return values. BIG_KEYP contains a
652 * pointer to the last page of the big key_data pair. Make sure that
653 * big_keyp has no following page (2 elements) or create an empty
654 * following page.
655 */
656
657 ret->newp = np;
658 ret->oldp = op;
659
660 tp = (u_short *)big_keyp->page;
661 big_keyp->flags |= BUF_MOD;
662 if (tp[0] > 2) {
663 /*
664 * There may be either one or two offsets on this page. If
665 * there is one, then the overflow page is linked on normally
666 * and tp[4] is OVFLPAGE. If there are two, tp[4] contains
667 * the second offset and needs to get stuffed in after the
668 * next overflow page is added.
669 */
670 n = tp[4];
671 free_space = FREESPACE(tp);
672 off = OFFSET(tp);
673 tp[0] -= 2;
674 FREESPACE(tp) = free_space + OVFLSIZE;
675 OFFSET(tp) = off;
676 tmpp = __add_ovflpage(hashp, big_keyp);
677 if (!tmpp)
678 return (-1);
679 tp[4] = n;
680 } else
681 tmpp = big_keyp;
682
683 if (change)
684 ret->newp = tmpp;
685 else
686 ret->oldp = tmpp;
687 return (0);
688 }