| /* |
| * Heap stringtable handling, string interning. |
| */ |
| |
| #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 |
| |
| #if defined(DUK_USE_MARK_AND_SWEEP) |
| #define DUK__PREVENT_MS_SIDE_EFFECTS(heap) do { \ |
| (heap)->mark_and_sweep_base_flags |= \ |
| DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive string table call */ \ |
| DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings, invalidation of call data argument, etc. */ \ |
| DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ \ |
| } while (0) |
| #endif |
| |
| /* |
| * Create a hstring and insert into the heap. The created object |
| * is directly garbage collectable with reference count zero. |
| * |
| * The caller must place the interned string into the stringtable |
| * immediately (without chance of a longjmp); otherwise the string |
| * is lost. |
| */ |
| |
| DUK_LOCAL |
| duk_hstring *duk__alloc_init_hstring(duk_heap *heap, |
| const duk_uint8_t *str, |
| duk_uint32_t blen, |
| duk_uint32_t strhash, |
| const duk_uint8_t *extdata) { |
| duk_hstring *res = NULL; |
| duk_uint8_t *data; |
| duk_size_t alloc_size; |
| duk_uarridx_t dummy; |
| duk_uint32_t clen; |
| |
| #if defined(DUK_USE_STRLEN16) |
| /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */ |
| if (blen > 0xffffUL) { |
| DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern")); |
| return NULL; |
| } |
| #endif |
| |
| if (extdata) { |
| alloc_size = (duk_size_t) sizeof(duk_hstring_external); |
| res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); |
| if (!res) { |
| goto alloc_error; |
| } |
| DUK_MEMZERO(res, sizeof(duk_hstring_external)); |
| #if defined(DUK_USE_EXPLICIT_NULL_INIT) |
| DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); |
| #endif |
| DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA); |
| |
| ((duk_hstring_external *) res)->extdata = extdata; |
| } else { |
| /* NUL terminate for convenient C access */ |
| alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1); |
| res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); |
| if (!res) { |
| goto alloc_error; |
| } |
| DUK_MEMZERO(res, sizeof(duk_hstring)); |
| #if defined(DUK_USE_EXPLICIT_NULL_INIT) |
| DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); |
| #endif |
| DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); |
| |
| data = (duk_uint8_t *) (res + 1); |
| DUK_MEMCPY(data, str, blen); |
| data[blen] = (duk_uint8_t) 0; |
| } |
| |
| DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(res)); |
| if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { |
| DUK_HSTRING_SET_ARRIDX(res); |
| } |
| |
| /* All strings beginning with 0xff are treated as "internal", |
| * even strings interned by the user. This allows user code to |
| * create internal properties too, and makes behavior consistent |
| * in case user code happens to use a string also used by Duktape |
| * (such as string has already been interned and has the 'internal' |
| * flag set). |
| */ |
| DUK_ASSERT(!DUK_HSTRING_HAS_INTERNAL(res)); |
| if (blen > 0 && str[0] == (duk_uint8_t) 0xff) { |
| DUK_HSTRING_SET_INTERNAL(res); |
| } |
| |
| DUK_HSTRING_SET_HASH(res, strhash); |
| DUK_HSTRING_SET_BYTELEN(res, blen); |
| |
| clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); |
| DUK_ASSERT(clen <= blen); |
| #if defined(DUK_USE_HSTRING_CLEN) |
| DUK_HSTRING_SET_CHARLEN(res, clen); |
| #endif |
| |
| /* Using an explicit 'ASCII' flag has larger footprint (one call site |
| * only) but is quite useful for the case when there's no explicit |
| * 'clen' in duk_hstring. |
| */ |
| DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res)); |
| if (clen == blen) { |
| DUK_HSTRING_SET_ASCII(res); |
| } |
| |
| DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%ld", |
| (unsigned long) DUK_HSTRING_GET_HASH(res), |
| (long) DUK_HSTRING_GET_BYTELEN(res), |
| (long) DUK_HSTRING_GET_CHARLEN(res), |
| (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0), |
| (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0))); |
| |
| return res; |
| |
| alloc_error: |
| DUK_FREE(heap, res); |
| return NULL; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| #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(heap->heap_udata, (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(heap->heap_udata, (void *) lst); |
| e->listlen = 2; |
| } |
| } else { |
| DUK_ASSERT(e->u.strlist16 != null16); |
| lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, 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(heap->heap_udata, (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(heap->heap_udata, e->u.str16); |
| DUK_ASSERT(h != NULL); |
| if (DUK_HSTRING_GET_BYTELEN(h) == blen && |
| DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { |
| return h; |
| } |
| } |
| } else { |
| DUK_ASSERT(e->u.strlist16 != null16); |
| lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, 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(heap->heap_udata, lst[i]); |
| DUK_ASSERT(h != NULL); |
| if (DUK_HSTRING_GET_BYTELEN(h) == blen && |
| DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) 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((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e->u.str), (size_t) 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((const void *) str, (const void *) DUK_HSTRING_GET_DATA(lst[i]), (size_t) 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(heap->heap_udata, (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(heap->heap_udata, 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_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0))); |
| #else |
| DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (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(heap->heap_udata, 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 (lst[j] != NULL) { |
| #endif |
| used++; |
| } |
| } |
| DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (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) |
| duk_uint16_t null16 = heap->heapptr_null16; |
| duk_uint16_t deleted16 = heap->heapptr_deleted16; |
| #endif |
| |
| n = (duk_uint_fast32_t) heap->st_size; |
| for (i = 0; i < n; i++) { |
| #if defined(DUK_USE_HEAPPTR16) |
| if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) { |
| #else |
| if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) { |
| #endif |
| res++; |
| } |
| } |
| return res; |
| } |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| 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_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; |
| #if defined(DUK_USE_HEAPPTR16) |
| duk_uint16_t null16 = heap->heapptr_null16; |
| duk_uint16_t deleted16 = heap->heapptr_deleted16; |
| #endif |
| |
| DUK_ASSERT(size > 0); |
| |
| i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size); |
| step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h)); |
| for (;;) { |
| #if defined(DUK_USE_HEAPPTR16) |
| duk_uint16_t e16 = entries16[i]; |
| #else |
| duk_hstring *e = entries[i]; |
| #endif |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| /* XXX: could check for e16 == 0 because NULL is guaranteed to |
| * encode to zero. |
| */ |
| if (e16 == null16) { |
| #else |
| if (e == NULL) { |
| #endif |
| DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i)); |
| #if defined(DUK_USE_HEAPPTR16) |
| entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); |
| #else |
| entries[i] = h; |
| #endif |
| (*p_used)++; |
| break; |
| #if defined(DUK_USE_HEAPPTR16) |
| } else if (e16 == deleted16) { |
| #else |
| } else if (e == DUK__DELETED_MARKER(heap)) { |
| #endif |
| /* st_used remains the same, DELETED is counted as used */ |
| DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i)); |
| #if defined(DUK_USE_HEAPPTR16) |
| entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); |
| #else |
| entries[i] = h; |
| #endif |
| break; |
| } |
| DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i)); |
| i = (i + step) % size; |
| |
| /* looping should never happen */ |
| DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); |
| } |
| } |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| 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_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; |
| |
| DUK_ASSERT(size > 0); |
| |
| i = DUK__HASH_INITIAL(strhash, size); |
| step = DUK__HASH_PROBE_STEP(strhash); |
| for (;;) { |
| duk_hstring *e; |
| #if defined(DUK_USE_HEAPPTR16) |
| e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]); |
| #else |
| e = entries[i]; |
| #endif |
| |
| if (!e) { |
| return NULL; |
| } |
| if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { |
| if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) { |
| DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)", |
| (long) i, (long) step, (long) size)); |
| return e; |
| } |
| } |
| DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)", |
| (long) i, (long) step, (long) size)); |
| i = (i + step) % size; |
| |
| /* looping should never happen */ |
| DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); |
| } |
| DUK_UNREACHABLE(); |
| } |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| 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_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) { |
| #endif |
| duk_uint32_t i; |
| duk_uint32_t step; |
| duk_uint32_t hash; |
| #if defined(DUK_USE_HEAPPTR16) |
| duk_uint16_t null16 = heap->heapptr_null16; |
| duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); |
| #endif |
| |
| DUK_ASSERT(size > 0); |
| |
| hash = DUK_HSTRING_GET_HASH(h); |
| i = DUK__HASH_INITIAL(hash, size); |
| step = DUK__HASH_PROBE_STEP(hash); |
| for (;;) { |
| #if defined(DUK_USE_HEAPPTR16) |
| duk_uint16_t e16 = entries16[i]; |
| #else |
| duk_hstring *e = entries[i]; |
| #endif |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| if (e16 == null16) { |
| #else |
| if (!e) { |
| #endif |
| DUK_UNREACHABLE(); |
| break; |
| } |
| #if defined(DUK_USE_HEAPPTR16) |
| if (e16 == h16) { |
| #else |
| if (e == h) { |
| #endif |
| /* st_used remains the same, DELETED is counted as used */ |
| DUK_DDD(DUK_DDDPRINT("free matching hit: %ld", (long) i)); |
| #if defined(DUK_USE_HEAPPTR16) |
| entries16[i] = heap->heapptr_deleted16; |
| #else |
| entries[i] = DUK__DELETED_MARKER(heap); |
| #endif |
| break; |
| } |
| |
| DUK_DDD(DUK_DDDPRINT("free matching miss: %ld", (long) i)); |
| i = (i + step) % size; |
| |
| /* looping should never happen */ |
| DUK_ASSERT(i != DUK__HASH_INITIAL(hash, size)); |
| } |
| } |
| |
| DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) { |
| #if defined(DUK_USE_DEBUG) |
| duk_uint32_t old_used = heap->st_used; |
| #endif |
| duk_uint32_t old_size = heap->st_size; |
| #if defined(DUK_USE_HEAPPTR16) |
| duk_uint16_t *old_entries = heap->strtable16; |
| duk_uint16_t *new_entries = NULL; |
| #else |
| duk_hstring **old_entries = heap->strtable; |
| duk_hstring **new_entries = NULL; |
| #endif |
| duk_uint32_t new_used = 0; |
| duk_uint32_t i; |
| |
| #if defined(DUK_USE_DEBUG) |
| DUK_UNREF(old_used); /* unused with some debug level combinations */ |
| #endif |
| |
| #ifdef DUK_USE_DDDPRINT |
| 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_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_probe(heap)); /* required for rehash to succeed, equality not that useful */ |
| DUK_ASSERT(old_entries); |
| |
| /* |
| * The attempt to allocate may cause a GC. Such a GC must not attempt to resize |
| * the stringtable (though it can be swept); finalizer execution and object |
| * compaction must also be postponed to avoid the pressure to add strings to the |
| * string table. Call site must prevent these. |
| */ |
| |
| #if defined(DUK_USE_MARK_AND_SWEEP) |
| DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE); |
| DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_FINALIZERS); |
| DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_OBJECT_COMPACTION); |
| #endif |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| new_entries = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * new_size); |
| #else |
| new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size); |
| #endif |
| |
| if (!new_entries) { |
| goto resize_error; |
| } |
| |
| #if defined(DUK_USE_EXPLICIT_NULL_INIT) |
| for (i = 0; i < new_size; i++) { |
| #if defined(DUK_USE_HEAPPTR16) |
| new_entries[i] = heap->heapptr_null16; |
| #else |
| new_entries[i] = NULL; |
| #endif |
| } |
| #else |
| #if defined(DUK_USE_HEAPPTR16) |
| /* Relies on NULL encoding to zero. */ |
| DUK_MEMZERO(new_entries, sizeof(duk_uint16_t) * new_size); |
| #else |
| DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size); |
| #endif |
| #endif |
| |
| /* Because new_size > duk__count_used_probe(heap), guaranteed to work */ |
| for (i = 0; i < old_size; i++) { |
| duk_hstring *e; |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, old_entries[i]); |
| #else |
| e = old_entries[i]; |
| #endif |
| if (e == NULL || e == DUK__DELETED_MARKER(heap)) { |
| continue; |
| } |
| /* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */ |
| duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e); |
| } |
| |
| #ifdef DUK_USE_DDPRINT |
| DUK_DD(DUK_DDPRINT("resized 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) new_used, |
| (long) (((double) new_used) / ((double) new_size) * 100.0))); |
| #endif |
| |
| #if defined(DUK_USE_HEAPPTR16) |
| DUK_FREE(heap, heap->strtable16); |
| heap->strtable16 = new_entries; |
| #else |
| DUK_FREE(heap, heap->strtable); |
| heap->strtable = new_entries; |
| #endif |
| heap->st_size = new_size; |
| heap->st_used = new_used; /* may be less, since DELETED entries are NULLed by rehash */ |
| |
| return 0; /* OK */ |
| |
| resize_error: |
| DUK_FREE(heap, new_entries); |
| return 1; /* FAIL */ |
| } |
| |
| 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_probe(heap); |
| if (new_size >= 0x80000000UL) { |
| new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME; |
| } else { |
| new_size = duk_util_get_hash_prime(DUK_STRTAB_GROW_ST_SIZE(new_size)); |
| new_size = duk_util_get_hash_prime(new_size); |
| } |
| DUK_ASSERT(new_size > 0); |
| |
| /* rehash even if old and new sizes are the same to get rid of |
| * DELETED entries. |
| */ |
| |
| ret = duk__resize_strtab_raw_probe(heap, new_size); |
| |
| return ret; |
| } |
| |
| 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; |
| |
| DUK_ASSERT(new_used <= heap->st_size); /* grow by at most one */ |
| new_free = heap->st_size - new_used; /* unsigned intentionally */ |
| |
| /* new_free / size <= 1 / DIV <=> new_free <= size / DIV */ |
| /* new_used / size <= 1 / DIV <=> new_used <= size / DIV */ |
| |
| tmp1 = heap->st_size / DUK_STRTAB_MIN_FREE_DIVISOR; |
| tmp2 = heap->st_size / DUK_STRTAB_MIN_USED_DIVISOR; |
| |
| if (new_free <= tmp1 || new_used <= tmp2) { |
| /* load factor too low or high, count actually used entries and resize */ |
| 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 |
| DUK_UNREF(h); |
| |
| 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_DD(DUK_DDPRINT("[%03d] -> %p", (int) i, (void *) h)); |
| } |
| } |
| #endif /* DUK_USE_DEBUG */ |
| |
| #endif /* DUK_USE_STRTAB_PROBE */ |
| |
| /* |
| * Raw intern and lookup |
| */ |
| |
| DUK_LOCAL duk_hstring *duk__do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { |
| duk_hstring *res; |
| const duk_uint8_t *extdata; |
| #if defined(DUK_USE_MARK_AND_SWEEP) |
| duk_small_uint_t prev_mark_and_sweep_base_flags; |
| #endif |
| |
| /* Prevent any side effects on the string table and the caller provided |
| * str/blen arguments while interning is in progress. For example, if |
| * the caller provided str/blen from a dynamic buffer, a finalizer might |
| * resize that dynamic buffer, invalidating the call arguments. |
| */ |
| #if defined(DUK_USE_MARK_AND_SWEEP) |
| DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); |
| prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; |
| DUK__PREVENT_MS_SIDE_EFFECTS(heap); |
| #endif |
| |
| #if defined(DUK_USE_STRTAB_PROBE) |
| if (duk__recheck_strtab_size_probe(heap, heap->st_used + 1)) { |
| goto failed; |
| } |
| #endif |
| |
| /* For manual testing only. */ |
| #if 0 |
| { |
| duk_size_t i; |
| DUK_PRINTF("INTERN: \""); |
| for (i = 0; i < blen; i++) { |
| duk_uint8_t x = str[i]; |
| if (x >= 0x20 && x <= 0x7e && x != '"' && x != '\\') { |
| DUK_PRINTF("%c", (int) x); /* char: use int cast */ |
| } else { |
| DUK_PRINTF("\\x%02lx", (long) x); |
| } |
| } |
| DUK_PRINTF("\"\n"); |
| } |
| #endif |
| |
| #if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) |
| extdata = (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen); |
| #else |
| extdata = (const duk_uint8_t *) NULL; |
| #endif |
| res = duk__alloc_init_hstring(heap, str, blen, strhash, extdata); |
| if (!res) { |
| goto failed; |
| } |
| |
| #if defined(DUK_USE_STRTAB_CHAIN) |
| if (duk__insert_hstring_chain(heap, res)) { |
| /* failed */ |
| DUK_FREE(heap, res); |
| goto failed; |
| } |
| #elif defined(DUK_USE_STRTAB_PROBE) |
| /* guaranteed to succeed */ |
| duk__insert_hstring_probe(heap, |
| #if defined(DUK_USE_HEAPPTR16) |
| heap->strtable16, |
| #else |
| heap->strtable, |
| #endif |
| heap->st_size, |
| &heap->st_used, |
| res); |
| #else |
| #error internal error, invalid strtab options |
| #endif |
| |
| /* 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 |
| * operations which require allocation (and possible gc). |
| */ |
| |
| done: |
| #if defined(DUK_USE_MARK_AND_SWEEP) |
| heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; |
| #endif |
| return res; |
| |
| failed: |
| res = NULL; |
| goto done; |
| } |
| |
| DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t *out_strhash) { |
| duk_hstring *res; |
| |
| DUK_ASSERT(out_strhash); |
| |
| *out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen); |
| |
| #if defined(DUK_USE_ROM_STRINGS) |
| { |
| duk_small_uint_t i; |
| /* XXX: This is VERY inefficient now, and should be e.g. a |
| * binary search or perfect hash, to be fixed. |
| */ |
| for (i = 0; i < (duk_small_uint_t) (sizeof(duk_rom_strings) / sizeof(duk_hstring *)); i++) { |
| duk_hstring *romstr; |
| romstr = (duk_hstring *) DUK_LOSE_CONST(duk_rom_strings[i]); |
| if (blen == DUK_HSTRING_GET_BYTELEN(romstr) && |
| DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(romstr), blen) == 0) { |
| DUK_DD(DUK_DDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", |
| romstr, (unsigned long) *out_strhash, (unsigned long) DUK_HSTRING_GET_HASH(romstr))); |
| DUK_ASSERT(*out_strhash == DUK_HSTRING_GET_HASH(romstr)); |
| *out_strhash = DUK_HSTRING_GET_HASH(romstr); |
| return romstr; |
| } |
| } |
| } |
| #endif /* DUK_USE_ROM_STRINGS */ |
| |
| #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, |
| #else |
| heap->strtable, |
| #endif |
| heap->st_size, |
| str, |
| blen, |
| *out_strhash); |
| #else |
| #error internal error, invalid strtab options |
| #endif |
| |
| return res; |
| } |
| |
| /* |
| * Exposed calls |
| */ |
| |
| #if 0 /*unused*/ |
| DUK_INTERNAL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { |
| duk_uint32_t strhash; /* dummy */ |
| return duk__do_lookup(heap, str, blen, &strhash); |
| } |
| #endif |
| |
| DUK_INTERNAL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { |
| duk_hstring *res; |
| duk_uint32_t strhash; |
| |
| /* caller is responsible for ensuring this */ |
| DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN); |
| |
| res = duk__do_lookup(heap, str, blen, &strhash); |
| if (res) { |
| return res; |
| } |
| |
| res = duk__do_intern(heap, str, blen, strhash); |
| return res; /* may be NULL */ |
| } |
| |
| DUK_INTERNAL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) { |
| duk_hstring *res = duk_heap_string_intern(thr->heap, str, blen); |
| if (!res) { |
| DUK_ERROR_ALLOC_DEFMSG(thr); |
| } |
| return res; |
| } |
| |
| #if 0 /*unused*/ |
| DUK_INTERNAL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val) { |
| char buf[DUK_STRTAB_U32_MAX_STRLEN+1]; |
| 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, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf)); |
| } |
| #endif |
| |
| DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val) { |
| char buf[DUK_STRTAB_U32_MAX_STRLEN+1]; |
| 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, (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) { |
| duk_hstring *res = duk_heap_string_intern_u32(thr->heap, val); |
| if (!res) { |
| DUK_ERROR_ALLOC_DEFMSG(thr); |
| } |
| return res; |
| } |
| |
| /* find and remove string from stringtable; caller must free the string itself */ |
| #if defined(DUK_USE_REFERENCE_COUNTING) |
| 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)); |
| |
| #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, |
| #else |
| heap->strtable, |
| #endif |
| heap->st_size, |
| h); |
| #else |
| #error internal error, invalid strtab options |
| #endif |
| } |
| #endif |
| |
| #if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE) |
| DUK_INTERNAL void duk_heap_force_strtab_resize(duk_heap *heap) { |
| duk_small_uint_t prev_mark_and_sweep_base_flags; |
| /* Force a resize so that DELETED entries are eliminated. |
| * 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_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); |
| prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; |
| DUK__PREVENT_MS_SIDE_EFFECTS(heap); |
| |
| #if defined(DUK_USE_STRTAB_CHAIN) |
| DUK_UNREF(heap); |
| #elif defined(DUK_USE_STRTAB_PROBE) |
| (void) duk__resize_strtab_probe(heap); |
| #endif |
| |
| heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; |
| } |
| #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(heap->heap_udata, 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(heap->heap_udata, 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(heap->heap_udata, 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->heap_udata, 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 |
| #undef DUK__DELETED_MARKER |