static void duk__remove_matching_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) { duk_uint32_t i; duk_uint32_t step; DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(h->hash, size); step = DUK__HASH_PROBE_STEP(h->hash); for (;;) { duk_hstring *e; e = entries[i]; if (!e) { DUK_UNREACHABLE(); break; } if (e == h) { /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("free matching hit: %d", i)); entries[i] = DUK__DELETED_MARKER(heap); break; } DUK_DDD(DUK_DDDPRINT("free matching miss: %d", i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(h->hash, size)); } }
static void duk__insert_hstring(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { duk_uint32_t i; duk_uint32_t step; 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 (;;) { duk_hstring *e; e = entries[i]; if (e == NULL) { DUK_DDD(DUK_DDDPRINT("insert hit (null): %d", i)); entries[i] = h; (*p_used)++; break; } else if (e == DUK__DELETED_MARKER(heap)) { /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %d", i)); entries[i] = h; break; } DUK_DDD(DUK_DDDPRINT("insert miss: %d", i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); } }
static duk_hstring *duk__find_matching_string(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { 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; e = entries[i]; if (!e) { return NULL; } if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { if (DUK_MEMCMP(str, DUK_HSTRING_GET_DATA(e), blen) == 0) { DUK_DDD(DUK_DDDPRINT("find matching hit: %d (step %d, size %d)", i, step, size)); return e; } } DUK_DDD(DUK_DDDPRINT("find matching miss: %d (step %d, size %d)", i, step, size)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); } DUK_UNREACHABLE(); }
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) { #ifdef DUK_USE_MARK_AND_SWEEP duk_small_uint_t prev_mark_and_sweep_base_flags; #endif #ifdef 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; #ifdef 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); #ifdef DUK_USE_MARK_AND_SWEEP DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); #endif /* * 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. */ #ifdef DUK_USE_MARK_AND_SWEEP prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; heap->mark_and_sweep_base_flags |= \ DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive call here */ DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings */ DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ #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 #ifdef DUK_USE_MARK_AND_SWEEP heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; #endif if (!new_entries) { goto resize_error; } #ifdef 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_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(); }