Пример #1
0
/*
 * hash_create -- create a new dynamic hash table
 *
 *	tabname: a name for the table (for debugging purposes)
 *	nelem: maximum number of elements expected
 *	*info: additional table parameters, as indicated by flags
 *	flags: bitmask indicating which parameters to take from *info
 *
 * Note: for a shared-memory hashtable, nelem needs to be a pretty good
 * estimate, since we can't expand the table on the fly.  But an unshared
 * hashtable can be expanded on-the-fly, so it's better for nelem to be
 * on the small side and let the table grow if it's exceeded.  An overly
 * large nelem will penalize hash_seq_search speed without buying much.
 */
HTAB *
hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
{
	HTAB	   *hashp;
	HASHHDR    *hctl;

	/*
	 * For shared hash tables, we have a local hash header (HTAB struct) that
	 * we allocate in TopMemoryContext; all else is in shared memory.
	 *
	 * For non-shared hash tables, everything including the hash header is in
	 * a memory context created specially for the hash table --- this makes
	 * hash_destroy very simple.  The memory context is made a child of either
	 * a context specified by the caller, or TopMemoryContext if nothing is
	 * specified.
	 */
	if (flags & HASH_SHARED_MEM)
	{
		/* Set up to allocate the hash header */
		CurrentDynaHashCxt = TopMemoryContext;
	}
	else
	{
		/* Create the hash table's private memory context */
		if (flags & HASH_CONTEXT)
			CurrentDynaHashCxt = info->hcxt;
		else
			CurrentDynaHashCxt = TopMemoryContext;
		CurrentDynaHashCxt = AllocSetContextCreate(CurrentDynaHashCxt,
												   tabname,
												   ALLOCSET_DEFAULT_MINSIZE,
												   ALLOCSET_DEFAULT_INITSIZE,
												   ALLOCSET_DEFAULT_MAXSIZE);
	}

	/* Initialize the hash header, plus a copy of the table name */
	hashp = (HTAB *) DynaHashAlloc(sizeof(HTAB) + strlen(tabname) +1);
	MemSet(hashp, 0, sizeof(HTAB));

	hashp->tabname = (char *) (hashp + 1);
	strcpy(hashp->tabname, tabname);

	if (flags & HASH_FUNCTION)
		hashp->hash = info->hash;
	else
		hashp->hash = string_hash;		/* default hash function */

	/*
	 * If you don't specify a match function, it defaults to string_compare if
	 * you used string_hash (either explicitly or by default) and to memcmp
	 * otherwise.  (Prior to PostgreSQL 7.4, memcmp was always used.)
	 */
	if (flags & HASH_COMPARE)
		hashp->match = info->match;
	else if (hashp->hash == string_hash)
		hashp->match = (HashCompareFunc) string_compare;
	else
		hashp->match = memcmp;

	/*
	 * Similarly, the key-copying function defaults to strlcpy or memcpy.
	 */
	if (flags & HASH_KEYCOPY)
		hashp->keycopy = info->keycopy;
	else if (hashp->hash == string_hash)
		hashp->keycopy = (HashCopyFunc) strlcpy;
	else
		hashp->keycopy = memcpy;

	if (flags & HASH_ALLOC)
		hashp->alloc = info->alloc;
	else
		hashp->alloc = DynaHashAlloc;

	if (flags & HASH_SHARED_MEM)
	{
		/*
		 * ctl structure and directory are preallocated for shared memory
		 * tables.	Note that HASH_DIRSIZE and HASH_ALLOC had better be set as
		 * well.
		 */
		hashp->hctl = info->hctl;
		hashp->dir = (HASHSEGMENT *) (((char *) info->hctl) + sizeof(HASHHDR));
		hashp->hcxt = NULL;
		hashp->isshared = true;

		/* hash table already exists, we're just attaching to it */
		if (flags & HASH_ATTACH)
		{
			/* make local copies of some heavily-used values */
			hctl = hashp->hctl;
			hashp->keysize = hctl->keysize;
			hashp->ssize = hctl->ssize;
			hashp->sshift = hctl->sshift;

			return hashp;
		}
	}
	else
	{
		/* setup hash table defaults */
		hashp->hctl = NULL;
		hashp->dir = NULL;
		hashp->hcxt = CurrentDynaHashCxt;
		hashp->isshared = false;
	}

	if (!hashp->hctl)
	{
		hashp->hctl = (HASHHDR *) hashp->alloc(sizeof(HASHHDR));
		if (!hashp->hctl)
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
	}

	hashp->frozen = false;

	hdefault(hashp);

	hctl = hashp->hctl;

	if (flags & HASH_PARTITION)
	{
		/* Doesn't make sense to partition a local hash table */
		Assert(flags & HASH_SHARED_MEM);

		/*
		 * The number of partitions had better be a power of 2. Also, it must
		 * be less than INT_MAX (see init_htab()), so call the int version of
		 * next_pow2.
		 */
		Assert(info->num_partitions == next_pow2_int(info->num_partitions));

		hctl->num_partitions = info->num_partitions;
	}

	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)
	{
		Assert(info->entrysize >= info->keysize);
		hctl->keysize = info->keysize;
		hctl->entrysize = info->entrysize;
	}

	/* make local copies of heavily-used constant fields */
	hashp->keysize = hctl->keysize;
	hashp->ssize = hctl->ssize;
	hashp->sshift = hctl->sshift;

	/* Build the hash directory structure */
	if (!init_htab(hashp, nelem))
		elog(ERROR, "failed to initialize hash table \"%s\"", hashp->tabname);

	/*
	 * For a shared hash table, preallocate the requested number of elements.
	 * This reduces problems with run-time out-of-shared-memory conditions.
	 *
	 * For a non-shared hash table, preallocate the requested number of
	 * elements if it's less than our chosen nelem_alloc.  This avoids wasting
	 * space if the caller correctly estimates a small table size.
	 */
	if ((flags & HASH_SHARED_MEM) ||
		nelem < hctl->nelem_alloc)
	{
		if (!element_alloc(hashp, (int) nelem))
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
	}

	if (flags & HASH_FIXED_SIZE)
		hashp->isfixed = true;
	return hashp;
}
Пример #2
0
HTAB *
hash_create(int nelem, HASHCTL *info, int flags)
{
    register HHDR *	hctl;
    HTAB * 		hashp;
    
    
    hashp = (HTAB *) MEM_ALLOC((unsigned long) sizeof(HTAB));
    memset(hashp, 0, sizeof(HTAB)); 
    
    if ( flags & HASH_FUNCTION ) {
	hashp->hash    = info->hash;
    } else {
	/* default */
	hashp->hash	 = string_hash;
    }
    
    if ( flags & HASH_SHARED_MEM )  {
	/* ctl structure is preallocated for shared memory tables */
	
	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) {
	    return(hashp);
	}
	
    } else {
	/* setup hash table defaults */
	
	hashp->alloc	  = (dhalloc_ptr) MEM_ALLOC;
	hashp->dir	  = NULL;
	hashp->segbase  = NULL;
	
    }
    
    if (! hashp->hctl) {
	hashp->hctl = (HHDR *) hashp->alloc((unsigned long)sizeof(HHDR));
	if (! hashp->hctl) {
	    return(0);
	}
    }
    
    if ( !hdefault(hashp) ) return(0);
    hctl = hashp->hctl;
#ifdef HASH_STATISTICS
    hctl->accesses = hctl->collisions = 0;
#endif
    
    if ( flags & HASH_BUCKET )   {
	hctl->bsize   = info->bsize;
	hctl->bshift  = my_log2(info->bsize);
    }
    if ( flags & HASH_SEGMENT )  {
	hctl->ssize   = info->ssize;
	hctl->sshift  = my_log2(info->ssize);
    }
    if ( flags & HASH_FFACTOR )  {
	hctl->ffactor = info->ffactor;
    }
    
    /*
     * SHM hash tables have fixed maximum size (allocate
     * a maximal sized directory).
     */
    if ( flags & HASH_DIRSIZE )  {
	hctl->max_dsize = my_log2(info->max_size);
	hctl->dsize     = my_log2(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->datasize   = info->datasize;
    }
    if ( flags & HASH_ALLOC )  {
	hashp->alloc = info->alloc;
    }
    
    if ( init_htab (hashp, nelem ) ) {
	hash_destroy(hashp);
	return(0);
    }
    return(hashp);
}
Пример #3
0
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 you don't specify a match function, it defaults to strncmp() if
	 * you used string_hash (either explicitly or by default) and to
	 * memcmp() otherwise.  (Prior to PostgreSQL 7.4, memcmp() was always
	 * used.)
	 */
	if (flags & HASH_COMPARE)
		hashp->match = info->match;
	else if (hashp->hash == string_hash)
		hashp->match = (HashCompareFunc) strncmp;
	else
		hashp->match = memcmp;

	/*
	 * Similarly, the key-copying function defaults to strncpy() or memcpy().
	 */
	if (hashp->hash == string_hash)
		hashp->keycopy = (HashCopyFunc) strncpy;
	else
		hashp->keycopy = memcpy;

	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 = CurrentDynaHashCxt;
		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
	{
		/* remaining hash table structures live in child of given context */
		hashp->hcxt = AllocSetContextCreate(CurrentDynaHashCxt,
											"DynaHashTable",
											ALLOCSET_DEFAULT_MINSIZE,
											ALLOCSET_DEFAULT_INITSIZE,
											ALLOCSET_DEFAULT_MAXSIZE);
		CurrentDynaHashCxt = hashp->hcxt;
	}

	if (!init_htab(hashp, nelem))
	{
		hash_destroy(hashp);
		return NULL;
	}
	return hashp;
}