1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* dynahash.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* dynamic hashing
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-07-16 07:23:30 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.26 1999/07/16 05:23:24 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
|
|
|
|
* Coded into C, with minor code improvements, and with hsearch(3) interface,
|
|
|
|
* by ejp@ausmelb.oz, Jul 26, 1988: 13:16;
|
|
|
|
* also, hcreate/hdestroy routines added to simulate hsearch(3).
|
|
|
|
*
|
|
|
|
* These routines simulate hsearch(3) and family, with the important
|
|
|
|
* difference that the hash table is dynamic - can grow indefinitely
|
|
|
|
* beyond its original size (as supplied to hcreate()).
|
|
|
|
*
|
|
|
|
* Performance appears to be comparable to that of hsearch(3).
|
|
|
|
* The 'source-code' options referred to in hsearch(3)'s 'man' page
|
|
|
|
* are not implemented; otherwise functionality is identical.
|
|
|
|
*
|
|
|
|
* Compilation controls:
|
|
|
|
* DEBUG controls some informative traces, mainly for debugging.
|
|
|
|
* HASH_STATISTICS causes HashAccesses and HashCollisions to be maintained;
|
|
|
|
* when combined with HASH_DEBUG, these are displayed by hdestroy().
|
|
|
|
*
|
|
|
|
* Problems & fixes to ejp@ausmelb.oz. WARNING: relies on pre-processor
|
|
|
|
* concatenation property, in probably unnecessary code 'optimisation'.
|
|
|
|
*
|
|
|
|
* Modified margo@postgres.berkeley.edu February 1990
|
1997-09-07 07:04:48 +02:00
|
|
|
* added multiple table interface
|
1996-07-09 08:22:35 +02:00
|
|
|
* Modified by sullivan@postgres.berkeley.edu April 1990
|
1997-09-07 07:04:48 +02:00
|
|
|
* changed ctl structure for shared memory
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-07-16 01:04:24 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "utils/dynahash.h"
|
|
|
|
#include "utils/hsearch.h"
|
|
|
|
#include "utils/memutils.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
1999-05-31 19:01:52 +02:00
|
|
|
* Fast MOD arithmetic, assuming that y is a power of 2 !
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define MOD(x,y) ((x) & ((y)-1))
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Private function prototypes
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static long *DynaHashAlloc(unsigned int size);
|
|
|
|
static void DynaHashFree(Pointer ptr);
|
1999-05-31 19:01:52 +02:00
|
|
|
static uint32 call_hash(HTAB *hashp, char *k);
|
1997-09-08 23:56:23 +02:00
|
|
|
static SEG_OFFSET seg_alloc(HTAB *hashp);
|
|
|
|
static int bucket_alloc(HTAB *hashp);
|
|
|
|
static int dir_realloc(HTAB *hashp);
|
1999-05-31 19:01:52 +02:00
|
|
|
static int expand_table(HTAB *hashp);
|
|
|
|
static int hdefault(HTAB *hashp);
|
|
|
|
static int init_htab(HTAB *hashp, int nelem);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
typedef long *((*dhalloc_ptr) ());
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#ifndef FRONTEND
|
|
|
|
/* ----------------
|
|
|
|
* memory allocation routines
|
|
|
|
*
|
|
|
|
* for postgres: all hash elements have to be in
|
|
|
|
* the global cache context. Otherwise the postgres
|
|
|
|
* garbage collector is going to corrupt them. -wei
|
|
|
|
*
|
|
|
|
* ??? the "cache" memory context is intended to store only
|
1997-09-07 07:04:48 +02:00
|
|
|
* system cache information. The user of the hashing
|
|
|
|
* routines should specify which context to use or we
|
|
|
|
* should create a separate memory context for these
|
|
|
|
* hash routines. For now I have modified this code to
|
|
|
|
* do the latter -cim 1/19/91
|
1996-07-09 08:22:35 +02:00
|
|
|
* ----------------
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
GlobalMemory DynaHashCxt = (GlobalMemory) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static long *
|
1996-07-09 08:22:35 +02:00
|
|
|
DynaHashAlloc(unsigned int size)
|
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!DynaHashCxt)
|
|
|
|
DynaHashCxt = CreateGlobalMemory("DynaHash");
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
return (long *)
|
|
|
|
MemoryContextAlloc((MemoryContext) DynaHashCxt, size);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DynaHashFree(Pointer ptr)
|
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
MemoryContextFree((MemoryContext) DynaHashCxt, ptr);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define MEM_ALLOC DynaHashAlloc
|
|
|
|
#define MEM_FREE DynaHashFree
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#else /* FRONTEND */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define MEM_ALLOC palloc
|
|
|
|
#define MEM_FREE pfree
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
#endif /* FRONTEND */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pointer access macros. Shared memory implementation cannot
|
1997-09-07 07:04:48 +02:00
|
|
|
* store pointers in the hash table data structures because
|
1996-07-09 08:22:35 +02:00
|
|
|
* pointer values will be different in different address spaces.
|
|
|
|
* these macros convert offsets to pointers and pointers to offsets.
|
|
|
|
* Shared memory need not be contiguous, but all addresses must be
|
|
|
|
* calculated relative to some offset (segbase).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define GET_SEG(hp,seg_num)\
|
|
|
|
(SEGMENT) (((unsigned long) (hp)->segbase) + (hp)->dir[seg_num])
|
|
|
|
|
|
|
|
#define GET_BUCKET(hp,bucket_offs)\
|
|
|
|
(ELEMENT *) (((unsigned long) (hp)->segbase) + bucket_offs)
|
|
|
|
|
|
|
|
#define MAKE_HASHOFFSET(hp,ptr)\
|
|
|
|
( ((unsigned long) ptr) - ((unsigned long) (hp)->segbase) )
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#if HASH_STATISTICS
|
1997-09-08 04:41:22 +02:00
|
|
|
static long hash_accesses,
|
|
|
|
hash_collisions,
|
|
|
|
hash_expansions;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/************************** CREATE ROUTINES **********************/
|
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
HTAB *
|
1997-09-08 23:56:23 +02:00
|
|
|
hash_create(int nelem, HASHCTL *info, int flags)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
HHDR *hctl;
|
1997-09-08 04:41:22 +02:00
|
|
|
HTAB *hashp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
hashp = (HTAB *) MEM_ALLOC((unsigned long) sizeof(HTAB));
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(hashp, 0, sizeof(HTAB));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (flags & HASH_FUNCTION)
|
|
|
|
hashp->hash = info->hash;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* default */
|
|
|
|
hashp->hash = string_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & HASH_SHARED_MEM)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ctl structure is preallocated for shared memory tables. Note
|
|
|
|
* that HASH_DIRSIZE had better be set as well.
|
1999-02-22 07:16:57 +01:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hashp->hctl = (HHDR *) info->hctl;
|
|
|
|
hashp->segbase = (char *) info->segbase;
|
|
|
|
hashp->alloc = info->alloc;
|
|
|
|
hashp->dir = (SEG_OFFSET *) info->dir;
|
|
|
|
|
|
|
|
/* hash table already exists, we're just attaching to it */
|
|
|
|
if (flags & HASH_ATTACH)
|
1998-09-01 05:29:17 +02:00
|
|
|
return hashp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* setup hash table defaults */
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
hashp->hctl = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
hashp->alloc = (dhalloc_ptr) MEM_ALLOC;
|
|
|
|
hashp->dir = NULL;
|
|
|
|
hashp->segbase = NULL;
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!hashp->hctl)
|
|
|
|
{
|
|
|
|
hashp->hctl = (HHDR *) hashp->alloc((unsigned long) sizeof(HHDR));
|
|
|
|
if (!hashp->hctl)
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!hdefault(hashp))
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
hctl = hashp->hctl;
|
1996-07-09 08:22:35 +02:00
|
|
|
#ifdef HASH_STATISTICS
|
1997-09-07 07:04:48 +02:00
|
|
|
hctl->accesses = hctl->collisions = 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (flags & HASH_SEGMENT)
|
|
|
|
{
|
|
|
|
hctl->ssize = info->ssize;
|
|
|
|
hctl->sshift = my_log2(info->ssize);
|
1999-05-31 19:01:52 +02:00
|
|
|
/* ssize had better be a power of 2 */
|
|
|
|
Assert(hctl->ssize == (1L << hctl->sshift));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
if (flags & HASH_FFACTOR)
|
|
|
|
hctl->ffactor = info->ffactor;
|
|
|
|
|
|
|
|
/*
|
1999-02-22 07:16:57 +01:00
|
|
|
* SHM hash tables have fixed directory size passed by the caller.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (flags & HASH_DIRSIZE)
|
|
|
|
{
|
1999-02-22 07:16:57 +01:00
|
|
|
hctl->max_dsize = info->max_dsize;
|
|
|
|
hctl->dsize = info->dsize;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hash table now allocates space for key and data but you have to say
|
|
|
|
* how much space to allocate
|
|
|
|
*/
|
|
|
|
if (flags & HASH_ELEM)
|
|
|
|
{
|
|
|
|
hctl->keysize = info->keysize;
|
|
|
|
hctl->datasize = info->datasize;
|
|
|
|
}
|
|
|
|
if (flags & HASH_ALLOC)
|
|
|
|
hashp->alloc = info->alloc;
|
|
|
|
|
|
|
|
if (init_htab(hashp, nelem))
|
|
|
|
{
|
|
|
|
hash_destroy(hashp);
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return hashp;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-02-22 07:16:57 +01:00
|
|
|
* Set default HHDR parameters.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
static int
|
1997-09-08 23:56:23 +02:00
|
|
|
hdefault(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HHDR *hctl;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(hashp->hctl, 0, sizeof(HHDR));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hctl = hashp->hctl;
|
|
|
|
hctl->ssize = DEF_SEGSIZE;
|
|
|
|
hctl->sshift = DEF_SEGSIZE_SHIFT;
|
|
|
|
hctl->dsize = DEF_DIRSIZE;
|
|
|
|
hctl->ffactor = DEF_FFACTOR;
|
|
|
|
hctl->nkeys = 0;
|
|
|
|
hctl->nsegs = 0;
|
|
|
|
|
|
|
|
/* I added these MS. */
|
|
|
|
|
|
|
|
/* default memory allocation for hash buckets */
|
|
|
|
hctl->keysize = sizeof(char *);
|
|
|
|
hctl->datasize = sizeof(char *);
|
|
|
|
|
|
|
|
/* table has no fixed maximum size */
|
|
|
|
hctl->max_dsize = NO_MAX_DSIZE;
|
|
|
|
|
|
|
|
/* garbage collection for HASH_REMOVE */
|
|
|
|
hctl->freeBucketIndex = INVALID_INDEX;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return 1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
1997-09-08 23:56:23 +02:00
|
|
|
init_htab(HTAB *hashp, int nelem)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-02-11 20:14:04 +01:00
|
|
|
SEG_OFFSET *segp;
|
1998-02-26 05:46:47 +01:00
|
|
|
int nbuckets;
|
|
|
|
int nsegs;
|
1997-09-08 04:41:22 +02:00
|
|
|
HHDR *hctl;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hctl = hashp->hctl;
|
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Divide number of elements by the fill factor to determine a desired
|
|
|
|
* number of buckets. Allocate space for the next greater power of
|
|
|
|
* two number of buckets
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
nelem = (nelem - 1) / hctl->ffactor + 1;
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
nbuckets = 1 << my_log2(nelem);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hctl->max_bucket = hctl->low_mask = nbuckets - 1;
|
|
|
|
hctl->high_mask = (nbuckets << 1) - 1;
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Figure number of directory segments needed, round up to a power of
|
|
|
|
* 2
|
1999-02-22 07:16:57 +01:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
nsegs = (nbuckets - 1) / hctl->ssize + 1;
|
|
|
|
nsegs = 1 << my_log2(nsegs);
|
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* Make sure directory is big enough. If pre-allocated directory is
|
|
|
|
* too small, choke (caller screwed up).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-02-22 07:16:57 +01:00
|
|
|
if (nsegs > hctl->dsize)
|
|
|
|
{
|
|
|
|
if (!(hashp->dir))
|
|
|
|
hctl->dsize = nsegs;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/* Allocate a directory */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!(hashp->dir))
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
hashp->dir = (SEG_OFFSET *) hashp->alloc(hctl->dsize * sizeof(SEG_OFFSET));
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!hashp->dir)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate initial segments */
|
|
|
|
for (segp = hashp->dir; hctl->nsegs < nsegs; hctl->nsegs++, segp++)
|
|
|
|
{
|
|
|
|
*segp = seg_alloc(hashp);
|
|
|
|
if (*segp == (SEG_OFFSET) 0)
|
|
|
|
{
|
|
|
|
hash_destroy(hashp);
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
#if HASH_DEBUG
|
1999-02-22 07:16:57 +01:00
|
|
|
fprintf(stderr, "%s\n%s%x\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",
|
1997-09-07 07:04:48 +02:00
|
|
|
"init_htab:",
|
|
|
|
"TABLE POINTER ", hashp,
|
|
|
|
"DIRECTORY SIZE ", hctl->dsize,
|
|
|
|
"SEGMENT SIZE ", hctl->ssize,
|
|
|
|
"SEGMENT SHIFT ", hctl->sshift,
|
|
|
|
"FILL FACTOR ", hctl->ffactor,
|
|
|
|
"MAX BUCKET ", hctl->max_bucket,
|
|
|
|
"HIGH MASK ", hctl->high_mask,
|
|
|
|
"LOW MASK ", hctl->low_mask,
|
|
|
|
"NSEGS ", hctl->nsegs,
|
|
|
|
"NKEYS ", hctl->nkeys);
|
|
|
|
#endif
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/*
|
|
|
|
* Estimate the space needed for a hashtable containing the given number
|
|
|
|
* of entries of given size.
|
|
|
|
* NOTE: this is used to estimate the footprint of hashtables in shared
|
|
|
|
* memory; therefore it does not count HTAB which is in local memory.
|
|
|
|
* NB: assumes that all hash structure parameters have default values!
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
hash_estimate_size(long num_entries, long keysize, long datasize)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
long size = 0;
|
|
|
|
long nBuckets,
|
|
|
|
nSegments,
|
|
|
|
nDirEntries,
|
|
|
|
nRecordAllocs,
|
|
|
|
recordSize;
|
1999-02-22 07:16:57 +01:00
|
|
|
|
|
|
|
/* estimate number of buckets wanted */
|
|
|
|
nBuckets = 1L << my_log2((num_entries - 1) / DEF_FFACTOR + 1);
|
|
|
|
/* # of segments needed for nBuckets */
|
1999-03-06 22:17:56 +01:00
|
|
|
nSegments = 1L << my_log2((nBuckets - 1) / DEF_SEGSIZE + 1);
|
|
|
|
/* directory entries */
|
|
|
|
nDirEntries = DEF_DIRSIZE;
|
|
|
|
while (nDirEntries < nSegments)
|
|
|
|
nDirEntries <<= 1; /* dir_alloc doubles dsize at each call */
|
1999-02-22 07:16:57 +01:00
|
|
|
|
|
|
|
/* fixed control info */
|
1999-05-25 18:15:34 +02:00
|
|
|
size += MAXALIGN(sizeof(HHDR)); /* but not HTAB, per above */
|
1999-03-06 22:17:56 +01:00
|
|
|
/* directory */
|
|
|
|
size += MAXALIGN(nDirEntries * sizeof(SEG_OFFSET));
|
1999-02-22 07:16:57 +01:00
|
|
|
/* segments */
|
|
|
|
size += nSegments * MAXALIGN(DEF_SEGSIZE * sizeof(BUCKET_INDEX));
|
|
|
|
/* records --- allocated in groups of BUCKET_ALLOC_INCR */
|
|
|
|
recordSize = sizeof(BUCKET_INDEX) + keysize + datasize;
|
|
|
|
recordSize = MAXALIGN(recordSize);
|
|
|
|
nRecordAllocs = (num_entries - 1) / BUCKET_ALLOC_INCR + 1;
|
|
|
|
size += nRecordAllocs * BUCKET_ALLOC_INCR * recordSize;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/********************** DESTROY ROUTINES ************************/
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/*
|
|
|
|
* XXX this sure looks thoroughly broken to me --- tgl 2/99.
|
|
|
|
* It's freeing every entry individually --- but they weren't
|
|
|
|
* allocated individually, see bucket_alloc!! Why doesn't it crash?
|
1999-05-31 19:01:52 +02:00
|
|
|
* ANSWER: it probably does crash, but is never invoked in normal
|
|
|
|
* operations...
|
1999-02-22 07:16:57 +01:00
|
|
|
*/
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
hash_destroy(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (hashp != NULL)
|
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
SEG_OFFSET segNum;
|
1997-09-08 04:41:22 +02:00
|
|
|
SEGMENT segp;
|
|
|
|
int nsegs = hashp->hctl->nsegs;
|
|
|
|
int j;
|
|
|
|
BUCKET_INDEX *elp,
|
|
|
|
p,
|
|
|
|
q;
|
|
|
|
ELEMENT *curr;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-03-06 22:17:56 +01:00
|
|
|
/* cannot destroy a shared memory hash table */
|
|
|
|
Assert(!hashp->segbase);
|
|
|
|
/* allocation method must be one we know how to free, too */
|
|
|
|
Assert(hashp->alloc == (dhalloc_ptr) MEM_ALLOC);
|
|
|
|
|
|
|
|
hash_stats("destroy", hashp);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
for (segNum = 0; nsegs > 0; nsegs--, segNum++)
|
|
|
|
{
|
|
|
|
|
|
|
|
segp = GET_SEG(hashp, segNum);
|
|
|
|
for (j = 0, elp = segp; j < hashp->hctl->ssize; j++, elp++)
|
|
|
|
{
|
|
|
|
for (p = *elp; p != INVALID_INDEX; p = q)
|
|
|
|
{
|
|
|
|
curr = GET_BUCKET(hashp, p);
|
|
|
|
q = curr->next;
|
|
|
|
MEM_FREE((char *) curr);
|
|
|
|
}
|
|
|
|
}
|
1999-03-06 22:17:56 +01:00
|
|
|
MEM_FREE((char *) segp);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
MEM_FREE((char *) hashp->dir);
|
|
|
|
MEM_FREE((char *) hashp->hctl);
|
|
|
|
MEM_FREE((char *) hashp);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1997-09-08 23:56:23 +02:00
|
|
|
hash_stats(char *where, HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
#if HASH_STATISTICS
|
|
|
|
|
|
|
|
fprintf(stderr, "%s: this HTAB -- accesses %ld collisions %ld\n",
|
|
|
|
where, hashp->hctl->accesses, hashp->hctl->collisions);
|
|
|
|
|
|
|
|
fprintf(stderr, "hash_stats: keys %ld keysize %ld maxp %d segmentcount %d\n",
|
|
|
|
hashp->hctl->nkeys, hashp->hctl->keysize,
|
|
|
|
hashp->hctl->max_bucket, hashp->hctl->nsegs);
|
|
|
|
fprintf(stderr, "%s: total accesses %ld total collisions %ld\n",
|
|
|
|
where, hash_accesses, hash_collisions);
|
|
|
|
fprintf(stderr, "hash_stats: total expansions %ld\n",
|
|
|
|
hash_expansions);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************SEARCH ROUTINES *****************************/
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static uint32
|
1999-05-31 19:01:52 +02:00
|
|
|
call_hash(HTAB *hashp, char *k)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-05-31 19:01:52 +02:00
|
|
|
HHDR *hctl = hashp->hctl;
|
1997-09-08 04:41:22 +02:00
|
|
|
long hash_val,
|
|
|
|
bucket;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-31 19:01:52 +02:00
|
|
|
hash_val = hashp->hash(k, (int) hctl->keysize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
bucket = hash_val & hctl->high_mask;
|
|
|
|
if (bucket > hctl->max_bucket)
|
|
|
|
bucket = bucket & hctl->low_mask;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return bucket;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hash_search -- look up key in table and perform action
|
|
|
|
*
|
|
|
|
* action is one of HASH_FIND/HASH_ENTER/HASH_REMOVE
|
|
|
|
*
|
|
|
|
* RETURNS: NULL if table is corrupted, a pointer to the element
|
1997-09-07 07:04:48 +02:00
|
|
|
* found/removed/entered if applicable, TRUE otherwise.
|
|
|
|
* foundPtr is TRUE if we found an element in the table
|
|
|
|
* (FALSE if we entered one).
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
long *
|
1997-09-08 23:56:23 +02:00
|
|
|
hash_search(HTAB *hashp,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *keyPtr,
|
|
|
|
HASHACTION action, /* HASH_FIND / HASH_ENTER / HASH_REMOVE
|
|
|
|
* HASH_FIND_SAVE / HASH_REMOVE_SAVED */
|
1997-09-08 23:56:23 +02:00
|
|
|
bool *foundPtr)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
uint32 bucket;
|
|
|
|
long segment_num;
|
|
|
|
long segment_ndx;
|
|
|
|
SEGMENT segp;
|
1998-02-26 05:46:47 +01:00
|
|
|
ELEMENT *curr;
|
1997-09-08 04:41:22 +02:00
|
|
|
HHDR *hctl;
|
|
|
|
BUCKET_INDEX currIndex;
|
|
|
|
BUCKET_INDEX *prevIndexPtr;
|
|
|
|
char *destAddr;
|
1997-09-07 07:04:48 +02:00
|
|
|
static struct State
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
ELEMENT *currElem;
|
|
|
|
BUCKET_INDEX currIndex;
|
|
|
|
BUCKET_INDEX *prevIndex;
|
|
|
|
} saveState;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert((hashp && keyPtr));
|
|
|
|
Assert((action == HASH_FIND) || (action == HASH_REMOVE) || (action == HASH_ENTER) || (action == HASH_FIND_SAVE) || (action == HASH_REMOVE_SAVED));
|
|
|
|
|
|
|
|
hctl = hashp->hctl;
|
|
|
|
|
|
|
|
#if HASH_STATISTICS
|
|
|
|
hash_accesses++;
|
|
|
|
hashp->hctl->accesses++;
|
|
|
|
#endif
|
|
|
|
if (action == HASH_REMOVE_SAVED)
|
|
|
|
{
|
|
|
|
curr = saveState.currElem;
|
|
|
|
currIndex = saveState.currIndex;
|
|
|
|
prevIndexPtr = saveState.prevIndex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to catch subsequent errors
|
|
|
|
*/
|
|
|
|
Assert(saveState.currElem && !(saveState.currElem = 0));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
1999-05-31 19:01:52 +02:00
|
|
|
bucket = call_hash(hashp, keyPtr);
|
1997-09-07 07:04:48 +02:00
|
|
|
segment_num = bucket >> hctl->sshift;
|
1999-05-31 19:01:52 +02:00
|
|
|
segment_ndx = MOD(bucket, hctl->ssize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
segp = GET_SEG(hashp, segment_num);
|
|
|
|
|
|
|
|
Assert(segp);
|
|
|
|
|
|
|
|
prevIndexPtr = &segp[segment_ndx];
|
1996-07-09 08:22:35 +02:00
|
|
|
currIndex = *prevIndexPtr;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Follow collision chain
|
|
|
|
*/
|
|
|
|
for (curr = NULL; currIndex != INVALID_INDEX;)
|
|
|
|
{
|
|
|
|
/* coerce bucket index into a pointer */
|
|
|
|
curr = GET_BUCKET(hashp, currIndex);
|
|
|
|
|
|
|
|
if (!memcmp((char *) &(curr->key), keyPtr, hctl->keysize))
|
|
|
|
break;
|
|
|
|
prevIndexPtr = &(curr->next);
|
|
|
|
currIndex = *prevIndexPtr;
|
|
|
|
#if HASH_STATISTICS
|
|
|
|
hash_collisions++;
|
|
|
|
hashp->hctl->collisions++;
|
|
|
|
#endif
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if we found an entry or if we weren't trying to insert, we're done
|
|
|
|
* now.
|
|
|
|
*/
|
|
|
|
*foundPtr = (bool) (currIndex != INVALID_INDEX);
|
|
|
|
switch (action)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case HASH_ENTER:
|
|
|
|
if (currIndex != INVALID_INDEX)
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curr->key);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
case HASH_REMOVE:
|
|
|
|
case HASH_REMOVE_SAVED:
|
|
|
|
if (currIndex != INVALID_INDEX)
|
|
|
|
{
|
|
|
|
Assert(hctl->nkeys > 0);
|
|
|
|
hctl->nkeys--;
|
|
|
|
|
1999-05-31 19:01:52 +02:00
|
|
|
/* remove record from hash bucket's chain. */
|
1997-09-08 04:41:22 +02:00
|
|
|
*prevIndexPtr = curr->next;
|
1999-05-31 19:01:52 +02:00
|
|
|
|
|
|
|
/* add the record to the freelist for this table. */
|
1997-09-08 04:41:22 +02:00
|
|
|
curr->next = hctl->freeBucketIndex;
|
|
|
|
hctl->freeBucketIndex = currIndex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* better hope the caller is synchronizing access to this
|
|
|
|
* element, because someone else is going to reuse it the
|
|
|
|
* next time something is added to the table
|
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curr->key);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return (long *) TRUE;
|
1997-09-08 04:41:22 +02:00
|
|
|
case HASH_FIND:
|
|
|
|
if (currIndex != INVALID_INDEX)
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curr->key);
|
|
|
|
return (long *) TRUE;
|
1997-09-08 04:41:22 +02:00
|
|
|
case HASH_FIND_SAVE:
|
|
|
|
if (currIndex != INVALID_INDEX)
|
|
|
|
{
|
|
|
|
saveState.currElem = curr;
|
|
|
|
saveState.prevIndex = prevIndexPtr;
|
|
|
|
saveState.currIndex = currIndex;
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curr->key);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return (long *) TRUE;
|
1997-09-08 04:41:22 +02:00
|
|
|
default:
|
|
|
|
/* can't get here */
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we got here, then we didn't find the element and we have to
|
|
|
|
* insert it into the hash table
|
|
|
|
*/
|
|
|
|
Assert(currIndex == INVALID_INDEX);
|
|
|
|
|
|
|
|
/* get the next free bucket */
|
1996-07-09 08:22:35 +02:00
|
|
|
currIndex = hctl->freeBucketIndex;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (currIndex == INVALID_INDEX)
|
|
|
|
{
|
|
|
|
/* no free elements. allocate another chunk of buckets */
|
|
|
|
if (!bucket_alloc(hashp))
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
currIndex = hctl->freeBucketIndex;
|
|
|
|
}
|
|
|
|
Assert(currIndex != INVALID_INDEX);
|
|
|
|
|
|
|
|
curr = GET_BUCKET(hashp, currIndex);
|
|
|
|
hctl->freeBucketIndex = curr->next;
|
|
|
|
|
|
|
|
/* link into chain */
|
|
|
|
*prevIndexPtr = currIndex;
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/* copy key into record */
|
1997-09-07 07:04:48 +02:00
|
|
|
destAddr = (char *) &(curr->key);
|
|
|
|
memmove(destAddr, keyPtr, hctl->keysize);
|
|
|
|
curr->next = INVALID_INDEX;
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* let the caller initialize the data field after hash_search returns.
|
|
|
|
*/
|
|
|
|
/* memmove(destAddr,keyPtr,hctl->keysize+hctl->datasize); */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if it is time to split the segment
|
|
|
|
*/
|
|
|
|
if (++hctl->nkeys / (hctl->max_bucket + 1) > hctl->ffactor)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: failure to expand table is not a fatal error, it just
|
|
|
|
* means we have to run at higher fill factor than we wanted.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-02-22 07:16:57 +01:00
|
|
|
expand_table(hashp);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curr->key);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hash_seq -- sequentially search through hash table and return
|
1997-09-07 07:04:48 +02:00
|
|
|
* all the elements one by one, return NULL on error and
|
|
|
|
* return TRUE in the end.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
long *
|
1997-09-08 23:56:23 +02:00
|
|
|
hash_seq(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
static uint32 curBucket = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
static BUCKET_INDEX curIndex;
|
1997-09-08 04:41:22 +02:00
|
|
|
ELEMENT *curElem;
|
|
|
|
long segment_num;
|
|
|
|
long segment_ndx;
|
|
|
|
SEGMENT segp;
|
|
|
|
HHDR *hctl;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (hashp == NULL)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reset static state
|
|
|
|
*/
|
|
|
|
curBucket = 0;
|
|
|
|
curIndex = INVALID_INDEX;
|
1998-09-01 05:29:17 +02:00
|
|
|
return (long *) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hctl = hashp->hctl;
|
|
|
|
while (curBucket <= hctl->max_bucket)
|
|
|
|
{
|
|
|
|
if (curIndex != INVALID_INDEX)
|
|
|
|
{
|
|
|
|
curElem = GET_BUCKET(hashp, curIndex);
|
|
|
|
curIndex = curElem->next;
|
|
|
|
if (curIndex == INVALID_INDEX) /* end of this bucket */
|
|
|
|
++curBucket;
|
1998-09-01 05:29:17 +02:00
|
|
|
return &(curElem->key);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the search within this bucket.
|
|
|
|
*/
|
|
|
|
segment_num = curBucket >> hctl->sshift;
|
1999-05-31 19:01:52 +02:00
|
|
|
segment_ndx = MOD(curBucket, hctl->ssize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* first find the right segment in the table directory.
|
|
|
|
*/
|
|
|
|
segp = GET_SEG(hashp, segment_num);
|
|
|
|
if (segp == NULL)
|
|
|
|
/* this is probably an error */
|
1998-09-01 05:29:17 +02:00
|
|
|
return (long *) NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* now find the right index into the segment for the first item in
|
|
|
|
* this bucket's chain. if the bucket is not empty (its entry in
|
|
|
|
* the dir is valid), we know this must correspond to a valid
|
|
|
|
* element and not a freed element because it came out of the
|
|
|
|
* directory of valid stuff. if there are elements in the bucket
|
|
|
|
* chains that point to the freelist we're in big trouble.
|
|
|
|
*/
|
|
|
|
curIndex = segp[segment_ndx];
|
|
|
|
|
|
|
|
if (curIndex == INVALID_INDEX) /* empty bucket */
|
|
|
|
++curBucket;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return (long *) TRUE; /* out of buckets */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/********************************* UTILITIES ************************/
|
1999-05-31 19:01:52 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Expand the table by adding one more hash bucket.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
static int
|
1997-09-08 23:56:23 +02:00
|
|
|
expand_table(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HHDR *hctl;
|
|
|
|
SEGMENT old_seg,
|
|
|
|
new_seg;
|
|
|
|
long old_bucket,
|
|
|
|
new_bucket;
|
|
|
|
long new_segnum,
|
|
|
|
new_segndx;
|
|
|
|
long old_segnum,
|
|
|
|
old_segndx;
|
|
|
|
ELEMENT *chain;
|
|
|
|
BUCKET_INDEX *old,
|
|
|
|
*newbi;
|
1998-02-11 20:14:04 +01:00
|
|
|
BUCKET_INDEX chainIndex,
|
1997-09-08 04:41:22 +02:00
|
|
|
nextIndex;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#ifdef HASH_STATISTICS
|
1997-09-07 07:04:48 +02:00
|
|
|
hash_expansions++;
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
hctl = hashp->hctl;
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
new_bucket = hctl->max_bucket + 1;
|
1997-09-07 07:04:48 +02:00
|
|
|
new_segnum = new_bucket >> hctl->sshift;
|
|
|
|
new_segndx = MOD(new_bucket, hctl->ssize);
|
|
|
|
|
|
|
|
if (new_segnum >= hctl->nsegs)
|
|
|
|
{
|
1999-02-22 07:16:57 +01:00
|
|
|
/* Allocate new segment if necessary -- could fail if dir full */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (new_segnum >= hctl->dsize)
|
1999-05-25 18:15:34 +02:00
|
|
|
if (!dir_realloc(hashp))
|
1999-02-22 07:16:57 +01:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!(hashp->dir[new_segnum] = seg_alloc(hashp)))
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
hctl->nsegs++;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-31 19:01:52 +02:00
|
|
|
/* OK, we created a new bucket */
|
1999-02-22 07:16:57 +01:00
|
|
|
hctl->max_bucket++;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-31 19:01:52 +02:00
|
|
|
/*
|
|
|
|
* *Before* changing masks, find old bucket corresponding to same hash
|
|
|
|
* values; values in that bucket may need to be relocated to new bucket.
|
|
|
|
* Note that new_bucket is certainly larger than low_mask at this point,
|
|
|
|
* so we can skip the first step of the regular hash mask calc.
|
|
|
|
*/
|
|
|
|
old_bucket = (new_bucket & hctl->low_mask);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we crossed a power of 2, readjust masks.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (new_bucket > hctl->high_mask)
|
|
|
|
{
|
|
|
|
hctl->low_mask = hctl->high_mask;
|
|
|
|
hctl->high_mask = new_bucket | hctl->low_mask;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-05-31 19:01:52 +02:00
|
|
|
* Relocate records to the new bucket. NOTE: because of the way the
|
|
|
|
* hash masking is done in call_hash, only one old bucket can need to
|
|
|
|
* be split at this point. With a different way of reducing the hash
|
|
|
|
* value, that might not be true!
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
old_segnum = old_bucket >> hctl->sshift;
|
|
|
|
old_segndx = MOD(old_bucket, hctl->ssize);
|
|
|
|
|
|
|
|
old_seg = GET_SEG(hashp, old_segnum);
|
|
|
|
new_seg = GET_SEG(hashp, new_segnum);
|
|
|
|
|
|
|
|
old = &old_seg[old_segndx];
|
|
|
|
newbi = &new_seg[new_segndx];
|
|
|
|
for (chainIndex = *old;
|
|
|
|
chainIndex != INVALID_INDEX;
|
|
|
|
chainIndex = nextIndex)
|
|
|
|
{
|
|
|
|
chain = GET_BUCKET(hashp, chainIndex);
|
|
|
|
nextIndex = chain->next;
|
1999-05-31 19:01:52 +02:00
|
|
|
if (call_hash(hashp, (char *) &(chain->key)) == old_bucket)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
*old = chainIndex;
|
|
|
|
old = &chain->next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*newbi = chainIndex;
|
|
|
|
newbi = &chain->next;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1999-05-31 19:01:52 +02:00
|
|
|
/* don't forget to terminate the rebuilt hash chains... */
|
|
|
|
*old = INVALID_INDEX;
|
|
|
|
*newbi = INVALID_INDEX;
|
1998-09-01 05:29:17 +02:00
|
|
|
return 1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
1997-09-08 23:56:23 +02:00
|
|
|
dir_realloc(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
char *p;
|
1999-02-22 07:16:57 +01:00
|
|
|
char *old_p;
|
|
|
|
long new_dsize;
|
1997-09-08 04:41:22 +02:00
|
|
|
long old_dirsize;
|
|
|
|
long new_dirsize;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (hashp->hctl->max_dsize != NO_MAX_DSIZE)
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* Reallocate directory */
|
1999-02-22 07:16:57 +01:00
|
|
|
new_dsize = hashp->hctl->dsize << 1;
|
|
|
|
old_dirsize = hashp->hctl->dsize * sizeof(SEG_OFFSET);
|
|
|
|
new_dirsize = new_dsize * sizeof(SEG_OFFSET);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
old_p = (char *) hashp->dir;
|
1997-09-07 07:04:48 +02:00
|
|
|
p = (char *) hashp->alloc((unsigned long) new_dirsize);
|
1999-02-22 07:16:57 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (p != NULL)
|
|
|
|
{
|
1999-02-22 07:16:57 +01:00
|
|
|
memmove(p, old_p, old_dirsize);
|
|
|
|
MemSet(p + old_dirsize, 0, new_dirsize - old_dirsize);
|
|
|
|
MEM_FREE((char *) old_p);
|
|
|
|
hashp->dir = (SEG_OFFSET *) p;
|
|
|
|
hashp->hctl->dsize = new_dsize;
|
1998-09-01 05:29:17 +02:00
|
|
|
return 1;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static SEG_OFFSET
|
1997-09-08 23:56:23 +02:00
|
|
|
seg_alloc(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
SEGMENT segp;
|
|
|
|
SEG_OFFSET segOffset;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
segp = (SEGMENT) hashp->alloc((unsigned long)
|
1999-05-25 18:15:34 +02:00
|
|
|
sizeof(BUCKET_INDEX) * hashp->hctl->ssize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!segp)
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet((char *) segp, 0,
|
1999-02-22 07:16:57 +01:00
|
|
|
(long) sizeof(BUCKET_INDEX) * hashp->hctl->ssize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
segOffset = MAKE_HASHOFFSET(hashp, segp);
|
1998-09-01 05:29:17 +02:00
|
|
|
return segOffset;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate some new buckets and link them into the free list
|
|
|
|
*/
|
|
|
|
static int
|
1997-09-08 23:56:23 +02:00
|
|
|
bucket_alloc(HTAB *hashp)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
|
|
|
ELEMENT *tmpBucket;
|
|
|
|
long bucketSize;
|
|
|
|
BUCKET_INDEX tmpIndex,
|
|
|
|
lastIndex;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/* Each bucket has a BUCKET_INDEX header plus user data. */
|
1999-02-03 22:18:02 +01:00
|
|
|
bucketSize = sizeof(BUCKET_INDEX) + hashp->hctl->keysize + hashp->hctl->datasize;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* make sure its aligned correctly */
|
1999-02-22 07:16:57 +01:00
|
|
|
bucketSize = MAXALIGN(bucketSize);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
tmpBucket = (ELEMENT *)
|
|
|
|
hashp->alloc((unsigned long) BUCKET_ALLOC_INCR * bucketSize);
|
|
|
|
|
|
|
|
if (!tmpBucket)
|
1998-09-01 05:29:17 +02:00
|
|
|
return 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-31 19:01:52 +02:00
|
|
|
/* tmpIndex is the shmem offset into the first bucket of the array */
|
1997-09-07 07:04:48 +02:00
|
|
|
tmpIndex = MAKE_HASHOFFSET(hashp, tmpBucket);
|
|
|
|
|
|
|
|
/* set the freebucket list to point to the first bucket */
|
|
|
|
lastIndex = hashp->hctl->freeBucketIndex;
|
|
|
|
hashp->hctl->freeBucketIndex = tmpIndex;
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
|
|
|
* initialize each bucket to point to the one behind it. NOTE: loop
|
|
|
|
* sets last bucket incorrectly; we fix below.
|
1999-02-22 07:16:57 +01:00
|
|
|
*/
|
|
|
|
for (i = 0; i < BUCKET_ALLOC_INCR; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
tmpBucket = GET_BUCKET(hashp, tmpIndex);
|
|
|
|
tmpIndex += bucketSize;
|
|
|
|
tmpBucket->next = tmpIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the last bucket points to the old freelist head (which is probably
|
1999-02-22 07:16:57 +01:00
|
|
|
* invalid or we wouldn't be here)
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
tmpBucket->next = lastIndex;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return 1;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
/* calculate ceil(log base 2) of num */
|
1996-07-09 08:22:35 +02:00
|
|
|
int
|
|
|
|
my_log2(long num)
|
|
|
|
{
|
1999-02-22 07:16:57 +01:00
|
|
|
int i;
|
|
|
|
long limit;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-22 07:16:57 +01:00
|
|
|
for (i = 0, limit = 1; limit < num; i++, limit <<= 1)
|
|
|
|
;
|
1998-09-01 05:29:17 +02:00
|
|
|
return i;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|