From 3441027b9ac2f135dea7ec155503be9d6331dfc1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
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
 /* <prefix>_hash <prefix>_create(uint32 nelements, void *private_data) */
 SH_SCOPE	SH_TYPE *SH_CREATE(uint32 nelements, void *private_data);
+#elif defined(SH_IN_PLACE)
+/* size_t <prefix>_estimate_size(uint32 nelements) */
+SH_SCOPE	size_t SH_ESTIMATE_SIZE(uint32 nelements);
+/* void <prefix>_create(<prefix>_hash *place, uint32 nelements, void *private_data) */
+SH_SCOPE	void SH_CREATE(SH_TYPE *place, uint32 nelements, void *private_data);
 #else
 /*
  * <prefix>_hash <prefix>_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 <prefix>_destroy(<prefix>_hash *tb) */
 SH_SCOPE void SH_DESTROY(SH_TYPE * tb);
+#endif
 
 /* void <prefix>_reset(<prefix>_hash *tb) */
 SH_SCOPE void SH_RESET(SH_TYPE * tb);
 
+#ifndef SH_IN_PLACE
 /* void <prefix>_grow(<prefix>_hash *tb) */
 SH_SCOPE void SH_GROW(SH_TYPE * tb, uint32 newsize);
+#endif
 
 /* <element> *<prefix>_insert(<prefix>_hash *tb, <key> 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

