/src/postgres/src/include/utils/memutils_memorychunk.h
Line | Count | Source |
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-2026, 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 | 694k | #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 | 697k | #define MEMORYCHUNK_BLOCKOFFSET_MASK UINT64CONST(0x3FFFFFFE) |
109 | | |
110 | | /* define the least significant base-0 bit of each portion of the hdrmask */ |
111 | 22.9M | #define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS |
112 | 22.2M | #define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1) |
113 | 11.0M | #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 | 3.78k | #define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \ |
121 | 3.78k | MEMORYCHUNK_VALUE_BASEBIT << \ |
122 | 3.78k | 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 | 701k | ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk))) |
137 | | /* Get the pointer from the MemoryChunk */ |
138 | | #define MemoryChunkGetPointer(c) \ |
139 | 11.0M | ((void *) ((char *) (c) + sizeof(MemoryChunk))) |
140 | | |
141 | | /* private macros for making the inline functions below more simple */ |
142 | | #define HdrMaskIsExternal(hdrmask) \ |
143 | 701k | ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT)) |
144 | | #define HdrMaskGetValue(hdrmask) \ |
145 | 694k | (((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 | 697k | (((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 | | MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, |
170 | | Size value, MemoryContextMethodID methodid) |
171 | 10.4M | { |
172 | 10.4M | Size blockoffset = (char *) chunk - (char *) block; |
173 | | |
174 | 10.4M | Assert((char *) chunk >= (char *) block); |
175 | 10.4M | Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset); |
176 | 10.4M | Assert(value <= MEMORYCHUNK_MAX_VALUE); |
177 | 10.4M | Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); |
178 | | |
179 | 10.4M | chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) | |
180 | 10.4M | (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) | |
181 | 10.4M | methodid; |
182 | 10.4M | } Unexecuted instantiation: nodeAgg.c:MemoryChunkSetHdrMask Unexecuted instantiation: alignedalloc.c:MemoryChunkSetHdrMask aset.c:MemoryChunkSetHdrMask Line | Count | Source | 171 | 10.4M | { | 172 | 10.4M | Size blockoffset = (char *) chunk - (char *) block; | 173 | | | 174 | 10.4M | Assert((char *) chunk >= (char *) block); | 175 | 10.4M | Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset); | 176 | 10.4M | Assert(value <= MEMORYCHUNK_MAX_VALUE); | 177 | 10.4M | Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); | 178 | | | 179 | 10.4M | chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) | | 180 | 10.4M | (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) | | 181 | 10.4M | methodid; | 182 | 10.4M | } |
Unexecuted instantiation: bump.c:MemoryChunkSetHdrMask Unexecuted instantiation: generation.c:MemoryChunkSetHdrMask Unexecuted instantiation: mcxt.c:MemoryChunkSetHdrMask Unexecuted instantiation: slab.c:MemoryChunkSetHdrMask |
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 | | MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, |
191 | | MemoryContextMethodID methodid) |
192 | 3.78k | { |
193 | 3.78k | Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); |
194 | | |
195 | 3.78k | chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) | |
196 | 3.78k | methodid; |
197 | 3.78k | } Unexecuted instantiation: nodeAgg.c:MemoryChunkSetHdrMaskExternal Unexecuted instantiation: alignedalloc.c:MemoryChunkSetHdrMaskExternal aset.c:MemoryChunkSetHdrMaskExternal Line | Count | Source | 192 | 3.78k | { | 193 | 3.78k | Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK); | 194 | | | 195 | 3.78k | chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) | | 196 | 3.78k | methodid; | 197 | 3.78k | } |
Unexecuted instantiation: bump.c:MemoryChunkSetHdrMaskExternal Unexecuted instantiation: generation.c:MemoryChunkSetHdrMaskExternal Unexecuted instantiation: mcxt.c:MemoryChunkSetHdrMaskExternal Unexecuted instantiation: slab.c:MemoryChunkSetHdrMaskExternal |
198 | | |
199 | | /* |
200 | | * MemoryChunkIsExternal |
201 | | * Return true if 'chunk' is marked as external. |
202 | | */ |
203 | | static inline bool |
204 | | MemoryChunkIsExternal(MemoryChunk *chunk) |
205 | 701k | { |
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 | 701k | Assert(!HdrMaskIsExternal(chunk->hdrmask) || |
211 | 701k | HdrMaskCheckMagic(chunk->hdrmask)); |
212 | | |
213 | 701k | return HdrMaskIsExternal(chunk->hdrmask); |
214 | 701k | } Unexecuted instantiation: nodeAgg.c:MemoryChunkIsExternal Unexecuted instantiation: alignedalloc.c:MemoryChunkIsExternal aset.c:MemoryChunkIsExternal Line | Count | Source | 205 | 701k | { | 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 | 701k | Assert(!HdrMaskIsExternal(chunk->hdrmask) || | 211 | 701k | HdrMaskCheckMagic(chunk->hdrmask)); | 212 | | | 213 | 701k | return HdrMaskIsExternal(chunk->hdrmask); | 214 | 701k | } |
Unexecuted instantiation: bump.c:MemoryChunkIsExternal Unexecuted instantiation: generation.c:MemoryChunkIsExternal Unexecuted instantiation: mcxt.c:MemoryChunkIsExternal Unexecuted instantiation: slab.c:MemoryChunkIsExternal |
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 | | MemoryChunkGetValue(MemoryChunk *chunk) |
223 | 694k | { |
224 | 694k | Assert(!HdrMaskIsExternal(chunk->hdrmask)); |
225 | | |
226 | 694k | return HdrMaskGetValue(chunk->hdrmask); |
227 | 694k | } Unexecuted instantiation: nodeAgg.c:MemoryChunkGetValue Unexecuted instantiation: alignedalloc.c:MemoryChunkGetValue aset.c:MemoryChunkGetValue Line | Count | Source | 223 | 694k | { | 224 | 694k | Assert(!HdrMaskIsExternal(chunk->hdrmask)); | 225 | | | 226 | | return HdrMaskGetValue(chunk->hdrmask); | 227 | 694k | } |
Unexecuted instantiation: bump.c:MemoryChunkGetValue Unexecuted instantiation: generation.c:MemoryChunkGetValue Unexecuted instantiation: mcxt.c:MemoryChunkGetValue Unexecuted instantiation: slab.c:MemoryChunkGetValue |
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 | | MemoryChunkGetBlock(MemoryChunk *chunk) |
236 | 697k | { |
237 | 697k | Assert(!HdrMaskIsExternal(chunk->hdrmask)); |
238 | | |
239 | | return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask)); |
240 | 697k | } Unexecuted instantiation: nodeAgg.c:MemoryChunkGetBlock Unexecuted instantiation: alignedalloc.c:MemoryChunkGetBlock aset.c:MemoryChunkGetBlock Line | Count | Source | 236 | 697k | { | 237 | 697k | Assert(!HdrMaskIsExternal(chunk->hdrmask)); | 238 | | | 239 | | return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask)); | 240 | 697k | } |
Unexecuted instantiation: bump.c:MemoryChunkGetBlock Unexecuted instantiation: generation.c:MemoryChunkGetBlock Unexecuted instantiation: mcxt.c:MemoryChunkGetBlock Unexecuted instantiation: slab.c:MemoryChunkGetBlock |
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 */ |