diff --git a/qjs.c b/qjs.c index f10a0c9..72230f3 100644 --- a/qjs.c +++ b/qjs.c @@ -226,6 +226,29 @@ static void js_trace_malloc_init(struct trace_malloc_data *s) free(s->base = malloc(8)); } +static void *js_trace_calloc(JSMallocState *s, size_t count, size_t size) +{ + void *ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(count != 0 && size != 0); + + if (size > 0) + if (unlikely(count != (count * size) / size)) + return NULL; + + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) + return NULL; + ptr = calloc(count, size); + js_trace_malloc_printf(s, "C %zd %zd -> %p\n", count, size, ptr); + if (ptr) { + s->malloc_count++; + s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD; + } + return ptr; +} + static void *js_trace_malloc(JSMallocState *s, size_t size) { void *ptr; @@ -288,6 +311,7 @@ static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) } static const JSMallocFunctions trace_mf = { + js_trace_calloc, js_trace_malloc, js_trace_free, js_trace_realloc, diff --git a/quickjs.c b/quickjs.c index e415b5b..61dab42 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1378,6 +1378,11 @@ static size_t js_malloc_usable_size_unknown(const void *ptr) return 0; } +void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) +{ + return rt->mf.js_calloc(&rt->malloc_state, count, size); +} + void *js_malloc_rt(JSRuntime *rt, size_t size) { return rt->mf.js_malloc(&rt->malloc_state, size); @@ -1404,13 +1409,16 @@ size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) return rt->mf.js_malloc_usable_size(ptr); } +/** + * This used to be implemented as malloc + memset, but using calloc + * yields better performance in initial, bursty allocations, something useful + * for QuickJS. + * + * More information: https://github.com/quickjs-ng/quickjs/pull/519 + */ void *js_mallocz_rt(JSRuntime *rt, size_t size) { - void *ptr; - ptr = js_malloc_rt(rt, size); - if (!ptr) - return NULL; - return memset(ptr, 0, size); + return js_calloc_rt(rt, 1, size); } /* called by libbf */ @@ -1420,6 +1428,18 @@ static void *js_bf_realloc(void *opaque, void *ptr, size_t size) return js_realloc_rt(rt, ptr, size); } +/* Throw out of memory in case of error */ +void *js_calloc(JSContext *ctx, size_t count, size_t size) +{ + void *ptr; + ptr = js_calloc_rt(ctx->rt, count, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + /* Throw out of memory in case of error */ void *js_malloc(JSContext *ctx, size_t size) { @@ -1631,10 +1651,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) ms.opaque = opaque; ms.malloc_limit = 0; - rt = mf->js_malloc(&ms, sizeof(JSRuntime)); + rt = mf->js_calloc(&ms, 1, sizeof(JSRuntime)); if (!rt) return NULL; - memset(rt, 0, sizeof(*rt)); rt->mf = *mf; if (!rt->mf.js_malloc_usable_size) { /* use dummy function if none provided */ @@ -1699,6 +1718,30 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) rt->user_opaque = opaque; } +static void *js_def_calloc(JSMallocState *s, size_t count, size_t size) +{ + void *ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(count != 0 && size != 0); + + if (size > 0) + if (unlikely(count != (count * size) / size)) + return NULL; + + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) + return NULL; + + ptr = calloc(count, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + static void *js_def_malloc(JSMallocState *s, size_t size) { void *ptr; @@ -1758,6 +1801,7 @@ static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) } static const JSMallocFunctions def_malloc_funcs = { + js_def_calloc, js_def_malloc, js_def_free, js_def_realloc, diff --git a/quickjs.h b/quickjs.h index 9fb40a7..f112597 100644 --- a/quickjs.h +++ b/quickjs.h @@ -291,6 +291,7 @@ typedef struct JSMallocState { } JSMallocState; typedef struct JSMallocFunctions { + void *(*js_calloc)(JSMallocState *s, size_t count, size_t size); void *(*js_malloc)(JSMallocState *s, size_t size); void (*js_free)(JSMallocState *s, void *ptr); void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size); @@ -358,12 +359,14 @@ JS_EXTERN JS_BOOL JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2); JS_EXTERN JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JS_EXTERN void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size); JS_EXTERN void *js_malloc_rt(JSRuntime *rt, size_t size); JS_EXTERN void js_free_rt(JSRuntime *rt, void *ptr); JS_EXTERN void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); JS_EXTERN size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); JS_EXTERN void *js_mallocz_rt(JSRuntime *rt, size_t size); +JS_EXTERN void *js_calloc(JSContext *ctx, size_t count, size_t size); JS_EXTERN void *js_malloc(JSContext *ctx, size_t size); JS_EXTERN void js_free(JSContext *ctx, void *ptr); JS_EXTERN void *js_realloc(JSContext *ctx, void *ptr, size_t size);