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