/*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. ***REMOVED*** - see * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)hash.c 8.9 (Berkeley) 6/16/94"; #endif /* LIBC_SCCS and not lint */ #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) #include #endif #if !defined(macintosh) #ifdef XP_OS2 #include #endif #include #endif #if defined(macintosh) #include #include #endif #include #include #include #include #include #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) #include #endif #if defined(_WIN32) || defined(_WINDOWS) #include #endif #include #include "mcom_db.h" #include "hash.h" #include "page.h" /* #include "extern.h" */ 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 *, uint); static int hash_fd(const DB *); static int hash_get(const DB *, const DBT *, DBT *, uint); static int hash_put(const DB *, DBT *, const DBT *, uint); static void *hash_realloc(SEGMENT **, size_t, size_t); static int hash_seq(const DB *, DBT *, DBT *, uint); static int hash_sync(const DB *, uint); static int hdestroy(HTAB *); static HTAB *init_hash(HTAB *, const char *, 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 DBM_ERROR (-1) #define ABNORMAL (1) #ifdef HASH_STATISTICS int hash_accesses, hash_collisions, hash_expansions, hash_overflows; #endif /* A new Lou (montulli@mozilla.com) routine. * * The database is screwed. * * This closes the file, flushing buffers as appropriate. */ static void dbm_remove_database(DB *dbp) { HTAB *hashp = (HTAB *)dbp->internal; assert(0); if (!hashp) return; hdestroy(hashp); dbp->internal = NULL; } /************************** INTERFACE ROUTINES ***************************/ /* OPEN/CLOSE */ extern DB * dbm_hash_open(const char *file, int flags, int mode, const HASHINFO *info, int dflags) { HTAB *hashp = NULL; struct stat statbuf; DB *dbp; int bpages, hdrsize, new_table, nsegs, save_errno; if ((flags & O_ACCMODE) == O_WRONLY) { errno = EINVAL; return NULL; } /* zero the statbuffer so that * we can check it for a non-zero * date to see if stat succeeded */ memset(&statbuf, 0, sizeof(struct stat)); if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) { errno = ENOMEM; return NULL; } hashp->fp = NO_FILE; if (file) hashp->filename = strdup(file); /* * 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; new_table = 0; if (!file || (flags & O_TRUNC) || (stat(file, &statbuf) && (errno == ENOENT))) { if (errno == ENOENT) errno = 0; /* Just in case someone looks at errno */ new_table = 1; } else if (statbuf.st_mtime && statbuf.st_size == 0) { /* check for a zero length file and delete it * if it exists */ new_table = 1; } hashp->file_size = statbuf.st_size; if (file) { #if defined(_WIN32) || defined(_WINDOWS) || defined(macintosh) || defined(XP_OS2) if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1) RETURN_ERROR(errno, error1); #else if ((hashp->fp = open(file, flags, mode)) == -1) RETURN_ERROR(errno, error1); (void)fcntl(hashp->fp, F_SETFD, 1); #endif } if (new_table) { if (!init_hash(hashp, file, (HASHINFO *)info)) RETURN_ERROR(errno, error1); } else { /* Table already exists */ if (info && info->hash) hashp->hash = info->hash; else hashp->hash = dbm_default_hash; hdrsize = read(hashp->fp, (char *)&hashp->hdr, sizeof(HASHHDR)); if (hdrsize == -1) RETURN_ERROR(errno, error1); if (hdrsize != sizeof(HASHHDR)) RETURN_ERROR(EFTYPE, error1); #if BYTE_ORDER == LITTLE_ENDIAN swap_header(hashp); #endif /* 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); if (hashp->NKEYS < 0) /* Old bad database. */ 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; hashp->nsegs = 0; if (alloc_segs(hashp, nsegs)) /* If alloc_segs fails, errno will have been set. */ RETURN_ERROR(errno, error1); /* 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(uint32 *)); } /* Initialize Buffer Manager */ if (info && info->cachesize) dbm_buf_init(hashp, (int32)info->cachesize); else dbm_buf_init(hashp, DEF_BUFSIZE); hashp->new_file = new_table; #ifdef macintosh hashp->save_file = file && !(hashp->flags & O_RDONLY); #else hashp->save_file = file && (hashp->flags & O_RDWR); #endif hashp->cbucket = -1; if (!(dbp = (DB *)malloc(sizeof(DB)))) { RETURN_ERROR(ENOMEM, error1); } 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 HASH_STATISTICS hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; #endif return (dbp); error1: hdestroy(hashp); errno = save_errno; return (NULL); } static int hash_close(DB *dbp) { HTAB *hashp; int retval; if (!dbp) return (DBM_ERROR); hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); retval = hdestroy(hashp); free(dbp); return (retval); } static int hash_fd(const DB *dbp) { HTAB *hashp; if (!dbp) return (DBM_ERROR); hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (hashp->fp == -1) { errno = ENOENT; return (-1); } return (hashp->fp); } /************************** LOCAL CREATION ROUTINES **********************/ static HTAB * init_hash(HTAB *hashp, const char *file, 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 = dbm_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); #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2) #if defined(__QNX__) && !defined(__QNXNTO__) hashp->BSIZE = 512; /* preferred blk size on qnx4 */ #else hashp->BSIZE = statbuf.st_blksize; #endif /* new code added by Lou to reduce block * size down below MAX_BSIZE */ if (hashp->BSIZE > MAX_BSIZE) hashp->BSIZE = MAX_BSIZE; #endif hashp->BSHIFT = dbm_log2((uint32)hashp->BSIZE); } if (info) { if (info->bsize) { /* Round pagesize up to power of 2 */ hashp->BSHIFT = dbm_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 sets 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 * set errno, so we just pass the error information along. * * Returns 0 on No Error */ static int init_htab(HTAB *hashp, int nelem) { register int nbuckets, nsegs; int 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 = dbm_log2((uint32)PR_MAX(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 (dbm_ibitmap(hashp, (int)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 = ((PR_MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> hashp->BSHIFT) + 1; nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; nsegs = 1 << dbm_log2((uint32)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 (dbm_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++) if (hashp->mapp[i]) free(hashp->mapp[i]); if (hashp->fp != -1) (void)close(hashp->fp); if (hashp->filename) { #if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2) if (hashp->is_temp) (void)unlink(hashp->filename); #endif free(hashp->filename); } if (hashp->tmp_buf) free(hashp->tmp_buf); if (hashp->tmp_key) free(hashp->tmp_key); free(hashp); if (save_errno) { errno = save_errno; return (DBM_ERROR); } return (SUCCESS); } #if defined(_WIN32) || defined(_WINDOWS) /* * Close and reopen file to force file length update on windows. * * Returns: * 0 == OK * -1 DBM_ERROR */ static int update_EOF(HTAB *hashp) { #if defined(DBM_REOPEN_ON_FLUSH) char *file = hashp->filename; off_t file_size; int flags; int mode = -1; struct stat statbuf; memset(&statbuf, 0, sizeof statbuf); /* make sure we won't lose the file by closing it. */ if (!file || (stat(file, &statbuf) && (errno == ENOENT))) { /* pretend we did it. */ return 0; } (void)close(hashp->fp); flags = hashp->flags & ~(O_TRUNC | O_CREAT | O_EXCL); if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1) return -1; file_size = lseek(hashp->fp, (off_t)0, SEEK_END); if (file_size == -1) return -1; hashp->file_size = file_size; return 0; #else int fd = hashp->fp; off_t file_size = lseek(fd, (off_t)0, SEEK_END); HANDLE handle = (HANDLE)_get_osfhandle(fd); BOOL cool = FlushFileBuffers(handle); #ifdef DEBUG3 if (!cool) { DWORD err = GetLastError(); (void)fprintf(stderr, "FlushFileBuffers failed, last error = %d, 0x%08x\n", err, err); } #endif if (file_size == -1) return -1; hashp->file_size = file_size; return cool ? 0 : -1; #endif } #endif /* * Write modified pages to disk * * Returns: * 0 == OK * -1 DBM_ERROR */ static int hash_sync(const DB *dbp, uint flags) { HTAB *hashp; if (flags != 0) { errno = EINVAL; return (DBM_ERROR); } if (!dbp) return (DBM_ERROR); hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (!hashp->save_file) return (0); if (dbm_buf_free(hashp, 0, 1) || flush_meta(hashp)) return (DBM_ERROR); #if defined(_WIN32) || defined(_WINDOWS) if (hashp->updateEOF && hashp->filename && !hashp->is_temp) { int status = update_EOF(hashp); hashp->updateEOF = 0; if (status) return status; } #endif 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 ((lseek(fp, (off_t)0, SEEK_SET) == -1) || ((wsize = write(fp, (char *)whdrp, sizeof(HASHHDR))) == -1)) return (-1); else if (wsize != sizeof(HASHHDR)) { errno = EFTYPE; hashp->dbmerrno = errno; return (-1); } for (i = 0; i < NCACHED; i++) if (hashp->mapp[i]) if (dbm_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 DBM_ERROR (i.e. key not found, etc) * -1 to indicate an internal DBM_ERROR (i.e. out of memory, etc) */ static int hash_get( const DB *dbp, const DBT *key, DBT *data, uint flag) { HTAB *hashp; int rv; hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (flag) { hashp->dbmerrno = errno = EINVAL; return (DBM_ERROR); } rv = hash_access(hashp, HASH_GET, (DBT *)key, data); if (rv == DATABASE_CORRUPTED_ERROR) { #if defined(unix) && defined(DEBUG) printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); #endif dbm_remove_database((DB *)dbp); } return (rv); } static int hash_put( const DB *dbp, DBT *key, const DBT *data, uint flag) { HTAB *hashp; int rv; hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (flag && flag != R_NOOVERWRITE) { hashp->dbmerrno = errno = EINVAL; return (DBM_ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->dbmerrno = errno = EPERM; return (DBM_ERROR); } rv = hash_access(hashp, flag == R_NOOVERWRITE ? HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data); if (rv == DATABASE_CORRUPTED_ERROR) { #if defined(unix) && defined(DEBUG) printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); #endif dbm_remove_database((DB *)dbp); } return (rv); } static int hash_delete( const DB *dbp, const DBT *key, uint flag) /* Ignored */ { HTAB *hashp; int rv; hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (flag && flag != R_CURSOR) { hashp->dbmerrno = errno = EINVAL; return (DBM_ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->dbmerrno = errno = EPERM; return (DBM_ERROR); } rv = hash_access(hashp, HASH_DELETE, (DBT *)key, NULL); if (rv == DATABASE_CORRUPTED_ERROR) { #if defined(unix) && defined(DEBUG) printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); #endif dbm_remove_database((DB *)dbp); } return (rv); } #define MAX_OVERFLOW_HASH_ACCESS_LOOPS 2000 /* * Assume that hashp has been set in wrapper routine. */ static int hash_access( HTAB *hashp, ACTION action, DBT *key, DBT *val) { register BUFHEAD *rbufp; BUFHEAD *bufp, *save_bufp; register uint16 *bp; register long n, ndx, off; register size_t size; register char *kp; uint16 pageno; uint32 ovfl_loop_count = 0; int32 last_overflow_page_no = -1; #ifdef HASH_STATISTICS hash_accesses++; #endif off = hashp->BSIZE; size = key->size; kp = (char *)key->data; rbufp = dbm_get_buf(hashp, dbm_call_hash(hashp, kp, size), NULL, 0); if (!rbufp) return (DATABASE_CORRUPTED_ERROR); save_bufp = rbufp; /* Pin the bucket chain */ rbufp->flags |= BUF_PIN; for (bp = (uint16 *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) { if (bp[1] >= REAL_KEY) { /* Real key/data pair */ if (size == (unsigned long)(off - *bp) && memcmp(kp, rbufp->page + *bp, size) == 0) goto found; off = bp[1]; #ifdef HASH_STATISTICS hash_collisions++; #endif bp += 2; ndx += 2; } else if (bp[1] == OVFLPAGE) { /* database corruption: overflow loop detection */ if (last_overflow_page_no == (int32)*bp) return (DATABASE_CORRUPTED_ERROR); last_overflow_page_no = *bp; rbufp = dbm_get_buf(hashp, *bp, rbufp, 0); if (!rbufp) { save_bufp->flags &= ~BUF_PIN; return (DBM_ERROR); } ovfl_loop_count++; if (ovfl_loop_count > MAX_OVERFLOW_HASH_ACCESS_LOOPS) return (DATABASE_CORRUPTED_ERROR); /* FOR LOOP INIT */ bp = (uint16 *)rbufp->page; n = *bp++; ndx = 1; off = hashp->BSIZE; } else if (bp[1] < REAL_KEY) { if ((ndx = dbm_find_bigpair(hashp, rbufp, ndx, kp, (int)size)) > 0) goto found; if (ndx == -2) { bufp = rbufp; if (!(pageno = dbm_find_last_page(hashp, &bufp))) { ndx = 0; rbufp = bufp; break; /* FOR */ } rbufp = dbm_get_buf(hashp, pageno, bufp, 0); if (!rbufp) { save_bufp->flags &= ~BUF_PIN; return (DBM_ERROR); } /* FOR LOOP INIT */ bp = (uint16 *)rbufp->page; n = *bp++; ndx = 1; off = hashp->BSIZE; } else { save_bufp->flags &= ~BUF_PIN; return (DBM_ERROR); } } } /* Not found */ switch (action) { case HASH_PUT: case HASH_PUTNEW: if (dbm_addel(hashp, rbufp, key, val)) { save_bufp->flags &= ~BUF_PIN; return (DBM_ERROR); } else { save_bufp->flags &= ~BUF_PIN; return (SUCCESS); } case HASH_GET: case HASH_DELETE: default: save_bufp->flags &= ~BUF_PIN; return (ABNORMAL); } found: switch (action) { case HASH_PUTNEW: save_bufp->flags &= ~BUF_PIN; return (ABNORMAL); case HASH_GET: bp = (uint16 *)rbufp->page; if (bp[ndx + 1] < REAL_KEY) { if (dbm_big_return(hashp, rbufp, ndx, val, 0)) return (DBM_ERROR); } else { val->data = (uint8 *)rbufp->page + (int)bp[ndx + 1]; val->size = bp[ndx] - bp[ndx + 1]; } break; case HASH_PUT: if ((dbm_delpair(hashp, rbufp, ndx)) || (dbm_addel(hashp, rbufp, key, val))) { save_bufp->flags &= ~BUF_PIN; return (DBM_ERROR); } break; case HASH_DELETE: if (dbm_delpair(hashp, rbufp, ndx)) return (DBM_ERROR); break; default: abort(); } save_bufp->flags &= ~BUF_PIN; return (SUCCESS); } static int hash_seq( const DB *dbp, DBT *key, DBT *data, uint flag) { register uint32 bucket; register BUFHEAD *bufp = NULL; HTAB *hashp; uint16 *bp, ndx; hashp = (HTAB *)dbp->internal; if (!hashp) return (DBM_ERROR); if (flag && flag != R_FIRST && flag != R_NEXT) { hashp->dbmerrno = errno = EINVAL; return (DBM_ERROR); } #ifdef HASH_STATISTICS hash_accesses++; #endif if ((hashp->cbucket < 0) || (flag == R_FIRST)) { hashp->cbucket = 0; hashp->cndx = 1; hashp->cpage = NULL; } for (bp = NULL; !bp || !bp[0];) { if (!(bufp = hashp->cpage)) { for (bucket = hashp->cbucket; bucket <= (uint32)hashp->MAX_BUCKET; bucket++, hashp->cndx = 1) { bufp = dbm_get_buf(hashp, bucket, NULL, 0); if (!bufp) return (DBM_ERROR); hashp->cpage = bufp; bp = (uint16 *)bufp->page; if (bp[0]) break; } hashp->cbucket = bucket; if (hashp->cbucket > hashp->MAX_BUCKET) { hashp->cbucket = -1; return (ABNORMAL); } } else bp = (uint16 *)hashp->cpage->page; #ifdef DEBUG assert(bp); assert(bufp); #endif while (bp[hashp->cndx + 1] == OVFLPAGE) { bufp = hashp->cpage = dbm_get_buf(hashp, bp[hashp->cndx], bufp, 0); if (!bufp) return (DBM_ERROR); bp = (uint16 *)(bufp->page); hashp->cndx = 1; } if (!bp[0]) { hashp->cpage = NULL; ++hashp->cbucket; } } ndx = hashp->cndx; if (bp[ndx + 1] < REAL_KEY) { if (dbm_big_keydata(hashp, bufp, key, data, 1)) return (DBM_ERROR); } else { key->data = (uint8 *)hashp->cpage->page + bp[ndx]; key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; data->data = (uint8 *)hashp->cpage->page + bp[ndx + 1]; data->size = bp[ndx] - bp[ndx + 1]; ndx += 2; if (ndx > bp[0]) { hashp->cpage = NULL; hashp->cbucket++; hashp->cndx = 1; } else hashp->cndx = ndx; } return (SUCCESS); } /********************************* UTILITIES ************************/ /* * Returns: * 0 ==> OK * -1 ==> Error */ extern int dbm_expand_table(HTAB *hashp) { uint32 old_bucket, new_bucket; int new_segnum, spare_ndx; size_t dirsize; #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((size_t)hashp->SGSIZE, sizeof(BUFHEAD *))) == 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 = dbm_log2((uint32)(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 > (uint32)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 (dbm_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, size_t oldsize, size_t newsize) { register void *p; if ((p = malloc(newsize))) { memmove(p, *p_ptr, oldsize); memset((char *)p + oldsize, 0, newsize - oldsize); free(*p_ptr); *p_ptr = (SEGMENT *)p; } return (p); } extern uint32 dbm_call_hash(HTAB *hashp, char *k, size_t len) { uint32 n, bucket; n = hashp->hash(k, len); bucket = n & hashp->HIGH_MASK; if (bucket > (uint32)hashp->MAX_BUCKET) bucket = bucket & hashp->LOW_MASK; return (bucket); } /* * Allocate segment table. On error, set errno. * * Returns 0 on success */ static int alloc_segs( HTAB *hashp, int nsegs) { register int i; register SEGMENT store; if ((hashp->dir = (SEGMENT *)calloc((size_t)hashp->DSIZE, sizeof(SEGMENT))) == NULL) { errno = ENOMEM; return (-1); } /* Allocate segments */ if ((store = (SEGMENT)calloc((size_t)nsegs << hashp->SSHIFT, sizeof(BUFHEAD *))) == NULL) { errno = ENOMEM; return (-1); } for (i = 0; i < nsegs; i++, hashp->nsegs++) 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