/*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. ***REMOVED*** - see * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE.
*/
#ifdefined(LIBC_SCCS) && !defined(lint) staticchar sccsid[] = "@(#)hash_bigkey.c 8.3 (Berkeley) 5/31/94"; #endif/* LIBC_SCCS and not lint */
/* * Big_insert * * You need to do an insert and the key/data pair is too big * * Returns: * 0 ==> OK *-1 ==> ERROR
*/ externint
dbm_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;
/* 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 = dbm_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);
}
/* * Called when bufp's page contains a partial key (index should be 1) * * All pages in the big key/data pair except bufp are freed. We cannot * free bufp because the page pointing to it is lost and we can't get rid * of its pointer. * * Returns: * 0 => OK *-1 => ERROR
*/ externint
dbm_big_delete(HTAB *hashp, BUFHEAD *bufp)
{ register BUFHEAD *last_bfp, *rbufp;
uint16 *bp, pageno; int key_done, n;
while (!key_done || (bp[2] != FULL_KEY_DATA)) { if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
key_done = 1;
/* * If there is freespace left on a FULL_KEY_DATA page, then * the data is short and fits entirely on this page, and this * is the last page.
*/ if (bp[2] == FULL_KEY_DATA && FREESPACE(bp)) break;
pageno = bp[bp[0] - 1];
rbufp->flags |= BUF_MOD;
rbufp = dbm_get_buf(hashp, pageno, rbufp, 0); if (last_bfp)
dbm_free_ovflpage(hashp, last_bfp);
last_bfp = rbufp; if (!rbufp) return (-1); /* Error. */
bp = (uint16 *)rbufp->page;
}
/* * If we get here then rbufp points to the last page of the big * key/data pair. Bufp points to the first one -- it should now be * empty pointing to the next page after this pair. Can't free it * because we don't have the page pointing to it.
*/
/* This is information from the last page of the pair. */
n = bp[0];
pageno = bp[n - 1];
/* Now, bp is the first page of the pair. */
bp = (uint16 *)bufp->page; if (n > 2) { /* There is an overflow page. */
bp[1] = pageno;
bp[2] = OVFLPAGE;
bufp->ovfl = rbufp->ovfl;
} else /* This is the last page. */
bufp->ovfl = NULL;
n -= 2;
bp[0] = n;
FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
OFFSET(bp) = hashp->BSIZE - 1;
bufp->flags |= BUF_MOD; if (rbufp)
dbm_free_ovflpage(hashp, rbufp); if (last_bfp != rbufp)
dbm_free_ovflpage(hashp, last_bfp);
hashp->NKEYS--; return (0);
} /* * Returns: * 0 = key not found * -1 = get next overflow page * -2 means key not found and this is big key/data * -3 error
*/ externint
dbm_find_bigpair(HTAB *hashp, BUFHEAD *bufp, int ndx, char *key, int size)
{ register uint16 *bp; registerchar *p; int ksize;
uint16 bytes; char *kkey;
bp = (uint16 *)bufp->page;
p = bufp->page;
ksize = size;
kkey = key;
/* * Given the buffer pointer of the first overflow page of a big pair, * find the end of the big pair * * This will set bpp to the buffer header of the last page of the big pair. * It will return the pageno of the overflow page following the last page * of the pair; 0 if there isn't any (i.e. big pair is the last key in the * bucket)
*/ extern uint16
dbm_find_last_page(HTAB *hashp, BUFHEAD **bpp)
{
BUFHEAD *bufp;
uint16 *bp, pageno;
uint n;
bufp = *bpp;
bp = (uint16 *)bufp->page; for (;;) {
n = bp[0];
/* * This is the last page if: the tag is FULL_KEY_DATA and * either only 2 entries OVFLPAGE marker is explicit there * is freespace on the page.
*/ if (bp[2] == FULL_KEY_DATA &&
((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp)))) break;
/* LJM bound the size of n to reasonable limits
*/ if (n > hashp->BSIZE / sizeof(uint16)) return (0);
pageno = bp[n - 1];
bufp = dbm_get_buf(hashp, pageno, bufp, 0); if (!bufp) return (0); /* Need to indicate an error! */
bp = (uint16 *)bufp->page;
}
/* * Return the data for the key/data pair that begins on this page at this * index (index should always be 1).
*/ externint
dbm_big_return(
HTAB *hashp,
BUFHEAD *bufp, int ndx,
DBT *val, int set_current)
{
BUFHEAD *save_p;
uint16 *bp, len, off, save_addr; char *tp; int save_flags;
bp = (uint16 *)bufp->page; while (bp[ndx + 1] == PARTIAL_KEY) {
bufp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1);
bp = (uint16 *)bufp->page;
ndx = 1;
}
if (bp[ndx + 1] == FULL_KEY) {
bufp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1);
bp = (uint16 *)bufp->page;
save_p = bufp;
save_addr = save_p->addr;
off = bp[1];
len = 0;
} elseif (!FREESPACE(bp)) { /* * This is a hack. We can't distinguish between * FULL_KEY_DATA that contains complete data or * incomplete data, so we require that if the data * is complete, there is at least 1 byte of free * space left.
*/
off = bp[bp[0]];
len = bp[1] - off;
save_p = bufp;
save_addr = bufp->addr;
bufp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1);
bp = (uint16 *)bufp->page;
} else { /* The data is all on one page. */
tp = (char *)bp;
off = bp[bp[0]];
val->data = (uint8 *)tp + off;
val->size = bp[1] - off; if (set_current) { if (bp[0] == 2) { /* No more buckets in
* chain */
hashp->cpage = NULL;
hashp->cbucket++;
hashp->cndx = 1;
} else {
hashp->cpage = dbm_get_buf(hashp,
bp[bp[0] - 1], bufp, 0); if (!hashp->cpage) return (-1);
hashp->cndx = 1; if (!((uint16 *)
hashp->cpage->page)[0]) {
hashp->cbucket++;
hashp->cpage = NULL;
}
}
} return (0);
}
/* pin our saved buf so that we don't lose if
* we run out of buffers */
save_flags = save_p->flags;
save_p->flags |= BUF_PIN;
val->size = collect_data(hashp, bufp, (int)len, set_current);
save_p->flags = save_flags; if (val->size == (size_t)-1) return (-1); if (save_p->addr != save_addr) { /* We are pretty short on buffers. */
errno = EINVAL; /* OUT OF BUFFERS */ return (-1);
}
memmove(hashp->tmp_buf, (save_p->page) + off, len);
val->data = (uint8 *)hashp->tmp_buf; return (0);
}
/* * Count how big the total datasize is by looping through the pages. Then * allocate a buffer and copy the data in the second loop. NOTE: Our caller * may already have a bp which it is holding onto. The caller is * responsible for copying that bp into our temp buffer. 'len' is how much * space to reserve for that buffer.
*/ staticint
collect_data(
HTAB *hashp,
BUFHEAD *bufp, int len, int set)
{ register uint16 *bp;
BUFHEAD *save_bufp; int save_flags; int mylen, totlen;
/* * save the input buf head because we need to walk the list twice. * pin it to make sure it doesn't leave the buffer pool. * This has the effect of growing the buffer pool if necessary.
*/
save_bufp = bufp;
save_flags = save_bufp->flags;
save_bufp->flags |= BUF_PIN;
/* read the length of the buffer */ for (totlen = len; bufp; bufp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0)) {
bp = (uint16 *)bufp->page;
mylen = hashp->BSIZE - bp[1];
/* if mylen ever goes negative it means that the * page is screwed up.
*/ if (mylen < 0) {
save_bufp->flags = save_flags; return (-1);
}
totlen += mylen; if (bp[2] == FULL_KEY_DATA) { /* End of Data */ break;
}
}
if (!bufp) {
save_bufp->flags = save_flags; return (-1);
}
/* allocate a temp buf */ if (hashp->tmp_buf)
free(hashp->tmp_buf); if ((hashp->tmp_buf = (char *)malloc((size_t)totlen)) == NULL) {
save_bufp->flags = save_flags; return (-1);
}
/* copy the buffers back into temp buf */ for (bufp = save_bufp; bufp;
bufp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0)) {
bp = (uint16 *)bufp->page;
mylen = hashp->BSIZE - bp[1];
memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], (size_t)mylen);
len += mylen; if (bp[2] == FULL_KEY_DATA) { break;
}
}
/* 'clear' the pin flags */
save_bufp->flags = save_flags;
/* update the database cursor */ if (set) {
hashp->cndx = 1; if (bp[0] == 2) { /* No more buckets in chain */
hashp->cpage = NULL;
hashp->cbucket++;
} else {
hashp->cpage = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!hashp->cpage) return (-1); elseif (!((uint16 *)hashp->cpage->page)[0]) {
hashp->cbucket++;
hashp->cpage = NULL;
}
}
} return (totlen);
}
/* * Fill in the key and data for this big pair.
*/ externint
dbm_big_keydata(
HTAB *hashp,
BUFHEAD *bufp,
DBT *key, DBT *val, int set)
{
key->size = collect_key(hashp, bufp, 0, val, set); if (key->size == (size_t)-1) return (-1);
key->data = (uint8 *)hashp->tmp_key; return (0);
}
/* * Count how big the total key size is by recursing through the pages. Then * collect the data, allocate a buffer and copy the key as you recurse up.
*/ staticint
collect_key(
HTAB *hashp,
BUFHEAD *bufp, int len,
DBT *val, int set)
{
BUFHEAD *xbp; char *p; int mylen, totlen;
uint16 *bp, save_addr;
p = bufp->page;
bp = (uint16 *)p;
mylen = hashp->BSIZE - bp[1];
save_addr = bufp->addr;
totlen = len + mylen; if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) { /* End of Key. */ if (hashp->tmp_key != NULL)
free(hashp->tmp_key); if ((hashp->tmp_key = (char *)malloc((size_t)totlen)) == NULL) return (-1); if (dbm_big_return(hashp, bufp, 1, val, set)) return (-1);
} else {
xbp = dbm_get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!xbp || ((totlen =
collect_key(hashp, xbp, totlen, val, set)) < 1)) return (-1);
} if (bufp->addr != save_addr) {
errno = EINVAL; /* MIS -- OUT OF BUFFERS */ return (-1);
}
memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], (size_t)mylen); return (totlen);
}
/* * Returns: * 0 => OK * -1 => error
*/ externint
dbm_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,
uint32 addr, /* Address of big_keyp */
uint32 obucket, /* Old Bucket */
SPLIT_RETURN *ret)
{ register BUFHEAD *tmpp; register uint16 *tp;
BUFHEAD *bp;
DBT key, val;
uint32 change;
uint16 free_space, n, off;
bp = big_keyp;
/* Now figure out where the big key/data goes */ if (dbm_big_keydata(hashp, big_keyp, &key, &val, 0)) return (-1);
change = (dbm_call_hash(hashp, (char *)key.data, key.size) != obucket);
/* * 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 *)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;
FREESPACE(tp) = free_space + OVFLSIZE;
OFFSET(tp) = off;
tmpp = dbm_add_ovflpage(hashp, big_keyp); if (!tmpp) return (-1);
tp[4] = n;
} else
tmpp = big_keyp;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.