From 38e876d964346d3e4b88c52732b7ce5966d796a5 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 1 Nov 2018 13:35:40 +0100 Subject: import original 1.85 implementation from openbsd libc --- _db1.85/hash/CVS/Entries | 12 + _db1.85/hash/CVS/Repository | 1 + _db1.85/hash/CVS/Tag | 1 + _db1.85/hash/README | 68 ++++ _db1.85/hash/extern.h | 62 +++ _db1.85/hash/hash.c | 953 ++++++++++++++++++++++++++++++++++++++++++++ _db1.85/hash/hash.h | 291 ++++++++++++++ _db1.85/hash/hash_bigkey.c | 639 +++++++++++++++++++++++++++++ _db1.85/hash/hash_buf.c | 357 +++++++++++++++++ _db1.85/hash/hash_func.c | 78 ++++ _db1.85/hash/hash_log2.c | 48 +++ _db1.85/hash/hash_page.c | 928 ++++++++++++++++++++++++++++++++++++++++++ _db1.85/hash/ndbm.c | 272 +++++++++++++ _db1.85/hash/page.h | 90 +++++ 14 files changed, 3800 insertions(+) create mode 100644 _db1.85/hash/CVS/Entries create mode 100644 _db1.85/hash/CVS/Repository create mode 100644 _db1.85/hash/CVS/Tag create mode 100644 _db1.85/hash/README create mode 100644 _db1.85/hash/extern.h create mode 100644 _db1.85/hash/hash.c create mode 100644 _db1.85/hash/hash.h create mode 100644 _db1.85/hash/hash_bigkey.c create mode 100644 _db1.85/hash/hash_buf.c create mode 100644 _db1.85/hash/hash_func.c create mode 100644 _db1.85/hash/hash_log2.c create mode 100644 _db1.85/hash/hash_page.c create mode 100644 _db1.85/hash/ndbm.c create mode 100644 _db1.85/hash/page.h (limited to '_db1.85/hash') diff --git a/_db1.85/hash/CVS/Entries b/_db1.85/hash/CVS/Entries new file mode 100644 index 0000000..70c9e91 --- /dev/null +++ b/_db1.85/hash/CVS/Entries @@ -0,0 +1,12 @@ +/README/1.4/Sun Feb 24 21:24:06 2002//TOPENBSD_6_4 +/extern.h/1.9/Sat Nov 12 14:28:26 2016//TOPENBSD_6_4 +/hash.c/1.29/Tue Apr 11 21:08:02 2017//TOPENBSD_6_4 +/hash.h/1.9/Mon Jun 21 23:13:22 2004//TOPENBSD_6_4 +/hash_bigkey.c/1.19/Mon Dec 28 22:08:18 2015//TOPENBSD_6_4 +/hash_buf.c/1.19/Fri Jan 16 16:48:51 2015//TOPENBSD_6_4 +/hash_func.c/1.11/Sat Nov 12 14:28:26 2016//TOPENBSD_6_4 +/hash_log2.c/1.8/Fri Aug 5 13:03:00 2005//TOPENBSD_6_4 +/hash_page.c/1.23/Tue Apr 11 21:08:02 2017//TOPENBSD_6_4 +/ndbm.c/1.26/Sat Nov 12 14:28:26 2016//TOPENBSD_6_4 +/page.h/1.6/Mon Jun 2 20:18:34 2003//TOPENBSD_6_4 +D diff --git a/_db1.85/hash/CVS/Repository b/_db1.85/hash/CVS/Repository new file mode 100644 index 0000000..9ec962b --- /dev/null +++ b/_db1.85/hash/CVS/Repository @@ -0,0 +1 @@ +src/lib/libc/db/hash diff --git a/_db1.85/hash/CVS/Tag b/_db1.85/hash/CVS/Tag new file mode 100644 index 0000000..b84d4de --- /dev/null +++ b/_db1.85/hash/CVS/Tag @@ -0,0 +1 @@ +TOPENBSD_6_4 diff --git a/_db1.85/hash/README b/_db1.85/hash/README new file mode 100644 index 0000000..3e382f2 --- /dev/null +++ b/_db1.85/hash/README @@ -0,0 +1,68 @@ +# $OpenBSD: README,v 1.4 2002/02/24 21:24:06 millert Exp $ + +This package implements a superset of the hsearch and dbm/ndbm libraries. + +Test Programs: + All test programs which need key/data pairs expect them entered + with key and data on separate lines + + tcreat3.c + Takes + bucketsize (bsize), + fill factor (ffactor), and + initial number of elements (nelem). + Creates a hash table named hashtest containing the + keys/data pairs entered from standard in. + thash4.c + Takes + bucketsize (bsize), + fill factor (ffactor), + initial number of elements (nelem) + bytes of cache (ncached), and + file from which to read data (fname) + Creates a table from the key/data pairs on standard in and + then does a read of each key/data in fname + tdel.c + Takes + bucketsize (bsize), and + fill factor (ffactor). + file from which to read data (fname) + Reads each key/data pair from fname and deletes the + key from the hash table hashtest + tseq.c + Reads the key/data pairs in the file hashtest and writes them + to standard out. + tread2.c + Takes + butes of cache (ncached). + Reads key/data pairs from standard in and looks them up + in the file hashtest. + tverify.c + Reads key/data pairs from standard in, looks them up + in the file hashtest, and verifies that the data is + correct. + +NOTES: + +The man page ../man/db.3 explains the interface to the hashing system. +The file hash.ps is a postscript copy of a paper explaining +the history, implementation, and performance of the hash package. + +"bugs" or idiosyncracies + +If you have a lot of overflows, it is possible to run out of overflow +pages. Currently, this will cause a message to be printed on stderr. +Eventually, this will be indicated by a return error code. + +If you are using the ndbm interface and exit without flushing or closing the +file, you may lose updates since the package buffers all writes. Also, +the db interface only creates a single database file. To avoid overwriting +the user's original file, the suffix ".db" is appended to the file name +passed to dbm_open. Additionally, if your code "knows" about the historic +.dir and .pag files, it will break. + +There is a fundamental difference between this package and the old hsearch. +Hsearch requires the user to maintain the keys and data in the application's +allocated memory while hash takes care of all storage management. The down +side is that the byte strings passed in the ENTRY structure must be null +terminated (both the keys and the data). diff --git a/_db1.85/hash/extern.h b/_db1.85/hash/extern.h new file mode 100644 index 0000000..ff9c4d1 --- /dev/null +++ b/_db1.85/hash/extern.h @@ -0,0 +1,62 @@ +/* $OpenBSD: extern.h,v 1.9 2016/05/29 20:47:49 guenther Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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. 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. + * + * @(#)extern.h 8.4 (Berkeley) 6/16/94 + */ + +__BEGIN_HIDDEN_DECLS +BUFHEAD *__add_ovflpage(HTAB *, BUFHEAD *); +int __addel(HTAB *, BUFHEAD *, const DBT *, const DBT *); +int __big_delete(HTAB *, BUFHEAD *); +int __big_insert(HTAB *, BUFHEAD *, const DBT *, const DBT *); +int __big_keydata(HTAB *, BUFHEAD *, DBT *, DBT *, int); +int __big_return(HTAB *, BUFHEAD *, int, DBT *, int); +int __big_split(HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *, + int, u_int32_t, SPLIT_RETURN *); +int __buf_free(HTAB *, int, int); +void __buf_init(HTAB *, int); +u_int32_t __call_hash(HTAB *, char *, int); +int __delpair(HTAB *, BUFHEAD *, int); +int __expand_table(HTAB *); +int __find_bigpair(HTAB *, BUFHEAD *, int, char *, int); +u_int16_t __find_last_page(HTAB *, BUFHEAD **); +void __free_ovflpage(HTAB *, BUFHEAD *); +BUFHEAD *__get_buf(HTAB *, u_int32_t, BUFHEAD *, int); +int __get_page(HTAB *, char *, u_int32_t, int, int, int); +int __ibitmap(HTAB *, int, int, int); +u_int32_t __log2(u_int32_t); +int __put_page(HTAB *, char *, u_int32_t, int, int); +void __reclaim_buf(HTAB *, BUFHEAD *); +int __split_page(HTAB *, u_int32_t, u_int32_t); + +#ifdef HASH_STATISTICS +extern int hash_accesses, hash_collisions, hash_expansions, hash_overflows; +#endif +__END_HIDDEN_DECLS diff --git a/_db1.85/hash/hash.c b/_db1.85/hash/hash.c new file mode 100644 index 0000000..2b1c223 --- /dev/null +++ b/_db1.85/hash/hash.c @@ -0,0 +1,953 @@ +/* $OpenBSD: hash.c,v 1.29 2016/09/21 04:38:56 guenther Exp $ */ + +/*- + * 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. 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) + +static int alloc_segs(HTAB *, int); +static int flush_meta(HTAB *); +static int hash_access(HTAB *, ACTION, DBT *, DBT *); +static int hash_close(DB *); +static int hash_delete(const DB *, const DBT *, u_int32_t); +static int hash_fd(const DB *); +static int hash_get(const DB *, const DBT *, DBT *, u_int32_t); +static int hash_put(const DB *, DBT *, const DBT *, u_int32_t); +static void *hash_realloc(SEGMENT **, int, int); +static int hash_seq(const DB *, DBT *, DBT *, u_int32_t); +static int hash_sync(const DB *, u_int32_t); +static int hdestroy(HTAB *); +static HTAB *init_hash(HTAB *, const char *, const HASHINFO *); +static int init_htab(HTAB *, int); +#if BYTE_ORDER == LITTLE_ENDIAN +static void swap_header(HTAB *); +static void swap_header_copy(HASHHDR *, HASHHDR *); +#endif + +/* Fast arithmetic, relying on powers of 2, */ +#define MOD(x, y) ((x) & ((y) - 1)) + +#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } + +/* Return values */ +#define SUCCESS (0) +#define ERROR (-1) +#define ABNORMAL (1) + +#ifdef HASH_STATISTICS +int hash_accesses, hash_collisions, hash_expansions, hash_overflows; +#endif + +/************************** INTERFACE ROUTINES ***************************/ +/* OPEN/CLOSE */ + +DB * +__hash_open(const char *file, int flags, int mode, + const HASHINFO *info, /* Special directives for create */ + int dflags) +{ + HTAB *hashp; + struct stat statbuf; + DB *dbp; + int bpages, hdrsize, new_table, nsegs, save_errno; + + if ((flags & O_ACCMODE) == O_WRONLY) { + errno = EINVAL; + return (NULL); + } + + if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) + return (NULL); + hashp->fp = -1; + + /* + * Even if user wants write only, we need to be able to read + * the actual file, so we need to open it read/write. But, the + * field in the hashp structure needs to be accurate so that + * we can check accesses. + */ + hashp->flags = flags; + + if (file) { + if ((hashp->fp = open(file, flags | O_CLOEXEC, mode)) == -1) + RETURN_ERROR(errno, error0); + new_table = fstat(hashp->fp, &statbuf) == 0 && + statbuf.st_size == 0 && (flags & O_ACCMODE) != O_RDONLY; + } else + new_table = 1; + + if (new_table) { + if (!(hashp = init_hash(hashp, file, info))) + RETURN_ERROR(errno, error1); + } else { + /* Table already exists */ + if (info && info->hash) + hashp->hash = info->hash; + else + hashp->hash = __default_hash; + + hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR)); +#if BYTE_ORDER == LITTLE_ENDIAN + swap_header(hashp); +#endif + if (hdrsize == -1) + RETURN_ERROR(errno, error1); + if (hdrsize != sizeof(HASHHDR)) + RETURN_ERROR(EFTYPE, error1); + /* Verify file type, versions and hash function */ + if (hashp->MAGIC != HASHMAGIC) + RETURN_ERROR(EFTYPE, error1); +#define OLDHASHVERSION 1 + if (hashp->VERSION != HASHVERSION && + hashp->VERSION != OLDHASHVERSION) + RETURN_ERROR(EFTYPE, error1); + if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) + RETURN_ERROR(EFTYPE, error1); + /* + * Figure out how many segments we need. Max_Bucket is the + * maximum bucket number, so the number of buckets is + * max_bucket + 1. + */ + nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / + hashp->SGSIZE; + if (alloc_segs(hashp, nsegs)) + /* + * If alloc_segs fails, table will have been destroyed + * and errno will have been set. + */ + return (NULL); + /* Read in bitmaps */ + bpages = (hashp->SPARES[hashp->OVFL_POINT] + + (hashp->BSIZE << BYTE_SHIFT) - 1) >> + (hashp->BSHIFT + BYTE_SHIFT); + + hashp->nmaps = bpages; + (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *)); + } + + /* Initialize Buffer Manager */ + if (info && info->cachesize) + __buf_init(hashp, info->cachesize); + else + __buf_init(hashp, DEF_BUFSIZE); + + hashp->new_file = new_table; + hashp->save_file = file && (hashp->flags & O_RDWR); + hashp->cbucket = -1; + if (!(dbp = (DB *)malloc(sizeof(DB)))) { + save_errno = errno; + hdestroy(hashp); + errno = save_errno; + return (NULL); + } + dbp->internal = hashp; + dbp->close = hash_close; + dbp->del = hash_delete; + dbp->fd = hash_fd; + dbp->get = hash_get; + dbp->put = hash_put; + dbp->seq = hash_seq; + dbp->sync = hash_sync; + dbp->type = DB_HASH; + +#ifdef DEBUG + (void)fprintf(stderr, +"%s\n%s%p\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", + "init_htab:", + "TABLE POINTER ", hashp, + "BUCKET SIZE ", hashp->BSIZE, + "BUCKET SHIFT ", hashp->BSHIFT, + "DIRECTORY SIZE ", hashp->DSIZE, + "SEGMENT SIZE ", hashp->SGSIZE, + "SEGMENT SHIFT ", hashp->SSHIFT, + "FILL FACTOR ", hashp->FFACTOR, + "MAX BUCKET ", hashp->MAX_BUCKET, + "OVFL POINT ", hashp->OVFL_POINT, + "LAST FREED ", hashp->LAST_FREED, + "HIGH MASK ", hashp->HIGH_MASK, + "LOW MASK ", hashp->LOW_MASK, + "NSEGS ", hashp->nsegs, + "NKEYS ", hashp->NKEYS); +#endif +#ifdef HASH_STATISTICS + hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; +#endif + return (dbp); + +error1: + if (hashp != NULL) + (void)close(hashp->fp); + +error0: + free(hashp); + errno = save_errno; + return (NULL); +} + +static int +hash_close(DB *dbp) +{ + HTAB *hashp; + int retval; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + retval = hdestroy(hashp); + free(dbp); + return (retval); +} + +static int +hash_fd(const DB *dbp) +{ + HTAB *hashp; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + if (hashp->fp == -1) { + errno = ENOENT; + return (-1); + } + return (hashp->fp); +} + +/************************** LOCAL CREATION ROUTINES **********************/ +static HTAB * +init_hash(HTAB *hashp, const char *file, const HASHINFO *info) +{ + struct stat statbuf; + int nelem; + + nelem = 1; + hashp->NKEYS = 0; + hashp->LORDER = BYTE_ORDER; + hashp->BSIZE = DEF_BUCKET_SIZE; + hashp->BSHIFT = DEF_BUCKET_SHIFT; + hashp->SGSIZE = DEF_SEGSIZE; + hashp->SSHIFT = DEF_SEGSIZE_SHIFT; + hashp->DSIZE = DEF_DIRSIZE; + hashp->FFACTOR = DEF_FFACTOR; + hashp->hash = __default_hash; + memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); + memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); + + /* Fix bucket size to be optimal for file system */ + if (file != NULL) { + if (stat(file, &statbuf)) + return (NULL); + hashp->BSIZE = statbuf.st_blksize; + hashp->BSHIFT = __log2(hashp->BSIZE); + } + + if (info) { + if (info->bsize) { + /* Round pagesize up to power of 2 */ + hashp->BSHIFT = __log2(info->bsize); + hashp->BSIZE = 1 << hashp->BSHIFT; + if (hashp->BSIZE > MAX_BSIZE) { + errno = EINVAL; + return (NULL); + } + } + if (info->ffactor) + hashp->FFACTOR = info->ffactor; + if (info->hash) + hashp->hash = info->hash; + if (info->nelem) + nelem = info->nelem; + if (info->lorder) { + if (info->lorder != BIG_ENDIAN && + info->lorder != LITTLE_ENDIAN) { + errno = EINVAL; + return (NULL); + } + hashp->LORDER = info->lorder; + } + } + /* init_htab should destroy the table and set errno if it fails */ + if (init_htab(hashp, nelem)) + return (NULL); + else + return (hashp); +} +/* + * This calls alloc_segs which may run out of memory. Alloc_segs will destroy + * the table and set errno, so we just pass the error information along. + * + * Returns 0 on No Error + */ +static int +init_htab(HTAB *hashp, int nelem) +{ + int nbuckets, nsegs, l2; + + /* + * Divide number of elements by the fill factor and determine a + * desired number of buckets. Allocate space for the next greater + * power of two number of buckets. + */ + nelem = (nelem - 1) / hashp->FFACTOR + 1; + + l2 = __log2(MAXIMUM(nelem, 2)); + nbuckets = 1 << l2; + + hashp->SPARES[l2] = l2 + 1; + hashp->SPARES[l2 + 1] = l2 + 1; + hashp->OVFL_POINT = l2; + hashp->LAST_FREED = 2; + + /* First bitmap page is at: splitpoint l2 page offset 1 */ + if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) + return (-1); + + hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; + hashp->HIGH_MASK = (nbuckets << 1) - 1; + hashp->HDRPAGES = ((MAXIMUM(sizeof(HASHHDR), MINHDRSIZE) - 1) >> + hashp->BSHIFT) + 1; + + nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; + nsegs = 1 << __log2(nsegs); + + if (nsegs > hashp->DSIZE) + hashp->DSIZE = nsegs; + return (alloc_segs(hashp, nsegs)); +} + +/********************** DESTROY/CLOSE ROUTINES ************************/ + +/* + * Flushes any changes to the file if necessary and destroys the hashp + * structure, freeing all allocated space. + */ +static int +hdestroy(HTAB *hashp) +{ + int i, save_errno; + + save_errno = 0; + +#ifdef HASH_STATISTICS + (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", + hash_accesses, hash_collisions); + (void)fprintf(stderr, "hdestroy: expansions %ld\n", + hash_expansions); + (void)fprintf(stderr, "hdestroy: overflows %ld\n", + hash_overflows); + (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", + hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); + + for (i = 0; i < NCACHED; i++) + (void)fprintf(stderr, + "spares[%d] = %d\n", i, hashp->SPARES[i]); +#endif + /* + * Call on buffer manager to free buffers, and if required, + * write them to disk. + */ + if (__buf_free(hashp, 1, hashp->save_file)) + save_errno = errno; + if (hashp->dir) { + free(*hashp->dir); /* Free initial segments */ + /* Free extra segments */ + while (hashp->exsegs--) + free(hashp->dir[--hashp->nsegs]); + free(hashp->dir); + } + if (flush_meta(hashp) && !save_errno) + save_errno = errno; + /* Free Bigmaps */ + for (i = 0; i < hashp->nmaps; i++) + free(hashp->mapp[i]); + free(hashp->tmp_key); + free(hashp->tmp_buf); + + if (hashp->fp != -1) + (void)close(hashp->fp); + + free(hashp); + + if (save_errno) { + errno = save_errno; + return (ERROR); + } + return (SUCCESS); +} +/* + * Write modified pages to disk + * + * Returns: + * 0 == OK + * -1 ERROR + */ +static int +hash_sync(const DB *dbp, u_int32_t flags) +{ + HTAB *hashp; + + if (flags != 0) { + errno = EINVAL; + return (ERROR); + } + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + if (!hashp->save_file) + return (0); + if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) + return (ERROR); + hashp->new_file = 0; + return (0); +} + +/* + * Returns: + * 0 == OK + * -1 indicates that errno should be set + */ +static int +flush_meta(HTAB *hashp) +{ + HASHHDR *whdrp; +#if BYTE_ORDER == LITTLE_ENDIAN + HASHHDR whdr; +#endif + int fp, i, wsize; + + if (!hashp->save_file) + return (0); + hashp->MAGIC = HASHMAGIC; + hashp->VERSION = HASHVERSION; + hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); + + fp = hashp->fp; + whdrp = &hashp->hdr; +#if BYTE_ORDER == LITTLE_ENDIAN + whdrp = &whdr; + swap_header_copy(&hashp->hdr, whdrp); +#endif + if ((wsize = pwrite(fp, whdrp, sizeof(HASHHDR), 0)) == -1) + return (-1); + else + if (wsize != sizeof(HASHHDR)) { + errno = EFTYPE; + hashp->err = errno; + return (-1); + } + for (i = 0; i < NCACHED; i++) + if (hashp->mapp[i]) + if (__put_page(hashp, (char *)hashp->mapp[i], + hashp->BITMAPS[i], 0, 1)) + return (-1); + return (0); +} + +/*******************************SEARCH ROUTINES *****************************/ +/* + * All the access routines return + * + * Returns: + * 0 on SUCCESS + * 1 to indicate an external ERROR (i.e. key not found, etc) + * -1 to indicate an internal ERROR (i.e. out of memory, etc) + */ +static int +hash_get(const DB *dbp, const DBT *key, DBT *data, u_int32_t flag) +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag) { + hashp->err = errno = EINVAL; + return (ERROR); + } + return (hash_access(hashp, HASH_GET, (DBT *)key, data)); +} + +static int +hash_put(const DB *dbp, DBT *key, const DBT *data, u_int32_t flag) +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_NOOVERWRITE) { + hashp->err = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->err = errno = EPERM; + return (ERROR); + } + return (hash_access(hashp, flag == R_NOOVERWRITE ? + HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); +} + +static int +hash_delete(const DB *dbp, const DBT *key, + u_int32_t flag) /* Ignored */ +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_CURSOR) { + hashp->err = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->err = errno = EPERM; + return (ERROR); + } + return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); +} + +/* + * 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); +} + +static int +hash_seq(const DB *dbp, DBT *key, DBT *data, u_int32_t flag) +{ + u_int32_t bucket; + BUFHEAD *bufp; + HTAB *hashp; + u_int16_t *bp, ndx; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_FIRST && flag != R_NEXT) { + hashp->err = errno = EINVAL; + return (ERROR); + } +#ifdef HASH_STATISTICS + hash_accesses++; +#endif + if ((hashp->cbucket < 0) || (flag == R_FIRST)) { + hashp->cbucket = 0; + hashp->cndx = 1; + hashp->cpage = NULL; + } + next_bucket: + for (bp = NULL; !bp || !bp[0]; ) { + if (!(bufp = hashp->cpage)) { + for (bucket = hashp->cbucket; + bucket <= hashp->MAX_BUCKET; + bucket++, hashp->cndx = 1) { + bufp = __get_buf(hashp, bucket, NULL, 0); + if (!bufp) + return (ERROR); + hashp->cpage = bufp; + bp = (u_int16_t *)bufp->page; + if (bp[0]) + break; + } + hashp->cbucket = bucket; + if (hashp->cbucket > hashp->MAX_BUCKET) { + hashp->cbucket = -1; + return (ABNORMAL); + } + } else { + bp = (u_int16_t *)hashp->cpage->page; + if (flag == R_NEXT) { + hashp->cndx += 2; + if (hashp->cndx > bp[0]) { + hashp->cpage = NULL; + hashp->cbucket++; + hashp->cndx = 1; + goto next_bucket; + } + } + } + +#ifdef DEBUG + assert(bp); + assert(bufp); +#endif + while (bp[hashp->cndx + 1] == OVFLPAGE) { + bufp = hashp->cpage = + __get_buf(hashp, bp[hashp->cndx], bufp, 0); + if (!bufp) + return (ERROR); + bp = (u_int16_t *)(bufp->page); + hashp->cndx = 1; + } + if (!bp[0]) { + hashp->cpage = NULL; + ++hashp->cbucket; + } + } + ndx = hashp->cndx; + if (bp[ndx + 1] < REAL_KEY) { + if (__big_keydata(hashp, bufp, key, data, 1)) + return (ERROR); + } else { + if (hashp->cpage == 0) + return (ERROR); + key->data = (u_char *)hashp->cpage->page + bp[ndx]; + key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; + data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; + data->size = bp[ndx] - bp[ndx + 1]; + } + return (SUCCESS); +} + +/********************************* UTILITIES ************************/ + +/* + * Returns: + * 0 ==> OK + * -1 ==> Error + */ +int +__expand_table(HTAB *hashp) +{ + u_int32_t old_bucket, new_bucket; + int dirsize, new_segnum, spare_ndx; + +#ifdef HASH_STATISTICS + hash_expansions++; +#endif + new_bucket = ++hashp->MAX_BUCKET; + old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); + + new_segnum = new_bucket >> hashp->SSHIFT; + + /* Check if we need a new segment */ + if (new_segnum >= hashp->nsegs) { + /* Check if we need to expand directory */ + if (new_segnum >= hashp->DSIZE) { + /* Reallocate directory */ + dirsize = hashp->DSIZE * sizeof(SEGMENT *); + if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) + return (-1); + hashp->DSIZE = dirsize << 1; + } + if ((hashp->dir[new_segnum] = + (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL) + return (-1); + hashp->exsegs++; + hashp->nsegs++; + } + /* + * If the split point is increasing (MAX_BUCKET's log base 2 + * * increases), we need to copy the current contents of the spare + * split bucket to the next bucket. + */ + spare_ndx = __log2(hashp->MAX_BUCKET + 1); + if (spare_ndx > hashp->OVFL_POINT) { + hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; + hashp->OVFL_POINT = spare_ndx; + } + + if (new_bucket > hashp->HIGH_MASK) { + /* Starting a new doubling */ + hashp->LOW_MASK = hashp->HIGH_MASK; + hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; + } + /* Relocate records to the new bucket */ + return (__split_page(hashp, old_bucket, new_bucket)); +} + +/* + * If realloc guarantees that the pointer is not destroyed if the realloc + * fails, then this routine can go away. + */ +static void * +hash_realloc(SEGMENT **p_ptr, int oldsize, int newsize) +{ + void *p; + + if ((p = malloc(newsize))) { + memmove(p, *p_ptr, oldsize); + memset((char *)p + oldsize, 0, newsize - oldsize); + free(*p_ptr); + *p_ptr = p; + } + return (p); +} + +u_int32_t +__call_hash(HTAB *hashp, char *k, int len) +{ + int n, bucket; + + n = hashp->hash(k, len); + bucket = n & hashp->HIGH_MASK; + if (bucket > hashp->MAX_BUCKET) + bucket = bucket & hashp->LOW_MASK; + return (bucket); +} + +/* + * Allocate segment table. On error, destroy the table and set errno. + * + * Returns 0 on success + */ +static int +alloc_segs(HTAB *hashp, int nsegs) +{ + int i; + SEGMENT store; + + int save_errno; + + if ((hashp->dir = + (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) { + save_errno = errno; + (void)hdestroy(hashp); + errno = save_errno; + return (-1); + } + hashp->nsegs = nsegs; + if (nsegs == 0) + return (0); + /* Allocate segments */ + if ((store = (SEGMENT)calloc(nsegs << hashp->SSHIFT, + sizeof(SEGMENT))) == NULL) { + save_errno = errno; + (void)hdestroy(hashp); + errno = save_errno; + return (-1); + } + for (i = 0; i < nsegs; i++) + hashp->dir[i] = &store[i << hashp->SSHIFT]; + return (0); +} + +#if BYTE_ORDER == LITTLE_ENDIAN +/* + * Hashp->hdr needs to be byteswapped. + */ +static void +swap_header_copy(HASHHDR *srcp, HASHHDR *destp) +{ + int i; + + P_32_COPY(srcp->magic, destp->magic); + P_32_COPY(srcp->version, destp->version); + P_32_COPY(srcp->lorder, destp->lorder); + P_32_COPY(srcp->bsize, destp->bsize); + P_32_COPY(srcp->bshift, destp->bshift); + P_32_COPY(srcp->dsize, destp->dsize); + P_32_COPY(srcp->ssize, destp->ssize); + P_32_COPY(srcp->sshift, destp->sshift); + P_32_COPY(srcp->ovfl_point, destp->ovfl_point); + P_32_COPY(srcp->last_freed, destp->last_freed); + P_32_COPY(srcp->max_bucket, destp->max_bucket); + P_32_COPY(srcp->high_mask, destp->high_mask); + P_32_COPY(srcp->low_mask, destp->low_mask); + P_32_COPY(srcp->ffactor, destp->ffactor); + P_32_COPY(srcp->nkeys, destp->nkeys); + P_32_COPY(srcp->hdrpages, destp->hdrpages); + P_32_COPY(srcp->h_charkey, destp->h_charkey); + for (i = 0; i < NCACHED; i++) { + P_32_COPY(srcp->spares[i], destp->spares[i]); + P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); + } +} + +static void +swap_header(HTAB *hashp) +{ + HASHHDR *hdrp; + int i; + + hdrp = &hashp->hdr; + + M_32_SWAP(hdrp->magic); + M_32_SWAP(hdrp->version); + M_32_SWAP(hdrp->lorder); + M_32_SWAP(hdrp->bsize); + M_32_SWAP(hdrp->bshift); + M_32_SWAP(hdrp->dsize); + M_32_SWAP(hdrp->ssize); + M_32_SWAP(hdrp->sshift); + M_32_SWAP(hdrp->ovfl_point); + M_32_SWAP(hdrp->last_freed); + M_32_SWAP(hdrp->max_bucket); + M_32_SWAP(hdrp->high_mask); + M_32_SWAP(hdrp->low_mask); + M_32_SWAP(hdrp->ffactor); + M_32_SWAP(hdrp->nkeys); + M_32_SWAP(hdrp->hdrpages); + M_32_SWAP(hdrp->h_charkey); + for (i = 0; i < NCACHED; i++) { + M_32_SWAP(hdrp->spares[i]); + M_16_SWAP(hdrp->bitmaps[i]); + } +} +#endif diff --git a/_db1.85/hash/hash.h b/_db1.85/hash/hash.h new file mode 100644 index 0000000..4486f6b --- /dev/null +++ b/_db1.85/hash/hash.h @@ -0,0 +1,291 @@ +/* $OpenBSD: hash.h,v 1.9 2004/06/21 23:13:22 marc Exp $ */ + +/*- + * 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. 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. + * + * @(#)hash.h 8.3 (Berkeley) 5/31/94 + */ + +/* Operations */ +typedef enum { + HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT +} ACTION; + +/* Buffer Management structures */ +typedef struct _bufhead BUFHEAD; + +struct _bufhead { + BUFHEAD *prev; /* LRU links */ + BUFHEAD *next; /* LRU links */ + BUFHEAD *ovfl; /* Overflow page buffer header */ + u_int32_t addr; /* Address of this page */ + char *page; /* Actual page data */ + char flags; +#define BUF_MOD 0x0001 +#define BUF_DISK 0x0002 +#define BUF_BUCKET 0x0004 +#define BUF_PIN 0x0008 +}; + +#define IS_BUCKET(X) ((X) & BUF_BUCKET) + +typedef BUFHEAD **SEGMENT; + +/* Hash Table Information */ +typedef struct hashhdr { /* Disk resident portion */ + int32_t magic; /* Magic NO for hash tables */ + int32_t version; /* Version ID */ + u_int32_t lorder; /* Byte Order */ + int32_t bsize; /* Bucket/Page Size */ + int32_t bshift; /* Bucket shift */ + int32_t dsize; /* Directory Size */ + int32_t ssize; /* Segment Size */ + int32_t sshift; /* Segment shift */ + int32_t ovfl_point; /* Where overflow pages are being + * allocated */ + int32_t last_freed; /* Last overflow page freed */ + int32_t max_bucket; /* ID of Maximum bucket in use */ + int32_t high_mask; /* Mask to modulo into entire table */ + int32_t low_mask; /* Mask to modulo into lower half of + * table */ + int32_t ffactor; /* Fill factor */ + int32_t nkeys; /* Number of keys in hash table */ + int32_t hdrpages; /* Size of table header */ + int32_t h_charkey; /* value of hash(CHARKEY) */ +#define NCACHED 32 /* number of bit maps and spare + * points */ + int32_t spares[NCACHED];/* spare pages for overflow */ + u_int16_t bitmaps[NCACHED]; /* address of overflow page + * bitmaps */ +} HASHHDR; + +typedef struct htab { /* Memory resident data structure */ + HASHHDR hdr; /* Header */ + int nsegs; /* Number of allocated segments */ + int exsegs; /* Number of extra allocated + * segments */ + u_int32_t /* Hash function */ + (*hash)(const void *, size_t); + int flags; /* Flag values */ + int fp; /* File pointer */ + char *tmp_buf; /* Temporary Buffer for BIG data */ + char *tmp_key; /* Temporary Buffer for BIG keys */ + BUFHEAD *cpage; /* Current page */ + int cbucket; /* Current bucket */ + int cndx; /* Index of next item on cpage */ + int err; /* Error Number -- for DBM + * compatibility */ + int new_file; /* Indicates if fd is backing store + * or no */ + int save_file; /* Indicates whether we need to flush + * file at + * exit */ + u_int32_t *mapp[NCACHED]; /* Pointers to page maps */ + int nmaps; /* Initial number of bitmaps */ + int nbufs; /* Number of buffers left to + * allocate */ + BUFHEAD bufhead; /* Header of buffer lru list */ + SEGMENT *dir; /* Hash Bucket directory */ +} HTAB; + +/* + * Constants + */ +#define MAX_BSIZE 65536 /* 2^16 */ +#define MIN_BUFFERS 6 +#define MINHDRSIZE 512 +#define DEF_BUFSIZE 65536 /* 64 K */ +#define DEF_BUCKET_SIZE 4096 +#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */ +#define DEF_SEGSIZE 256 +#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */ +#define DEF_DIRSIZE 256 +#define DEF_FFACTOR 65536 +#define MIN_FFACTOR 4 +#define SPLTMAX 8 +#define CHARKEY "%$sniglet^&" +#define NUMKEY 1038583 +#define BYTE_SHIFT 3 +#define INT_TO_BYTE 2 +#define INT_BYTE_SHIFT 5 +#define ALL_SET ((u_int32_t)0xFFFFFFFF) +#define ALL_CLEAR 0 + +#define PTROF(X) ((BUFHEAD *)((ptrdiff_t)(X)&~0x3)) +#define ISMOD(X) ((u_int32_t)(ptrdiff_t)(X)&0x1) +#define DOMOD(X) ((X) = (char *)((ptrdiff_t)(X)|0x1)) +#define ISDISK(X) ((u_int32_t)(ptrdiff_t)(X)&0x2) +#define DODISK(X) ((X) = (char *)((ptrdiff_t)(X)|0x2)) + +#define BITS_PER_MAP 32 + +/* Given the address of the beginning of a big map, clear/set the nth bit */ +#define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP))) +#define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP))) +#define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP))) + +/* Overflow management */ +/* + * Overflow page numbers are allocated per split point. At each doubling of + * the table, we can allocate extra pages. So, an overflow page number has + * the top 5 bits indicate which split point and the lower 11 bits indicate + * which page at that split point is indicated (pages within split points are + * numberered starting with 1). + */ + +#define SPLITSHIFT 11 +#define SPLITMASK 0x7FF +#define SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT) +#define OPAGENUM(N) ((N) & SPLITMASK) +#define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) + (O)) + +#define BUCKET_TO_PAGE(B) \ + (B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__log2((B)+1)-1] : 0) +#define OADDR_TO_PAGE(B) \ + BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B)); + +/* + * page.h contains a detailed description of the page format. + * + * Normally, keys and data are accessed from offset tables in the top of + * each page which point to the beginning of the key and data. There are + * four flag values which may be stored in these offset tables which indicate + * the following: + * + * + * OVFLPAGE Rather than a key data pair, this pair contains + * the address of an overflow page. The format of + * the pair is: + * OVERFLOW_PAGE_NUMBER OVFLPAGE + * + * PARTIAL_KEY This must be the first key/data pair on a page + * and implies that page contains only a partial key. + * That is, the key is too big to fit on a single page + * so it starts on this page and continues on the next. + * The format of the page is: + * KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE + * + * KEY_OFF -- offset of the beginning of the key + * PARTIAL_KEY -- 1 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * FULL_KEY This must be the first key/data pair on the page. It + * is used in two cases. + * + * Case 1: + * There is a complete key on the page but no data + * (because it wouldn't fit). The next page contains + * the data. + * + * Page format it: + * KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE + * + * KEY_OFF -- offset of the beginning of the key + * FULL_KEY -- 2 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * Case 2: + * This page contains no key, but part of a large + * data field, which is continued on the next page. + * + * Page format it: + * DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE + * + * KEY_OFF -- offset of the beginning of the data on + * this page + * FULL_KEY -- 2 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * FULL_KEY_DATA + * This must be the first key/data pair on the page. + * There are two cases: + * + * Case 1: + * This page contains a key and the beginning of the + * data field, but the data field is continued on the + * next page. + * + * Page format is: + * KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF + * + * KEY_OFF -- offset of the beginning of the key + * FULL_KEY_DATA -- 3 + * OVFL_PAGENO - page number of the next overflow page + * DATA_OFF -- offset of the beginning of the data + * + * Case 2: + * This page contains the last page of a big data pair. + * There is no key, only the tail end of the data + * on this page. + * + * Page format is: + * DATA_OFF FULL_KEY_DATA + * + * DATA_OFF -- offset of the beginning of the data on + * this page + * FULL_KEY_DATA -- 3 + * OVFL_PAGENO - page number of the next overflow page + * OVFLPAGE -- 0 + * + * OVFL_PAGENO and OVFLPAGE are optional (they are + * not present if there is no next page). + */ + +#define OVFLPAGE 0 +#define PARTIAL_KEY 1 +#define FULL_KEY 2 +#define FULL_KEY_DATA 3 +#define REAL_KEY 4 + +/* Short hands for accessing structure */ +#define BSIZE hdr.bsize +#define BSHIFT hdr.bshift +#define DSIZE hdr.dsize +#define SGSIZE hdr.ssize +#define SSHIFT hdr.sshift +#define LORDER hdr.lorder +#define OVFL_POINT hdr.ovfl_point +#define LAST_FREED hdr.last_freed +#define MAX_BUCKET hdr.max_bucket +#define FFACTOR hdr.ffactor +#define HIGH_MASK hdr.high_mask +#define LOW_MASK hdr.low_mask +#define NKEYS hdr.nkeys +#define HDRPAGES hdr.hdrpages +#define SPARES hdr.spares +#define BITMAPS hdr.bitmaps +#define VERSION hdr.version +#define MAGIC hdr.magic +#define NEXT_FREE hdr.next_free +#define H_CHARKEY hdr.h_charkey diff --git a/_db1.85/hash/hash_bigkey.c b/_db1.85/hash/hash_bigkey.c new file mode 100644 index 0000000..c981654 --- /dev/null +++ b/_db1.85/hash/hash_bigkey.c @@ -0,0 +1,639 @@ +/* $OpenBSD: hash_bigkey.c,v 1.19 2015/12/28 22:08:18 mmcc Exp $ */ + +/*- + * 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. 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. + */ + +/* + * PACKAGE: hash + * DESCRIPTION: + * Big key/data handling for the hashing package. + * + * ROUTINES: + * External + * __big_keydata + * __big_split + * __big_insert + * __big_return + * __big_delete + * __find_last_page + * Internal + * collect_key + * collect_data + */ + +#include +#include +#include +#include + +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +static int collect_key(HTAB *, BUFHEAD *, int, DBT *, int); +static int collect_data(HTAB *, BUFHEAD *, int, int); + +/* + * 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) +{ + u_int16_t *p; + int key_size, n, val_size; + u_int16_t space, move_bytes, off; + char *cp, *key_data, *val_data; + + cp = bufp->page; /* Character pointer of p. */ + p = (u_int16_t *)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 = MINIMUM(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) { + space = FREESPACE(p); + if (space) { + move_bytes = MINIMUM(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, 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 = (u_int16_t *)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 = MINIMUM(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 = (u_int16_t *)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 + */ +int +__big_delete(HTAB *hashp, BUFHEAD *bufp) +{ + BUFHEAD *last_bfp, *rbufp; + u_int16_t *bp, pageno; + int key_done, n; + + rbufp = bufp; + last_bfp = NULL; + bp = (u_int16_t *)bufp->page; + pageno = 0; + key_done = 0; + + 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 = __get_buf(hashp, pageno, rbufp, 0); + if (last_bfp) + __free_ovflpage(hashp, last_bfp); + last_bfp = rbufp; + if (!rbufp) + return (-1); /* Error. */ + bp = (u_int16_t *)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 = (u_int16_t *)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; + + bufp->flags |= BUF_MOD; + if (rbufp) + __free_ovflpage(hashp, rbufp); + if (last_bfp && last_bfp != rbufp) + __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 + */ +int +__find_bigpair(HTAB *hashp, BUFHEAD *bufp, int ndx, char *key, int size) +{ + u_int16_t *bp; + char *p; + int ksize; + u_int16_t bytes; + char *kkey; + + bp = (u_int16_t *)bufp->page; + p = bufp->page; + ksize = size; + kkey = key; + + for (bytes = hashp->BSIZE - bp[ndx]; + bytes <= size && bp[ndx + 1] == PARTIAL_KEY; + bytes = hashp->BSIZE - bp[ndx]) { + if (memcmp(p + bp[ndx], kkey, bytes)) + return (-2); + kkey += bytes; + ksize -= bytes; + bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0); + if (!bufp) + return (-3); + p = bufp->page; + bp = (u_int16_t *)p; + ndx = 1; + } + + if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) { +#ifdef HASH_STATISTICS + ++hash_collisions; +#endif + return (-2); + } else + return (ndx); +} + +/* + * 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) + */ +u_int16_t +__find_last_page(HTAB *hashp, BUFHEAD **bpp) +{ + BUFHEAD *bufp; + u_int16_t *bp, pageno; + int n; + + bufp = *bpp; + bp = (u_int16_t *)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; + + pageno = bp[n - 1]; + bufp = __get_buf(hashp, pageno, bufp, 0); + if (!bufp) + return (0); /* Need to indicate an error! */ + bp = (u_int16_t *)bufp->page; + } + + *bpp = bufp; + if (bp[0] > 2) + return (bp[3]); + else + return (0); +} + +/* + * Return the data for the key/data pair that begins on this page at this + * index (index should always be 1). + */ +int +__big_return(HTAB *hashp, BUFHEAD *bufp, int ndx, DBT *val, int set_current) +{ + BUFHEAD *save_p; + u_int16_t *bp, len, off, save_addr; + char *tp; + + bp = (u_int16_t *)bufp->page; + while (bp[ndx + 1] == PARTIAL_KEY) { + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_int16_t *)bufp->page; + ndx = 1; + } + + if (bp[ndx + 1] == FULL_KEY) { + bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_int16_t *)bufp->page; + save_p = bufp; + save_addr = save_p->addr; + off = bp[1]; + len = 0; + } else + if (!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 = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!bufp) + return (-1); + bp = (u_int16_t *)bufp->page; + } else { + /* The data is all on one page. */ + tp = (char *)bp; + off = bp[bp[0]]; + val->data = (u_char *)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 = __get_buf(hashp, + bp[bp[0] - 1], bufp, 0); + if (!hashp->cpage) + return (-1); + hashp->cndx = 1; + if (!((u_int16_t *) + hashp->cpage->page)[0]) { + hashp->cbucket++; + hashp->cpage = NULL; + } + } + } + return (0); + } + + val->size = (size_t)collect_data(hashp, bufp, (int)len, set_current); + 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 = (u_char *)hashp->tmp_buf; + return (0); +} +/* + * Count how big the total datasize is by recursing through the pages. Then + * allocate a buffer and copy the data as you recurse up. + */ +static int +collect_data(HTAB *hashp, BUFHEAD *bufp, int len, int set) +{ + u_int16_t *bp; + char *p; + BUFHEAD *xbp; + u_int16_t save_addr; + int mylen, totlen; + + p = bufp->page; + bp = (u_int16_t *)p; + mylen = hashp->BSIZE - bp[1]; + save_addr = bufp->addr; + + if (bp[2] == FULL_KEY_DATA) { /* End of Data */ + totlen = len + mylen; + free(hashp->tmp_buf); + if ((hashp->tmp_buf = (char *)malloc(totlen)) == NULL) + return (-1); + if (set) { + hashp->cndx = 1; + if (bp[0] == 2) { /* No more buckets in chain */ + hashp->cpage = NULL; + hashp->cbucket++; + } else { + hashp->cpage = + __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!hashp->cpage) + return (-1); + else if (!((u_int16_t *)hashp->cpage->page)[0]) { + hashp->cbucket++; + hashp->cpage = NULL; + } + } + } + } else { + xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); + if (!xbp || ((totlen = + collect_data(hashp, xbp, len + mylen, set)) < 1)) + return (-1); + } + if (bufp->addr != save_addr) { + errno = EINVAL; /* Out of buffers. */ + return (-1); + } + memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen); + return (totlen); +} + +/* + * Fill in the key and data for this big pair. + */ +int +__big_keydata(HTAB *hashp, BUFHEAD *bufp, DBT *key, DBT *val, int set) +{ + key->size = (size_t)collect_key(hashp, bufp, 0, val, set); + if (key->size == (size_t)-1) + return (-1); + key->data = (u_char *)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. + */ +static int +collect_key(HTAB *hashp, BUFHEAD *bufp, int len, DBT *val, int set) +{ + BUFHEAD *xbp; + char *p; + int mylen, totlen; + u_int16_t *bp, save_addr; + + p = bufp->page; + bp = (u_int16_t *)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. */ + free(hashp->tmp_key); + if ((hashp->tmp_key = (char *)malloc(totlen)) == NULL) + return (-1); + if (__big_return(hashp, bufp, 1, val, set)) + return (-1); + } else { + xbp = __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], mylen); + return (totlen); +} + +/* + * 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 */ + BUFHEAD *big_keyp, /* Pointer to first page containing the big key/data */ + int addr, /* Address of big_keyp */ + u_int32_t obucket, /* Old Bucket */ + SPLIT_RETURN *ret) +{ + BUFHEAD *bp, *tmpp; + DBT key, val; + u_int32_t change; + u_int16_t free_space, n, off, *tp; + + 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, key.size) != obucket); + + if ((ret->next_addr = __find_last_page(hashp, &big_keyp))) { + if (!(ret->nextp = + __get_buf(hashp, 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 */ +#ifdef DEBUG + assert(np->ovfl == NULL); +#endif + 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 = (u_int16_t *)tmpp->page; +#ifdef DEBUG + assert(FREESPACE(tp) >= OVFLSIZE); +#endif + n = tp[0]; + off = OFFSET(tp); + free_space = FREESPACE(tp); + tp[++n] = (u_int16_t)addr; + tp[++n] = OVFLPAGE; + tp[0] = n; + OFFSET(tp) = off; + FREESPACE(tp) = free_space - OVFLSIZE; + + /* + * 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 = (u_int16_t *)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 = __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); +} diff --git a/_db1.85/hash/hash_buf.c b/_db1.85/hash/hash_buf.c new file mode 100644 index 0000000..9645826 --- /dev/null +++ b/_db1.85/hash/hash_buf.c @@ -0,0 +1,357 @@ +/* $OpenBSD: hash_buf.c,v 1.19 2015/01/16 16:48:51 deraadt Exp $ */ + +/*- + * 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. 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. + */ + +/* + * PACKAGE: hash + * + * DESCRIPTION: + * Contains buffer management + * + * ROUTINES: + * External + * __buf_init + * __get_buf + * __buf_free + * __reclaim_buf + * Internal + * newbuf + */ + +#include +#include +#include +#include +#include + +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) + +static BUFHEAD *newbuf(HTAB *, u_int32_t, BUFHEAD *); + +/* Unlink B from its place in the lru */ +#define BUF_REMOVE(B) { \ + (B)->prev->next = (B)->next; \ + (B)->next->prev = (B)->prev; \ +} + +/* Insert B after P */ +#define BUF_INSERT(B, P) { \ + (B)->next = (P)->next; \ + (B)->prev = (P); \ + (P)->next = (B); \ + (B)->next->prev = (B); \ +} + +#define MRU hashp->bufhead.next +#define LRU hashp->bufhead.prev + +#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead) +#define LRU_INSERT(B) BUF_INSERT((B), LRU) + +/* + * We are looking for a buffer with address "addr". If prev_bp is NULL, then + * address is a bucket index. If prev_bp is not NULL, then it points to the + * page previous to an overflow page that we are trying to find. + * + * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer + * be valid. Therefore, you must always verify that its address matches the + * address you are seeking. + */ +BUFHEAD * +__get_buf(HTAB *hashp, u_int32_t addr, + BUFHEAD *prev_bp, /* If prev_bp set, indicates a new overflow page. */ + int newpage) +{ + BUFHEAD *bp; + u_int32_t is_disk_mask; + int is_disk, segment_ndx; + SEGMENT segp; + + is_disk = 0; + is_disk_mask = 0; + if (prev_bp) { + bp = prev_bp->ovfl; + if (!bp || (bp->addr != addr)) + bp = NULL; + if (!newpage) + is_disk = BUF_DISK; + } else { + /* Grab buffer out of directory */ + segment_ndx = addr & (hashp->SGSIZE - 1); + + /* valid segment ensured by __call_hash() */ + segp = hashp->dir[addr >> hashp->SSHIFT]; +#ifdef DEBUG + assert(segp != NULL); +#endif + bp = PTROF(segp[segment_ndx]); + is_disk_mask = ISDISK(segp[segment_ndx]); + is_disk = is_disk_mask || !hashp->new_file; + } + + if (!bp) { + bp = newbuf(hashp, addr, prev_bp); + if (!bp || + __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0)) + return (NULL); + if (!prev_bp) + segp[segment_ndx] = + (BUFHEAD *)((ptrdiff_t)bp | is_disk_mask); + } else { + BUF_REMOVE(bp); + MRU_INSERT(bp); + } + return (bp); +} + +/* + * We need a buffer for this page. Either allocate one, or evict a resident + * one (if we have as many buffers as we're allowed) and put this one in. + * + * If newbuf finds an error (returning NULL), it also sets errno. + */ +static BUFHEAD * +newbuf(HTAB *hashp, u_int32_t addr, BUFHEAD *prev_bp) +{ + BUFHEAD *bp; /* The buffer we're going to use */ + BUFHEAD *xbp; /* Temp pointer */ + BUFHEAD *next_xbp; + SEGMENT segp; + int segment_ndx; + u_int16_t oaddr, *shortp; + + oaddr = 0; + bp = LRU; + + /* It is bad to overwrite the page under the cursor. */ + if (bp == hashp->cpage) { + BUF_REMOVE(bp); + MRU_INSERT(bp); + bp = LRU; + } + + /* If prev_bp is part of bp overflow, create a new buffer. */ + if (hashp->nbufs == 0 && prev_bp && bp->ovfl) { + BUFHEAD *ovfl; + + for (ovfl = bp->ovfl; ovfl ; ovfl = ovfl->ovfl) { + if (ovfl == prev_bp) { + hashp->nbufs++; + break; + } + } + } + + /* + * If LRU buffer is pinned, the buffer pool is too small. We need to + * allocate more buffers. + */ + if (hashp->nbufs || (bp->flags & BUF_PIN) || bp == hashp->cpage) { + /* Allocate a new one */ + if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL) + return (NULL); + memset(bp, 0xff, sizeof(BUFHEAD)); + if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) { + free(bp); + return (NULL); + } + memset(bp->page, 0xff, hashp->BSIZE); + if (hashp->nbufs) + hashp->nbufs--; + } else { + /* Kick someone out */ + BUF_REMOVE(bp); + /* + * If this is an overflow page with addr 0, it's already been + * flushed back in an overflow chain and initialized. + */ + if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) { + /* + * Set oaddr before __put_page so that you get it + * before bytes are swapped. + */ + shortp = (u_int16_t *)bp->page; + if (shortp[0]) + oaddr = shortp[shortp[0] - 1]; + if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page, + bp->addr, (int)IS_BUCKET(bp->flags), 0)) + return (NULL); + /* + * Update the pointer to this page (i.e. invalidate it). + * + * If this is a new file (i.e. we created it at open + * time), make sure that we mark pages which have been + * written to disk so we retrieve them from disk later, + * rather than allocating new pages. + */ + if (IS_BUCKET(bp->flags)) { + segment_ndx = bp->addr & (hashp->SGSIZE - 1); + segp = hashp->dir[bp->addr >> hashp->SSHIFT]; +#ifdef DEBUG + assert(segp != NULL); +#endif + + if (hashp->new_file && + ((bp->flags & BUF_MOD) || + ISDISK(segp[segment_ndx]))) + segp[segment_ndx] = (BUFHEAD *)BUF_DISK; + else + segp[segment_ndx] = NULL; + } + /* + * Since overflow pages can only be access by means of + * their bucket, free overflow pages associated with + * this bucket. + */ + for (xbp = bp; xbp->ovfl;) { + next_xbp = xbp->ovfl; + xbp->ovfl = 0; + xbp = next_xbp; + + /* Check that ovfl pointer is up date. */ + if (IS_BUCKET(xbp->flags) || + (oaddr != xbp->addr)) + break; + + shortp = (u_int16_t *)xbp->page; + if (shortp[0]) + /* set before __put_page */ + oaddr = shortp[shortp[0] - 1]; + if ((xbp->flags & BUF_MOD) && __put_page(hashp, + xbp->page, xbp->addr, 0, 0)) + return (NULL); + xbp->addr = 0; + xbp->flags = 0; + BUF_REMOVE(xbp); + LRU_INSERT(xbp); + } + } + } + + /* Now assign this buffer */ + bp->addr = addr; +#ifdef DEBUG1 + (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", + bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0); +#endif + bp->ovfl = NULL; + if (prev_bp) { + /* + * If prev_bp is set, this is an overflow page, hook it in to + * the buffer overflow links. + */ +#ifdef DEBUG1 + (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", + prev_bp->addr, (prev_bp->ovfl ? prev_bp->ovfl->addr : 0), + (bp ? bp->addr : 0)); +#endif + prev_bp->ovfl = bp; + bp->flags = 0; + } else + bp->flags = BUF_BUCKET; + MRU_INSERT(bp); + return (bp); +} + +void +__buf_init(HTAB *hashp, int nbytes) +{ + BUFHEAD *bfp; + int npages; + + bfp = &(hashp->bufhead); + npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; + npages = MAXIMUM(npages, MIN_BUFFERS); + + hashp->nbufs = npages; + bfp->next = bfp; + bfp->prev = bfp; + /* + * This space is calloc'd so these are already null. + * + * bfp->ovfl = NULL; + * bfp->flags = 0; + * bfp->page = NULL; + * bfp->addr = 0; + */ +} + +int +__buf_free(HTAB *hashp, int do_free, int to_disk) +{ + BUFHEAD *bp; + + /* Need to make sure that buffer manager has been initialized */ + if (!LRU) + return (0); + for (bp = LRU; bp != &hashp->bufhead;) { + /* Check that the buffer is valid */ + if (bp->addr || IS_BUCKET(bp->flags)) { + if (to_disk && (bp->flags & BUF_MOD) && + __put_page(hashp, bp->page, + bp->addr, IS_BUCKET(bp->flags), 0)) + return (-1); + } + /* Check if we are freeing stuff */ + if (do_free) { + if (bp->page) { + (void)memset(bp->page, 0, hashp->BSIZE); + free(bp->page); + } + BUF_REMOVE(bp); + free(bp); + bp = LRU; + } else + bp = bp->prev; + } + return (0); +} + +void +__reclaim_buf(HTAB *hashp, BUFHEAD *bp) +{ + bp->ovfl = 0; + bp->addr = 0; + bp->flags = 0; + BUF_REMOVE(bp); + LRU_INSERT(bp); +} diff --git a/_db1.85/hash/hash_func.c b/_db1.85/hash/hash_func.c new file mode 100644 index 0000000..6d96a73 --- /dev/null +++ b/_db1.85/hash/hash_func.c @@ -0,0 +1,78 @@ +/* $OpenBSD: hash_func.c,v 1.11 2016/05/29 20:47:49 guenther Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * 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. 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. + */ + +#include + +#include + +/* Chris Torek's hash function. */ +u_int32_t +__default_hash(const void *key, size_t len) +{ + u_int32_t h, loop; + u_int8_t *k; + +#define HASH4a h = (h << 5) - h + *k++; +#define HASH4b h = (h << 5) + h + *k++; +#define HASH4 HASH4b + + h = 0; + k = (u_int8_t *)key; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASH4; + case 7: + HASH4; + case 6: + HASH4; + case 5: + HASH4; + case 4: + HASH4; + case 3: + HASH4; + case 2: + HASH4; + case 1: + HASH4; + } while (--loop); + } + + } + return (h); +} diff --git a/_db1.85/hash/hash_log2.c b/_db1.85/hash/hash_log2.c new file mode 100644 index 0000000..533e2cd --- /dev/null +++ b/_db1.85/hash/hash_log2.c @@ -0,0 +1,48 @@ +/* $OpenBSD: hash_log2.c,v 1.8 2005/08/05 13:03:00 espie Exp $ */ + +/*- + * 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. 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. + */ + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +u_int32_t +__log2(u_int32_t num) +{ + u_int32_t i, limit; + + limit = 1; + for (i = 0; limit < num; limit = limit << 1, i++); + return (i); +} diff --git a/_db1.85/hash/hash_page.c b/_db1.85/hash/hash_page.c new file mode 100644 index 0000000..8e273db --- /dev/null +++ b/_db1.85/hash/hash_page.c @@ -0,0 +1,928 @@ +/* $OpenBSD: hash_page.c,v 1.23 2016/12/18 17:07:58 krw Exp $ */ + +/*- + * 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. 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. + */ + +/* + * PACKAGE: hashing + * + * DESCRIPTION: + * Page manipulation for hashing package. + * + * ROUTINES: + * + * External + * __get_page + * __add_ovflpage + * Internal + * overflow_page + * open_temp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include "hash.h" +#include "page.h" +#include "extern.h" + +static u_int32_t *fetch_bitmap(HTAB *, int); +static u_int32_t first_free(u_int32_t); +static int open_temp(HTAB *); +static u_int16_t overflow_page(HTAB *); +static void putpair(char *, const DBT *, const DBT *); +static void squeeze_key(u_int16_t *, const DBT *, const DBT *); +static int ugly_split(HTAB *, u_int32_t, BUFHEAD *, BUFHEAD *, int, int); + +#define PAGE_INIT(P) { \ + ((u_int16_t *)(P))[0] = 0; \ + ((u_int16_t *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_int16_t); \ + ((u_int16_t *)(P))[2] = hashp->BSIZE; \ +} + +/* + * This is called AFTER we have verified that there is room on the page for + * the pair (PAIRFITS has returned true) so we go right ahead and start moving + * stuff on. + */ +static void +putpair(char *p, const DBT *key, const DBT *val) +{ + u_int16_t *bp, n, off; + + bp = (u_int16_t *)p; + + /* Enter the key first. */ + n = bp[0]; + + off = OFFSET(bp) - key->size; + memmove(p + off, key->data, key->size); + bp[++n] = off; + + /* Now the data. */ + off -= val->size; + memmove(p + off, val->data, val->size); + bp[++n] = off; + + /* Adjust page info. */ + bp[0] = n; + bp[n + 1] = off - ((n + 3) * sizeof(u_int16_t)); + bp[n + 2] = off; +} + +/* + * Returns: + * 0 OK + * -1 error + */ +int +__delpair(HTAB *hashp, BUFHEAD *bufp, int ndx) +{ + u_int16_t *bp, newoff, pairlen; + int n; + + bp = (u_int16_t *)bufp->page; + n = bp[0]; + + if (bp[ndx + 1] < REAL_KEY) + return (__big_delete(hashp, bufp)); + if (ndx != 1) + newoff = bp[ndx - 1]; + else + newoff = hashp->BSIZE; + pairlen = newoff - bp[ndx + 1]; + + if (ndx != (n - 1)) { + /* Hard Case -- need to shuffle keys */ + int i; + char *src = bufp->page + (int)OFFSET(bp); + char *dst = src + (int)pairlen; + memmove(dst, src, bp[ndx + 1] - OFFSET(bp)); + + /* Now adjust the pointers */ + for (i = ndx + 2; i <= n; i += 2) { + if (bp[i + 1] == OVFLPAGE) { + bp[i - 2] = bp[i]; + bp[i - 1] = bp[i + 1]; + } else { + bp[i - 2] = bp[i] + pairlen; + bp[i - 1] = bp[i + 1] + pairlen; + } + } + if (ndx == hashp->cndx) { + /* + * We just removed pair we were "pointing" to. + * By moving back the cndx we ensure subsequent + * hash_seq() calls won't skip over any entries. + */ + hashp->cndx -= 2; + } + } + /* Finally adjust the page data */ + bp[n] = OFFSET(bp) + pairlen; + bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_int16_t); + bp[0] = n - 2; + hashp->NKEYS--; + + bufp->flags |= BUF_MOD; + return (0); +} +/* + * Returns: + * 0 ==> OK + * -1 ==> Error + */ +int +__split_page(HTAB *hashp, u_int32_t obucket, u_int32_t nbucket) +{ + BUFHEAD *new_bufp, *old_bufp; + u_int16_t *ino; + char *np; + DBT key, val; + int n, ndx, retval; + u_int16_t copyto, diff, off, moved; + char *op; + + copyto = (u_int16_t)hashp->BSIZE; + off = (u_int16_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 = (u_int16_t *)(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 = (u_char *)op + ino[n]; + key.size = off - ino[n]; + + if (__call_hash(hashp, 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 = (u_char *)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; + FREESPACE(ino) = copyto - sizeof(u_int16_t) * (ino[0] + 3); + OFFSET(ino) = copyto; + +#ifdef DEBUG3 + (void)fprintf(stderr, "split %d/%d\n", + ((u_int16_t *)np)[0] / 2, + ((u_int16_t *)op)[0] / 2); +#endif + /* unpin both pages */ + old_bufp->flags &= ~BUF_PIN; + new_bufp->flags &= ~BUF_PIN; + 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, + u_int32_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 */ + u_int16_t *ino; /* Page keys come off of */ + u_int16_t *np; /* New page */ + u_int16_t *op; /* Page keys go on to if they aren't moving */ + + BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ + DBT key, val; + SPLIT_RETURN ret; + u_int16_t n, off, ov_addr, scopyto; + char *cino; /* Character value of ino */ + + bufp = old_bufp; + ino = (u_int16_t *)old_bufp->page; + np = (u_int16_t *)new_bufp->page; + op = (u_int16_t *)old_bufp->page; + last_bfp = NULL; + scopyto = (u_int16_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, bufp->addr, obucket, &ret)) + return (-1); + old_bufp = ret.oldp; + if (!old_bufp) + return (-1); + op = (u_int16_t *)old_bufp->page; + new_bufp = ret.newp; + if (!new_bufp) + return (-1); + np = (u_int16_t *)new_bufp->page; + bufp = ret.nextp; + if (!bufp) + return (0); + cino = (char *)bufp->page; + ino = (u_int16_t *)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(u_int16_t) * (ino[0] + 3); + OFFSET(ino) = scopyto; + + bufp = __get_buf(hashp, ov_addr, bufp, 0); + if (!bufp) + return (-1); + + ino = (u_int16_t *)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 = (u_char *)cino + ino[n]; + key.size = off - ino[n]; + val.data = (u_char *)cino + ino[n + 1]; + val.size = ino[n] - ino[n + 1]; + off = ino[n + 1]; + + if (__call_hash(hashp, 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 = (u_int16_t *)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 = (u_int16_t *)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 + */ +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 if (bp[bp[0]] != OVFLPAGE) { + /* Short key/data pairs, no more pages */ + break; + } else { + /* Try to squeeze key on this page */ + if (bp[2] >= REAL_KEY && + FREESPACE(bp) >= PAIRSIZE(key, val)) { + squeeze_key(bp, key, val); + goto stats; + } 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); + } +stats: + 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: + * pointer on success + * NULL on error + */ +BUFHEAD * +__add_ovflpage(HTAB *hashp, BUFHEAD *bufp) +{ + u_int16_t *sp, ndx, ovfl_num; +#ifdef DEBUG1 + int tmp1, tmp2; +#endif + sp = (u_int16_t *)bufp->page; + + /* Check if we are dynamically determining the fill factor */ + if (hashp->FFACTOR == DEF_FFACTOR) { + hashp->FFACTOR = sp[0] >> 1; + if (hashp->FFACTOR < MIN_FFACTOR) + hashp->FFACTOR = MIN_FFACTOR; + } + bufp->flags |= BUF_MOD; + ovfl_num = overflow_page(hashp); +#ifdef DEBUG1 + tmp1 = bufp->addr; + tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; +#endif + if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1))) + return (NULL); + bufp->ovfl->flags |= BUF_MOD; +#ifdef DEBUG1 + (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", + tmp1, tmp2, bufp->ovfl->addr); +#endif + ndx = sp[0]; + /* + * Since a pair is allocated on a page only if there's room to add + * an overflow page, we know that the OVFL information will fit on + * the page. + */ + sp[ndx + 4] = OFFSET(sp); + sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; + sp[ndx + 1] = ovfl_num; + sp[ndx + 2] = OVFLPAGE; + sp[0] = ndx + 2; +#ifdef HASH_STATISTICS + hash_overflows++; +#endif + return (bufp->ovfl); +} + +/* + * Returns: + * 0 indicates SUCCESS + * -1 indicates FAILURE + */ +int +__get_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_disk, + int is_bitmap) +{ + int fd, page, size, rsize; + u_int16_t *bp; + + fd = hashp->fp; + size = hashp->BSIZE; + + if ((fd == -1) || !is_disk) { + PAGE_INIT(p); + return (0); + } + if (is_bucket) + page = BUCKET_TO_PAGE(bucket); + else + page = OADDR_TO_PAGE(bucket); + if ((rsize = pread(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) + return (-1); + bp = (u_int16_t *)p; + if (!rsize) + bp[0] = 0; /* We hit the EOF, so initialize a new page */ + else + if (rsize != size) { + errno = EFTYPE; + return (-1); + } + if (!is_bitmap && !bp[0]) { + PAGE_INIT(p); + } else + if (hashp->LORDER != BYTE_ORDER) { + int i, max; + + if (is_bitmap) { + max = hashp->BSIZE >> 2; /* divide by 4 */ + for (i = 0; i < max; i++) + M_32_SWAP(((int *)p)[i]); + } else { + M_16_SWAP(bp[0]); + max = bp[0] + 2; + for (i = 1; i <= max; i++) + M_16_SWAP(bp[i]); + } + } + return (0); +} + +/* + * Write page p to disk + * + * Returns: + * 0 ==> OK + * -1 ==>failure + */ +int +__put_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_bitmap) +{ + int fd, page, size, wsize; + + size = hashp->BSIZE; + if ((hashp->fp == -1) && open_temp(hashp)) + return (-1); + fd = hashp->fp; + + if (hashp->LORDER != BYTE_ORDER) { + int i, max; + + if (is_bitmap) { + max = hashp->BSIZE >> 2; /* divide by 4 */ + for (i = 0; i < max; i++) + M_32_SWAP(((int *)p)[i]); + } else { + max = ((u_int16_t *)p)[0] + 2; + for (i = 0; i <= max; i++) + M_16_SWAP(((u_int16_t *)p)[i]); + } + } + if (is_bucket) + page = BUCKET_TO_PAGE(bucket); + else + page = OADDR_TO_PAGE(bucket); + if ((wsize = pwrite(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) + /* Errno is set */ + return (-1); + if (wsize != size) { + errno = EFTYPE; + return (-1); + } + return (0); +} + +#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) +/* + * Initialize a new bitmap page. Bitmap pages are left in memory + * once they are read in. + */ +int +__ibitmap(HTAB *hashp, int pnum, int nbits, int ndx) +{ + u_int32_t *ip; + int clearbytes, clearints; + + if ((ip = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) + return (1); + hashp->nmaps++; + clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; + clearbytes = clearints << INT_TO_BYTE; + (void)memset((char *)ip, 0, clearbytes); + (void)memset(((char *)ip) + clearbytes, 0xFF, + hashp->BSIZE - clearbytes); + ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); + SETBIT(ip, 0); + hashp->BITMAPS[ndx] = (u_int16_t)pnum; + hashp->mapp[ndx] = ip; + return (0); +} + +static u_int32_t +first_free(u_int32_t map) +{ + u_int32_t i, mask; + + mask = 0x1; + for (i = 0; i < BITS_PER_MAP; i++) { + if (!(mask & map)) + return (i); + mask = mask << 1; + } + return (i); +} + +static u_int16_t +overflow_page(HTAB *hashp) +{ + u_int32_t *freep; + int max_free, offset, splitnum; + u_int16_t addr; + int bit, first_page, free_bit, free_page, i, in_use_bits, j; +#ifdef DEBUG2 + int tmp1, tmp2; +#endif + splitnum = hashp->OVFL_POINT; + max_free = hashp->SPARES[splitnum]; + + free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); + free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); + + /* Look through all the free maps to find the first free block */ + first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); + for ( i = first_page; i <= free_page; i++ ) { + if (!(freep = (u_int32_t *)hashp->mapp[i]) && + !(freep = fetch_bitmap(hashp, i))) + return (0); + if (i == free_page) + in_use_bits = free_bit; + else + in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; + + if (i == first_page) { + bit = hashp->LAST_FREED & + ((hashp->BSIZE << BYTE_SHIFT) - 1); + j = bit / BITS_PER_MAP; + bit = bit & ~(BITS_PER_MAP - 1); + } else { + bit = 0; + j = 0; + } + for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) + if (freep[j] != ALL_SET) + goto found; + } + + /* No Free Page Found */ + hashp->LAST_FREED = hashp->SPARES[splitnum]; + hashp->SPARES[splitnum]++; + offset = hashp->SPARES[splitnum] - + (splitnum ? hashp->SPARES[splitnum - 1] : 0); + +#define OVMSG "HASH: Out of overflow pages. Increase page size\n" + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + errno = EFBIG; + return (0); + } + hashp->OVFL_POINT = splitnum; + hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; + hashp->SPARES[splitnum-1]--; + offset = 1; + } + + /* Check if we need to allocate a new bitmap page */ + if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { + free_page++; + if (free_page >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + errno = EFBIG; + return (0); + } + /* + * This is tricky. The 1 indicates that you want the new page + * allocated with 1 clear bit. Actually, you are going to + * allocate 2 pages from this map. The first is going to be + * the map page, the second is the overflow page we were + * looking for. The init_bitmap routine automatically, sets + * the first bit of itself to indicate that the bitmap itself + * is in use. We would explicitly set the second bit, but + * don't have to if we tell init_bitmap not to leave it clear + * in the first place. + */ + if (__ibitmap(hashp, + (int)OADDR_OF(splitnum, offset), 1, free_page)) + return (0); + hashp->SPARES[splitnum]++; +#ifdef DEBUG2 + free_bit = 2; +#endif + offset++; + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, + sizeof(OVMSG) - 1); + errno = EFBIG; + return (0); + } + hashp->OVFL_POINT = splitnum; + hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; + hashp->SPARES[splitnum-1]--; + offset = 0; + } + } else { + /* + * Free_bit addresses the last used bit. Bump it to address + * the first available bit. + */ + free_bit++; + SETBIT(freep, free_bit); + } + + /* Calculate address of the new overflow page */ + addr = OADDR_OF(splitnum, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, free_bit, free_page); +#endif + return (addr); + +found: + bit = bit + first_free(freep[j]); + SETBIT(freep, bit); +#ifdef DEBUG2 + tmp1 = bit; + tmp2 = i; +#endif + /* + * Bits are addressed starting with 0, but overflow pages are addressed + * beginning at 1. Bit is a bit addressnumber, so we need to increment + * it to convert it to a page number. + */ + bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); + if (bit >= hashp->LAST_FREED) + hashp->LAST_FREED = bit - 1; + + /* Calculate the split number for this page */ + for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); + offset = (i ? bit - hashp->SPARES[i - 1] : bit); + if (offset >= SPLITMASK) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + errno = EFBIG; + return (0); /* Out of overflow pages */ + } + addr = OADDR_OF(i, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, tmp1, tmp2); +#endif + + /* Allocate and return the overflow page */ + return (addr); +} + +/* + * Mark this overflow page as free. + */ +void +__free_ovflpage(HTAB *hashp, BUFHEAD *obufp) +{ + u_int16_t addr; + u_int32_t *freep; + int bit_address, free_page, free_bit; + u_int16_t ndx; + + addr = obufp->addr; +#ifdef DEBUG1 + (void)fprintf(stderr, "Freeing %d\n", addr); +#endif + ndx = (((u_int16_t)addr) >> SPLITSHIFT); + bit_address = + (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; + if (bit_address < hashp->LAST_FREED) + hashp->LAST_FREED = bit_address; + free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); + free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); + + if (!(freep = hashp->mapp[free_page])) + freep = fetch_bitmap(hashp, free_page); +#ifdef DEBUG + /* + * This had better never happen. It means we tried to read a bitmap + * that has already had overflow pages allocated off it, and we + * failed to read it from the file. + */ + if (!freep) + assert(0); +#endif + CLRBIT(freep, free_bit); +#ifdef DEBUG2 + (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", + obufp->addr, free_bit, free_page); +#endif + __reclaim_buf(hashp, obufp); +} + +/* + * Returns: + * 0 success + * -1 failure + */ +static int +open_temp(HTAB *hashp) +{ + sigset_t set, oset; + int len; + char *envtmp = NULL; + char path[PATH_MAX]; + + if (issetugid() == 0) + envtmp = getenv("TMPDIR"); + len = snprintf(path, + sizeof(path), "%s/_hash.XXXXXX", envtmp ? envtmp : "/tmp"); + if (len < 0 || len >= sizeof(path)) { + errno = ENAMETOOLONG; + return (-1); + } + + /* Block signals; make sure file goes away at process exit. */ + (void)sigfillset(&set); + (void)sigprocmask(SIG_BLOCK, &set, &oset); + if ((hashp->fp = mkostemp(path, O_CLOEXEC)) != -1) { + (void)unlink(path); + } + (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); + return (hashp->fp != -1 ? 0 : -1); +} + +/* + * We have to know that the key will fit, but the last entry on the page is + * an overflow pair, so we need to shift things. + */ +static void +squeeze_key(u_int16_t *sp, const DBT *key, const DBT *val) +{ + char *p; + u_int16_t free_space, n, off, pageno; + + p = (char *)sp; + n = sp[0]; + free_space = FREESPACE(sp); + off = OFFSET(sp); + + pageno = sp[n - 1]; + off -= key->size; + sp[n - 1] = off; + memmove(p + off, key->data, key->size); + off -= val->size; + sp[n] = off; + memmove(p + off, val->data, val->size); + sp[0] = n + 2; + sp[n + 1] = pageno; + sp[n + 2] = OVFLPAGE; + FREESPACE(sp) = free_space - PAIRSIZE(key, val); + OFFSET(sp) = off; +} + +static u_int32_t * +fetch_bitmap(HTAB *hashp, int ndx) +{ + if (ndx >= hashp->nmaps) + return (NULL); + if ((hashp->mapp[ndx] = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) + return (NULL); + if (__get_page(hashp, + (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) { + free(hashp->mapp[ndx]); + return (NULL); + } + return (hashp->mapp[ndx]); +} + +#ifdef DEBUG4 +int +print_chain(int addr) +{ + BUFHEAD *bufp; + short *bp, oaddr; + + (void)fprintf(stderr, "%d ", addr); + bufp = __get_buf(hashp, addr, NULL, 0); + bp = (short *)bufp->page; + while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || + ((bp[0] > 2) && bp[2] < REAL_KEY))) { + oaddr = bp[bp[0] - 1]; + (void)fprintf(stderr, "%d ", (int)oaddr); + bufp = __get_buf(hashp, (int)oaddr, bufp, 0); + bp = (short *)bufp->page; + } + (void)fprintf(stderr, "\n"); +} +#endif diff --git a/_db1.85/hash/ndbm.c b/_db1.85/hash/ndbm.c new file mode 100644 index 0000000..01c521f --- /dev/null +++ b/_db1.85/hash/ndbm.c @@ -0,0 +1,272 @@ +/* $OpenBSD: ndbm.c,v 1.26 2016/05/07 21:58:06 tedu Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * 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. 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. + */ + +#include +#include +#include +#include +#include + +#include +#include "hash.h" + +/* + * + * This package provides dbm and ndbm compatible interfaces to DB. + * First are the DBM routines, which call the NDBM routines, and + * the NDBM routines, which call the DB routines. + */ +static DBM *__cur_db; + +static DBM *_dbm_open(const char *, const char *, int, mode_t); + +/* + * Returns: + * *DBM on success + * NULL on failure + */ +static DBM * +_dbm_open(file, suff, flags, mode) + const char *file; + const char *suff; + int flags; + mode_t mode; +{ + HASHINFO info; + char path[PATH_MAX]; + int len; + + len = snprintf(path, sizeof path, "%s%s", file, suff); + if (len < 0 || len >= sizeof path) { + errno = ENAMETOOLONG; + return (NULL); + } + /* O_WRONLY not supported by db(3) but traditional ndbm allowed it. */ + if ((flags & O_ACCMODE) == O_WRONLY) { + flags &= ~O_WRONLY; + flags |= O_RDWR; + } + info.bsize = 4096; + info.ffactor = 40; + info.nelem = 1; + info.cachesize = 0; + info.hash = NULL; + info.lorder = 0; + return ((DBM *)__hash_open(path, flags, mode, &info, 0)); +} + +/* + * Returns: + * *DBM on success + * NULL on failure + */ +DBM * +dbm_open(file, flags, mode) + const char *file; + int flags; + mode_t mode; +{ + + return(_dbm_open(file, DBM_SUFFIX, flags, mode)); +} + +/* + * Returns: + * Nothing. + */ +void +dbm_close(db) + DBM *db; +{ + + (void)(db->close)(db); +} +DEF_WEAK(dbm_close); + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_fetch(db, key) + DBM *db; + datum key; +{ + datum retdata; + int status; + DBT dbtkey, dbtretdata; + + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + status = (db->get)(db, &dbtkey, &dbtretdata, 0); + if (status) { + dbtretdata.data = NULL; + dbtretdata.size = 0; + } + retdata.dptr = dbtretdata.data; + retdata.dsize = dbtretdata.size; + return (retdata); +} +DEF_WEAK(dbm_fetch); + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_firstkey(db) + DBM *db; +{ + int status; + datum retkey; + DBT dbtretkey, dbtretdata; + + status = (db->seq)(db, &dbtretkey, &dbtretdata, R_FIRST); + if (status) + dbtretkey.data = NULL; + retkey.dptr = dbtretkey.data; + retkey.dsize = dbtretkey.size; + return (retkey); +} +DEF_WEAK(dbm_firstkey); + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_nextkey(db) + DBM *db; +{ + int status; + datum retkey; + DBT dbtretkey, dbtretdata; + + status = (db->seq)(db, &dbtretkey, &dbtretdata, R_NEXT); + if (status) + dbtretkey.data = NULL; + retkey.dptr = dbtretkey.data; + retkey.dsize = dbtretkey.size; + return (retkey); +} +DEF_WEAK(dbm_nextkey); + +/* + * Returns: + * 0 on success + * <0 on failure + */ +int +dbm_delete(db, key) + DBM *db; + datum key; +{ + int status; + DBT dbtkey; + + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + status = (db->del)(db, &dbtkey, 0); + if (status) + return (-1); + else + return (0); +} +DEF_WEAK(dbm_delete); + +/* + * Returns: + * 0 on success + * <0 on failure + * 1 if DBM_INSERT and entry exists + */ +int +dbm_store(db, key, data, flags) + DBM *db; + datum key, data; + int flags; +{ + DBT dbtkey, dbtdata; + + dbtkey.data = key.dptr; + dbtkey.size = key.dsize; + dbtdata.data = data.dptr; + dbtdata.size = data.dsize; + return ((db->put)(db, &dbtkey, &dbtdata, + (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); +} +DEF_WEAK(dbm_store); + +int +dbm_error(db) + DBM *db; +{ + HTAB *hp; + + hp = (HTAB *)db->internal; + return (hp->err); +} + +int +dbm_clearerr(db) + DBM *db; +{ + HTAB *hp; + + hp = (HTAB *)db->internal; + hp->err = 0; + return (0); +} + +int +dbm_dirfno(db) + DBM *db; +{ + + return(((HTAB *)db->internal)->fp); +} + +int +dbm_rdonly(dbp) + DBM *dbp; +{ + HTAB *hashp = (HTAB *)dbp->internal; + + /* Could use DBM_RDONLY instead if we wanted... */ + return ((hashp->flags & O_ACCMODE) == O_RDONLY); +} +DEF_WEAK(dbm_rdonly); diff --git a/_db1.85/hash/page.h b/_db1.85/hash/page.h new file mode 100644 index 0000000..b5b4f6a --- /dev/null +++ b/_db1.85/hash/page.h @@ -0,0 +1,90 @@ +/* $OpenBSD: page.h,v 1.6 2003/06/02 20:18:34 millert Exp $ */ + +/*- + * 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. 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. + * + * @(#)page.h 8.2 (Berkeley) 5/31/94 + */ + +/* + * Definitions for hashing page file format. + */ + +/* + * routines dealing with a data page + * + * page format: + * +------------------------------+ + * p | n | keyoff | datoff | keyoff | + * +------------+--------+--------+ + * | datoff | free | ptr | --> | + * +--------+---------------------+ + * | F R E E A R E A | + * +--------------+---------------+ + * | <---- - - - | data | + * +--------+-----+----+----------+ + * | key | data | key | + * +--------+----------+----------+ + * + * Pointer to the free space is always: p[p[0] + 2] + * Amount of free space on the page is: p[p[0] + 1] + */ + +/* + * How many bytes required for this pair? + * 2 shorts in the table at the top of the page + room for the + * key and room for the data + * + * We prohibit entering a pair on a page unless there is also room to append + * an overflow page. The reason for this it that you can get in a situation + * where a single key/data pair fits on a page, but you can't append an + * overflow page and later you'd have to split the key/data and handle like + * a big pair. + * You might as well do this up front. + */ + +#define PAIRSIZE(K,D) (2*sizeof(u_int16_t) + (K)->size + (D)->size) +#define BIGOVERHEAD (4*sizeof(u_int16_t)) +#define KEYSIZE(K) (4*sizeof(u_int16_t) + (K)->size); +#define OVFLSIZE (2*sizeof(u_int16_t)) +#define FREESPACE(P) ((P)[(P)[0]+1]) +#define OFFSET(P) ((P)[(P)[0]+2]) +#define PAIRFITS(P,K,D) \ + (((P)[2] >= REAL_KEY) && \ + (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P))) +#define PAGE_META(N) (((N)+3) * sizeof(u_int16_t)) + +typedef struct { + BUFHEAD *newp; + BUFHEAD *oldp; + BUFHEAD *nextp; + u_int16_t next_addr; +} SPLIT_RETURN; -- cgit v1.2.3