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