summaryrefslogtreecommitdiff
path: root/src/include/utils/memutils_memorychunk.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/utils/memutils_memorychunk.h')
-rw-r--r--src/include/utils/memutils_memorychunk.h237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/include/utils/memutils_memorychunk.h b/src/include/utils/memutils_memorychunk.h
new file mode 100644
index 00000000000..685c177b681
--- /dev/null
+++ b/src/include/utils/memutils_memorychunk.h
@@ -0,0 +1,237 @@
+/*-------------------------------------------------------------------------
+ *
+ * memutils_memorychunk.h
+ * Here we define a struct named MemoryChunk which implementations of
+ * MemoryContexts may use as a header for chunks of memory they allocate.
+ *
+ * MemoryChunk provides a lightweight header that a MemoryContext can use to
+ * store a reference back to the block the which the given chunk is allocated
+ * on and also an additional 30-bits to store another value such as the size
+ * of the allocated chunk.
+ *
+ * Although MemoryChunks are used by each of our MemoryContexts, future
+ * implementations may choose to implement their own method for storing chunk
+ * headers. The only requirement is that the header ends with an 8-byte value
+ * which the least significant 3-bits of are set to the MemoryContextMethodID
+ * of the given context.
+ *
+ * By default, a MemoryChunk is 8 bytes in size, however, when
+ * MEMORY_CONTEXT_CHECKING is defined the header becomes 16 bytes in size due
+ * to the additional requested_size field. The MemoryContext may use this
+ * field for whatever they wish, but it is intended to be used for additional
+ * checks which are only done in MEMORY_CONTEXT_CHECKING builds.
+ *
+ * The MemoryChunk contains a uint64 field named 'hdrmask'. This field is
+ * used to encode 4 separate pieces of information. Starting with the least
+ * significant bits of 'hdrmask', the bit space is reserved as follows:
+ *
+ * 1. 3-bits to indicate the MemoryContextMethodID as defined by
+ * MEMORY_CONTEXT_METHODID_MASK
+ * 2. 1-bit to denote an "external" chunk (see below)
+ * 3. 30-bits reserved for the MemoryContext to use for anything it
+ * requires. Most MemoryContext likely want to store the size of the
+ * chunk here.
+ * 4. 30-bits for the number of bytes that must be subtracted from the chunk
+ * to obtain the address of the block that the chunk is stored on.
+ *
+ * In some cases, for example when memory allocations become large, it's
+ * possible fields 3 and 4 above are not large enough to store the values
+ * required for the chunk. In this case, the MemoryContext can choose to mark
+ * the chunk as "external" by calling the MemoryChunkSetExternal() function.
+ * When this is done, fields 3 and 4 are unavailable for use by the
+ * MemoryContext and it's up to the MemoryContext itself to devise its own
+ * method for getting the reference to the block.
+ *
+ * Interface:
+ *
+ * MemoryChunkSetHdrMask:
+ * Used to set up a non-external MemoryChunk.
+ *
+ * MemoryChunkSetHdrMaskExternal:
+ * Used to set up an externally managed MemoryChunk.
+ *
+ * MemoryChunkIsExternal:
+ * Determine if the given MemoryChunk is externally managed, i.e.
+ * MemoryChunkSetHdrMaskExternal() was called on the chunk.
+ *
+ * MemoryChunkGetValue:
+ * For non-external chunks, return the stored 30-bit value as it was set
+ * in the call to MemoryChunkSetHdrMask().
+ *
+ * MemoryChunkGetBlock:
+ * For non-external chunks, return a pointer to the block as it was set
+ * in the call to MemoryChunkSetHdrMask().
+ *
+ * Also exports:
+ * MEMORYCHUNK_MAX_VALUE
+ * MEMORYCHUNK_MAX_BLOCKOFFSET
+ * PointerGetMemoryChunk
+ * MemoryChunkGetPointer
+ *
+ * Portions Copyright (c) 2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/memutils_memorychunk.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef MEMUTILS_MEMORYCHUNK_H
+#define MEMUTILS_MEMORYCHUNK_H
+
+#include "utils/memutils_internal.h"
+
+ /*
+ * The maximum allowed value that MemoryContexts can store in the value
+ * field. Must be 1 less than a power of 2.
+ */
+#define MEMORYCHUNK_MAX_VALUE UINT64CONST(0x3FFFFFFF)
+
+/*
+ * The maximum distance in bytes that a MemoryChunk can be offset from the
+ * block that is storing the chunk. Must be 1 less than a power of 2.
+ */
+#define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)
+
+/* define the least significant base-0 bit of each portion of the hdrmask */
+#define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
+#define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
+#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 30)
+
+/*
+ * A magic number for storing in the free bits of an external chunk. This
+ * must mask out the bits used for storing the MemoryContextMethodID and the
+ * external bit.
+ */
+#define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \
+ MEMORYCHUNK_VALUE_BASEBIT << \
+ MEMORYCHUNK_VALUE_BASEBIT)
+
+typedef struct MemoryChunk
+{
+#ifdef MEMORY_CONTEXT_CHECKING
+ Size requested_size;
+#endif
+
+ /* bitfield for storing details about the chunk */
+ uint64 hdrmask; /* must be last */
+} MemoryChunk;
+
+/* Get the MemoryChunk from the pointer */
+#define PointerGetMemoryChunk(p) \
+ ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk)))
+/* Get the pointer from the MemoryChunk */
+#define MemoryChunkGetPointer(c) \
+ ((void *) ((char *) (c) + sizeof(MemoryChunk)))
+
+/* private macros for making the inline functions below more simple */
+#define HdrMaskIsExternal(hdrmask) \
+ ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT))
+#define HdrMaskGetValue(hdrmask) \
+ (((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
+
+/*
+ * We should have used up all the bits here, so the compiler is likely to
+ * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET.
+ */
+#define HdrMaskBlockOffset(hdrmask) \
+ (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET)
+
+/* For external chunks only, check the magic number matches */
+#define HdrMaskCheckMagic(hdrmask) \
+ (MEMORYCHUNK_MAGIC == \
+ ((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT << MEMORYCHUNK_VALUE_BASEBIT))
+/*
+ * MemoryChunkSetHdrMask
+ * Store the given 'block', 'chunk_size' and 'methodid' in the given
+ * MemoryChunk.
+ *
+ * The number of bytes between 'block' and 'chunk' must be <=
+ * MEMORYCHUNK_MAX_BLOCKOFFSET.
+ * 'value' must be <= MEMORYCHUNK_MAX_VALUE.
+ */
+static inline void
+MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
+ Size value, MemoryContextMethodID methodid)
+{
+ Size blockoffset = (char *) chunk - (char *) block;
+
+ Assert((char *) chunk > (char *) block);
+ Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET);
+ Assert(value <= MEMORYCHUNK_MAX_VALUE);
+ Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK);
+
+ chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
+ (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
+ methodid;
+}
+
+/*
+ * MemoryChunkSetHdrMaskExternal
+ * Set 'chunk' as an externally managed chunk. Here we only record the
+ * MemoryContextMethodID and set the external chunk bit.
+ */
+static inline void
+MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk,
+ MemoryContextMethodID methodid)
+{
+ Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK);
+
+ chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) |
+ methodid;
+}
+
+/*
+ * MemoryChunkIsExternal
+ * Return true if 'chunk' is marked as external.
+ */
+static inline bool
+MemoryChunkIsExternal(MemoryChunk *chunk)
+{
+ /*
+ * External chunks should always store MEMORYCHUNK_MAGIC in the upper
+ * portion of the hdrmask, check that nothing has stomped on that.
+ */
+ Assert(!HdrMaskIsExternal(chunk->hdrmask) ||
+ HdrMaskCheckMagic(chunk->hdrmask));
+
+ return HdrMaskIsExternal(chunk->hdrmask);
+}
+
+/*
+ * MemoryChunkGetValue
+ * For non-external chunks, returns the value field as it was set in
+ * MemoryChunkSetHdrMask.
+ */
+static inline Size
+MemoryChunkGetValue(MemoryChunk *chunk)
+{
+ Assert(!HdrMaskIsExternal(chunk->hdrmask));
+
+ return HdrMaskGetValue(chunk->hdrmask);
+}
+
+/*
+ * MemoryChunkGetBlock
+ * For non-external chunks, returns the pointer to the block as was set
+ * in MemoryChunkSetHdrMask.
+ */
+static inline void *
+MemoryChunkGetBlock(MemoryChunk *chunk)
+{
+ Assert(!HdrMaskIsExternal(chunk->hdrmask));
+
+ return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask));
+}
+
+/* cleanup all internal definitions */
+#undef MEMORYCHUNK_EXTERNAL_BASEBIT
+#undef MEMORYCHUNK_VALUE_BASEBIT
+#undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
+#undef MEMORYCHUNK_MAGIC
+#undef HdrMaskIsExternal
+#undef HdrMaskGetValue
+#undef HdrMaskBlockOffset
+#undef HdrMaskCheckMagic
+
+#endif /* MEMUTILS_MEMORYCHUNK_H */