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 */
329