Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * memutils_memorychunk.h 4 : * Here we define a struct named MemoryChunk which implementations of 5 : * MemoryContexts may use as a header for chunks of memory they allocate. 6 : * 7 : * MemoryChunk provides a lightweight header that a MemoryContext can use to 8 : * store a reference back to the block which the given chunk is allocated on 9 : * and also an additional 30-bits to store another value such as the size of 10 : * the allocated chunk. 11 : * 12 : * Although MemoryChunks are used by each of our MemoryContexts, future 13 : * implementations may choose to implement their own method for storing chunk 14 : * headers. The only requirement is that the header ends with an 8-byte value 15 : * which the least significant 4-bits of are set to the MemoryContextMethodID 16 : * of the given context. 17 : * 18 : * By default, a MemoryChunk is 8 bytes in size, however, when 19 : * MEMORY_CONTEXT_CHECKING is defined the header becomes 16 bytes in size due 20 : * to the additional requested_size field. The MemoryContext may use this 21 : * field for whatever they wish, but it is intended to be used for additional 22 : * checks which are only done in MEMORY_CONTEXT_CHECKING builds. 23 : * 24 : * The MemoryChunk contains a uint64 field named 'hdrmask'. This field is 25 : * used to encode 4 separate pieces of information. Starting with the least 26 : * significant bits of 'hdrmask', the bit space is reserved as follows: 27 : * 28 : * 1. 4-bits to indicate the MemoryContextMethodID as defined by 29 : * MEMORY_CONTEXT_METHODID_MASK 30 : * 2. 1-bit to denote an "external" chunk (see below) 31 : * 3. 30-bits reserved for the MemoryContext to use for anything it 32 : * requires. Most MemoryContexts likely want to store the size of the 33 : * chunk here. 34 : * 4. 30-bits for the number of bytes that must be subtracted from the chunk 35 : * to obtain the address of the block that the chunk is stored on. 36 : * 37 : * If you're paying close attention, you'll notice this adds up to 65 bits 38 : * rather than 64 bits. This is because the highest-order bit of #3 is the 39 : * same bit as the lowest-order bit of #4. We can do this as we insist that 40 : * the chunk and block pointers are both MAXALIGNed, therefore the relative 41 : * offset between those will always be a MAXALIGNed value which means the 42 : * lowest order bit is always 0. When fetching the chunk to block offset we 43 : * mask out the lowest-order bit to ensure it's still zero. 44 : * 45 : * In some cases, for example when memory allocations become large, it's 46 : * possible fields 3 and 4 above are not large enough to store the values 47 : * required for the chunk. In this case, the MemoryContext can choose to mark 48 : * the chunk as "external" by calling the MemoryChunkSetHdrMaskExternal() 49 : * function. When this is done, fields 3 and 4 are unavailable for use by the 50 : * MemoryContext and it's up to the MemoryContext itself to devise its own 51 : * method for getting the reference to the block. 52 : * 53 : * Interface: 54 : * 55 : * MemoryChunkSetHdrMask: 56 : * Used to set up a non-external MemoryChunk. 57 : * 58 : * MemoryChunkSetHdrMaskExternal: 59 : * Used to set up an externally managed MemoryChunk. 60 : * 61 : * MemoryChunkIsExternal: 62 : * Determine if the given MemoryChunk is externally managed, i.e. 63 : * MemoryChunkSetHdrMaskExternal() was called on the chunk. 64 : * 65 : * MemoryChunkGetValue: 66 : * For non-external chunks, return the stored 30-bit value as it was set 67 : * in the call to MemoryChunkSetHdrMask(). 68 : * 69 : * MemoryChunkGetBlock: 70 : * For non-external chunks, return a pointer to the block as it was set 71 : * in the call to MemoryChunkSetHdrMask(). 72 : * 73 : * Also exports: 74 : * MEMORYCHUNK_MAX_VALUE 75 : * MEMORYCHUNK_MAX_BLOCKOFFSET 76 : * PointerGetMemoryChunk 77 : * MemoryChunkGetPointer 78 : * 79 : * Portions Copyright (c) 2022-2024, PostgreSQL Global Development Group 80 : * Portions Copyright (c) 1994, Regents of the University of California 81 : * 82 : * src/include/utils/memutils_memorychunk.h 83 : * 84 : *------------------------------------------------------------------------- 85 : */ 86 : 87 : #ifndef MEMUTILS_MEMORYCHUNK_H 88 : #define MEMUTILS_MEMORYCHUNK_H 89 : 90 : #include "utils/memutils_internal.h" 91 : 92 : /* 93 : * The maximum allowed value that MemoryContexts can store in the value 94 : * field. Must be 1 less than a power of 2. 95 : */ 96 : #define MEMORYCHUNK_MAX_VALUE UINT64CONST(0x3FFFFFFF) 97 : 98 : /* 99 : * The maximum distance in bytes that a MemoryChunk can be offset from the 100 : * block that is storing the chunk. Must be 1 less than a power of 2. 101 : */ 102 : #define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF) 103 : 104 : /* 105 : * As above, but mask out the lowest-order (always zero) bit as this is shared 106 : * with the MemoryChunkGetValue field. 107 : */ 108 : #define MEMORYCHUNK_BLOCKOFFSET_MASK UINT64CONST(0x3FFFFFFE) 109 : 110 : /* define the least significant base-0 bit of each portion of the hdrmask */ 111 : #define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS 112 : #define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1) 113 : #define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 29) 114 : 115 : /* 116 : * A magic number for storing in the free bits of an external chunk. This 117 : * must mask out the bits used for storing the MemoryContextMethodID and the 118 : * external bit. 119 : */ 120 : #define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \ 121 : MEMORYCHUNK_VALUE_BASEBIT << \ 122 : MEMORYCHUNK_VALUE_BASEBIT) 123 : 124 : typedef struct MemoryChunk 125 : { 126 : #ifdef MEMORY_CONTEXT_CHECKING 127 : Size requested_size; 128 : #endif 129 : 130 : /* bitfield for storing details about the chunk */ 131 : uint64 hdrmask; /* must be last */ 132 : } MemoryChunk; 133 : 134 : /* Get the MemoryChunk from the pointer */ 135 : #define PointerGetMemoryChunk(p) \ 136 : ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk))) 137 : /* Get the pointer from the MemoryChunk */ 138 : #define MemoryChunkGetPointer(c) \ 139 : ((void *) ((char *) (c) + sizeof(MemoryChunk))) 140 : 141 : /* private macros for making the inline functions below more simple */ 142 : #define HdrMaskIsExternal(hdrmask) \ 143 : ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT)) 144 : #define HdrMaskGetValue(hdrmask) \ 145 : (((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE) 146 : 147 : /* 148 : * Shift the block offset down to the 0th bit position and mask off the single 149 : * bit that's shared with the MemoryChunkGetValue field. 150 : */ 151 : #define HdrMaskBlockOffset(hdrmask) \ 152 : (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_BLOCKOFFSET_MASK) 153 : 154 : /* For external chunks only, check the magic number matches */ 155 : #define HdrMaskCheckMagic(hdrmask) \ 156 : (MEMORYCHUNK_MAGIC == \ 157 : ((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT << MEMORYCHUNK_VALUE_BASEBIT)) 158 : /* 159 : * MemoryChunkSetHdrMask 160 : * Store the given 'block', 'chunk_size' and 'methodid' in the given 161 : * MemoryChunk. 162 : * 163 : * The number of bytes between 'block' and 'chunk' must be <= 164 : * MEMORYCHUNK_MAX_BLOCKOFFSET. 165 : * 'value' must be <= MEMORYCHUNK_MAX_VALUE. 166 : * Both 'chunk' and 'block' must be MAXALIGNed pointers. 167 : */ 168 : static inline void 169 855876792 : MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, 170 : Size value, MemoryContextMethodID methodid) 171 : { 172 855876792 : Size blockoffset = (char *) chunk - (char *) block; 173 : 174 : Assert((char *) chunk >= (char *) block); 175 : Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset); 176 : Assert(value <= MEMORYCHUNK_MAX_VALUE); 177 : Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); 178 : 179 855876792 : chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) | 180 855876792 : (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) | 181 : methodid; 182 855876792 : } 183 : 184 : /* 185 : * MemoryChunkSetHdrMaskExternal 186 : * Set 'chunk' as an externally managed chunk. Here we only record the 187 : * MemoryContextMethodID and set the external chunk bit. 188 : */ 189 : static inline void 190 15831098 : MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, 191 : MemoryContextMethodID methodid) 192 : { 193 : Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); 194 : 195 15831098 : chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) | 196 : methodid; 197 15831098 : } 198 : 199 : /* 200 : * MemoryChunkIsExternal 201 : * Return true if 'chunk' is marked as external. 202 : */ 203 : static inline bool 204 444295916 : MemoryChunkIsExternal(MemoryChunk *chunk) 205 : { 206 : /* 207 : * External chunks should always store MEMORYCHUNK_MAGIC in the upper 208 : * portion of the hdrmask, check that nothing has stomped on that. 209 : */ 210 : Assert(!HdrMaskIsExternal(chunk->hdrmask) || 211 : HdrMaskCheckMagic(chunk->hdrmask)); 212 : 213 444295916 : return HdrMaskIsExternal(chunk->hdrmask); 214 : } 215 : 216 : /* 217 : * MemoryChunkGetValue 218 : * For non-external chunks, returns the value field as it was set in 219 : * MemoryChunkSetHdrMask. 220 : */ 221 : static inline Size 222 414049464 : MemoryChunkGetValue(MemoryChunk *chunk) 223 : { 224 : Assert(!HdrMaskIsExternal(chunk->hdrmask)); 225 : 226 414049464 : return HdrMaskGetValue(chunk->hdrmask); 227 : } 228 : 229 : /* 230 : * MemoryChunkGetBlock 231 : * For non-external chunks, returns the pointer to the block as was set 232 : * in MemoryChunkSetHdrMask. 233 : */ 234 : static inline void * 235 396780962 : MemoryChunkGetBlock(MemoryChunk *chunk) 236 : { 237 : Assert(!HdrMaskIsExternal(chunk->hdrmask)); 238 : 239 396780962 : return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask)); 240 : } 241 : 242 : /* cleanup all internal definitions */ 243 : #undef MEMORYCHUNK_BLOCKOFFSET_MASK 244 : #undef MEMORYCHUNK_EXTERNAL_BASEBIT 245 : #undef MEMORYCHUNK_VALUE_BASEBIT 246 : #undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT 247 : #undef MEMORYCHUNK_MAGIC 248 : #undef HdrMaskIsExternal 249 : #undef HdrMaskGetValue 250 : #undef HdrMaskBlockOffset 251 : #undef HdrMaskCheckMagic 252 : 253 : #endif /* MEMUTILS_MEMORYCHUNK_H */