Example #1
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);
}
Example #2
0
/*
 * Assume that hashp has been set in wrapper routine.
 */
static int
hash_access(HTAB *hashp, ACTION action, DBT *key, DBT *val)
{
	BUFHEAD *rbufp;
	BUFHEAD *bufp, *save_bufp;
	u_int16_t *bp;
	int n, ndx, off, size;
	char *kp;
	u_int16_t pageno;

#ifdef HASH_STATISTICS
	hash_accesses++;
#endif

	off = hashp->BSIZE;
	size = key->size;
	kp = (char *)key->data;
	rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
	if (!rbufp)
		return (ERROR);
	save_bufp = rbufp;

	/* Pin the bucket chain */
	rbufp->flags |= BUF_PIN;
	for (bp = (u_int16_t *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
		if (bp[1] >= REAL_KEY) {
			/* Real key/data pair */
			if (size == off - *bp &&
			    memcmp(kp, rbufp->page + *bp, size) == 0)
				goto found;
			off = bp[1];
#ifdef HASH_STATISTICS
			hash_collisions++;
#endif
			bp += 2;
			ndx += 2;
		} else if (bp[1] == OVFLPAGE) {
			rbufp = __get_buf(hashp, *bp, rbufp, 0);
			if (!rbufp) {
				save_bufp->flags &= ~BUF_PIN;
				return (ERROR);
			}
			/* FOR LOOP INIT */
			bp = (u_int16_t *)rbufp->page;
			n = *bp++;
			ndx = 1;
			off = hashp->BSIZE;
		} else if (bp[1] < REAL_KEY) {
			if ((ndx =
			    __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
				goto found;
			if (ndx == -2) {
				bufp = rbufp;
				if (!(pageno =
				    __find_last_page(hashp, &bufp))) {
					ndx = 0;
					rbufp = bufp;
					break;	/* FOR */
				}
				rbufp = __get_buf(hashp, pageno, bufp, 0);
				if (!rbufp) {
					save_bufp->flags &= ~BUF_PIN;
					return (ERROR);
				}
				/* FOR LOOP INIT */
				bp = (u_int16_t *)rbufp->page;
				n = *bp++;
				ndx = 1;
				off = hashp->BSIZE;
			} else {
				save_bufp->flags &= ~BUF_PIN;
				return (ERROR);
			}
		}

	/* Not found */
	switch (action) {
	case HASH_PUT:
	case HASH_PUTNEW:
		if (__addel(hashp, rbufp, key, val)) {
			save_bufp->flags &= ~BUF_PIN;
			return (ERROR);
		} else {
			save_bufp->flags &= ~BUF_PIN;
			return (SUCCESS);
		}
	case HASH_GET:
	case HASH_DELETE:
	default:
		save_bufp->flags &= ~BUF_PIN;
		return (ABNORMAL);
	}

found:
	switch (action) {
	case HASH_PUTNEW:
		save_bufp->flags &= ~BUF_PIN;
		return (ABNORMAL);
	case HASH_GET:
		bp = (u_int16_t *)rbufp->page;
		if (bp[ndx + 1] < REAL_KEY) {
			if (__big_return(hashp, rbufp, ndx, val, 0))
				return (ERROR);
		} else {
			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
			val->size = bp[ndx] - bp[ndx + 1];
		}
		break;
	case HASH_PUT:
		if ((__delpair(hashp, rbufp, ndx)) ||
		    (__addel(hashp, rbufp, key, val))) {
			save_bufp->flags &= ~BUF_PIN;
			return (ERROR);
		}
		break;
	case HASH_DELETE:
		if (__delpair(hashp, rbufp, ndx))
			return (ERROR);
		break;
	default:
		abort();
	}
	save_bufp->flags &= ~BUF_PIN;
	return (SUCCESS);
}
Example #3
0
/*
 * Returns:
 *	 0 ==> OK
 *	-1 ==> Error
 */
int
__split_page(HTAB *hashp, uint32_t obucket, uint32_t nbucket)
{
	BUFHEAD *new_bufp, *old_bufp;
	uint16_t *ino;
	char *np;
	DBT key, val;
	int n, ndx, retval;
	uint16_t copyto, diff, off, moved;
	char *op;
	size_t temp;

	copyto = (uint16_t)hashp->BSIZE;
	off = (uint16_t)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_t *)(void *)(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_t *)op + ino[n];
		key.size = off - ino[n];

		if (__call_hash(hashp, key.data, (int)key.size) == obucket) {
			/* Don't switch page */
			diff = copyto - off;
			if (diff) {
				copyto = ino[n + 1] + diff;
				memmove(op + copyto, op + ino[n + 1],
				    (size_t)(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_t *)op + ino[n + 1];
			val.size = ino[n] - ino[n + 1];
			putpair(np, &key, &val);
			moved += 2;
		}

		off = ino[n + 1];
	}

	/* Now clean up the page */
	ino[0] -= moved;
	temp = sizeof(uint16_t) * (ino[0] + 3);
	_DIAGASSERT(copyto >= temp);
	FREESPACE(ino) = (uint16_t)(copyto - temp);
	OFFSET(ino) = copyto;

#ifdef DEBUG3
	(void)fprintf(stderr, "split %d/%d\n",
	    ((uint16_t *)np)[0] / 2,
	    ((uint16_t *)op)[0] / 2);
#endif
	/* unpin both pages */
	old_bufp->flags &= ~BUF_PIN;
	new_bufp->flags &= ~BUF_PIN;
	return (0);
}
Example #4
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);
}
Example #5
0
/*
 * Assume that hashp has been set in wrapper routine.
 */
static int
hash_access(
	HTAB *hashp,
	ACTION action,
	DBT *key, DBT *val)
{
	register BUFHEAD *rbufp;
	BUFHEAD *bufp, *save_bufp;
	register uint16 *bp;
	register long n, ndx, off;
	register size_t size;
	register char *kp;
	uint16 pageno;
	uint32 ovfl_loop_count=0;
    int32 last_overflow_page_no = -1;

#ifdef HASH_STATISTICS
	hash_accesses++;
#endif

	off = hashp->BSIZE;
	size = key->size;
	kp = (char *)key->data;
	rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
	if (!rbufp)
		return (DATABASE_CORRUPTED_ERROR);
	save_bufp = rbufp;

	/* Pin the bucket chain */
	rbufp->flags |= BUF_PIN;
	for (bp = (uint16 *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
	{

		if (bp[1] >= REAL_KEY) {
			/* Real key/data pair */
			if (size == (unsigned long)(off - *bp) &&
			    memcmp(kp, rbufp->page + *bp, size) == 0)
				goto found;
			off = bp[1];
#ifdef HASH_STATISTICS
			hash_collisions++;
#endif
			bp += 2;
			ndx += 2;
		} else if (bp[1] == OVFLPAGE) {

            /* database corruption: overflow loop detection */
            if(last_overflow_page_no == (int32)*bp)
    			return (DATABASE_CORRUPTED_ERROR);

            last_overflow_page_no = *bp;

			rbufp = __get_buf(hashp, *bp, rbufp, 0);
			if (!rbufp) {
				save_bufp->flags &= ~BUF_PIN;
				return (DBM_ERROR);
			}

            ovfl_loop_count++;
            if(ovfl_loop_count > MAX_OVERFLOW_HASH_ACCESS_LOOPS)
    			return (DATABASE_CORRUPTED_ERROR);

			/* FOR LOOP INIT */
			bp = (uint16 *)rbufp->page;
			n = *bp++;
			ndx = 1;
			off = hashp->BSIZE;
		} else if (bp[1] < REAL_KEY) {
			if ((ndx =
			    __find_bigpair(hashp, rbufp, ndx, kp, (int)size)) > 0)
				goto found;
			if (ndx == -2) {
				bufp = rbufp;
				if (!(pageno =
				    __find_last_page(hashp, &bufp))) {
					ndx = 0;
					rbufp = bufp;
					break;	/* FOR */
				}
				rbufp = __get_buf(hashp, pageno, bufp, 0);
				if (!rbufp) {
					save_bufp->flags &= ~BUF_PIN;
					return (DBM_ERROR);
				}
				/* FOR LOOP INIT */
				bp = (uint16 *)rbufp->page;
				n = *bp++;
				ndx = 1;
				off = hashp->BSIZE;
			} else {
				save_bufp->flags &= ~BUF_PIN;
				return (DBM_ERROR);

			}
		}
	}

	/* Not found */
	switch (action) {
	case HASH_PUT:
	case HASH_PUTNEW:
		if (__addel(hashp, rbufp, key, val)) {
			save_bufp->flags &= ~BUF_PIN;
			return (DBM_ERROR);
		} else {
			save_bufp->flags &= ~BUF_PIN;
			return (SUCCESS);
		}
	case HASH_GET:
	case HASH_DELETE:
	default:
		save_bufp->flags &= ~BUF_PIN;
		return (ABNORMAL);
	}

found:
	switch (action) {
	case HASH_PUTNEW:
		save_bufp->flags &= ~BUF_PIN;
		return (ABNORMAL);
	case HASH_GET:
		bp = (uint16 *)rbufp->page;
		if (bp[ndx + 1] < REAL_KEY) {
			if (__big_return(hashp, rbufp, ndx, val, 0))
				return (DBM_ERROR);
		} else {
			val->data = (uint8 *)rbufp->page + (int)bp[ndx + 1];
			val->size = bp[ndx] - bp[ndx + 1];
		}
		break;
	case HASH_PUT:
		if ((__delpair(hashp, rbufp, ndx)) ||
		    (__addel(hashp, rbufp, key, val))) {
			save_bufp->flags &= ~BUF_PIN;
			return (DBM_ERROR);
		}
		break;
	case HASH_DELETE:
		if (__delpair(hashp, rbufp, ndx))
			return (DBM_ERROR);
		break;
	default:
		abort();
	}
	save_bufp->flags &= ~BUF_PIN;
	return (SUCCESS);
}
Example #6
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);
}
Example #7
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);
}