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