Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/security/nss/lib/dbm/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 30 kB image not shown  

Quelle  hash.c   Sprache: C

 
/*-
 * 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 <sys/param.h>
#endif

#if !defined(macintosh)
#include <sys/stat.h>
#endif

#if defined(macintosh)
#include <unix.h>
#include <unistd.h>
#endif

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh)
#include <unistd.h>
#endif
#if defined(_WIN32) || defined(_WINDOWS)
#include <windows.h>
#endif

#include <assert.h>

#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)
        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)
#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)
        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

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.