| /*------------------------------------------------------------------------- |
| * |
| * 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 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 MemoryChunkSetHdrMaskExternal() |
| * 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-2023, 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((int) 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((int) 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 */ |