summaryrefslogtreecommitdiff
path: root/src/backend/utils/hash/dynahash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/hash/dynahash.c')
-rw-r--r--src/backend/utils/hash/dynahash.c950
1 files changed, 0 insertions, 950 deletions
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
deleted file mode 100644
index 3b65344086f..00000000000
--- a/src/backend/utils/hash/dynahash.c
+++ /dev/null
@@ -1,950 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * dynahash.c
- * dynamic hash tables
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.43 2002/06/20 20:29:39 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- *
- * 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
- * added multiple table interface
- * Modified by sullivan@postgres.berkeley.edu April 1990
- * changed ctl structure for shared memory
- */
-
-#include "postgres.h"
-
-#include <sys/types.h>
-
-#include "utils/dynahash.h"
-#include "utils/hsearch.h"
-#include "utils/memutils.h"
-
-/*
- * Key (also entry) part of a HASHELEMENT
- */
-#define ELEMENTKEY(helem) (((char *)(helem)) + MAXALIGN(sizeof(HASHELEMENT)))
-
-/*
- * Fast MOD arithmetic, assuming that y is a power of 2 !
- */
-#define MOD(x,y) ((x) & ((y)-1))
-
-/*
- * Private function prototypes
- */
-static void *DynaHashAlloc(Size size);
-static uint32 call_hash(HTAB *hashp, void *k);
-static HASHSEGMENT seg_alloc(HTAB *hashp);
-static bool element_alloc(HTAB *hashp);
-static bool dir_realloc(HTAB *hashp);
-static bool expand_table(HTAB *hashp);
-static bool hdefault(HTAB *hashp);
-static bool init_htab(HTAB *hashp, long nelem);
-static void hash_corrupted(HTAB *hashp);
-
-
-/*
- * memory allocation routines
- */
-static MemoryContext DynaHashCxt = NULL;
-static MemoryContext CurrentDynaHashCxt = NULL;
-
-static void *
-DynaHashAlloc(Size size)
-{
- Assert(MemoryContextIsValid(CurrentDynaHashCxt));
- return MemoryContextAlloc(CurrentDynaHashCxt, size);
-}
-
-#define MEM_ALLOC DynaHashAlloc
-#define MEM_FREE pfree
-
-
-#if HASH_STATISTICS
-static long hash_accesses,
- hash_collisions,
- hash_expansions;
-#endif
-
-
-/************************** CREATE ROUTINES **********************/
-
-HTAB *
-hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
-{
- HTAB *hashp;
- HASHHDR *hctl;
-
- /* First time through, create a memory context for hash tables */
- if (!DynaHashCxt)
- DynaHashCxt = AllocSetContextCreate(TopMemoryContext,
- "DynaHash",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
- /* Select allocation context for this hash table */
- if (flags & HASH_CONTEXT)
- CurrentDynaHashCxt = info->hcxt;
- else
- CurrentDynaHashCxt = DynaHashCxt;
-
- /* Initialize the hash header */
- hashp = (HTAB *) MEM_ALLOC(sizeof(HTAB));
- if (!hashp)
- return NULL;
- MemSet(hashp, 0, sizeof(HTAB));
-
- hashp->tabname = (char *) MEM_ALLOC(strlen(tabname) + 1);
- strcpy(hashp->tabname, tabname);
-
- if (flags & HASH_FUNCTION)
- hashp->hash = info->hash;
- else
- hashp->hash = string_hash; /* default hash function */
-
- if (flags & HASH_SHARED_MEM)
- {
- /*
- * ctl structure is preallocated for shared memory tables. Note
- * that HASH_DIRSIZE had better be set as well.
- */
- hashp->hctl = info->hctl;
- hashp->dir = info->dir;
- hashp->alloc = info->alloc;
- hashp->hcxt = NULL;
- hashp->isshared = true;
-
- /* hash table already exists, we're just attaching to it */
- if (flags & HASH_ATTACH)
- return hashp;
- }
- else
- {
- /* setup hash table defaults */
- hashp->hctl = NULL;
- hashp->dir = NULL;
- hashp->alloc = MEM_ALLOC;
- hashp->hcxt = DynaHashCxt;
- hashp->isshared = false;
- }
-
- if (!hashp->hctl)
- {
- hashp->hctl = (HASHHDR *) hashp->alloc(sizeof(HASHHDR));
- if (!hashp->hctl)
- return NULL;
- }
-
- if (!hdefault(hashp))
- return NULL;
-
- hctl = hashp->hctl;
-#ifdef HASH_STATISTICS
- hctl->accesses = hctl->collisions = 0;
-#endif
-
- if (flags & HASH_SEGMENT)
- {
- hctl->ssize = info->ssize;
- hctl->sshift = my_log2(info->ssize);
- /* ssize had better be a power of 2 */
- Assert(hctl->ssize == (1L << hctl->sshift));
- }
- if (flags & HASH_FFACTOR)
- hctl->ffactor = info->ffactor;
-
- /*
- * SHM hash tables have fixed directory size passed by the caller.
- */
- if (flags & HASH_DIRSIZE)
- {
- hctl->max_dsize = info->max_dsize;
- hctl->dsize = info->dsize;
- }
-
- /*
- * 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->entrysize = info->entrysize;
- }
-
- if (flags & HASH_ALLOC)
- hashp->alloc = info->alloc;
- else
- {
- if (flags & HASH_CONTEXT)
- {
- /* hash table structures live in child of given context */
- CurrentDynaHashCxt = AllocSetContextCreate(info->hcxt,
- "DynaHashTable",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- hashp->hcxt = CurrentDynaHashCxt;
- }
- else
- {
- /* hash table structures live in child of DynaHashCxt */
- CurrentDynaHashCxt = AllocSetContextCreate(DynaHashCxt,
- "DynaHashTable",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- hashp->hcxt = CurrentDynaHashCxt;
- }
- }
-
- if (!init_htab(hashp, nelem))
- {
- hash_destroy(hashp);
- return NULL;
- }
- return hashp;
-}
-
-/*
- * Set default HASHHDR parameters.
- */
-static bool
-hdefault(HTAB *hashp)
-{
- HASHHDR *hctl = hashp->hctl;
-
- MemSet(hctl, 0, sizeof(HASHHDR));
-
- hctl->ssize = DEF_SEGSIZE;
- hctl->sshift = DEF_SEGSIZE_SHIFT;
- hctl->dsize = DEF_DIRSIZE;
- hctl->ffactor = DEF_FFACTOR;
- hctl->nentries = 0;
- hctl->nsegs = 0;
-
- /* I added these MS. */
-
- /* rather pointless defaults for key & entry size */
- hctl->keysize = sizeof(char *);
- hctl->entrysize = 2 * sizeof(char *);
-
- /* table has no fixed maximum size */
- hctl->max_dsize = NO_MAX_DSIZE;
-
- /* garbage collection for HASH_REMOVE */
- hctl->freeList = NULL;
-
- return true;
-}
-
-
-static bool
-init_htab(HTAB *hashp, long nelem)
-{
- HASHHDR *hctl = hashp->hctl;
- HASHSEGMENT *segp;
- int nbuckets;
- int nsegs;
-
- /*
- * 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
- */
- nelem = (nelem - 1) / hctl->ffactor + 1;
-
- nbuckets = 1 << my_log2(nelem);
-
- hctl->max_bucket = hctl->low_mask = nbuckets - 1;
- hctl->high_mask = (nbuckets << 1) - 1;
-
- /*
- * Figure number of directory segments needed, round up to a power of
- * 2
- */
- nsegs = (nbuckets - 1) / hctl->ssize + 1;
- nsegs = 1 << my_log2(nsegs);
-
- /*
- * Make sure directory is big enough. If pre-allocated directory is
- * too small, choke (caller screwed up).
- */
- if (nsegs > hctl->dsize)
- {
- if (!(hashp->dir))
- hctl->dsize = nsegs;
- else
- return false;
- }
-
- /* Allocate a directory */
- if (!(hashp->dir))
- {
- CurrentDynaHashCxt = hashp->hcxt;
- hashp->dir = (HASHSEGMENT *)
- hashp->alloc(hctl->dsize * sizeof(HASHSEGMENT));
- if (!hashp->dir)
- return false;
- }
-
- /* Allocate initial segments */
- for (segp = hashp->dir; hctl->nsegs < nsegs; hctl->nsegs++, segp++)
- {
- *segp = seg_alloc(hashp);
- if (*segp == NULL)
- return false;
- }
-
-#if HASH_DEBUG
- fprintf(stderr, "init_htab:\n%s%p\n%s%ld\n%s%ld\n%s%d\n%s%ld\n%s%u\n%s%x\n%s%x\n%s%ld\n%s%ld\n",
- "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,
- "NENTRIES ", hctl->nentries);
-#endif
- return true;
-}
-
-/*
- * 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 entrysize)
-{
- long size = 0;
- long nBuckets,
- nSegments,
- nDirEntries,
- nElementAllocs,
- elementSize;
-
- /* estimate number of buckets wanted */
- nBuckets = 1L << my_log2((num_entries - 1) / DEF_FFACTOR + 1);
- /* # of segments needed for nBuckets */
- 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 */
-
- /* fixed control info */
- size += MAXALIGN(sizeof(HASHHDR)); /* but not HTAB, per above */
- /* directory */
- size += MAXALIGN(nDirEntries * sizeof(HASHSEGMENT));
- /* segments */
- size += nSegments * MAXALIGN(DEF_SEGSIZE * sizeof(HASHBUCKET));
- /* elements --- allocated in groups of HASHELEMENT_ALLOC_INCR */
- elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(entrysize);
- nElementAllocs = (num_entries - 1) / HASHELEMENT_ALLOC_INCR + 1;
- size += nElementAllocs * HASHELEMENT_ALLOC_INCR * elementSize;
-
- return size;
-}
-
-/*
- * Select an appropriate directory size for a hashtable with the given
- * maximum number of entries.
- * This is only needed for hashtables in shared memory, whose directories
- * cannot be expanded dynamically.
- * NB: assumes that all hash structure parameters have default values!
- *
- * XXX this had better agree with the behavior of init_htab()...
- */
-long
-hash_select_dirsize(long num_entries)
-{
- long nBuckets,
- nSegments,
- nDirEntries;
-
- /* estimate number of buckets wanted */
- nBuckets = 1L << my_log2((num_entries - 1) / DEF_FFACTOR + 1);
- /* # of segments needed for nBuckets */
- 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 */
-
- return nDirEntries;
-}
-
-
-/********************** DESTROY ROUTINES ************************/
-
-void
-hash_destroy(HTAB *hashp)
-{
- if (hashp != NULL)
- {
- /* allocation method must be one we know how to free, too */
- Assert(hashp->alloc == MEM_ALLOC);
- /* so this hashtable must have it's own context */
- Assert(hashp->hcxt != NULL);
-
- hash_stats("destroy", hashp);
-
- /*
- * Free buckets, dir etc. by destroying the hash table's memory
- * context.
- */
- MemoryContextDelete(hashp->hcxt);
-
- /*
- * Free the HTAB and control structure, which are allocated in the
- * parent context (DynaHashCxt or the context given by the caller
- * of hash_create()).
- */
- MEM_FREE(hashp->hctl);
- MEM_FREE(hashp->tabname);
- MEM_FREE(hashp);
- }
-}
-
-void
-hash_stats(const char *where, HTAB *hashp)
-{
-#if HASH_STATISTICS
-
- fprintf(stderr, "%s: this HTAB -- accesses %ld collisions %ld\n",
- where, hashp->hctl->accesses, hashp->hctl->collisions);
-
- fprintf(stderr, "hash_stats: entries %ld keysize %ld maxp %u segmentcount %ld\n",
- hashp->hctl->nentries, 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
-
-}
-
-/*******************************SEARCH ROUTINES *****************************/
-
-static uint32
-call_hash(HTAB *hashp, void *k)
-{
- HASHHDR *hctl = hashp->hctl;
- uint32 hash_val,
- bucket;
-
- hash_val = hashp->hash(k, (int) hctl->keysize);
-
- bucket = hash_val & hctl->high_mask;
- if (bucket > hctl->max_bucket)
- bucket = bucket & hctl->low_mask;
-
- return bucket;
-}
-
-/*----------
- * hash_search -- look up key in table and perform action
- *
- * action is one of:
- * HASH_FIND: look up key in table
- * HASH_ENTER: look up key in table, creating entry if not present
- * HASH_REMOVE: look up key in table, remove entry if present
- * HASH_FIND_SAVE: look up key in table, also save in static var
- * HASH_REMOVE_SAVED: remove entry saved by HASH_FIND_SAVE
- *
- * Return value is a pointer to the element found/entered/removed if any,
- * or NULL if no match was found. (NB: in the case of the REMOVE actions,
- * the result is a dangling pointer that shouldn't be dereferenced!)
- * A NULL result for HASH_ENTER implies we ran out of memory.
- *
- * If foundPtr isn't NULL, then *foundPtr is set TRUE if we found an
- * existing entry in the table, FALSE otherwise. This is needed in the
- * HASH_ENTER case, but is redundant with the return value otherwise.
- *
- * The HASH_FIND_SAVE/HASH_REMOVE_SAVED interface is a hack to save one
- * table lookup in a find/process/remove scenario. Note that no other
- * addition or removal in the table can safely happen in between.
- *----------
- */
-void *
-hash_search(HTAB *hashp,
- void *keyPtr,
- HASHACTION action,
- bool *foundPtr)
-{
- HASHHDR *hctl = hashp->hctl;
- uint32 bucket;
- long segment_num;
- long segment_ndx;
- HASHSEGMENT segp;
- HASHBUCKET currBucket;
- HASHBUCKET *prevBucketPtr;
-
- static struct State
- {
- HASHBUCKET currBucket;
- HASHBUCKET *prevBucketPtr;
- } saveState;
-
-#if HASH_STATISTICS
- hash_accesses++;
- hctl->accesses++;
-#endif
-
- /*
- * Do the initial lookup (or recall result of prior lookup)
- */
- if (action == HASH_REMOVE_SAVED)
- {
- currBucket = saveState.currBucket;
- prevBucketPtr = saveState.prevBucketPtr;
-
- /*
- * Try to catch subsequent errors
- */
- Assert(currBucket && !(saveState.currBucket = NULL));
- }
- else
- {
- bucket = call_hash(hashp, keyPtr);
- segment_num = bucket >> hctl->sshift;
- segment_ndx = MOD(bucket, hctl->ssize);
-
- segp = hashp->dir[segment_num];
-
- if (segp == NULL)
- hash_corrupted(hashp);
-
- prevBucketPtr = &segp[segment_ndx];
- currBucket = *prevBucketPtr;
-
- /*
- * Follow collision chain looking for matching key
- */
- while (currBucket != NULL)
- {
- if (memcmp(ELEMENTKEY(currBucket), keyPtr, hctl->keysize) == 0)
- break;
- prevBucketPtr = &(currBucket->link);
- currBucket = *prevBucketPtr;
-#if HASH_STATISTICS
- hash_collisions++;
- hctl->collisions++;
-#endif
- }
- }
-
- if (foundPtr)
- *foundPtr = (bool) (currBucket != NULL);
-
- /*
- * OK, now what?
- */
- switch (action)
- {
- case HASH_FIND:
- if (currBucket != NULL)
- return (void *) ELEMENTKEY(currBucket);
- return NULL;
-
- case HASH_FIND_SAVE:
- if (currBucket != NULL)
- {
- saveState.currBucket = currBucket;
- saveState.prevBucketPtr = prevBucketPtr;
- return (void *) ELEMENTKEY(currBucket);
- }
- return NULL;
-
- case HASH_REMOVE:
- case HASH_REMOVE_SAVED:
- if (currBucket != NULL)
- {
- Assert(hctl->nentries > 0);
- hctl->nentries--;
-
- /* remove record from hash bucket's chain. */
- *prevBucketPtr = currBucket->link;
-
- /* add the record to the freelist for this table. */
- currBucket->link = hctl->freeList;
- hctl->freeList = currBucket;
-
- /*
- * 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
- */
- return (void *) ELEMENTKEY(currBucket);
- }
- return NULL;
-
- case HASH_ENTER:
- /* Return existing element if found, else create one */
- if (currBucket != NULL)
- return (void *) ELEMENTKEY(currBucket);
-
- /* get the next free element */
- currBucket = hctl->freeList;
- if (currBucket == NULL)
- {
- /* no free elements. allocate another chunk of buckets */
- if (!element_alloc(hashp))
- return NULL; /* out of memory */
- currBucket = hctl->freeList;
- Assert(currBucket != NULL);
- }
-
- hctl->freeList = currBucket->link;
-
- /* link into hashbucket chain */
- *prevBucketPtr = currBucket;
- currBucket->link = NULL;
-
- /* copy key into record */
- memcpy(ELEMENTKEY(currBucket), keyPtr, hctl->keysize);
-
- /* caller is expected to fill the data field on return */
-
- /* Check if it is time to split the segment */
- if (++hctl->nentries / (long) (hctl->max_bucket + 1) > hctl->ffactor)
- {
- /*
- * NOTE: failure to expand table is not a fatal error, it
- * just means we have to run at higher fill factor than we
- * wanted.
- */
- expand_table(hashp);
- }
-
- return (void *) ELEMENTKEY(currBucket);
- }
-
- elog(ERROR, "hash_search: bogus action %d", (int) action);
-
- return NULL; /* keep compiler quiet */
-}
-
-/*
- * hash_seq_init/_search
- * Sequentially search through hash table and return
- * all the elements one by one, return NULL when no more.
- *
- * NOTE: caller may delete the returned element before continuing the scan.
- * However, deleting any other element while the scan is in progress is
- * UNDEFINED (it might be the one that curIndex is pointing at!). Also,
- * if elements are added to the table while the scan is in progress, it is
- * unspecified whether they will be visited by the scan or not.
- */
-void
-hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
-{
- status->hashp = hashp;
- status->curBucket = 0;
- status->curEntry = NULL;
-}
-
-void *
-hash_seq_search(HASH_SEQ_STATUS *status)
-{
- HTAB *hashp = status->hashp;
- HASHHDR *hctl = hashp->hctl;
-
- while (status->curBucket <= hctl->max_bucket)
- {
- long segment_num;
- long segment_ndx;
- HASHSEGMENT segp;
-
- if (status->curEntry != NULL)
- {
- /* Continuing scan of curBucket... */
- HASHELEMENT *curElem;
-
- curElem = status->curEntry;
- status->curEntry = curElem->link;
- if (status->curEntry == NULL) /* end of this bucket */
- ++status->curBucket;
- return (void *) ELEMENTKEY(curElem);
- }
-
- /*
- * initialize the search within this bucket.
- */
- segment_num = status->curBucket >> hctl->sshift;
- segment_ndx = MOD(status->curBucket, hctl->ssize);
-
- /*
- * first find the right segment in the table directory.
- */
- segp = hashp->dir[segment_num];
- if (segp == NULL)
- hash_corrupted(hashp);
-
- /*
- * 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.
- */
- status->curEntry = segp[segment_ndx];
-
- if (status->curEntry == NULL) /* empty bucket */
- ++status->curBucket;
- }
-
- return NULL; /* out of buckets */
-}
-
-
-/********************************* UTILITIES ************************/
-
-/*
- * Expand the table by adding one more hash bucket.
- */
-static bool
-expand_table(HTAB *hashp)
-{
- HASHHDR *hctl = hashp->hctl;
- HASHSEGMENT old_seg,
- new_seg;
- long old_bucket,
- new_bucket;
- long new_segnum,
- new_segndx;
- long old_segnum,
- old_segndx;
- HASHBUCKET *oldlink,
- *newlink;
- HASHBUCKET currElement,
- nextElement;
-
-#ifdef HASH_STATISTICS
- hash_expansions++;
-#endif
-
- new_bucket = hctl->max_bucket + 1;
- new_segnum = new_bucket >> hctl->sshift;
- new_segndx = MOD(new_bucket, hctl->ssize);
-
- if (new_segnum >= hctl->nsegs)
- {
- /* Allocate new segment if necessary -- could fail if dir full */
- if (new_segnum >= hctl->dsize)
- if (!dir_realloc(hashp))
- return false;
- if (!(hashp->dir[new_segnum] = seg_alloc(hashp)))
- return false;
- hctl->nsegs++;
- }
-
- /* OK, we created a new bucket */
- hctl->max_bucket++;
-
- /*
- * *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.
- */
- if ((uint32) new_bucket > hctl->high_mask)
- {
- hctl->low_mask = hctl->high_mask;
- hctl->high_mask = (uint32) new_bucket | hctl->low_mask;
- }
-
- /*
- * 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!
- */
- old_segnum = old_bucket >> hctl->sshift;
- old_segndx = MOD(old_bucket, hctl->ssize);
-
- old_seg = hashp->dir[old_segnum];
- new_seg = hashp->dir[new_segnum];
-
- oldlink = &old_seg[old_segndx];
- newlink = &new_seg[new_segndx];
-
- for (currElement = *oldlink;
- currElement != NULL;
- currElement = nextElement)
- {
- nextElement = currElement->link;
- if ((long) call_hash(hashp, (void *) ELEMENTKEY(currElement))
- == old_bucket)
- {
- *oldlink = currElement;
- oldlink = &currElement->link;
- }
- else
- {
- *newlink = currElement;
- newlink = &currElement->link;
- }
- }
- /* don't forget to terminate the rebuilt hash chains... */
- *oldlink = NULL;
- *newlink = NULL;
-
- return true;
-}
-
-
-static bool
-dir_realloc(HTAB *hashp)
-{
- HASHSEGMENT *p;
- HASHSEGMENT *old_p;
- long new_dsize;
- long old_dirsize;
- long new_dirsize;
-
- if (hashp->hctl->max_dsize != NO_MAX_DSIZE)
- return false;
-
- /* Reallocate directory */
- new_dsize = hashp->hctl->dsize << 1;
- old_dirsize = hashp->hctl->dsize * sizeof(HASHSEGMENT);
- new_dirsize = new_dsize * sizeof(HASHSEGMENT);
-
- old_p = hashp->dir;
- CurrentDynaHashCxt = hashp->hcxt;
- p = (HASHSEGMENT *) hashp->alloc((Size) new_dirsize);
-
- if (p != NULL)
- {
- memcpy(p, old_p, old_dirsize);
- MemSet(((char *) p) + old_dirsize, 0, new_dirsize - old_dirsize);
- MEM_FREE((char *) old_p);
- hashp->dir = p;
- hashp->hctl->dsize = new_dsize;
- return true;
- }
-
- return false;
-}
-
-
-static HASHSEGMENT
-seg_alloc(HTAB *hashp)
-{
- HASHSEGMENT segp;
-
- CurrentDynaHashCxt = hashp->hcxt;
- segp = (HASHSEGMENT) hashp->alloc(sizeof(HASHBUCKET) * hashp->hctl->ssize);
-
- if (!segp)
- return NULL;
-
- MemSet(segp, 0, sizeof(HASHBUCKET) * hashp->hctl->ssize);
-
- return segp;
-}
-
-/*
- * allocate some new elements and link them into the free list
- */
-static bool
-element_alloc(HTAB *hashp)
-{
- HASHHDR *hctl = hashp->hctl;
- Size elementSize;
- HASHELEMENT *tmpElement;
- int i;
-
- /* Each element has a HASHELEMENT header plus user data. */
- elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(hctl->entrysize);
-
- CurrentDynaHashCxt = hashp->hcxt;
- tmpElement = (HASHELEMENT *)
- hashp->alloc(HASHELEMENT_ALLOC_INCR * elementSize);
-
- if (!tmpElement)
- return false;
-
- /* link all the new entries into the freelist */
- for (i = 0; i < HASHELEMENT_ALLOC_INCR; i++)
- {
- tmpElement->link = hctl->freeList;
- hctl->freeList = tmpElement;
- tmpElement = (HASHELEMENT *) (((char *) tmpElement) + elementSize);
- }
-
- return true;
-}
-
-/* complain when we have detected a corrupted hashtable */
-static void
-hash_corrupted(HTAB *hashp)
-{
- /*
- * If the corruption is in a shared hashtable, we'd better force a
- * systemwide restart. Otherwise, just shut down this one backend.
- */
- if (hashp->isshared)
- elog(PANIC, "Hash table '%s' corrupted", hashp->tabname);
- else
- elog(FATAL, "Hash table '%s' corrupted", hashp->tabname);
-}
-
-/* calculate ceil(log base 2) of num */
-int
-my_log2(long num)
-{
- int i;
- long limit;
-
- for (i = 0, limit = 1; limit < num; i++, limit <<= 1)
- ;
- return i;
-}