133 lines
3.9 KiB
C
133 lines
3.9 KiB
C
|
/*
|
||
|
* duk_hbuffer allocation and freeing.
|
||
|
*/
|
||
|
|
||
|
#include "duk_internal.h"
|
||
|
|
||
|
/* Allocate a new duk_hbuffer of a certain type and return a pointer to it
|
||
|
* (NULL on error). Write buffer data pointer to 'out_bufdata' (only if
|
||
|
* allocation successful).
|
||
|
*/
|
||
|
DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata) {
|
||
|
duk_hbuffer *res = NULL;
|
||
|
duk_size_t header_size;
|
||
|
duk_size_t alloc_size;
|
||
|
|
||
|
DUK_ASSERT(heap != NULL);
|
||
|
DUK_ASSERT(out_bufdata != NULL);
|
||
|
|
||
|
DUK_DDD(DUK_DDDPRINT("allocate hbuffer"));
|
||
|
|
||
|
/* Size sanity check. Should not be necessary because caller is
|
||
|
* required to check this, but we don't want to cause a segfault
|
||
|
* if the size wraps either in duk_size_t computation or when
|
||
|
* storing the size in a 16-bit field.
|
||
|
*/
|
||
|
if (size > DUK_HBUFFER_MAX_BYTELEN) {
|
||
|
DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size));
|
||
|
return NULL; /* no need to write 'out_bufdata' */
|
||
|
}
|
||
|
|
||
|
if (flags & DUK_BUF_FLAG_EXTERNAL) {
|
||
|
header_size = sizeof(duk_hbuffer_external);
|
||
|
alloc_size = sizeof(duk_hbuffer_external);
|
||
|
} else if (flags & DUK_BUF_FLAG_DYNAMIC) {
|
||
|
header_size = sizeof(duk_hbuffer_dynamic);
|
||
|
alloc_size = sizeof(duk_hbuffer_dynamic);
|
||
|
} else {
|
||
|
header_size = sizeof(duk_hbuffer_fixed);
|
||
|
alloc_size = sizeof(duk_hbuffer_fixed) + size;
|
||
|
DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed)); /* no wrapping */
|
||
|
}
|
||
|
|
||
|
res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size);
|
||
|
if (DUK_UNLIKELY(res == NULL)) {
|
||
|
goto alloc_error;
|
||
|
}
|
||
|
|
||
|
/* zero everything unless requested not to do so */
|
||
|
#if defined(DUK_USE_ZERO_BUFFER_DATA)
|
||
|
duk_memzero((void *) res,
|
||
|
(flags & DUK_BUF_FLAG_NOZERO) ? header_size : alloc_size);
|
||
|
#else
|
||
|
duk_memzero((void *) res, header_size);
|
||
|
#endif
|
||
|
|
||
|
if (flags & DUK_BUF_FLAG_EXTERNAL) {
|
||
|
duk_hbuffer_external *h;
|
||
|
h = (duk_hbuffer_external *) res;
|
||
|
DUK_UNREF(h);
|
||
|
*out_bufdata = NULL;
|
||
|
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
|
||
|
#if defined(DUK_USE_HEAPPTR16)
|
||
|
/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
|
||
|
#else
|
||
|
DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, h, NULL);
|
||
|
#endif
|
||
|
#endif
|
||
|
DUK_ASSERT(DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, h) == NULL);
|
||
|
} else if (flags & DUK_BUF_FLAG_DYNAMIC) {
|
||
|
duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res;
|
||
|
void *ptr;
|
||
|
|
||
|
if (size > 0) {
|
||
|
DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL)); /* alloc external with size zero */
|
||
|
DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer"));
|
||
|
#if defined(DUK_USE_ZERO_BUFFER_DATA)
|
||
|
ptr = DUK_ALLOC_ZEROED(heap, size);
|
||
|
#else
|
||
|
ptr = DUK_ALLOC(heap, size);
|
||
|
#endif
|
||
|
if (DUK_UNLIKELY(ptr == NULL)) {
|
||
|
/* Because size > 0, NULL check is correct */
|
||
|
goto alloc_error;
|
||
|
}
|
||
|
*out_bufdata = ptr;
|
||
|
|
||
|
DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr);
|
||
|
} else {
|
||
|
*out_bufdata = NULL;
|
||
|
#if defined(DUK_USE_EXPLICIT_NULL_INIT)
|
||
|
#if defined(DUK_USE_HEAPPTR16)
|
||
|
/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */
|
||
|
#else
|
||
|
DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, NULL);
|
||
|
#endif
|
||
|
#endif
|
||
|
DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, h) == NULL);
|
||
|
}
|
||
|
} else {
|
||
|
*out_bufdata = (void *) ((duk_hbuffer_fixed *) (void *) res + 1);
|
||
|
}
|
||
|
|
||
|
DUK_HBUFFER_SET_SIZE(res, size);
|
||
|
|
||
|
DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER);
|
||
|
if (flags & DUK_BUF_FLAG_DYNAMIC) {
|
||
|
DUK_HBUFFER_SET_DYNAMIC(res);
|
||
|
if (flags & DUK_BUF_FLAG_EXTERNAL) {
|
||
|
DUK_HBUFFER_SET_EXTERNAL(res);
|
||
|
}
|
||
|
} else {
|
||
|
DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL));
|
||
|
}
|
||
|
DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr);
|
||
|
|
||
|
DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res));
|
||
|
return res;
|
||
|
|
||
|
alloc_error:
|
||
|
DUK_DD(DUK_DDPRINT("hbuffer allocation failed"));
|
||
|
|
||
|
DUK_FREE(heap, res);
|
||
|
return NULL; /* no need to write 'out_bufdata' */
|
||
|
}
|
||
|
|
||
|
/* For indirect allocs. */
|
||
|
|
||
|
DUK_INTERNAL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud) {
|
||
|
duk_hbuffer_dynamic *buf = (duk_hbuffer_dynamic *) ud;
|
||
|
DUK_UNREF(heap);
|
||
|
return (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, buf);
|
||
|
}
|