]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
1 | /* |
2 | * Heap buffer representation. | |
3 | * | |
4 | * Heap allocated user data buffer which is either: | |
5 | * | |
6 | * 1. A fixed size buffer (data follows header statically) | |
7 | * 2. A dynamic size buffer (data pointer follows header) | |
8 | * | |
9 | * The data pointer for a variable size buffer of zero size may be NULL. | |
10 | */ | |
11 | ||
12 | #ifndef DUK_HBUFFER_H_INCLUDED | |
13 | #define DUK_HBUFFER_H_INCLUDED | |
14 | ||
15 | /* | |
16 | * Flags | |
17 | * | |
18 | * Fixed buffer: 0 | |
19 | * Dynamic buffer: DUK_HBUFFER_FLAG_DYNAMIC | |
20 | * External buffer: DUK_HBUFFER_FLAG_DYNAMIC | DUK_HBUFFER_FLAG_EXTERNAL | |
21 | */ | |
22 | ||
23 | #define DUK_HBUFFER_FLAG_DYNAMIC DUK_HEAPHDR_USER_FLAG(0) /* buffer is behind a pointer, dynamic or external */ | |
24 | #define DUK_HBUFFER_FLAG_EXTERNAL DUK_HEAPHDR_USER_FLAG(1) /* buffer pointer is to an externally allocated buffer */ | |
25 | ||
26 | #define DUK_HBUFFER_HAS_DYNAMIC(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) | |
27 | #define DUK_HBUFFER_HAS_EXTERNAL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) | |
28 | ||
29 | #define DUK_HBUFFER_SET_DYNAMIC(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) | |
30 | #define DUK_HBUFFER_SET_EXTERNAL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) | |
31 | ||
32 | #define DUK_HBUFFER_CLEAR_DYNAMIC(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) | |
33 | #define DUK_HBUFFER_CLEAR_EXTERNAL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) | |
34 | ||
35 | /* | |
36 | * Misc defines | |
37 | */ | |
38 | ||
39 | /* Impose a maximum buffer length for now. Restricted artificially to | |
40 | * ensure resize computations or adding a heap header length won't | |
41 | * overflow size_t and that a signed duk_int_t can hold a buffer | |
42 | * length. The limit should be synchronized with DUK_HSTRING_MAX_BYTELEN. | |
43 | */ | |
44 | ||
45 | #if defined(DUK_USE_BUFLEN16) | |
46 | #define DUK_HBUFFER_MAX_BYTELEN (0x0000ffffUL) | |
47 | #else | |
48 | /* Intentionally not 0x7fffffffUL; at least JSON code expects that | |
49 | * 2*len + 2 fits in 32 bits. | |
50 | */ | |
51 | #define DUK_HBUFFER_MAX_BYTELEN (0x7ffffffeUL) | |
52 | #endif | |
53 | ||
54 | /* | |
55 | * Field access | |
56 | */ | |
57 | ||
58 | /* Get/set the current user visible size, without accounting for a dynamic | |
59 | * buffer's "spare" (= usable size). | |
60 | */ | |
61 | #if defined(DUK_USE_BUFLEN16) | |
62 | /* size stored in duk_heaphdr unused flag bits */ | |
63 | #define DUK_HBUFFER_GET_SIZE(x) ((x)->hdr.h_flags >> 16) | |
64 | #define DUK_HBUFFER_SET_SIZE(x,v) do { \ | |
65 | duk_size_t duk__v; \ | |
66 | duk__v = (v); \ | |
67 | DUK_ASSERT(duk__v <= 0xffffUL); \ | |
68 | (x)->hdr.h_flags = ((x)->hdr.h_flags & 0x0000ffffUL) | (((duk_uint32_t) duk__v) << 16); \ | |
69 | } while (0) | |
70 | #define DUK_HBUFFER_ADD_SIZE(x,dv) do { \ | |
71 | (x)->hdr.h_flags += ((dv) << 16); \ | |
72 | } while (0) | |
73 | #define DUK_HBUFFER_SUB_SIZE(x,dv) do { \ | |
74 | (x)->hdr.h_flags -= ((dv) << 16); \ | |
75 | } while (0) | |
76 | #else | |
77 | #define DUK_HBUFFER_GET_SIZE(x) (((duk_hbuffer *) (x))->size) | |
78 | #define DUK_HBUFFER_SET_SIZE(x,v) do { \ | |
79 | ((duk_hbuffer *) (x))->size = (v); \ | |
80 | } while (0) | |
81 | #define DUK_HBUFFER_ADD_SIZE(x,dv) do { \ | |
82 | (x)->size += (dv); \ | |
83 | } while (0) | |
84 | #define DUK_HBUFFER_SUB_SIZE(x,dv) do { \ | |
85 | (x)->size -= (dv); \ | |
86 | } while (0) | |
87 | #endif | |
88 | ||
89 | #define DUK_HBUFFER_FIXED_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) | |
90 | #define DUK_HBUFFER_FIXED_SET_SIZE(x,v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x)) | |
91 | ||
92 | #define DUK_HBUFFER_DYNAMIC_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) | |
93 | #define DUK_HBUFFER_DYNAMIC_SET_SIZE(x,v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v)) | |
94 | #define DUK_HBUFFER_DYNAMIC_ADD_SIZE(x,dv) DUK_HBUFFER_ADD_SIZE((duk_hbuffer *) (x), (dv)) | |
95 | #define DUK_HBUFFER_DYNAMIC_SUB_SIZE(x,dv) DUK_HBUFFER_SUB_SIZE((duk_hbuffer *) (x), (dv)) | |
96 | ||
97 | #define DUK_HBUFFER_EXTERNAL_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) | |
98 | #define DUK_HBUFFER_EXTERNAL_SET_SIZE(x,v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v)) | |
99 | ||
100 | #define DUK_HBUFFER_FIXED_GET_DATA_PTR(heap,x) ((duk_uint8_t *) (((duk_hbuffer_fixed *) (x)) + 1)) | |
101 | ||
102 | #if defined(DUK_USE_HEAPPTR16) | |
103 | #define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap,x) \ | |
104 | ((void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, ((duk_heaphdr *) (x))->h_extra16)) | |
105 | #define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap,x,v) do { \ | |
106 | ((duk_heaphdr *) (x))->h_extra16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ | |
107 | } while (0) | |
108 | #define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap,x) do { \ | |
109 | ((duk_heaphdr *) (x))->h_extra16 = 0; /* assume 0 <=> NULL */ \ | |
110 | } while (0) | |
111 | #else | |
112 | #define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap,x) ((x)->curr_alloc) | |
113 | #define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap,x,v) do { \ | |
114 | (x)->curr_alloc = (void *) (v); \ | |
115 | } while (0) | |
116 | #define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap,x) do { \ | |
117 | (x)->curr_alloc = (void *) NULL; \ | |
118 | } while (0) | |
119 | #endif | |
120 | ||
121 | /* No pointer compression because pointer is potentially outside of | |
122 | * Duktape heap. | |
123 | */ | |
124 | #if defined(DUK_USE_HEAPPTR16) | |
125 | #define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap,x) \ | |
126 | ((void *) (x)->curr_alloc) | |
127 | #define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap,x,v) do { \ | |
128 | (x)->curr_alloc = (void *) (v); \ | |
129 | } while (0) | |
130 | #define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap,x) do { \ | |
131 | (x)->curr_alloc = (void *) NULL; \ | |
132 | } while (0) | |
133 | #else | |
134 | #define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap,x) \ | |
135 | ((void *) (x)->curr_alloc) | |
136 | #define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap,x,v) do { \ | |
137 | (x)->curr_alloc = (void *) (v); \ | |
138 | } while (0) | |
139 | #define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap,x) do { \ | |
140 | (x)->curr_alloc = (void *) NULL; \ | |
141 | } while (0) | |
142 | #endif | |
143 | ||
144 | /* Get a pointer to the current buffer contents (matching current allocation | |
145 | * size). May be NULL for zero size dynamic/external buffer. | |
146 | */ | |
147 | #if defined(DUK_USE_HEAPPTR16) | |
148 | #define DUK_HBUFFER_GET_DATA_PTR(heap,x) ( \ | |
149 | DUK_HBUFFER_HAS_DYNAMIC((x)) ? \ | |
150 | ( \ | |
151 | DUK_HBUFFER_HAS_EXTERNAL((x)) ? \ | |
152 | DUK_HBUFFER_EXTERNAL_GET_DATA_PTR((heap), (duk_hbuffer_external *) (x)) : \ | |
153 | DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) \ | |
154 | ) : \ | |
155 | DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (x)) \ | |
156 | ) | |
157 | #else | |
158 | /* Without heap pointer compression duk_hbuffer_dynamic and duk_hbuffer_external | |
159 | * have the same layout so checking for fixed vs. dynamic (or external) is enough. | |
160 | */ | |
161 | #define DUK_HBUFFER_GET_DATA_PTR(heap,x) ( \ | |
162 | DUK_HBUFFER_HAS_DYNAMIC((x)) ? \ | |
163 | DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) : \ | |
164 | DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (x)) \ | |
165 | ) | |
166 | #endif | |
167 | ||
168 | /* | |
169 | * Structs | |
170 | */ | |
171 | ||
172 | /* Shared prefix for all buffer types. */ | |
173 | struct duk_hbuffer { | |
174 | duk_heaphdr hdr; | |
175 | ||
176 | /* It's not strictly necessary to track the current size, but | |
177 | * it is useful for writing robust native code. | |
178 | */ | |
179 | ||
180 | /* Current size (not counting a dynamic buffer's "spare"). */ | |
181 | #if defined(DUK_USE_BUFLEN16) | |
182 | /* Stored in duk_heaphdr unused flags. */ | |
183 | #else | |
184 | duk_size_t size; | |
185 | #endif | |
186 | ||
187 | /* | |
188 | * Data following the header depends on the DUK_HBUFFER_FLAG_DYNAMIC | |
189 | * flag. | |
190 | * | |
191 | * If the flag is clear (the buffer is a fixed size one), the buffer | |
192 | * data follows the header directly, consisting of 'size' bytes. | |
193 | * | |
194 | * If the flag is set, the actual buffer is allocated separately, and | |
195 | * a few control fields follow the header. Specifically: | |
196 | * | |
197 | * - a "void *" pointing to the current allocation | |
198 | * - a duk_size_t indicating the full allocated size (always >= 'size') | |
199 | * | |
200 | * If DUK_HBUFFER_FLAG_EXTERNAL is set, the buffer has been allocated | |
201 | * by user code, so that Duktape won't be able to resize it and won't | |
202 | * free it. This allows buffers to point to e.g. an externally | |
203 | * allocated structure such as a frame buffer. | |
204 | * | |
205 | * Unlike strings, no terminator byte (NUL) is guaranteed after the | |
206 | * data. This would be convenient, but would pad aligned user buffers | |
207 | * unnecessarily upwards in size. For instance, if user code requested | |
208 | * a 64-byte dynamic buffer, 65 bytes would actually be allocated which | |
209 | * would then potentially round upwards to perhaps 68 or 72 bytes. | |
210 | */ | |
211 | }; | |
212 | ||
213 | /* Fixed buffer; data follows struct, with proper alignment guaranteed by | |
214 | * struct size. | |
215 | */ | |
216 | #if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA) | |
217 | #pragma pack(push, 8) | |
218 | #endif | |
219 | struct duk_hbuffer_fixed { | |
220 | /* A union is used here as a portable struct size / alignment trick: | |
221 | * by adding a 32-bit or a 64-bit (unused) union member, the size of | |
222 | * the struct is effectively forced to be a multiple of 4 or 8 bytes | |
223 | * (respectively) without increasing the size of the struct unless | |
224 | * necessary. | |
225 | */ | |
226 | union { | |
227 | struct { | |
228 | duk_heaphdr hdr; | |
229 | #if defined(DUK_USE_BUFLEN16) | |
230 | /* Stored in duk_heaphdr unused flags. */ | |
231 | #else | |
232 | duk_size_t size; | |
233 | #endif | |
234 | } s; | |
235 | #if (DUK_USE_ALIGN_BY == 4) | |
236 | duk_uint32_t dummy_for_align4; | |
237 | #elif (DUK_USE_ALIGN_BY == 8) | |
238 | duk_double_t dummy_for_align8; | |
239 | #elif (DUK_USE_ALIGN_BY == 1) | |
240 | /* no extra padding */ | |
241 | #else | |
242 | #error invalid DUK_USE_ALIGN_BY | |
243 | #endif | |
244 | } u; | |
245 | ||
246 | /* | |
247 | * Data follows the struct header. The struct size is padded by the | |
248 | * compiler based on the struct members. This guarantees that the | |
249 | * buffer data will be aligned-by-4 but not necessarily aligned-by-8. | |
250 | * | |
251 | * On platforms where alignment does not matter, the struct padding | |
252 | * could be removed (if there is any). On platforms where alignment | |
253 | * by 8 is required, the struct size must be forced to be a multiple | |
254 | * of 8 by some means. Without it, some user code may break, and also | |
255 | * Duktape itself breaks (e.g. the compiler stores duk_tvals in a | |
256 | * dynamic buffer). | |
257 | */ | |
258 | } | |
259 | #if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_GCC_ATTR) | |
260 | __attribute__ ((aligned (8))) | |
261 | #elif (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_CLANG_ATTR) | |
262 | __attribute__ ((aligned (8))) | |
263 | #endif | |
264 | ; | |
265 | #if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA) | |
266 | #pragma pack(pop) | |
267 | #endif | |
268 | ||
269 | /* Dynamic buffer with 'curr_alloc' pointing to a dynamic area allocated using | |
270 | * heap allocation primitives. Also used for external buffers when low memory | |
271 | * options are not used. | |
272 | */ | |
273 | struct duk_hbuffer_dynamic { | |
274 | duk_heaphdr hdr; | |
275 | ||
276 | #if defined(DUK_USE_BUFLEN16) | |
277 | /* Stored in duk_heaphdr unused flags. */ | |
278 | #else | |
279 | duk_size_t size; | |
280 | #endif | |
281 | ||
282 | #if defined(DUK_USE_HEAPPTR16) | |
283 | /* Stored in duk_heaphdr h_extra16. */ | |
284 | #else | |
285 | void *curr_alloc; /* may be NULL if alloc_size == 0 */ | |
286 | #endif | |
287 | ||
288 | /* | |
289 | * Allocation size for 'curr_alloc' is alloc_size. There is no | |
290 | * automatic NUL terminator for buffers (see above for rationale). | |
291 | * | |
292 | * 'curr_alloc' is explicitly allocated with heap allocation | |
293 | * primitives and will thus always have alignment suitable for | |
294 | * e.g. duk_tval and an IEEE double. | |
295 | */ | |
296 | }; | |
297 | ||
298 | /* External buffer with 'curr_alloc' managed by user code and pointing to an | |
299 | * arbitrary address. When heap pointer compression is not used, this struct | |
300 | * has the same layout as duk_hbuffer_dynamic. | |
301 | */ | |
302 | struct duk_hbuffer_external { | |
303 | duk_heaphdr hdr; | |
304 | ||
305 | #if defined(DUK_USE_BUFLEN16) | |
306 | /* Stored in duk_heaphdr unused flags. */ | |
307 | #else | |
308 | duk_size_t size; | |
309 | #endif | |
310 | ||
311 | /* Cannot be compressed as a heap pointer because may point to | |
312 | * an arbitrary address. | |
313 | */ | |
314 | void *curr_alloc; /* may be NULL if alloc_size == 0 */ | |
315 | }; | |
316 | ||
317 | /* | |
318 | * Prototypes | |
319 | */ | |
320 | ||
321 | DUK_INTERNAL_DECL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata); | |
322 | DUK_INTERNAL_DECL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud); /* indirect allocs */ | |
323 | ||
324 | /* dynamic buffer ops */ | |
325 | DUK_INTERNAL_DECL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size); | |
326 | DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf); | |
327 | ||
328 | #endif /* DUK_HBUFFER_H_INCLUDED */ |