From 3441027b9ac2f135dea7ec155503be9d6331dfc1 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Sat, 27 Mar 2021 08:34:58 +1300 Subject: [PATCH v13 2/5] Make simplehash easy to use in shmem. Allow "in-place" creation of a simplehash hash table of fixed size, suitable for use in shared memory. No calling out to allocators, and no ability to grow. --- src/include/lib/simplehash.h | 56 +++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h index 395be1ca9a..32d3fa58fe 100644 --- a/src/include/lib/simplehash.h +++ b/src/include/lib/simplehash.h @@ -120,6 +120,7 @@ #define SH_ALLOCATE SH_MAKE_NAME(allocate) #define SH_FREE SH_MAKE_NAME(free) #define SH_STAT SH_MAKE_NAME(stat) +#define SH_ESTIMATE_SIZE SH_MAKE_NAME(estimate_size) /* internal helper functions (no externally visible prototypes) */ #define SH_COMPUTE_PARAMETERS SH_MAKE_NAME(compute_parameters) @@ -153,16 +154,22 @@ typedef struct SH_TYPE /* boundary after which to grow hashtable */ uint32 grow_threshold; +#ifndef SH_IN_PLACE /* hash buckets */ SH_ELEMENT_TYPE *data; +#endif -#ifndef SH_RAW_ALLOCATOR +#if !defined(SH_RAW_ALLOCATOR) && !defined(SH_IN_PLACE) /* memory context to use for allocations */ MemoryContext ctx; #endif /* user defined data, useful for callbacks */ void *private_data; + +#ifdef SH_IN_PLACE + SH_ELEMENT_TYPE data[FLEXIBLE_ARRAY_MEMBER]; +#endif } SH_TYPE; typedef enum SH_STATUS @@ -182,6 +189,11 @@ typedef struct SH_ITERATOR #ifdef SH_RAW_ALLOCATOR /* _hash _create(uint32 nelements, void *private_data) */ SH_SCOPE SH_TYPE *SH_CREATE(uint32 nelements, void *private_data); +#elif defined(SH_IN_PLACE) +/* size_t _estimate_size(uint32 nelements) */ +SH_SCOPE size_t SH_ESTIMATE_SIZE(uint32 nelements); +/* void _create(_hash *place, uint32 nelements, void *private_data) */ +SH_SCOPE void SH_CREATE(SH_TYPE *place, uint32 nelements, void *private_data); #else /* * _hash _create(MemoryContext ctx, uint32 nelements, @@ -191,14 +203,18 @@ SH_SCOPE SH_TYPE *SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data); #endif +#ifndef SH_IN_PLACE /* void _destroy(_hash *tb) */ SH_SCOPE void SH_DESTROY(SH_TYPE * tb); +#endif /* void _reset(_hash *tb) */ SH_SCOPE void SH_RESET(SH_TYPE * tb); +#ifndef SH_IN_PLACE /* void _grow(_hash *tb) */ SH_SCOPE void SH_GROW(SH_TYPE * tb, uint32 newsize); +#endif /* *_insert(_hash *tb, key, bool *found) */ SH_SCOPE SH_ELEMENT_TYPE *SH_INSERT(SH_TYPE * tb, SH_KEY_TYPE key, bool *found); @@ -241,7 +257,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb); /* generate implementation of the hash table */ #ifdef SH_DEFINE -#ifndef SH_RAW_ALLOCATOR +#if !defined(SH_RAW_ALLOCATOR) && !defined(SH_IN_PLACE) #include "utils/memutils.h" #endif @@ -383,11 +399,13 @@ SH_ENTRY_HASH(SH_TYPE * tb, SH_ELEMENT_TYPE * entry) #endif } +#ifndef SH_IN_PLACE /* default memory allocator function */ static inline void *SH_ALLOCATE(SH_TYPE * type, Size size); static inline void SH_FREE(SH_TYPE * type, void *pointer); +#endif -#ifndef SH_USE_NONDEFAULT_ALLOCATOR +#if !defined(SH_USE_NONDEFAULT_ALLOCATOR) && !defined(SH_IN_PLACE) /* default memory allocator function */ static inline void * @@ -410,6 +428,22 @@ SH_FREE(SH_TYPE * type, void *pointer) #endif +#ifdef SH_IN_PLACE +/* + * Compute the amount of memory required for a fixed sized in-place hash table. + */ +SH_SCOPE size_t +SH_ESTIMATE_SIZE(uint32 nelements) +{ + size_t size; + + size = Max(nelements, 2); + size = pg_nextpower2_64(size); + + return offsetof(SH_TYPE, data) + sizeof(SH_ELEMENT_TYPE) * size; +} +#endif + /* * Create a hash table with enough space for `nelements` distinct members. * Memory for the hash table is allocated from the passed-in context. If @@ -422,6 +456,9 @@ SH_FREE(SH_TYPE * type, void *pointer) #ifdef SH_RAW_ALLOCATOR SH_SCOPE SH_TYPE * SH_CREATE(uint32 nelements, void *private_data) +#elif defined(SH_IN_PLACE) +SH_SCOPE void +SH_CREATE(SH_TYPE *place, uint32 nelements, void *private_data) #else SH_SCOPE SH_TYPE * SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data) @@ -432,6 +469,8 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data) #ifdef SH_RAW_ALLOCATOR tb = SH_RAW_ALLOCATOR(sizeof(SH_TYPE)); +#elif defined(SH_IN_PLACE) + tb = place; #else tb = MemoryContextAllocZero(ctx, sizeof(SH_TYPE)); tb->ctx = ctx; @@ -443,11 +482,15 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data) SH_COMPUTE_PARAMETERS(tb, size); +#if defined(SH_IN_PLACE) + memset(&tb->data, 0, sizeof(SH_ELEMENT_TYPE) * tb->size); +#else tb->data = SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * tb->size); - return tb; +#endif } +#ifndef SH_IN_PLACE /* destroy a previously created hash table */ SH_SCOPE void SH_DESTROY(SH_TYPE * tb) @@ -455,6 +498,7 @@ SH_DESTROY(SH_TYPE * tb) SH_FREE(tb, tb->data); pfree(tb); } +#endif /* reset the contents of a previously created hash table */ SH_SCOPE void @@ -464,6 +508,7 @@ SH_RESET(SH_TYPE * tb) tb->members = 0; } +#ifndef SH_IN_PLACE /* * Grow a hash table to at least `newsize` buckets. * @@ -576,6 +621,7 @@ SH_GROW(SH_TYPE * tb, uint32 newsize) SH_FREE(tb, olddata); } +#endif /* * This is a separate static inline function, so it can be reliably be inlined @@ -592,6 +638,7 @@ SH_INSERT_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash, bool *found) restart: insertdist = 0; +#ifndef SH_IN_PLACE /* * We do the grow check even if the key is actually present, to avoid * doing the check inside the loop. This also lets us avoid having to @@ -614,6 +661,7 @@ restart: SH_GROW(tb, tb->size * 2); /* SH_STAT(tb); */ } +#endif /* perform insert, start bucket search at optimal location */ data = tb->data; -- 2.30.1