1 /*
2  *  duk_hbuffer allocation and freeing.
3  */
4 
5 #include "duk_internal.h"
6 
7 /* Allocate a new duk_hbuffer of a certain type and return a pointer to it
8  * (NULL on error).  Write buffer data pointer to 'out_bufdata' (only if
9  * allocation successful).
10  */
duk_hbuffer_alloc(duk_heap * heap,duk_size_t size,duk_small_uint_t flags,void ** out_bufdata)11 DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata) {
12 	duk_hbuffer *res = NULL;
13 	duk_size_t header_size;
14 	duk_size_t alloc_size;
15 
16 	DUK_ASSERT(heap != NULL);
17 	DUK_ASSERT(out_bufdata != NULL);
18 
19 	DUK_DDD(DUK_DDDPRINT("allocate hbuffer"));
20 
21 	/* Size sanity check.  Should not be necessary because caller is
22 	 * required to check this, but we don't want to cause a segfault
23 	 * if the size wraps either in duk_size_t computation or when
24 	 * storing the size in a 16-bit field.
25 	 */
26 	if (size > DUK_HBUFFER_MAX_BYTELEN) {
27 		DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size));
28 		return NULL;  /* no need to write 'out_bufdata' */
29 	}
30 
31 	if (flags & DUK_BUF_FLAG_EXTERNAL) {
32 		header_size = sizeof(duk_hbuffer_external);
33 		alloc_size = sizeof(duk_hbuffer_external);
34 	} else if (flags & DUK_BUF_FLAG_DYNAMIC) {
35 		header_size = sizeof(duk_hbuffer_dynamic);
36 		alloc_size = sizeof(duk_hbuffer_dynamic);
37 	} else {
38 		header_size = sizeof(duk_hbuffer_fixed);
39 		alloc_size = sizeof(duk_hbuffer_fixed) + size;
40 		DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed));  /* no wrapping */
41 	}
42 
43 	res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size);
44 	if (!res) {
45 		goto error;
46 	}
47 
48 	/* zero everything unless requested not to do so */
49 #if defined(DUK_USE_ZERO_BUFFER_DATA)
50 	DUK_MEMZERO((void *) res,
51 	            (flags & DUK_BUF_FLAG_NOZERO) ? header_size : alloc_size);
52 #else
53 	DUK_MEMZERO((void *) res, header_size);
54 #endif
55 
56 	if (flags & DUK_BUF_FLAG_EXTERNAL) {
57 		duk_hbuffer_external *h;
58 		h = (duk_hbuffer_external *) res;
59 		DUK_UNREF(h);
60 		*out_bufdata = NULL;
61 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
62 #if defined(DUK_USE_HEAPPTR16)
63 /* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
64 #else
65 		DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, h, NULL);
66 #endif
67 #endif
68 		DUK_ASSERT(DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, h) == NULL);
69 	} else if (flags & DUK_BUF_FLAG_DYNAMIC) {
70 		duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res;
71 		void *ptr;
72 
73 		if (size > 0) {
74 			DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL));  /* alloc external with size zero */
75 			DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer"));
76 #ifdef DUK_USE_ZERO_BUFFER_DATA
77 			ptr = DUK_ALLOC_ZEROED(heap, size);
78 #else
79 			ptr = DUK_ALLOC(heap, size);
80 #endif
81 			if (!ptr) {
82 				/* Because size > 0, NULL check is correct */
83 				goto error;
84 			}
85 			*out_bufdata = ptr;
86 
87 			DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr);
88 		} else {
89 			*out_bufdata = NULL;
90 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
91 #if defined(DUK_USE_HEAPPTR16)
92 /* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
93 #else
94 			DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, NULL);
95 #endif
96 #endif
97 			DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, h) == NULL);
98 		}
99 	} else {
100 		*out_bufdata = (void *) ((duk_hbuffer_fixed *) res + 1);
101 	}
102 
103 	DUK_HBUFFER_SET_SIZE(res, size);
104 
105 	DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER);
106 	if (flags & DUK_BUF_FLAG_DYNAMIC) {
107 		DUK_HBUFFER_SET_DYNAMIC(res);
108 		if (flags & DUK_BUF_FLAG_EXTERNAL) {
109 			DUK_HBUFFER_SET_EXTERNAL(res);
110 		}
111 	} else {
112 		DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL));
113 	}
114         DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr);
115 
116 	DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res));
117 	return res;
118 
119  error:
120 	DUK_DD(DUK_DDPRINT("hbuffer allocation failed"));
121 
122 	DUK_FREE(heap, res);
123 	return NULL;  /* no need to write 'out_bufdata' */
124 }
125 
126 /* For indirect allocs. */
127 
duk_hbuffer_get_dynalloc_ptr(duk_heap * heap,void * ud)128 DUK_INTERNAL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud) {
129 	duk_hbuffer_dynamic *buf = (duk_hbuffer_dynamic *) ud;
130 	DUK_UNREF(heap);
131 	return (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, buf);
132 }
133