PostgreSQL Source Code git master
memutils_memorychunk.h
Go to the documentation of this file.
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-2025, 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
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
124typedef 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 */
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 */
168static inline void
171{
172 Size blockoffset = (char *) chunk - (char *) block;
173
174 Assert((char *) chunk >= (char *) block);
175 Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset);
177 Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
178
179 chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
181 methodid;
182}
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 */
189static inline void
191 MemoryContextMethodID methodid)
192{
193 Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
194
196 methodid;
197}
198
199/*
200 * MemoryChunkIsExternal
201 * Return true if 'chunk' is marked as external.
202 */
203static inline bool
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 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 */
221static inline Size
223{
224 Assert(!HdrMaskIsExternal(chunk->hdrmask));
225
226 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 */
234static inline void *
236{
237 Assert(!HdrMaskIsExternal(chunk->hdrmask));
238
239 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 */
#define Assert(condition)
Definition: c.h:812
uint64_t uint64
Definition: c.h:486
size_t Size
Definition: c.h:559
uint64 chunk
static struct @161 value
#define MEMORY_CONTEXT_METHODID_MASK
MemoryContextMethodID
#define MEMORYCHUNK_MAX_VALUE
static Size MemoryChunkGetValue(MemoryChunk *chunk)
#define MEMORYCHUNK_VALUE_BASEBIT
#define MEMORYCHUNK_BLOCKOFFSET_MASK
#define MEMORYCHUNK_EXTERNAL_BASEBIT
#define HdrMaskBlockOffset(hdrmask)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
#define HdrMaskCheckMagic(hdrmask)
#define HdrMaskIsExternal(hdrmask)
struct MemoryChunk MemoryChunk
#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
#define MEMORYCHUNK_MAGIC
#define HdrMaskGetValue(hdrmask)