/* * Add the given pair to the page * * Returns: * 0 ==> OK * 1 ==> failure */ extern int __addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { u_int16_t *bp, *sop; int do_expand; bp = (u_int16_t *)bufp->page; do_expand = 0; while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY)) /* Exception case */ if (bp[2] == FULL_KEY_DATA && bp[0] == 2) /* This is the last page of a big key/data pair and we need to add another page */ break; else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1); bp = (u_int16_t *)bufp->page; } else /* Try to squeeze key on this page */ if (FREESPACE(bp) > PAIRSIZE(key, val)) { squeeze_key(bp, key, val); return (0); } else { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1); bp = (u_int16_t *)bufp->page; } if (PAIRFITS(bp, key, val)) putpair(bufp->page, key, val); else { do_expand = 1; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); sop = (u_int16_t *)bufp->page; if (PAIRFITS(sop, key, val)) putpair((char *)sop, key, val); else if (__big_insert(hashp, bufp, key, val)) return (-1); } bufp->flags |= BUF_MOD; /* * If the average number of keys per bucket exceeds the fill factor, * expand the table. */ hashp->NKEYS++; if (do_expand || (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) return (__expand_table(hashp)); return (0); }
/* * Called when we encounter an overflow or big key/data page during split * handling. This is special cased since we have to begin checking whether * the key/data pairs fit on their respective pages and because we may need * overflow pages for both the old and new pages. * * The first page might be a page with regular key/data pairs in which case * we have a regular overflow condition and just need to go on to the next * page or it might be a big key/data pair in which case we need to fix the * big key/data pair. * * Returns: * 0 ==> success * -1 ==> failure */ static int ugly_split( HTAB *hashp, uint32_t obucket, /* Same as __split_page. */ BUFHEAD *old_bufp, BUFHEAD *new_bufp, int copyto, /* First byte on page which contains key/data values. */ int moved /* Number of pairs moved to new page. */ ) { BUFHEAD *bufp; /* Buffer header for ino */ uint16_t *ino; /* Page keys come off of */ uint16_t *np; /* New page */ uint16_t *op; /* Page keys go on to if they aren't moving */ size_t temp; BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ DBT key, val; SPLIT_RETURN ret; uint16_t n, off, ov_addr, scopyto; char *cino; /* Character value of ino */ bufp = old_bufp; ino = (uint16_t *)(void *)old_bufp->page; np = (uint16_t *)(void *)new_bufp->page; op = (uint16_t *)(void *)old_bufp->page; last_bfp = NULL; scopyto = (uint16_t)copyto; /* ANSI */ n = ino[0] - 1; while (n < ino[0]) { if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { if (__big_split(hashp, old_bufp, new_bufp, bufp, (int)bufp->addr, obucket, &ret)) return (-1); old_bufp = ret.oldp; if (!old_bufp) return (-1); op = (uint16_t *)(void *)old_bufp->page; new_bufp = ret.newp; if (!new_bufp) return (-1); np = (uint16_t *)(void *)new_bufp->page; bufp = ret.nextp; if (!bufp) return (0); cino = (char *)bufp->page; ino = (uint16_t *)(void *)cino; last_bfp = ret.nextp; } else if (ino[n + 1] == OVFLPAGE) { ov_addr = ino[n]; /* * Fix up the old page -- the extra 2 are the fields * which contained the overflow information. */ ino[0] -= (moved + 2); temp = sizeof(uint16_t) * (ino[0] + 3); _DIAGASSERT(scopyto >= temp); FREESPACE(ino) = (uint16_t)(scopyto - temp); OFFSET(ino) = scopyto; bufp = __get_buf(hashp, (uint32_t)ov_addr, bufp, 0); if (!bufp) return (-1); ino = (uint16_t *)(void *)bufp->page; n = 1; scopyto = hashp->BSIZE; moved = 0; if (last_bfp) __free_ovflpage(hashp, last_bfp); last_bfp = bufp; } /* Move regular sized pairs of there are any */ off = hashp->BSIZE; for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { cino = (char *)(void *)ino; key.data = (uint8_t *)cino + ino[n]; key.size = off - ino[n]; val.data = (uint8_t *)cino + ino[n + 1]; val.size = ino[n] - ino[n + 1]; off = ino[n + 1]; if (__call_hash(hashp, key.data, (int)key.size) == obucket) { /* Keep on old page */ if (PAIRFITS(op, (&key), (&val))) putpair((char *)(void *)op, &key, &val); else { old_bufp = __add_ovflpage(hashp, old_bufp); if (!old_bufp) return (-1); op = (uint16_t *)(void *)old_bufp->page; putpair((char *)(void *)op, &key, &val); } old_bufp->flags |= BUF_MOD; } else { /* Move to new page */ if (PAIRFITS(np, (&key), (&val))) putpair((char *)(void *)np, &key, &val); else { new_bufp = __add_ovflpage(hashp, new_bufp); if (!new_bufp) return (-1); np = (uint16_t *)(void *)new_bufp->page; putpair((char *)(void *)np, &key, &val); } new_bufp->flags |= BUF_MOD; } } } if (last_bfp) __free_ovflpage(hashp, last_bfp); return (0); }
static int ugly_split(HTAB *hashp, uint32 obucket, BUFHEAD *old_bufp, BUFHEAD *new_bufp, /* Same as __split_page. */ int copyto, int moved) /* int copyto; First byte on page which contains key/data values. */ /* int moved; Number of pairs moved to new page. */ { register BUFHEAD *bufp; /* Buffer header for ino */ register uint16 *ino; /* Page keys come off of */ register uint16 *np; /* New page */ register uint16 *op; /* Page keys go on to if they aren't moving */ uint32 loop_detection = 0; BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ DBT key, val; SPLIT_RETURN ret; uint16 n, off, ov_addr, scopyto; char *cino; /* Character value of ino */ int status; bufp = old_bufp; ino = (uint16 *)old_bufp->page; np = (uint16 *)new_bufp->page; op = (uint16 *)old_bufp->page; last_bfp = NULL; scopyto = (uint16)copyto; /* ANSI */ n = ino[0] - 1; while (n < ino[0]) { /* this function goes nuts sometimes and never returns. * I havent found the problem yet but I need a solution * so if we loop too often we assume a database curruption error * :LJM */ loop_detection++; if (loop_detection > MAX_UGLY_SPLIT_LOOPS) return DATABASE_CORRUPTED_ERROR; if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { if ((status = __big_split(hashp, old_bufp, new_bufp, bufp, bufp->addr, obucket, &ret))) return (status); old_bufp = ret.oldp; if (!old_bufp) return (-1); op = (uint16 *)old_bufp->page; new_bufp = ret.newp; if (!new_bufp) return (-1); np = (uint16 *)new_bufp->page; bufp = ret.nextp; if (!bufp) return (0); cino = (char *)bufp->page; ino = (uint16 *)cino; last_bfp = ret.nextp; } else if (ino[n + 1] == OVFLPAGE) { ov_addr = ino[n]; /* * Fix up the old page -- the extra 2 are the fields * which contained the overflow information. */ ino[0] -= (moved + 2); FREESPACE(ino) = scopyto - sizeof(uint16) * (ino[0] + 3); OFFSET(ino) = scopyto; bufp = __get_buf(hashp, ov_addr, bufp, 0); if (!bufp) return (-1); ino = (uint16 *)bufp->page; n = 1; scopyto = hashp->BSIZE; moved = 0; if (last_bfp) __free_ovflpage(hashp, last_bfp); last_bfp = bufp; } /* Move regular sized pairs of there are any */ off = hashp->BSIZE; for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { cino = (char *)ino; key.data = (uint8 *)cino + ino[n]; key.size = off - ino[n]; val.data = (uint8 *)cino + ino[n + 1]; val.size = ino[n] - ino[n + 1]; off = ino[n + 1]; if (__call_hash(hashp, (char *)key.data, key.size) == obucket) { /* Keep on old page */ if (PAIRFITS(op, (&key), (&val))) putpair((char *)op, &key, &val); else { old_bufp = __add_ovflpage(hashp, old_bufp); if (!old_bufp) return (-1); op = (uint16 *)old_bufp->page; putpair((char *)op, &key, &val); } old_bufp->flags |= BUF_MOD; } else { /* Move to new page */ if (PAIRFITS(np, (&key), (&val))) putpair((char *)np, &key, &val); else { new_bufp = __add_ovflpage(hashp, new_bufp); if (!new_bufp) return (-1); np = (uint16 *)new_bufp->page; putpair((char *)np, &key, &val); } new_bufp->flags |= BUF_MOD; } } } if (last_bfp) __free_ovflpage(hashp, last_bfp); return (0); }
/* * Add the given pair to the page * * Returns: * 0 ==> OK * 1 ==> failure */ extern int __addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { register uint16 *bp, *sop; int do_expand; bp = (uint16 *)bufp->page; do_expand = 0; while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY)) /* Exception case */ if (bp[2] == FULL_KEY_DATA && bp[0] == 2) /* This is the last page of a big key/data pair and we need to add another page */ break; else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) { #ifdef DEBUG assert(0); #endif return (-1); } bp = (uint16 *)bufp->page; } else /* Try to squeeze key on this page */ if (FREESPACE(bp) > PAIRSIZE(key, val)) { { squeeze_key(bp, key, val); /* LJM: I added this because I think it was * left out on accident. * if this isn't incremented nkeys will not * be the actual number of keys in the db. */ hashp->NKEYS++; return (0); } } else { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) { #ifdef DEBUG assert(0); #endif return (-1); } bp = (uint16 *)bufp->page; } if (PAIRFITS(bp, key, val)) putpair(bufp->page, key, (DBT *)val); else { do_expand = 1; bufp = __add_ovflpage(hashp, bufp); if (!bufp) { #ifdef DEBUG assert(0); #endif return (-1); } sop = (uint16 *)bufp->page; if (PAIRFITS(sop, key, val)) putpair((char *)sop, key, (DBT *)val); else if (__big_insert(hashp, bufp, key, val)) { #ifdef DEBUG assert(0); #endif return (-1); } } bufp->flags |= BUF_MOD; /* * If the average number of keys per bucket exceeds the fill factor, * expand the table. */ hashp->NKEYS++; if (do_expand || (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) return (__expand_table(hashp)); return (0); }
/* * Returns: * 0 ==> OK * -1 ==> Error */ extern int __split_page(HTAB *hashp, uint32 obucket, uint32 nbucket) { register BUFHEAD *new_bufp, *old_bufp; register uint16 *ino; register uint16 *tmp_uint16_array; register char *np; DBT key, val; uint16 n, ndx; int retval; uint16 copyto, diff, moved; size_t off; char *op; copyto = (uint16)hashp->BSIZE; off = (uint16)hashp->BSIZE; old_bufp = __get_buf(hashp, obucket, NULL, 0); if (old_bufp == NULL) return (-1); new_bufp = __get_buf(hashp, nbucket, NULL, 0); if (new_bufp == NULL) return (-1); old_bufp->flags |= (BUF_MOD | BUF_PIN); new_bufp->flags |= (BUF_MOD | BUF_PIN); ino = (uint16 *)(op = old_bufp->page); np = new_bufp->page; moved = 0; for (n = 1, ndx = 1; n < ino[0]; n += 2) { if (ino[n + 1] < REAL_KEY) { retval = ugly_split(hashp, obucket, old_bufp, new_bufp, (int)copyto, (int)moved); old_bufp->flags &= ~BUF_PIN; new_bufp->flags &= ~BUF_PIN; return (retval); } key.data = (uint8 *)op + ino[n]; /* check here for ino[n] being greater than * off. If it is then the database has * been corrupted. */ if (ino[n] > off) return (DATABASE_CORRUPTED_ERROR); key.size = off - ino[n]; #ifdef DEBUG /* make sure the size is positive */ assert(((int)key.size) > -1); #endif if (__call_hash(hashp, (char *)key.data, key.size) == obucket) { /* Don't switch page */ diff = copyto - off; if (diff) { copyto = ino[n + 1] + diff; memmove(op + copyto, op + ino[n + 1], off - ino[n + 1]); ino[ndx] = copyto + ino[n] - ino[n + 1]; ino[ndx + 1] = copyto; } else copyto = ino[n + 1]; ndx += 2; } else { /* Switch page */ val.data = (uint8 *)op + ino[n + 1]; val.size = ino[n] - ino[n + 1]; /* if the pair doesn't fit something is horribly * wrong. LJM */ tmp_uint16_array = (uint16 *)np; if (!PAIRFITS(tmp_uint16_array, &key, &val)) return (DATABASE_CORRUPTED_ERROR); putpair(np, &key, &val); moved += 2; } off = ino[n + 1]; } /* Now clean up the page */ ino[0] -= moved; FREESPACE(ino) = copyto - sizeof(uint16) * (ino[0] + 3); OFFSET(ino) = copyto; #ifdef DEBUG3 (void)fprintf(stderr, "split %d/%d\n", ((uint16 *)np)[0] / 2, ((uint16 *)op)[0] / 2); #endif /* unpin both pages */ old_bufp->flags &= ~BUF_PIN; new_bufp->flags &= ~BUF_PIN; return (0); }