/* * 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); }
/* * Big_insert * * You need to do an insert and the key/data pair is too big * * Returns: * 0 ==> OK *-1 ==> ERROR */ int __big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { uint16_t *p, n; size_t key_size, val_size; uint16_t space, move_bytes, off; char *cp, *key_data, *val_data; size_t temp; cp = bufp->page; /* Character pointer of p. */ p = (uint16_t *)(void *)cp; key_data = (char *)key->data; _DBFIT(key->size, int); key_size = key->size; val_data = (char *)val->data; _DBFIT(val->size, int); val_size = val->size; /* First move the Key */ temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; while (key_size) { move_bytes = MIN(space, key_size); off = OFFSET(p) - move_bytes; memmove(cp + off, key_data, (size_t)move_bytes); key_size -= move_bytes; key_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; temp = off - PAGE_META(n); _DBFIT(temp, uint16_t); FREESPACE(p) = (uint16_t)temp; OFFSET(p) = off; p[n] = PARTIAL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); n = p[0]; if (!key_size) { space = FREESPACE(p); if (space) { move_bytes = MIN(space, val_size); /* * If the data would fit exactly in the * remaining space, we must overflow it to the * next page; otherwise the invariant that the * data must end on a page with FREESPACE * non-zero would fail. */ if (space == val_size && val_size == val->size) goto toolarge; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, (size_t)move_bytes); val_data += move_bytes; val_size -= move_bytes; p[n] = off; p[n - 2] = FULL_KEY_DATA; FREESPACE(p) = FREESPACE(p) - move_bytes; OFFSET(p) = off; } else { toolarge: p[n - 2] = FULL_KEY; } } p = (uint16_t *)(void *)bufp->page; cp = bufp->page; bufp->flags |= BUF_MOD; temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; } /* Now move the data */ temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; while (val_size) { move_bytes = MIN(space, val_size); /* * Here's the hack to make sure that if the data ends on the * same page as the key ends, FREESPACE is at least one. */ if (space == val_size && val_size == val->size) move_bytes--; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, (size_t)move_bytes); val_size -= move_bytes; val_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; temp = off - PAGE_META(n); _DBFIT(temp, uint16_t); FREESPACE(p) = (uint16_t)temp; OFFSET(p) = off; if (val_size) { p[n] = FULL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); cp = bufp->page; p = (uint16_t *)(void *)cp; } else p[n] = FULL_KEY_DATA; bufp->flags |= BUF_MOD; temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; } return (0); }
/* * Returns: * 0 => OK * -1 => error */ int __big_split( HTAB *hashp, BUFHEAD *op, /* Pointer to where to put keys that go in old bucket */ BUFHEAD *np, /* Pointer to new bucket page */ /* Pointer to first page containing the big key/data */ BUFHEAD *big_keyp, int addr, /* Address of big_keyp */ uint32_t obucket,/* Old Bucket */ SPLIT_RETURN *ret ) { BUFHEAD *tmpp; uint16_t *tp; BUFHEAD *bp; DBT key, val; uint32_t change; uint16_t free_space, n, off; size_t temp; bp = big_keyp; /* Now figure out where the big key/data goes */ if (__big_keydata(hashp, big_keyp, &key, &val, 0)) return (-1); change = (__call_hash(hashp, key.data, (int)key.size) != obucket); if ((ret->next_addr = __find_last_page(hashp, &big_keyp)) != 0) { if (!(ret->nextp = __get_buf(hashp, (uint32_t)ret->next_addr, big_keyp, 0))) return (-1); } else ret->nextp = NULL; /* Now make one of np/op point to the big key/data pair */ _DIAGASSERT(np->ovfl == NULL); if (change) tmpp = np; else tmpp = op; tmpp->flags |= BUF_MOD; #ifdef DEBUG1 (void)fprintf(stderr, "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr, (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0)); #endif tmpp->ovfl = bp; /* one of op/np point to big_keyp */ tp = (uint16_t *)(void *)tmpp->page; _DIAGASSERT(FREESPACE(tp) >= OVFLSIZE); n = tp[0]; off = OFFSET(tp); free_space = FREESPACE(tp); tp[++n] = (uint16_t)addr; tp[++n] = OVFLPAGE; tp[0] = n; OFFSET(tp) = off; temp = free_space - OVFLSIZE; _DBFIT(temp, uint16_t); FREESPACE(tp) = (uint16_t)temp; /* * Finally, set the new and old return values. BIG_KEYP contains a * pointer to the last page of the big key_data pair. Make sure that * big_keyp has no following page (2 elements) or create an empty * following page. */ ret->newp = np; ret->oldp = op; tp = (uint16_t *)(void *)big_keyp->page; big_keyp->flags |= BUF_MOD; if (tp[0] > 2) { /* * There may be either one or two offsets on this page. If * there is one, then the overflow page is linked on normally * and tp[4] is OVFLPAGE. If there are two, tp[4] contains * the second offset and needs to get stuffed in after the * next overflow page is added. */ n = tp[4]; free_space = FREESPACE(tp); off = OFFSET(tp); tp[0] -= 2; temp = free_space + OVFLSIZE; _DBFIT(temp, uint16_t); FREESPACE(tp) = (uint16_t)temp; OFFSET(tp) = off; tmpp = __add_ovflpage(hashp, big_keyp); if (!tmpp) return (-1); tp[4] = n; } else tmpp = big_keyp; if (change) ret->newp = tmpp; else ret->oldp = tmpp; 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); }
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); }
/* * Big_insert * * You need to do an insert and the key/data pair is too big * * Returns: * 0 ==> OK *-1 ==> ERROR */ extern int __big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { register uint16 *p; uint key_size, n, val_size; uint16 space, move_bytes, off; char *cp, *key_data, *val_data; cp = bufp->page; /* Character pointer of p. */ p = (uint16 *)cp; key_data = (char *)key->data; key_size = key->size; val_data = (char *)val->data; val_size = val->size; /* First move the Key */ for (space = FREESPACE(p) - BIGOVERHEAD; key_size; space = FREESPACE(p) - BIGOVERHEAD) { move_bytes = PR_MIN(space, key_size); off = OFFSET(p) - move_bytes; memmove(cp + off, key_data, move_bytes); key_size -= move_bytes; key_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; FREESPACE(p) = off - PAGE_META(n); OFFSET(p) = off; p[n] = PARTIAL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); n = p[0]; if (!key_size) { if (FREESPACE(p)) { move_bytes = PR_MIN(FREESPACE(p), val_size); off = OFFSET(p) - move_bytes; p[n] = off; memmove(cp + off, val_data, move_bytes); val_data += move_bytes; val_size -= move_bytes; p[n - 2] = FULL_KEY_DATA; FREESPACE(p) = FREESPACE(p) - move_bytes; OFFSET(p) = off; } else p[n - 2] = FULL_KEY; } p = (uint16 *)bufp->page; cp = bufp->page; bufp->flags |= BUF_MOD; } /* Now move the data */ for (space = FREESPACE(p) - BIGOVERHEAD; val_size; space = FREESPACE(p) - BIGOVERHEAD) { move_bytes = PR_MIN(space, val_size); /* * Here's the hack to make sure that if the data ends on the * same page as the key ends, FREESPACE is at least one. */ if (space == val_size && val_size == val->size) move_bytes--; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, move_bytes); val_size -= move_bytes; val_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; FREESPACE(p) = off - PAGE_META(n); OFFSET(p) = off; if (val_size) { p[n] = FULL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); cp = bufp->page; p = (uint16 *)cp; } else p[n] = FULL_KEY_DATA; bufp->flags |= BUF_MOD; } return (0); }