Browse Source

Merge branch 'strtab-fixed-arraychain'

Add an alternate string table algorithm with a fixed size hash table and
separate chaining with arrays.  This eliminates large continuous memory
allocations for the string table structure.  The remaining allocations
(for chains) are small, typically 8 to 64 bytes, and match well with other
small allocations which is convenient for a pool allocator.

The alternate string table is enabled with DUK_OPT_STRTAB_CHAIN and the
top level hash table size is given with DUK_OPT_STRTAB_CHAIN_SIZE.  A good
starting point is -DDUK_OPT_STRTAB_CHAIN_SIZE=128.

As a result of this merge there are two string table algorithms (probing
and chaining), and heap pointer compressed variants of each.  As a future
work item the two algorithms could be merged so that one algorithm could
serve both low memory and other environments well.

Other cleanup may be necessary as separate work items.
pull/92/head
Sami Vaarala 10 years ago
parent
commit
b51d5a5129
  1. 8
      Makefile
  2. 32
      RELEASES.rst
  3. 42
      doc/feature-options.rst
  4. 31
      doc/low-memory.rst
  5. 44
      examples/cmdline/duk_cmdline.c
  6. 92
      examples/cmdline/duk_cmdline_ajduk.c
  7. 13
      src/duk_features.h.in
  8. 7
      src/duk_features_sanity.h.in
  9. 2
      src/duk_forwdecl.h
  10. 66
      src/duk_heap.h
  11. 147
      src/duk_heap_alloc.c
  12. 131
      src/duk_heap_markandsweep.c
  13. 594
      src/duk_heap_stringtable.c
  14. 7
      src/duk_hthread_builtins.c
  15. 4
      src/duk_util.h
  16. 7
      src/duk_util_hashprime.c

8
Makefile

@ -810,7 +810,7 @@ ajtcl:
CCOPTS_AJDUK = -m32
#CCOPTS_AJDUK += '-fpack-struct=1'
CCOPTS_AJDUK += -Wno-unused-parameter -Wno-pedantic -Wno-sign-compare -Wno-missing-field-initializers
CCOPTS_AJDUK += -Wno-unused-parameter -Wno-pedantic -Wno-sign-compare -Wno-missing-field-initializers -Wno-unused-result
CCOPTS_AJDUK += -UDUK_CMDLINE_FANCY -DDUK_CMDLINE_AJSHEAP -D_POSIX_C_SOURCE=200809L
CCOPTS_AJDUK += -DDUK_OPT_FORCE_ALIGN=4
CCOPTS_AJDUK += -DDUK_OPT_ASSERTIONS
@ -820,6 +820,8 @@ CCOPTS_AJDUK += -DDUK_OPT_STRHASH16
CCOPTS_AJDUK += -DDUK_OPT_STRLEN16
CCOPTS_AJDUK += -DDUK_OPT_BUFLEN16
CCOPTS_AJDUK += -DDUK_OPT_OBJSIZES16
CCOPTS_AJDUK += -DDUK_OPT_STRTAB_CHAIN
CCOPTS_AJDUK += -DDUK_OPT_STRTAB_CHAIN_SIZE=128
CCOPTS_AJDUK += -DDUK_OPT_HEAPPTR16
CCOPTS_AJDUK += '-DDUK_OPT_HEAPPTR_ENC16(p)=ajsheap_enc16(p)'
CCOPTS_AJDUK += '-DDUK_OPT_HEAPPTR_DEC16(x)=ajsheap_dec16(x)'
@ -828,7 +830,9 @@ CCOPTS_AJDUK += -DDUK_OPT_EXTERNAL_STRINGS
#CCOPTS_AJDUK += '-DDUK_OPT_EXTSTR_FREE(ptr)=ajsheap_extstr_free_1((ptr))'
CCOPTS_AJDUK += '-DDUK_OPT_EXTSTR_INTERN_CHECK(ptr,len)=ajsheap_extstr_check_2((ptr),(len))'
CCOPTS_AJDUK += '-DDUK_OPT_EXTSTR_FREE(ptr)=ajsheap_extstr_free_2((ptr))'
CCOPTS_AJDUK += '-DDUK_OPT_DECLARE=extern uint8_t *ajsheap_ram; extern duk_uint16_t ajsheap_enc16(void *p); extern void *ajsheap_dec16(duk_uint16_t x); extern const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len); extern const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len); extern void ajsheap_extstr_free_1(const void *ptr); extern void ajsheap_extstr_free_2(const void *ptr);'
#CCOPTS_AJDUK += '-DDUK_OPT_EXTSTR_INTERN_CHECK(ptr,len)=ajsheap_extstr_check_3((ptr),(len))'
#CCOPTS_AJDUK += '-DDUK_OPT_EXTSTR_FREE(ptr)=ajsheap_extstr_free_3((ptr))'
CCOPTS_AJDUK += '-DDUK_OPT_DECLARE=extern uint8_t *ajsheap_ram; extern duk_uint16_t ajsheap_enc16(void *p); extern void *ajsheap_dec16(duk_uint16_t x); extern const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len); extern const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len); extern const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len); extern void ajsheap_extstr_free_1(const void *ptr); extern void ajsheap_extstr_free_2(const void *ptr); extern void ajsheap_extstr_free_3(const void *ptr);'
#CCOPTS_AJDUK += -DDUK_OPT_DEBUG -DDUK_OPT_DPRINT
#CCOPTS_AJDUK += -DDUK_OPT_DEBUG -DDUK_OPT_DPRINT -DDUK_OPT_DDPRINT -DDUK_OPT_DDDPRINT

32
RELEASES.rst

@ -632,26 +632,30 @@ Planned
* Add lightfunc (DUK_TYPE_LIGHTFUNC) primitive type, representing a
Duktape/C function with a plain tagged value without any heap allocations
* Add duk_push_c_lightfunc() and duk_is_lightfunc() API calls to push user
lightfuncs on the value stack and to check if a value is a lightfunc
* Add feature option DUK_OPT_LIGHTFUNC_BUILTINS which causes Duktape to use
lightfuncs for almost all built-in functions, saving around 14kB of Duktape
heap on 32-bit platforms
* Add multiple feature options to use 16-bit fields and pointer compression
for reducing memory usage on low memory systems, see doc/low-memory.rst
for detailed discussion
* Add support for external strings which are like dynamic buffers: a fixed
size string header is allocated from the Duktape heap, but the data is
behind a pointer
* Add DUK_OPT_EXTERNAL_STRINGS to enable support for external strings which
are like dynamic buffers: a fixed size string header is allocated from the
Duktape heap, but the data is behind a pointer
* Add DUK_OPT_EXTERNAL_STRINGS, DUK_OPT_EXTSTR_INTERN_CHECK. and
DUK_OPT_EXTSTR_FREE feature options to take advantage of external strings
for low memory environments; see doc/low-memory.rst for detailed discussion
* Add DUK_OPT_EXTSTR_INTERN_CHECK and DUK_OPT_EXTSTR_FREE (used with
DUK_OPT_EXTERNAL_STRINGS) to allow string data to be held outside the
Duktape heap, which is useful in low memory environments; see
doc/low-memory.rst for detailed discussion
* Add duk_push_c_lightfunc() API call to push user lightfuncs on the
value stack
* Add duk_is_lightfunc() API call to type check for lightfuncs
* Add feature option DUK_OPT_LIGHTFUNC_BUILTINS which causes Duktape to use
lightfuncs for almost all built-in functions, saving around 14kB of Duktape
heap on 32-bit platforms
* Add DUK_OPT_STRTAB_CHAIN and DUK_OPT_STRTAB_CHAIN_SIZE=nnn which enable
an alternate string table algorithm intended for low memory environments;
the algorithm uses separate chaining with arrays, making allocation
behavior easier to handle using a pool allocator, see doc/low-memory.rst
* Add duk_is_error() API call to check if a value inherits from Error

42
doc/feature-options.rst

@ -214,27 +214,27 @@ to you. They involve some compromises in e.g. performance or compliance
to reduce memory usage.
DUK_OPT_REFCOUNT16
==================
------------------
Use a 16-bit reference count field (for low memory environments).
DUK_OPT_STRHASH16
=================
-----------------
Use a 16-bit string hash field (for low memory environments).
DUK_OPT_STRLEN16
================
----------------
Use a 16-bit string length field (for low memory environments).
DUK_OPT_BUFLEN16
================
----------------
Use a 16-bit buffer length field (for low memory environments).
DUK_OPT_OBJSIZE16
=================
-----------------
Use a 16-bit object entry and array part sizes (for low memory environments).
Also automatically drops support for an object hash part to further reduce
@ -242,7 +242,7 @@ memory usage; there are rarely large objects in low memory environments simply
because there's no memory to store a lot of properties.
DUK_OPT_HEAPPTR16, DUK_OPT_HEAPPTR_ENC16, DUK_OPT_HEAPPTR_DEC16
===============================================================
---------------------------------------------------------------
Enable "compression" of Duktape heap pointers into an unsigned 16-bit value
and provide the macros for encoding and decoding a pointer:
@ -275,7 +275,7 @@ downsides:
which is much slower than an inline implementation.
DUK_OPT_DATAPTR16, DUK_OPT_DATAPTR_ENC16, DUK_OPT_DATAPTR_DEC16
===============================================================
---------------------------------------------------------------
Enable "compression" of arbitrary data pointers into an unsigned 16-bit value
and provide the macros for encoding and decoding a pointer:
@ -296,7 +296,7 @@ and provide the macros for encoding and decoding a pointer:
any data pointers at the moment.
DUK_OPT_FUNCPTR16, DUK_OPT_FUNCPTR_ENC16, DUK_OPT_FUNCPTR_DEC16
===============================================================
---------------------------------------------------------------
Enable "compression" of arbitrary C function pointers into an unsigned 16-bit
value and provide the macros for encoding and decoding a pointer:
@ -318,7 +318,7 @@ value and provide the macros for encoding and decoding a pointer:
NULL function pointer.
DUK_OPT_EXTSTR_INTERN_CHECK(ptr,len)
====================================
------------------------------------
Provide a hook for checking if data for a certain string can be used from
external memory (outside of Duktape heap, e.g. memory mapped flash).
@ -356,7 +356,7 @@ See ``low-memory.rst`` for more discussion how to use this feature option
in practice.
DUK_OPT_EXTSTR_FREE(ptr)
========================
------------------------
Optional counterpart to ``DUK_OPT_EXTSTR_INTERN_CHECK``, with the following
semantics:
@ -375,6 +375,28 @@ semantics:
even if pushed by the user using an API call; this may need to be
rethought at that time.
DUK_OPT_STRTAB_CHAIN, DUK_OPT_STRTAB_CHAIN_SIZE
-----------------------------------------------
Replace the default (open addressing, probing) string table structure with one
based on separate chaining. There is a fixed-size top level hash table (whose
size is defined using ``DUK_OPT_STRTAB_CHAIN_SIZE``), with each entry in the
hash table being: (a) NULL, (b) a ``duk_hstring`` pointer, or (c) a pointer
to an array of ``duk_hstring`` pointers. The pointer arrays are gappy (the
gaps are reused on new inserts) and are never shrunk at the moment.
This option is intended for low memory environments to make Duktape's memory
behavior match a typical pool-based allocator better:
* The top level fixed structure never changes size, so there is no hash table
resize, and thus no need for resize temporaries. The default string table
algorithm needs resizing from time to time and doesn't resize in place, so
you effectively need twice the string table size temporarily during a resize.
* The pointer arrays vary in size, but their size (typically 8 to 64 bytes,
depending on the load factor) matches that of many other allocations which
works well with a pooled allocator.
Ecmascript feature options
==========================

31
doc/low-memory.rst

@ -91,7 +91,7 @@ Suggested feature options
* If you don't need regexp support, use:
- ``DUK_OPT_NO_REGEXP_SUPPORT``.
- ``DUK_OPT_NO_REGEXP_SUPPORT``
* Duktape debug code uses a large, static temporary buffer for formatting
debug log lines. If you're running with debugging enabled, use e.g.
@ -128,9 +128,9 @@ system RAM):
- ``DUK_OPT_HEAPPTR16``
- ``DUK_OPT_HEAPPTR_ENC16``
- ``DUK_OPT_HEAPPTR_ENC16(p)``
- ``DUK_OPT_HEAPPTR_DEC16``
- ``DUK_OPT_HEAPPTR_DEC16(x)``
* Enable data pointer compression if possible. Note that these pointers can
point to arbitrary memory locations (outside Duktape heap) so this may not
@ -138,28 +138,32 @@ system RAM):
- ``DUK_OPT_DATAPTR16``
- ``DUK_OPT_DATAPTR_ENC16``
- ``DUK_OPT_DATAPTR_ENC16(p)``
- ``DUK_OPT_DATAPTR_DEC16``
- ``DUK_OPT_DATAPTR_DEC16(x)``
- **UNIMPLEMENTED AT THE MOMENT**
* Enable C function pointer compression if possible. Duktape compiles to
around 200kB of code, so assuming an alignment of 4 this may only be
possible if there is less than 56kB of user code.
possible if there is less than 56kB of user code:
- ``DUK_OPT_FUNCPTR16``
- ``DUK_OPT_FUNCPTR_ENC16``
- ``DUK_OPT_FUNCPTR_ENC16(p)``
- ``DUK_OPT_FUNCPTR_DEC16``
- ``DUK_OPT_FUNCPTR_DEC16(x)``
- **UNIMPLEMENTED AT THE MOMENT**
* Enable struct packing in compiler options if your platform doesn't have
strict alignment requirements, e.g. on gcc/x86 you can:
* Enable a low memory optimized string table variant which uses a fixed size
top level hash table and array chaining to resolve collisions. This makes
memory behavior more predictable and avoids a large continuous allocation
used by the default string table:
- `-fpack-struct=1` or `-fpack-struct=2`
- ``DUK_OPT_STRTAB_CHAIN``
- ``DUK_OPT_STRTAB_CHAIN_SIZE=128`` (other values possible also)
* Use "external" strings to allocate most strings from flash (there are
multiple strategies for this, see separate section):
@ -170,6 +174,11 @@ system RAM):
- ``DUK_OPT_EXTSTR_FREE(ptr)``
* Enable struct packing in compiler options if your platform doesn't have
strict alignment requirements, e.g. on gcc/x86 you can:
- ``-fpack-struct=1`` or ``-fpack-struct=2``
Notes on low memory measures
============================

44
examples/cmdline/duk_cmdline.c

@ -130,6 +130,8 @@ static void print_pop_error(duk_context *ctx, FILE *f) {
}
static int wrapped_compile_execute(duk_context *ctx) {
const char *src_data;
duk_size_t src_len;
int comp_flags;
/* XXX: Here it'd be nice to get some stats for the compilation result
@ -138,8 +140,18 @@ static int wrapped_compile_execute(duk_context *ctx) {
* the public API.
*/
/* Use duk_compile_lstring_filename() variant which avoids interning
* the source code. This only really matters for low memory environments.
*/
/* [ ... src_data src_len filename ] */
comp_flags = 0;
duk_compile(ctx, comp_flags);
src_data = (const char *) duk_require_pointer(ctx, -3);
src_len = (duk_size_t) duk_require_uint(ctx, -2);
duk_compile_lstring_filename(ctx, comp_flags, src_data, src_len);
/* [ ... src_data src_len function ] */
duk_push_global_object(ctx); /* 'this' binding */
duk_call_method(ctx, 0);
@ -199,15 +211,17 @@ static int handle_fh(duk_context *ctx, FILE *f, const char *filename) {
got = fread((void *) buf, (size_t) 1, (size_t) len, f);
duk_push_lstring(ctx, buf, got);
duk_push_pointer(ctx, (void *) buf);
duk_push_uint(ctx, (duk_uint_t) got);
duk_push_string(ctx, filename);
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
free(buf);
buf = NULL;
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/);
if (rc != DUK_EXEC_SUCCESS) {
print_pop_error(ctx, stderr);
goto error;
@ -253,12 +267,13 @@ static int handle_eval(duk_context *ctx, const char *code) {
int rc;
int retval = -1;
duk_push_string(ctx, code);
duk_push_pointer(ctx, (void *) code);
duk_push_uint(ctx, (duk_uint_t) strlen(code));
duk_push_string(ctx, "eval");
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/);
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
if (rc != DUK_EXEC_SUCCESS) {
print_pop_error(ctx, stderr);
} else {
@ -311,12 +326,13 @@ static int handle_interactive(duk_context *ctx) {
}
}
duk_push_lstring(ctx, buffer, idx);
duk_push_pointer(ctx, (void *) buffer);
duk_push_uint(ctx, (duk_uint_t) idx);
duk_push_string(ctx, "input");
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/);
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
if (rc != DUK_EXEC_SUCCESS) {
/* in interactive mode, write to stdout */
print_pop_error(ctx, stdout);
@ -367,17 +383,19 @@ static int handle_interactive(duk_context *ctx) {
add_history(buffer);
}
duk_push_lstring(ctx, buffer, strlen(buffer));
duk_push_pointer(ctx, (void *) buffer);
duk_push_uint(ctx, (duk_uint_t) strlen(buffer));
duk_push_string(ctx, "input");
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
if (buffer) {
free(buffer);
buffer = NULL;
}
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/);
if (rc != DUK_EXEC_SUCCESS) {
/* in interactive mode, write to stdout */
print_pop_error(ctx, stdout);

92
examples/cmdline/duk_cmdline_ajduk.c

@ -14,12 +14,15 @@
* Helpers
*/
static void safe_print_chars(const char *p, duk_size_t len) {
static void safe_print_chars(const char *p, duk_size_t len, int until_nul) {
duk_size_t i;
printf("\"");
for (i = 0; i < len; i++) {
unsigned char x = (unsigned char) p[i];
if (until_nul && x == 0U) {
break;
}
if (x < 0x20 || x >= 0x7e || x == '"' || x == '\'' || x == '\\') {
printf("\\x%02x", (int) x);
} else {
@ -58,14 +61,17 @@ static const AJS_HeapConfig ajsheap_config[] = {
{ 52, 50, AJS_POOL_BORROW, 0 },
{ 56, 50, AJS_POOL_BORROW, 0 },
{ 60, 50, AJS_POOL_BORROW, 0 },
{ 64, 50, 0, 0 },
{ 128, 80, 0, 0 },
{ 256, 16, 0, 0 },
{ 512, 16, 0, 0 },
{ 1024, 6, 0, 0 },
{ 2048, 5, 0, 0 },
{ 64, 50, AJS_POOL_BORROW, 0 },
{ 128, 80, AJS_POOL_BORROW, 0 },
{ 256, 16, AJS_POOL_BORROW, 0 },
{ 512, 16, AJS_POOL_BORROW, 0 },
{ 1024, 6, AJS_POOL_BORROW, 0 },
{ 1352, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression */
{ 2048, 5, AJS_POOL_BORROW, 0 },
{ 4096, 3, 0, 0 },
{ 8192, 1, 0, 0 }
{ 8192, 3, 0, 0 },
{ 16384, 1, 0, 0 },
{ 32768, 1, 0, 0 }
};
uint8_t *ajsheap_ram = NULL;
@ -81,7 +87,7 @@ void ajsheap_init(void) {
num_pools, /* numPools */
0); /* heapNum */
ajsheap_ram = (uint8_t *) malloc(heap_sz[0]);
if (!ajsheap_ram) {
if (ajsheap_ram == NULL) {
fprintf(stderr, "Failed to allocate AJS heap\n");
fflush(stderr);
exit(1);
@ -220,7 +226,7 @@ const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) {
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> existing %p (used=%ld)\n",
(void *) ret, (long) ajsheap_strdata_used);
#endif
@ -236,7 +242,7 @@ const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) {
if (ajsheap_strdata_used + len + 1 > sizeof(ajsheap_strdata)) {
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> no space (used=%ld)\n", (long) ajsheap_strdata_used);
#endif
return NULL;
@ -254,16 +260,18 @@ const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) {
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_1(void *ptr) {
void ajsheap_extstr_free_1(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_1: freeing extstr %p\n", ptr);
printf("ajsheap_extstr_free_1: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
@ -655,7 +663,7 @@ const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) {
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> constant string index %ld\n", (long) i);
#endif
return (void *) strdata_duk_builtin_strings[i];
@ -665,17 +673,65 @@ const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) {
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> not found\n");
#endif
return NULL;
}
void ajsheap_extstr_free_2(void *ptr) {
void ajsheap_extstr_free_2(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_2: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
/*
* External strings strategy intended for valgrind testing: external strings
* are allocated using malloc()/free() so that valgrind can be used to ensure
* that strings are e.g. freed exactly once.
*/
const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len) {
duk_uint8_t *ret;
(void) safe_print_chars; /* potentially unused */
ret = malloc((size_t) len + 1);
if (ret == NULL) {
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> malloc failed, return NULL\n");
#endif
return (const void *) NULL;
}
if (len > 0) {
memcpy((void *) ret, ptr, (size_t) len);
}
ret[len] = (duk_uint8_t) 0;
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p\n", (void *) ret);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_3(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_2: freeing extstr %p\n", ptr);
printf("ajsheap_extstr_free_3: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
free((void *) ptr);
}
#else /* DUK_CMDLINE_AJSHEAP */

13
src/duk_features.h.in

@ -2135,6 +2135,19 @@ typedef FILE duk_file;
#define DUK_USE_GC_TORTURE
#endif
/*
* String table options
*/
#if defined(DUK_OPT_STRTAB_CHAIN) && defined(DUK_OPT_STRTAB_CHAIN_SIZE)
/* Low memory algorithm: separate chaining using arrays, fixed size hash */
#define DUK_USE_STRTAB_CHAIN
#define DUK_USE_STRTAB_CHAIN_SIZE DUK_OPT_STRTAB_CHAIN_SIZE
#else
/* Default algorithm: open addressing (probing) */
#define DUK_USE_STRTAB_PROBE
#endif
/*
* Error handling options
*/

7
src/duk_features_sanity.h.in

@ -82,4 +82,11 @@
#endif
#endif
#if defined(DUK_USE_STRTAB_CHAIN) && defined(DUK_USE_STRTAB_PROBE)
#error both DUK_USE_STRTAB_CHAIN and DUK_USE_STRTAB_PROBE defined
#endif
#if !defined(DUK_USE_STRTAB_CHAIN) && !defined(DUK_USE_STRTAB_PROBE)
#error neither DUK_USE_STRTAB_CHAIN nor DUK_USE_STRTAB_PROBE is defined
#endif
#endif /* DUK_FEATURES_SANITY_H_INCLUDED */

2
src/duk_forwdecl.h

@ -34,6 +34,7 @@ struct duk_activation;
struct duk_catcher;
struct duk_strcache;
struct duk_ljstate;
struct duk_strtab_entry;
#ifdef DUK_USE_DEBUG
struct duk_fixedbuffer;
@ -79,6 +80,7 @@ typedef struct duk_activation duk_activation;
typedef struct duk_catcher duk_catcher;
typedef struct duk_strcache duk_strcache;
typedef struct duk_ljstate duk_ljstate;
typedef struct duk_strtab_entry duk_strtab_entry;
#ifdef DUK_USE_DEBUG
typedef struct duk_fixedbuffer duk_fixedbuffer;

66
src/duk_heap.h

@ -79,7 +79,7 @@
* happens e.g. in call handling.
*/
#ifdef DUK_USE_INTERRUPT_COUNTER
#if defined(DUK_USE_INTERRUPT_COUNTER)
#define DUK_HEAP_SWITCH_THREAD(heap,newthr) duk_heap_switch_thread((heap), (newthr))
#else
#define DUK_HEAP_SWITCH_THREAD(heap,newthr) do { \
@ -151,7 +151,7 @@
* smaller value. The default interval must be small enough to allow
* for reasonable execution timeout checking.
*/
#ifdef DUK_USE_INTERRUPT_COUNTER
#if defined(DUK_USE_INTERRUPT_COUNTER)
#define DUK_HEAP_INTCTR_DEFAULT (256L * 1024L)
#endif
@ -173,10 +173,13 @@
#define DUK_STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */
#define DUK_STRTAB_HIGHEST_32BIT_PRIME 0xfffffffbUL
/* probe sequence */
/* probe sequence (open addressing) */
#define DUK_STRTAB_HASH_INITIAL(hash,h_size) ((hash) % (h_size))
#define DUK_STRTAB_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash))
/* fixed top level hashtable size (separate chaining) */
#define DUK_STRTAB_CHAIN_SIZE DUK_USE_STRTAB_CHAIN_SIZE
/*
* Built-in strings
*/
@ -198,10 +201,10 @@
((heap)->alloc_func((heap)->alloc_udata, (size)))
#define DUK_REALLOC_RAW(heap,ptr,newsize) \
((heap)->realloc_func((heap)->alloc_udata, (ptr), (newsize)))
((heap)->realloc_func((heap)->alloc_udata, (void *) (ptr), (newsize)))
#define DUK_FREE_RAW(heap,ptr) \
((heap)->free_func((heap)->alloc_udata, (ptr)))
((heap)->free_func((heap)->alloc_udata, (void *) (ptr)))
/*
* Memory calls: relative to heap, GC interaction, but no error throwing.
@ -283,6 +286,29 @@ struct duk_ljstate {
duk_tval value2; /* 2nd related value (type specific) */
};
/*
* Stringtable entry for fixed size stringtable
*/
struct duk_strtab_entry {
#if defined(DUK_USE_HEAPPTR16)
/* A 16-bit listlen makes sense with 16-bit heap pointers: there
* won't be space for 64k strings anyway.
*/
duk_uint16_t listlen; /* if 0, 'str16' used, if > 0, 'strlist16' used */
union {
duk_uint16_t strlist16;
duk_uint16_t str16;
} u;
#else
duk_size_t listlen; /* if 0, 'str' used, if > 0, 'strlist' used */
union {
duk_hstring **strlist;
duk_hstring *str;
} u;
#endif
};
/*
* Main heap structure
*/
@ -316,14 +342,14 @@ struct duk_heap {
* "finalized"; avoids recursive C calls when refcounts go to zero in a
* chain of objects.
*/
#ifdef DUK_USE_REFERENCE_COUNTING
#if defined(DUK_USE_REFERENCE_COUNTING)
duk_heaphdr *refzero_list;
duk_heaphdr *refzero_list_tail;
#endif
#ifdef DUK_USE_MARK_AND_SWEEP
#if defined(DUK_USE_MARK_AND_SWEEP)
/* mark-and-sweep control */
#ifdef DUK_USE_VOLUNTARY_GC
#if defined(DUK_USE_VOLUNTARY_GC)
duk_int_t mark_and_sweep_trigger_counter;
#endif
duk_int_t mark_and_sweep_recursion_depth;
@ -364,12 +390,13 @@ struct duk_heap {
duk_uint32_t rnd_state;
/* interrupt counter */
#ifdef DUK_USE_INTERRUPT_COUNTER
#if defined(DUK_USE_INTERRUPT_COUNTER)
duk_int_t interrupt_init; /* start value for current countdown */
duk_int_t interrupt_counter; /* countdown state (mirrored in current thread state) */
#endif
/* string intern table (weak refs) */
#if defined(DUK_USE_STRTAB_PROBE)
#if defined(DUK_USE_HEAPPTR16)
duk_uint16_t *strtable16;
#else
@ -377,6 +404,14 @@ struct duk_heap {
#endif
duk_uint32_t st_size; /* alloc size in elements */
duk_uint32_t st_used; /* used elements (includes DELETED) */
#endif
/* XXX: static alloc is OK until separate chaining stringtable
* resizing is implemented.
*/
#if defined(DUK_USE_STRTAB_CHAIN)
duk_strtab_entry strtable[DUK_STRTAB_CHAIN_SIZE];
#endif
/* string access cache (codepoint offset -> byte offset) for fast string
* character looping; 'weak' reference which needs special handling in GC.
@ -411,7 +446,7 @@ DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_h
#if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING)
DUK_INTERNAL_DECL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
#endif
#ifdef DUK_USE_INTERRUPT_COUNTER
#if defined(DUK_USE_INTERRUPT_COUNTER)
DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr);
#endif
@ -427,13 +462,18 @@ DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_ui
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val);
DUK_INTERNAL_DECL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h);
#if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE)
DUK_INTERNAL_DECL void duk_heap_force_stringtable_resize(duk_heap *heap);
DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap);
#endif
DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap);
#if defined(DUK_USE_DEBUG)
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap);
#endif
DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h);
DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset);
#ifdef DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS
#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS)
DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size);
DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize);
DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr);
@ -455,7 +495,7 @@ DUK_INTERNAL_DECL void duk_heap_refcount_finalize_heaphdr(duk_hthread *thr, duk_
/* no refcounting */
#endif
#ifdef DUK_USE_MARK_AND_SWEEP
#if defined(DUK_USE_MARK_AND_SWEEP)
DUK_INTERNAL_DECL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags);
#endif

147
src/duk_heap_alloc.c

@ -166,45 +166,8 @@ DUK_LOCAL void duk__free_markandsweep_finalize_list(duk_heap *heap) {
#endif
DUK_LOCAL void duk__free_stringtable(duk_heap *heap) {
duk_uint_fast32_t i;
/* strings are only tracked by stringtable */
#if defined(DUK_USE_HEAPPTR16)
if (heap->strtable16) {
#else
if (heap->strtable) {
#endif
for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) {
duk_hstring *e;
#if defined(DUK_USE_HEAPPTR16)
e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
#else
e = heap->strtable[i];
#endif
if (e == NULL || e == DUK_STRTAB_DELETED_MARKER(heap)) {
continue;
}
DUK_ASSERT(e != NULL);
/* strings may have inner refs (extdata) in some cases */
duk_free_hstring_inner(heap, (duk_hstring *) e);
DUK_DDD(DUK_DDDPRINT("FINALFREE (string): %!iO",
(duk_heaphdr *) e));
DUK_FREE(heap, e);
#if 0 /* not strictly necessary */
heap->strtable[i] = NULL;
#endif
}
#if defined(DUK_USE_HEAPPTR16)
DUK_FREE(heap, heap->strtable16);
#else
DUK_FREE(heap, heap->strtable);
#endif
#if 0 /* not strictly necessary */
heap->strtable = NULL;
#endif
}
duk_heap_free_strtab(heap);
}
DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
@ -258,6 +221,10 @@ DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) {
DUK_INTERNAL void duk_heap_free(duk_heap *heap) {
DUK_D(DUK_DPRINT("free heap: %p", (void *) heap));
#if defined(DUK_USE_DEBUG)
duk_heap_dump_strtab(heap);
#endif
/* Execute finalizers before freeing the heap, even for reachable
* objects, and regardless of whether or not mark-and-sweep is
* enabled. This gives finalizers the chance to free any native
@ -547,6 +514,9 @@ DUK_LOCAL void duk__dump_type_sizes(void) {
DUK__DUMPSZ(duk_propvalue);
DUK__DUMPSZ(duk_propdesc);
DUK__DUMPSZ(duk_heap);
#if defined(DUK_USE_STRTAB_CHAIN)
DUK__DUMPSZ(duk_strtab_entry);
#endif
DUK__DUMPSZ(duk_activation);
DUK__DUMPSZ(duk_catcher);
DUK__DUMPSZ(duk_strcache);
@ -626,13 +596,18 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
DUK_D(DUK_DPRINT("allocate heap"));
/* Debug dump type sizes */
/*
* Debug dump type sizes
*/
#ifdef DUK_USE_DEBUG
duk__dump_type_sizes();
duk__dump_type_limits();
#endif
/* If selftests enabled, run them as early as possible. */
/*
* If selftests enabled, run them as early as possible
*/
#ifdef DUK_USE_SELF_TESTS
DUK_D(DUK_DPRINT("running self tests"));
duk_selftest_run_tests();
@ -655,6 +630,10 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
} while (0);
#endif
/*
* Computed values (e.g. INFINITY)
*/
#ifdef DUK_USE_COMPUTED_INFINITY
do {
/* Similar workaround for INFINITY. */
@ -664,13 +643,21 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
} while (0);
#endif
/* use a raw call, all macros expect the heap to be initialized */
/*
* Allocate heap struct
*
* Use a raw call, all macros expect the heap to be initialized
*/
res = (duk_heap *) alloc_func(alloc_udata, sizeof(duk_heap));
if (!res) {
goto error;
}
/* zero everything */
/*
* Zero the struct, and start initializing roughly in order
*/
DUK_MEMZERO(res, sizeof(*res));
/* explicit NULL inits */
@ -688,16 +675,23 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
res->curr_thread = NULL;
res->heap_object = NULL;
res->log_buffer = NULL;
res->strtable = NULL;
#if defined(DUK_USE_STRTAB_CHAIN)
/* nothing to NULL */
#elif defined(DUK_USE_STRTAB_PROBE)
#if defined(DUK_USE_HEAPPTR16)
res->strtable16 = (duk_uint16_t *) NULL;
#else
res->strtable = (duk_hstring **) NULL;
#endif
#endif
{
duk_small_uint_t i;
for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) {
res->strs[i] = NULL;
}
}
#endif
#endif /* DUK_USE_EXPLICIT_NULL_INIT */
/* initialize the structure, roughly in order */
res->alloc_func = alloc_func;
res->realloc_func = realloc_func;
res->free_func = free_func;
@ -743,17 +737,42 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
#error initial heap stringtable size is defined incorrectly
#endif
/*
* Init stringtable: fixed variant
*/
#if defined(DUK_USE_STRTAB_CHAIN)
DUK_MEMZERO(res->strtable, sizeof(duk_strtab_entry) * DUK_STRTAB_CHAIN_SIZE);
#ifdef DUK_USE_EXPLICIT_NULL_INIT
{
duk_small_uint_t i;
for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
#if defined(DUK_USE_HEAPPTR16)
res->strtable[i].u.str16 = res->heapptr_null16;
#else
res->strtable[i].u.str = NULL;
#endif
}
}
#endif /* DUK_USE_EXPLICIT_NULL_INIT */
#endif /* DUK_USE_STRTAB_CHAIN */
/*
* Init stringtable: probe variant
*/
#if defined(DUK_USE_STRTAB_PROBE)
#if defined(DUK_USE_HEAPPTR16)
res->strtable16 = (duk_uint16_t *) alloc_func(alloc_udata, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE);
if (!res->strtable16) {
goto error;
}
#else
#else /* DUK_USE_HEAPPTR16 */
res->strtable = (duk_hstring **) alloc_func(alloc_udata, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE);
if (!res->strtable) {
goto error;
}
#endif
#endif /* DUK_USE_HEAPPTR16 */
res->st_size = DUK_STRTAB_INITIAL_SIZE;
#ifdef DUK_USE_EXPLICIT_NULL_INIT
{
@ -767,15 +786,19 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
#endif
}
}
#else
#else /* DUK_USE_EXPLICIT_NULL_INIT */
#if defined(DUK_USE_HEAPPTR16)
DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE);
#else
DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE);
#endif
#endif
#endif /* DUK_USE_EXPLICIT_NULL_INIT */
#endif /* DUK_USE_STRTAB_PROBE */
/*
* Init stringcache
*/
/* strcache init */
#ifdef DUK_USE_EXPLICIT_NULL_INIT
{
duk_small_uint_t i;
@ -791,19 +814,28 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
* passing here could be removed.
*/
/* built-in strings */
/*
* Init built-in strings
*/
DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS"));
if (!duk__init_heap_strings(res)) {
goto error;
}
/* heap thread */
/*
* Init the heap thread
*/
DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD"));
if (!duk__init_heap_thread(res)) {
goto error;
}
/* heap object */
/*
* Init the heap object
*/
DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT"));
DUK_ASSERT(res->heap_thread != NULL);
res->heap_object = duk_hobject_alloc(res, DUK_HOBJECT_FLAG_EXTENSIBLE |
@ -813,7 +845,10 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
}
DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object);
/* log buffer */
/*
* Init log buffer
*/
DUK_DD(DUK_DDPRINT("HEAP: INIT LOG BUFFER"));
res->log_buffer = (duk_hbuffer_dynamic *) duk_hbuffer_alloc(res,
DUK_BI_LOGGER_SHORT_MSG_LIMIT,
@ -823,6 +858,10 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
}
DUK_HBUFFER_INCREF(res->heap_thread, res->log_buffer);
/*
* All done
*/
DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res));
return res;

131
src/duk_heap_markandsweep.c

@ -506,7 +506,123 @@ DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) {
* Sweep stringtable
*/
DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) {
#if defined(DUK_USE_STRTAB_CHAIN)
/* XXX: skip count_free w/o debug? */
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL void duk__sweep_string_chain16(duk_heap *heap, duk_uint16_t *slot, duk_size_t *count_keep, duk_size_t *count_free) {
duk_uint16_t h16 = *slot;
duk_hstring *h;
duk_uint16_t null16 = heap->heapptr_null16;
if (h16 == null16) {
/* nop */
return;
}
h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(h16);
DUK_ASSERT(h != NULL);
if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) {
DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h);
(*count_keep)++;
} else {
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0);
#endif
/* deal with weak references first */
duk_heap_strcache_string_remove(heap, (duk_hstring *) h);
*slot = null16;
/* free inner references (these exist e.g. when external
* strings are enabled)
*/
duk_free_hstring_inner(heap, h);
DUK_FREE(heap, h);
(*count_free)++;
}
}
#else /* DUK_USE_HEAPPTR16 */
DUK_LOCAL void duk__sweep_string_chain(duk_heap *heap, duk_hstring **slot, duk_size_t *count_keep, duk_size_t *count_free) {
duk_hstring *h = *slot;
if (h == NULL) {
/* nop */
return;
}
if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) {
DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h);
(*count_keep)++;
} else {
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0);
#endif
/* deal with weak references first */
duk_heap_strcache_string_remove(heap, (duk_hstring *) h);
*slot = NULL;
/* free inner references (these exist e.g. when external
* strings are enabled)
*/
duk_free_hstring_inner(heap, h);
DUK_FREE(heap, h);
(*count_free)++;
}
}
#endif /* DUK_USE_HEAPPTR16 */
DUK_LOCAL void duk__sweep_stringtable_chain(duk_heap *heap, duk_size_t *out_count_keep) {
duk_strtab_entry *e;
duk_uint_fast32_t i;
duk_size_t count_free = 0;
duk_size_t count_keep = 0;
duk_size_t j, n;
#if defined(DUK_USE_HEAPPTR16)
duk_uint16_t *lst;
#else
duk_hstring **lst;
#endif
DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap));
/* Non-zero refcounts should not happen for unreachable strings,
* because we refcount finalize all unreachable objects which
* should have decreased unreachable string refcounts to zero
* (even for cycles).
*/
for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
e = heap->strtable + i;
if (e->listlen == 0) {
#if defined(DUK_USE_HEAPPTR16)
duk__sweep_string_chain16(heap, &e->u.str16, &count_keep, &count_free);
#else
duk__sweep_string_chain(heap, &e->u.str, &count_keep, &count_free);
#endif
} else {
#if defined(DUK_USE_HEAPPTR16)
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
#else
lst = e->u.strlist;
#endif
for (j = 0, n = e->listlen; j < n; j++) {
#if defined(DUK_USE_HEAPPTR16)
duk__sweep_string_chain16(heap, lst + j, &count_keep, &count_free);
#else
duk__sweep_string_chain(heap, lst + j, &count_keep, &count_free);
#endif
}
}
}
DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept",
(long) count_free, (long) count_keep));
*out_count_keep = count_keep;
}
#endif /* DUK_USE_STRTAB_CHAIN */
#if defined(DUK_USE_STRTAB_PROBE)
DUK_LOCAL void duk__sweep_stringtable_probe(duk_heap *heap, duk_size_t *out_count_keep) {
duk_hstring *h;
duk_uint_fast32_t i;
#ifdef DUK_USE_DEBUG
@ -564,7 +680,7 @@ DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep
duk_free_hstring_inner(heap, (duk_hstring *) h);
/* finally free the struct itself */
DUK_FREE(heap, (duk_heaphdr *) h);
DUK_FREE(heap, h);
}
#ifdef DUK_USE_DEBUG
@ -573,6 +689,7 @@ DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep
#endif
*out_count_keep = count_keep;
}
#endif /* DUK_USE_STRTAB_PROBE */
/*
* Sweep heap
@ -1043,7 +1160,13 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t
duk__finalize_refcounts(heap);
#endif
duk__sweep_heap(heap, flags, &count_keep_obj);
duk__sweep_stringtable(heap, &count_keep_str);
#if defined(DUK_USE_STRTAB_CHAIN)
duk__sweep_stringtable_chain(heap, &count_keep_str);
#elif defined(DUK_USE_STRTAB_PROBE)
duk__sweep_stringtable_probe(heap, &count_keep_str);
#else
#error internal error, invalid strtab options
#endif
#ifdef DUK_USE_REFERENCE_COUNTING
duk__clear_refzero_list_flags(heap);
#endif
@ -1085,7 +1208,7 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t
#if defined(DUK_USE_MS_STRINGTABLE_RESIZE)
if (!(flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE)) {
DUK_DD(DUK_DDPRINT("resize stringtable: %p", (void *) heap));
duk_heap_force_stringtable_resize(heap);
duk_heap_force_strtab_resize(heap);
} else {
DUK_D(DUK_DPRINT("stringtable resize skipped because DUK_MS_FLAG_NO_STRINGTABLE_RESIZE is set"));
}

594
src/duk_heap_stringtable.c

@ -4,9 +4,11 @@
#include "duk_internal.h"
#if defined(DUK_USE_STRTAB_PROBE)
#define DUK__HASH_INITIAL(hash,h_size) DUK_STRTAB_HASH_INITIAL((hash),(h_size))
#define DUK__HASH_PROBE_STEP(hash) DUK_STRTAB_HASH_PROBE_STEP((hash))
#define DUK__DELETED_MARKER(heap) DUK_STRTAB_DELETED_MARKER((heap))
#endif
/*
* Create a hstring and insert into the heap. The created object
@ -104,10 +106,344 @@ duk_hstring *duk__alloc_init_hstring(duk_heap *heap,
}
/*
* Count actually used (non-NULL, non-DELETED) entries
* String table algorithm: fixed size string table with array chaining
*
* The top level string table has a fixed size, with each slot holding
* either NULL, string pointer, or pointer to a separately allocated
* string pointer list.
*
* This is good for low memory environments using a pool allocator: the
* top level allocation has a fixed size and the pointer lists have quite
* small allocation size, which further matches the typical pool sizes
* needed by objects, strings, property tables, etc.
*/
DUK_LOCAL duk_int_t duk__count_used(duk_heap *heap) {
#if defined(DUK_USE_STRTAB_CHAIN)
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_uint16_t *lst;
duk_uint16_t *new_lst;
duk_size_t i, n;
duk_uint16_t null16 = heap->heapptr_null16;
duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16((void *) h);
DUK_ASSERT(heap != NULL);
DUK_ASSERT(h != NULL);
slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
if (e->u.str16 == null16) {
e->u.str16 = h16;
} else {
/* Now two entries in the same slot, alloc list */
lst = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * 2);
if (lst == NULL) {
return 1; /* fail */
}
lst[0] = e->u.str16;
lst[1] = h16;
e->u.strlist16 = DUK_USE_HEAPPTR_ENC16((void *) lst);
e->listlen = 2;
}
} else {
DUK_ASSERT(e->u.strlist16 != null16);
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
DUK_ASSERT(lst != NULL);
for (i = 0, n = e->listlen; i < n; i++) {
if (lst[i] == null16) {
lst[i] = h16;
return 0;
}
}
if (e->listlen + 1 == 0) {
/* Overflow, relevant mainly when listlen is 16 bits. */
return 1; /* fail */
}
new_lst = (duk_uint16_t *) DUK_REALLOC(heap, lst, sizeof(duk_uint16_t) * (e->listlen + 1));
if (new_lst == NULL) {
return 1; /* fail */
}
new_lst[e->listlen++] = h16;
e->u.strlist16 = DUK_USE_HEAPPTR_ENC16((void *) new_lst);
}
return 0;
}
#else /* DUK_USE_HEAPPTR16 */
DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_hstring **lst;
duk_hstring **new_lst;
duk_size_t i, n;
DUK_ASSERT(heap != NULL);
DUK_ASSERT(h != NULL);
slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
if (e->u.str == NULL) {
e->u.str = h;
} else {
/* Now two entries in the same slot, alloc list */
lst = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * 2);
if (lst == NULL) {
return 1; /* fail */
}
lst[0] = e->u.str;
lst[1] = h;
e->u.strlist = lst;
e->listlen = 2;
}
} else {
DUK_ASSERT(e->u.strlist != NULL);
lst = e->u.strlist;
for (i = 0, n = e->listlen; i < n; i++) {
if (lst[i] == NULL) {
lst[i] = h;
return 0;
}
}
if (e->listlen + 1 == 0) {
/* Overflow, relevant mainly when listlen is 16 bits. */
return 1; /* fail */
}
new_lst = (duk_hstring **) DUK_REALLOC(heap, e->u.strlist, sizeof(duk_hstring *) * (e->listlen + 1));
if (new_lst == NULL) {
return 1; /* fail */
}
new_lst[e->listlen++] = h;
e->u.strlist = new_lst;
}
return 0;
}
#endif /* DUK_USE_HEAPPTR16 */
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_uint16_t *lst;
duk_size_t i, n;
duk_uint16_t null16 = heap->heapptr_null16;
DUK_ASSERT(heap != NULL);
slotidx = strhash % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
if (e->u.str16 != null16) {
duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(e->u.str16);
DUK_ASSERT(h != NULL);
if (DUK_HSTRING_GET_BYTELEN(h) == blen &&
DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(h), blen) == 0) {
return h;
}
}
} else {
DUK_ASSERT(e->u.strlist16 != null16);
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
DUK_ASSERT(lst != NULL);
for (i = 0, n = e->listlen; i < n; i++) {
if (lst[i] != null16) {
duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(lst[i]);
DUK_ASSERT(h != NULL);
if (DUK_HSTRING_GET_BYTELEN(h) == blen &&
DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(h), blen) == 0) {
return h;
}
}
}
}
return NULL;
}
#else /* DUK_USE_HEAPPTR16 */
DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_hstring **lst;
duk_size_t i, n;
DUK_ASSERT(heap != NULL);
slotidx = strhash % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
if (e->u.str != NULL &&
DUK_HSTRING_GET_BYTELEN(e->u.str) == blen &&
DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(e->u.str), blen) == 0) {
return e->u.str;
}
} else {
DUK_ASSERT(e->u.strlist != NULL);
lst = e->u.strlist;
for (i = 0, n = e->listlen; i < n; i++) {
if (lst[i] != NULL &&
DUK_HSTRING_GET_BYTELEN(lst[i]) == blen &&
DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(lst[i]), blen) == 0) {
return lst[i];
}
}
}
return NULL;
}
#endif /* DUK_USE_HEAPPTR16 */
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_uint16_t *lst;
duk_size_t i, n;
duk_uint16_t h16;
duk_uint16_t null16 = heap->heapptr_null16;
DUK_ASSERT(heap != NULL);
DUK_ASSERT(h != NULL);
slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
DUK_ASSERT(h != NULL);
h16 = DUK_USE_HEAPPTR_ENC16((void *) h);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
if (e->u.str16 == h16) {
e->u.str16 = null16;
return;
}
} else {
DUK_ASSERT(e->u.strlist16 != null16);
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
DUK_ASSERT(lst != NULL);
for (i = 0, n = e->listlen; i < n; i++) {
if (lst[i] == h16) {
lst[i] = null16;
return;
}
}
}
DUK_D(DUK_DPRINT("failed to find string that should be in stringtable"));
DUK_UNREACHABLE();
return;
}
#else /* DUK_USE_HEAPPTR16 */
DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) {
duk_small_uint_t slotidx;
duk_strtab_entry *e;
duk_hstring **lst;
duk_size_t i, n;
DUK_ASSERT(heap != NULL);
DUK_ASSERT(h != NULL);
slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
e = heap->strtable + slotidx;
if (e->listlen == 0) {
DUK_ASSERT(h != NULL);
if (e->u.str == h) {
e->u.str = NULL;
return;
}
} else {
DUK_ASSERT(e->u.strlist != NULL);
lst = e->u.strlist;
for (i = 0, n = e->listlen; i < n; i++) {
DUK_ASSERT(h != NULL);
if (lst[i] == h) {
lst[i] = NULL;
return;
}
}
}
DUK_D(DUK_DPRINT("failed to find string that should be in stringtable"));
DUK_UNREACHABLE();
return;
}
#endif /* DUK_USE_HEAPPTR16 */
#if defined(DUK_USE_DEBUG)
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
duk_strtab_entry *e;
duk_small_uint_t i;
duk_size_t j, n, used;
#if defined(DUK_USE_HEAPPTR16)
duk_uint16_t *lst;
duk_uint16_t null16 = heap->heapptr_null16;
#else
duk_hstring **lst;
#endif
DUK_ASSERT(heap != NULL);
for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
e = heap->strtable + i;
if (e->listlen == 0) {
#if defined(DUK_USE_HEAPPTR16)
DUK_PRINTF("[%03d] -> plain %d\n", (int) i, (int) (e->u.str16 != null16 ? 1 : 0));
#else
DUK_PRINTF("[%03d] -> plain %d\n", (int) i, (int) (e->u.str ? 1 : 0));
#endif
} else {
used = 0;
#if defined(DUK_USE_HEAPPTR16)
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
#else
lst = e->u.strlist;
#endif
DUK_ASSERT(lst != NULL);
for (j = 0, n = e->listlen; j < n; j++) {
#if defined(DUK_USE_HEAPPTR16)
if (lst[j] != null16) {
#else
if (list[j] != NULL) {
#endif
used++;
}
}
DUK_PRINTF("[%03d] -> array %d/%d\n", (int) i, (int) used, (int) e->listlen);
}
}
}
#endif /* DUK_USE_DEBUG */
#endif /* DUK_USE_STRTAB_CHAIN */
/*
* String table algorithm: closed hashing with a probe sequence
*
* This is the default algorithm and works fine for environments with
* minimal memory constraints.
*/
#if defined(DUK_USE_STRTAB_PROBE)
/* Count actually used (non-NULL, non-DELETED) entries. */
DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) {
duk_int_t res = 0;
duk_uint_fast32_t i, n;
#if defined(DUK_USE_HEAPPTR16)
@ -128,14 +464,10 @@ DUK_LOCAL duk_int_t duk__count_used(duk_heap *heap) {
return res;
}
/*
* Hashtable lookup and insert helpers
*/
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL void duk__insert_hstring(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
#else
DUK_LOCAL void duk__insert_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) {
#endif
duk_uint32_t i;
duk_uint32_t step;
@ -194,9 +526,9 @@ DUK_LOCAL void duk__insert_hstring(duk_heap *heap, duk_hstring **entries, duk_ui
}
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL duk_hstring *duk__find_matching_string(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
#else
DUK_LOCAL duk_hstring *duk__find_matching_string(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
#endif
duk_uint32_t i;
duk_uint32_t step;
@ -234,9 +566,9 @@ DUK_LOCAL duk_hstring *duk__find_matching_string(duk_heap *heap, duk_hstring **e
}
#if defined(DUK_USE_HEAPPTR16)
DUK_LOCAL void duk__remove_matching_hstring(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) {
DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) {
#else
DUK_LOCAL void duk__remove_matching_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) {
DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) {
#endif
duk_uint32_t i;
duk_uint32_t step;
@ -289,11 +621,7 @@ DUK_LOCAL void duk__remove_matching_hstring(duk_heap *heap, duk_hstring **entrie
}
}
/*
* Hash resizing and resizing policy
*/
DUK_LOCAL duk_bool_t duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_size) {
DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) {
#ifdef DUK_USE_MARK_AND_SWEEP
duk_small_uint_t prev_mark_and_sweep_base_flags;
#endif
@ -319,11 +647,11 @@ DUK_LOCAL duk_bool_t duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_siz
DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load",
(long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used,
(long) (((double) old_used) / ((double) old_size) * 100.0),
(long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used(heap),
(long) (((double) duk__count_used(heap)) / ((double) new_size) * 100.0)));
(long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used_probe(heap),
(long) (((double) duk__count_used_probe(heap)) / ((double) new_size) * 100.0)));
#endif
DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used(heap)); /* required for rehash to succeed, equality not that useful */
DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used_probe(heap)); /* required for rehash to succeed, equality not that useful */
DUK_ASSERT(old_entries);
#ifdef DUK_USE_MARK_AND_SWEEP
DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0);
@ -375,7 +703,7 @@ DUK_LOCAL duk_bool_t duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_siz
#endif
#endif
/* Because new_size > duk__count_used(heap), guaranteed to work */
/* Because new_size > duk__count_used_probe(heap), guaranteed to work */
for (i = 0; i < old_size; i++) {
duk_hstring *e;
@ -388,7 +716,7 @@ DUK_LOCAL duk_bool_t duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_siz
continue;
}
/* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */
duk__insert_hstring(heap, new_entries, new_size, &new_used, e);
duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e);
}
#ifdef DUK_USE_DDPRINT
@ -416,11 +744,11 @@ DUK_LOCAL duk_bool_t duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_siz
return 1; /* FAIL */
}
DUK_LOCAL duk_bool_t duk__resize_strtab(duk_heap *heap) {
DUK_LOCAL duk_bool_t duk__resize_strtab_probe(duk_heap *heap) {
duk_uint32_t new_size;
duk_bool_t ret;
new_size = (duk_uint32_t) duk__count_used(heap);
new_size = (duk_uint32_t) duk__count_used_probe(heap);
if (new_size >= 0x80000000UL) {
new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME;
} else {
@ -433,12 +761,12 @@ DUK_LOCAL duk_bool_t duk__resize_strtab(duk_heap *heap) {
* DELETED entries.
*/
ret = duk__resize_strtab_raw(heap, new_size);
ret = duk__resize_strtab_raw_probe(heap, new_size);
return ret;
}
DUK_LOCAL duk_bool_t duk__recheck_strtab_size(duk_heap *heap, duk_uint32_t new_used) {
DUK_LOCAL duk_bool_t duk__recheck_strtab_size_probe(duk_heap *heap, duk_uint32_t new_used) {
duk_uint32_t new_free;
duk_uint32_t tmp1;
duk_uint32_t tmp2;
@ -454,12 +782,38 @@ DUK_LOCAL duk_bool_t duk__recheck_strtab_size(duk_heap *heap, duk_uint32_t new_u
if (new_free <= tmp1 || new_used <= tmp2) {
/* load factor too low or high, count actually used entries and resize */
return duk__resize_strtab(heap);
return duk__resize_strtab_probe(heap);
} else {
return 0; /* OK */
}
}
#if defined(DUK_USE_DEBUG)
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
duk_uint32_t i;
duk_hstring *h;
DUK_ASSERT(heap != NULL);
#if defined(DUK_USE_HEAPPTR16)
DUK_ASSERT(heap->strtable16 != NULL);
#else
DUK_ASSERT(heap->strtable != NULL);
#endif
for (i = 0; i < heap->st_size; i++) {
#if defined(DUK_USE_HEAPPTR16)
h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
#else
h = heap->strtable[i];
#endif
DUK_PRINTF("[%03d] -> %p\n", (int) i, (void *) h);
}
}
#endif /* DUK_USE_DEBUG */
#endif /* DUK_USE_STRTAB_PROBE */
/*
* Raw intern and lookup
*/
@ -468,9 +822,11 @@ DUK_LOCAL duk_hstring *duk__do_intern(duk_heap *heap, const duk_uint8_t *str, du
duk_hstring *res;
const duk_uint8_t *extdata;
if (duk__recheck_strtab_size(heap, heap->st_used + 1)) {
#if defined(DUK_USE_STRTAB_PROBE)
if (duk__recheck_strtab_size_probe(heap, heap->st_used + 1)) {
return NULL;
}
#endif
/* For manual testing only. */
#if 0
@ -499,15 +855,26 @@ DUK_LOCAL duk_hstring *duk__do_intern(duk_heap *heap, const duk_uint8_t *str, du
return NULL;
}
duk__insert_hstring(heap,
#if defined(DUK_USE_STRTAB_CHAIN)
if (duk__insert_hstring_chain(heap, res)) {
/* failed */
DUK_FREE(heap, res);
return NULL;
}
#elif defined(DUK_USE_STRTAB_PROBE)
/* guaranteed to succeed */
duk__insert_hstring_probe(heap,
#if defined(DUK_USE_HEAPPTR16)
heap->strtable16,
heap->strtable16,
#else
heap->strtable,
heap->strtable,
#endif
heap->st_size,
&heap->st_used,
res);
#else
#error internal error, invalid strtab options
#endif
heap->st_size,
&heap->st_used,
res); /* guaranteed to succeed */
/* Note: hstring is in heap but has refcount zero and is not strongly reachable.
* Caller should increase refcount and make the hstring reachable before any
@ -524,16 +891,23 @@ DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, du
*out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen);
res = duk__find_matching_string(heap,
#if defined(DUK_USE_STRTAB_CHAIN)
res = duk__find_matching_string_chain(heap, str, blen, *out_strhash);
#elif defined(DUK_USE_STRTAB_PROBE)
res = duk__find_matching_string_probe(heap,
#if defined(DUK_USE_HEAPPTR16)
heap->strtable16,
heap->strtable16,
#else
heap->strtable,
heap->strtable,
#endif
heap->st_size,
str,
blen,
*out_strhash);
heap->st_size,
str,
blen,
*out_strhash);
#else
#error internal error, invalid strtab options
#endif
return res;
}
@ -578,7 +952,7 @@ DUK_INTERNAL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_
DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val);
buf[sizeof(buf) - 1] = (char) 0;
DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */
return duk_heap_string_lookup(heap, (duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
return duk_heap_string_lookup(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
}
#endif
@ -587,7 +961,7 @@ DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_
DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val);
buf[sizeof(buf) - 1] = (char) 0;
DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */
return duk_heap_string_intern(heap, (duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
return duk_heap_string_intern(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
}
DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) {
@ -602,27 +976,141 @@ DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, d
DUK_INTERNAL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h) {
DUK_DDD(DUK_DDDPRINT("remove string from stringtable: %!O", (duk_heaphdr *) h));
duk__remove_matching_hstring(heap,
#if defined(DUK_USE_STRTAB_CHAIN)
duk__remove_matching_hstring_chain(heap, h);
#elif defined(DUK_USE_STRTAB_PROBE)
duk__remove_matching_hstring_probe(heap,
#if defined(DUK_USE_HEAPPTR16)
heap->strtable16,
heap->strtable16,
#else
heap->strtable,
heap->strtable,
#endif
heap->st_size,
h);
#else
#error internal error, invalid strtab options
#endif
heap->st_size,
h);
}
#if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE)
DUK_INTERNAL void duk_heap_force_stringtable_resize(duk_heap *heap) {
DUK_INTERNAL void duk_heap_force_strtab_resize(duk_heap *heap) {
/* Force a resize so that DELETED entries are eliminated.
* Another option would be duk__recheck_strtab_size(); but since
* that happens on every intern anyway, this whole check
* can now be disabled.
* Another option would be duk__recheck_strtab_size_probe();
* but since that happens on every intern anyway, this whole
* check can now be disabled.
*/
duk__resize_strtab(heap);
#if defined(DUK_USE_STRTAB_CHAIN)
DUK_UNREF(heap);
#elif defined(DUK_USE_STRTAB_PROBE)
duk__resize_strtab_probe(heap);
#endif
}
#endif
#if defined(DUK_USE_STRTAB_CHAIN)
DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) {
/* Free strings in the stringtable and any allocations needed
* by the stringtable itself.
*/
duk_uint_fast32_t i, j;
duk_strtab_entry *e;
#if defined(DUK_USE_HEAPPTR16)
duk_uint16_t *lst;
duk_uint16_t null16 = heap->heapptr_null16;
#else
duk_hstring **lst;
#endif
duk_hstring *h;
for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
e = heap->strtable + i;
if (e->listlen > 0) {
#if defined(DUK_USE_HEAPPTR16)
lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(e->u.strlist16);
#else
lst = e->u.strlist;
#endif
DUK_ASSERT(lst != NULL);
for (j = 0; j < e->listlen; j++) {
#if defined(DUK_USE_HEAPPTR16)
h = DUK_USE_HEAPPTR_DEC16(lst[j]);
lst[j] = null16;
#else
h = lst[j];
lst[j] = NULL;
#endif
/* strings may have inner refs (extdata) in some cases */
if (h != NULL) {
duk_free_hstring_inner(heap, h);
DUK_FREE(heap, h);
}
}
#if defined(DUK_USE_HEAPPTR16)
e->u.strlist16 = null16;
#else
e->u.strlist = NULL;
#endif
DUK_FREE(heap, lst);
} else {
#if defined(DUK_USE_HEAPPTR16)
h = DUK_USE_HEAPPTR_DEC16(e->u.str16);
e->u.str16 = null16;
#else
h = e->u.str;
e->u.str = NULL;
#endif
if (h != NULL) {
duk_free_hstring_inner(heap, h);
DUK_FREE(heap, h);
}
}
e->listlen = 0;
}
}
#endif /* DUK_USE_STRTAB_CHAIN */
#if defined(DUK_USE_STRTAB_PROBE)
DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) {
duk_uint_fast32_t i;
duk_hstring *h;
#if defined(DUK_USE_HEAPPTR16)
if (heap->strtable16) {
#else
if (heap->strtable) {
#endif
for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) {
#if defined(DUK_USE_HEAPPTR16)
h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
#else
h = heap->strtable[i];
#endif
if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) {
continue;
}
DUK_ASSERT(h != NULL);
/* strings may have inner refs (extdata) in some cases */
duk_free_hstring_inner(heap, h);
DUK_FREE(heap, h);
#if 0 /* not strictly necessary */
heap->strtable[i] = NULL;
#endif
}
#if defined(DUK_USE_HEAPPTR16)
DUK_FREE(heap, heap->strtable16);
#else
DUK_FREE(heap, heap->strtable);
#endif
#if 0 /* not strictly necessary */
heap->strtable = NULL;
#endif
}
}
#endif /* DUK_USE_STRTAB_PROBE */
/* Undefine local defines */
#undef DUK__HASH_INITIAL
#undef DUK__HASH_PROBE_STEP

7
src/duk_hthread_builtins.c

@ -546,6 +546,13 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
"u"
#endif
" "
#if defined(DUK_USE_STRTAB_CHAIN)
"c" /* chain */
#elif defined(DUK_USE_STRTAB_PROBE)
"p" /* probe */
#else
"?"
#endif
#if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16)
"n"
#endif

4
src/duk_util.h

@ -45,13 +45,17 @@ DUK_INTERNAL_DECL duk_int8_t duk_hex_dectab[256];
#endif /* !DUK_SINGLE_FILE */
/* Note: assumes that duk_util_probe_steps size is 32 */
#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE)
#if !defined(DUK_SINGLE_FILE)
DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32];
#endif /* !DUK_SINGLE_FILE */
#endif
DUK_INTERNAL_DECL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed);
#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE)
DUK_INTERNAL_DECL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size);
#endif
DUK_INTERNAL_DECL duk_int32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits);
DUK_INTERNAL_DECL duk_small_int_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx);

7
src/duk_util_hashprime.c

@ -13,6 +13,11 @@
#include "duk_internal.h"
/* Awkward inclusion condition: drop out of compilation if not needed by any
* call site: object hash part or probing stringtable.
*/
#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE)
/* hash size ratio goal, must match genhashsizes.py */
#define DUK__HASH_SIZE_RATIO 1177 /* floor(1.15 * (1 << 10)) */
@ -68,3 +73,5 @@ DUK_INTERNAL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size) {
}
return 0;
}
#endif /* DUK_USE_HOBJECT_HASH_PART || DUK_USE_STRTAB_PROBE */

Loading…
Cancel
Save