2N/A/*-
2N/A * Copyright (c) 1990, 1993, 1994
2N/A * The Regents of the University of California. All rights reserved.
2N/A *
2N/A * This code is derived from software contributed to Berkeley by
2N/A * Margo Seltzer.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by the University of
2N/A * California, Berkeley and its contributors.
2N/A * 4. Neither the name of the University nor the names of its contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A */
2N/A
2N/A#if defined(LIBC_SCCS) && !defined(lint)
2N/Astatic char sccsid[] = "@(#)hash_bigkey.c 8.5 (Berkeley) 11/2/95";
2N/A#endif /* LIBC_SCCS and not lint */
2N/A
2N/A/*
2N/A * PACKAGE: hash
2N/A * DESCRIPTION:
2N/A * Big key/data handling for the hashing package.
2N/A *
2N/A * ROUTINES:
2N/A * External
2N/A * __big_keydata
2N/A * __big_split
2N/A * __big_insert
2N/A * __big_return
2N/A * __big_delete
2N/A * __find_last_page
2N/A * Internal
2N/A * collect_key
2N/A * collect_data
2N/A */
2N/A#include <sys/types.h>
2N/A
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A
2N/A#ifdef DEBUG
2N/A#include <assert.h>
2N/A#endif
2N/A
2N/A#include "db-int.h"
2N/A#include "hash.h"
2N/A#include "page.h"
2N/A#include "extern.h"
2N/A
2N/Astatic int32_t collect_key __P((HTAB *, PAGE16 *, int32_t, db_pgno_t *));
2N/Astatic int32_t collect_data __P((HTAB *, PAGE16 *, int32_t));
2N/A
2N/A/*
2N/A * Big_insert
2N/A *
2N/A * You need to do an insert and the key/data pair is greater than
2N/A * MINFILL * the bucket size
2N/A *
2N/A * Returns:
2N/A * 0 ==> OK
2N/A * -1 ==> ERROR
2N/A */
2N/Aint32_t
2N/A__big_insert(hashp, pagep, key, val)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A const DBT *key, *val;
2N/A{
2N/A size_t key_size, val_size;
2N/A indx_t key_move_bytes, val_move_bytes;
2N/A int8_t *key_data, *val_data, base_page;
2N/A
2N/A key_data = (int8_t *)key->data;
2N/A key_size = key->size;
2N/A val_data = (int8_t *)val->data;
2N/A val_size = val->size;
2N/A
2N/A NUM_ENT(pagep) = NUM_ENT(pagep) + 1;
2N/A
2N/A for (base_page = 1; key_size + val_size;) {
2N/A /* Add a page! */
2N/A pagep =
2N/A __add_bigpage(hashp, pagep, NUM_ENT(pagep) - 1, base_page);
2N/A if (!pagep)
2N/A return (-1);
2N/A
2N/A /* There's just going to be one entry on this page. */
2N/A NUM_ENT(pagep) = 1;
2N/A
2N/A /* Move the key's data. */
2N/A key_move_bytes = MIN(FREESPACE(pagep), key_size);
2N/A /* Mark the page as to how much key & data is on this page. */
2N/A BIGKEYLEN(pagep) = key_move_bytes;
2N/A val_move_bytes =
2N/A MIN(FREESPACE(pagep) - key_move_bytes, val_size);
2N/A BIGDATALEN(pagep) = val_move_bytes;
2N/A
2N/A /* Note big pages build beginning --> end, not vice versa. */
2N/A if (key_move_bytes)
2N/A memmove(BIGKEY(pagep), key_data, key_move_bytes);
2N/A if (val_move_bytes)
2N/A memmove(BIGDATA(pagep), val_data, val_move_bytes);
2N/A
2N/A key_size -= key_move_bytes;
2N/A key_data += key_move_bytes;
2N/A val_size -= val_move_bytes;
2N/A val_data += val_move_bytes;
2N/A
2N/A base_page = 0;
2N/A }
2N/A __put_page(hashp, pagep, A_RAW, 1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Called when we need to delete a big pair.
2N/A *
2N/A * Returns:
2N/A * 0 => OK
2N/A * -1 => ERROR
2N/A */
2N/Aint32_t
2N/A#ifdef __STDC__
2N/A__big_delete(HTAB *hashp, PAGE16 *pagep, indx_t ndx)
2N/A#else
2N/A__big_delete(hashp, pagep, ndx)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A u_int32_t ndx; /* Index of big pair on base page. */
2N/A#endif
2N/A{
2N/A PAGE16 *last_pagep;
2N/A
2N/A /* Get first page with big key/data. */
2N/A pagep = __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Traverse through the pages, freeing the previous one (except
2N/A * the first) at each new page.
2N/A */
2N/A while (NEXT_PGNO(pagep) != INVALID_PGNO) {
2N/A last_pagep = pagep;
2N/A pagep = __get_page(hashp, NEXT_PGNO(pagep), A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A __delete_page(hashp, last_pagep, A_OVFL);
2N/A }
2N/A
2N/A /* Free the last page in the chain. */
2N/A __delete_page(hashp, pagep, A_OVFL);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Given a key, indicates whether the big key at cursorp matches the
2N/A * given key.
2N/A *
2N/A * Returns:
2N/A * 1 = Found!
2N/A * 0 = Key not found
2N/A * -1 error
2N/A */
2N/Aint32_t
2N/A__find_bigpair(hashp, cursorp, key, size)
2N/A HTAB *hashp;
2N/A CURSOR *cursorp;
2N/A int8_t *key;
2N/A int32_t size;
2N/A{
2N/A PAGE16 *pagep, *hold_pagep;
2N/A db_pgno_t next_pgno;
2N/A int32_t ksize;
2N/A u_int16_t bytes;
2N/A int8_t *kkey;
2N/A
2N/A ksize = size;
2N/A kkey = key;
2N/A bytes = 0;
2N/A
2N/A hold_pagep = NULL;
2N/A /* Chances are, hashp->cpage is the base page. */
2N/A if (cursorp->pagep)
2N/A pagep = hold_pagep = cursorp->pagep;
2N/A else {
2N/A pagep = __get_page(hashp, cursorp->pgno, A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Now, get the first page with the big stuff on it.
2N/A *
2N/A * XXX
2N/A * KLUDGE: we know that cursor is looking at the _next_ item, so
2N/A * we have to look at pgndx - 1.
2N/A */
2N/A next_pgno = OADDR_TO_PAGE(DATA_OFF(pagep, (cursorp->pgndx - 1)));
2N/A if (!hold_pagep)
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A pagep = __get_page(hashp, next_pgno, A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A
2N/A /* While there are both keys to compare. */
2N/A while ((ksize > 0) && (BIGKEYLEN(pagep))) {
2N/A if (ksize < KEY_OFF(pagep, 0) ||
2N/A memcmp(BIGKEY(pagep), kkey, BIGKEYLEN(pagep))) {
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A return (0);
2N/A }
2N/A kkey += BIGKEYLEN(pagep);
2N/A ksize -= BIGKEYLEN(pagep);
2N/A if (NEXT_PGNO(pagep) != INVALID_PGNO) {
2N/A next_pgno = NEXT_PGNO(pagep);
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A pagep = __get_page(hashp, next_pgno, A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A }
2N/A }
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A#ifdef DEBUG
2N/A assert(ksize >= 0);
2N/A#endif
2N/A if (ksize != 0) {
2N/A#ifdef HASH_STATISTICS
2N/A ++hash_collisions;
2N/A#endif
2N/A return (0);
2N/A } else
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Fill in the key and data for this big pair.
2N/A */
2N/Aint32_t
2N/A__big_keydata(hashp, pagep, key, val, ndx)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A DBT *key, *val;
2N/A int32_t ndx;
2N/A{
2N/A ITEM_INFO ii;
2N/A PAGE16 *key_pagep;
2N/A db_pgno_t last_page;
2N/A
2N/A key_pagep =
2N/A __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW);
2N/A if (!key_pagep)
2N/A return (-1);
2N/A key->size = collect_key(hashp, key_pagep, 0, &last_page);
2N/A key->data = hashp->bigkey_buf;
2N/A __put_page(hashp, key_pagep, A_RAW, 0);
2N/A
2N/A if (key->size == -1)
2N/A return (-1);
2N/A
2N/A /* Create an item_info to direct __big_return to the beginning pgno. */
2N/A ii.pgno = last_page;
2N/A return (__big_return(hashp, &ii, val, 1));
2N/A}
2N/A
2N/A/*
2N/A * Return the big key on page, ndx.
2N/A */
2N/Aint32_t
2N/A#ifdef __STDC__
2N/A__get_bigkey(HTAB *hashp, PAGE16 *pagep, indx_t ndx, DBT *key)
2N/A#else
2N/A__get_bigkey(hashp, pagep, ndx, key)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A u_int32_t ndx;
2N/A DBT *key;
2N/A#endif
2N/A{
2N/A PAGE16 *key_pagep;
2N/A
2N/A /* Solaris Kerberos */
2N/A if (!pagep)
2N/A return (-1);
2N/A
2N/A key_pagep =
2N/A __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW);
2N/A
2N/A /* Solaris Kerberos */
2N/A if (!key_pagep)
2N/A return (-1);
2N/A
2N/A key->size = collect_key(hashp, key_pagep, 0, NULL);
2N/A key->data = hashp->bigkey_buf;
2N/A
2N/A __put_page(hashp, key_pagep, A_RAW, 0);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Return the big key and data indicated in item_info.
2N/A */
2N/Aint32_t
2N/A__big_return(hashp, item_info, val, on_bigkey_page)
2N/A HTAB *hashp;
2N/A ITEM_INFO *item_info;
2N/A DBT *val;
2N/A int32_t on_bigkey_page;
2N/A{
2N/A PAGE16 *pagep;
2N/A db_pgno_t next_pgno;
2N/A
2N/A if (!on_bigkey_page) {
2N/A /* Get first page with big pair on it. */
2N/A pagep = __get_page(hashp,
2N/A OADDR_TO_PAGE(item_info->data_off), A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A } else {
2N/A pagep = __get_page(hashp, item_info->pgno, A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A }
2N/A
2N/A /* Traverse through the bigkey pages until a page with data is found. */
2N/A while (!BIGDATALEN(pagep)) {
2N/A next_pgno = NEXT_PGNO(pagep);
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A pagep = __get_page(hashp, next_pgno, A_RAW);
2N/A if (!pagep)
2N/A return (-1);
2N/A }
2N/A
2N/A val->size = collect_data(hashp, pagep, 0);
2N/A if (val->size < 1)
2N/A return (-1);
2N/A val->data = (void *)hashp->bigdata_buf;
2N/A
2N/A __put_page(hashp, pagep, A_RAW, 0);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Given a page with a big key on it, traverse through the pages counting data
2N/A * length, and collect all of the data on the way up. Store the key in
2N/A * hashp->bigkey_buf. last_page indicates to the calling function what the
2N/A * last page with key on it is; this will help if you later want to retrieve
2N/A * the data portion.
2N/A *
2N/A * Does the work for __get_bigkey.
2N/A *
2N/A * Return total length of data; -1 if error.
2N/A */
2N/Astatic int32_t
2N/Acollect_key(hashp, pagep, len, last_page)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A int32_t len;
2N/A db_pgno_t *last_page;
2N/A{
2N/A PAGE16 *next_pagep;
2N/A int32_t totlen, retval;
2N/A db_pgno_t next_pgno;
2N/A#ifdef DEBUG
2N/A db_pgno_t save_addr;
2N/A#endif
2N/A
2N/A /* If this is the last page with key. */
2N/A if (BIGDATALEN(pagep)) {
2N/A totlen = len + BIGKEYLEN(pagep);
2N/A if (hashp->bigkey_buf)
2N/A free(hashp->bigkey_buf);
2N/A hashp->bigkey_buf = (u_int8_t *)malloc(totlen);
2N/A if (!hashp->bigkey_buf)
2N/A return (-1);
2N/A memcpy(hashp->bigkey_buf + len,
2N/A BIGKEY(pagep), BIGKEYLEN(pagep));
2N/A if (last_page)
2N/A *last_page = ADDR(pagep);
2N/A return (totlen);
2N/A }
2N/A
2N/A /* Key filled up all of last key page, so we've gone 1 too far. */
2N/A if (BIGKEYLEN(pagep) == 0) {
2N/A if (hashp->bigkey_buf)
2N/A free(hashp->bigkey_buf);
2N/A hashp->bigkey_buf = (u_int8_t *)malloc(len);
2N/A return (hashp->bigkey_buf ? len : -1);
2N/A }
2N/A totlen = len + BIGKEYLEN(pagep);
2N/A
2N/A /* Set pagep to the next page in the chain. */
2N/A if (last_page)
2N/A *last_page = ADDR(pagep);
2N/A next_pgno = NEXT_PGNO(pagep);
2N/A next_pagep = __get_page(hashp, next_pgno, A_RAW);
2N/A if (!next_pagep)
2N/A return (-1);
2N/A#ifdef DEBUG
2N/A save_addr = ADDR(pagep);
2N/A#endif
2N/A retval = collect_key(hashp, next_pagep, totlen, last_page);
2N/A
2N/A#ifdef DEBUG
2N/A assert(save_addr == ADDR(pagep));
2N/A#endif
2N/A memcpy(hashp->bigkey_buf + len, BIGKEY(pagep), BIGKEYLEN(pagep));
2N/A __put_page(hashp, next_pagep, A_RAW, 0);
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Given a page with big data on it, recur through the pages counting data
2N/A * length, and collect all of the data on the way up. Store the data in
2N/A * hashp->bigdata_buf.
2N/A *
2N/A * Does the work for __big_return.
2N/A *
2N/A * Return total length of data; -1 if error.
2N/A */
2N/Astatic int32_t
2N/Acollect_data(hashp, pagep, len)
2N/A HTAB *hashp;
2N/A PAGE16 *pagep;
2N/A int32_t len;
2N/A{
2N/A PAGE16 *next_pagep;
2N/A int32_t totlen, retval;
2N/A db_pgno_t next_pgno;
2N/A#ifdef DEBUG
2N/A db_pgno_t save_addr;
2N/A#endif
2N/A
2N/A /* If there is no next page. */
2N/A if (NEXT_PGNO(pagep) == INVALID_PGNO) {
2N/A if (hashp->bigdata_buf)
2N/A free(hashp->bigdata_buf);
2N/A totlen = len + BIGDATALEN(pagep);
2N/A hashp->bigdata_buf = (u_int8_t *)malloc(totlen);
2N/A if (!hashp->bigdata_buf)
2N/A return (-1);
2N/A memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep),
2N/A BIGDATA(pagep), BIGDATALEN(pagep));
2N/A return (totlen);
2N/A }
2N/A totlen = len + BIGDATALEN(pagep);
2N/A
2N/A /* Set pagep to the next page in the chain. */
2N/A next_pgno = NEXT_PGNO(pagep);
2N/A next_pagep = __get_page(hashp, next_pgno, A_RAW);
2N/A if (!next_pagep)
2N/A return (-1);
2N/A
2N/A#ifdef DEBUG
2N/A save_addr = ADDR(pagep);
2N/A#endif
2N/A retval = collect_data(hashp, next_pagep, totlen);
2N/A#ifdef DEBUG
2N/A assert(save_addr == ADDR(pagep));
2N/A#endif
2N/A memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep),
2N/A BIGDATA(pagep), BIGDATALEN(pagep));
2N/A __put_page(hashp, next_pagep, A_RAW, 0);
2N/A
2N/A return (retval);
2N/A}