]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_heap_stringtable.c
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.5.2 / src-separate / duk_heap_stringtable.c
1 /*
2 * Heap stringtable handling, string interning.
3 */
4
5 #include "duk_internal.h"
6
7 #if defined(DUK_USE_STRTAB_PROBE)
8 #define DUK__HASH_INITIAL(hash,h_size) DUK_STRTAB_HASH_INITIAL((hash),(h_size))
9 #define DUK__HASH_PROBE_STEP(hash) DUK_STRTAB_HASH_PROBE_STEP((hash))
10 #define DUK__DELETED_MARKER(heap) DUK_STRTAB_DELETED_MARKER((heap))
11 #endif
12
13 #if defined(DUK_USE_MARK_AND_SWEEP)
14 #define DUK__PREVENT_MS_SIDE_EFFECTS(heap) do { \
15 (heap)->mark_and_sweep_base_flags |= \
16 DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive string table call */ \
17 DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings, invalidation of call data argument, etc. */ \
18 DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ \
19 } while (0)
20 #endif
21
22 /*
23 * Create a hstring and insert into the heap. The created object
24 * is directly garbage collectable with reference count zero.
25 *
26 * The caller must place the interned string into the stringtable
27 * immediately (without chance of a longjmp); otherwise the string
28 * is lost.
29 */
30
31 DUK_LOCAL
32 duk_hstring *duk__alloc_init_hstring(duk_heap *heap,
33 const duk_uint8_t *str,
34 duk_uint32_t blen,
35 duk_uint32_t strhash,
36 const duk_uint8_t *extdata) {
37 duk_hstring *res = NULL;
38 duk_uint8_t *data;
39 duk_size_t alloc_size;
40 duk_uarridx_t dummy;
41 duk_uint32_t clen;
42
43 #if defined(DUK_USE_STRLEN16)
44 /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */
45 if (blen > 0xffffUL) {
46 DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern"));
47 return NULL;
48 }
49 #endif
50
51 if (extdata) {
52 alloc_size = (duk_size_t) sizeof(duk_hstring_external);
53 res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
54 if (!res) {
55 goto alloc_error;
56 }
57 DUK_MEMZERO(res, sizeof(duk_hstring_external));
58 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
59 DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
60 #endif
61 DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA);
62
63 ((duk_hstring_external *) res)->extdata = extdata;
64 } else {
65 /* NUL terminate for convenient C access */
66 alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1);
67 res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
68 if (!res) {
69 goto alloc_error;
70 }
71 DUK_MEMZERO(res, sizeof(duk_hstring));
72 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
73 DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
74 #endif
75 DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0);
76
77 data = (duk_uint8_t *) (res + 1);
78 DUK_MEMCPY(data, str, blen);
79 data[blen] = (duk_uint8_t) 0;
80 }
81
82 DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(res));
83 if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) {
84 DUK_HSTRING_SET_ARRIDX(res);
85 }
86
87 /* All strings beginning with 0xff are treated as "internal",
88 * even strings interned by the user. This allows user code to
89 * create internal properties too, and makes behavior consistent
90 * in case user code happens to use a string also used by Duktape
91 * (such as string has already been interned and has the 'internal'
92 * flag set).
93 */
94 DUK_ASSERT(!DUK_HSTRING_HAS_INTERNAL(res));
95 if (blen > 0 && str[0] == (duk_uint8_t) 0xff) {
96 DUK_HSTRING_SET_INTERNAL(res);
97 }
98
99 DUK_HSTRING_SET_HASH(res, strhash);
100 DUK_HSTRING_SET_BYTELEN(res, blen);
101
102 clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen);
103 DUK_ASSERT(clen <= blen);
104 #if defined(DUK_USE_HSTRING_CLEN)
105 DUK_HSTRING_SET_CHARLEN(res, clen);
106 #endif
107
108 /* Using an explicit 'ASCII' flag has larger footprint (one call site
109 * only) but is quite useful for the case when there's no explicit
110 * 'clen' in duk_hstring.
111 */
112 DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res));
113 if (clen == blen) {
114 DUK_HSTRING_SET_ASCII(res);
115 }
116
117 DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%ld",
118 (unsigned long) DUK_HSTRING_GET_HASH(res),
119 (long) DUK_HSTRING_GET_BYTELEN(res),
120 (long) DUK_HSTRING_GET_CHARLEN(res),
121 (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0),
122 (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0)));
123
124 return res;
125
126 alloc_error:
127 DUK_FREE(heap, res);
128 return NULL;
129 }
130
131 /*
132 * String table algorithm: fixed size string table with array chaining
133 *
134 * The top level string table has a fixed size, with each slot holding
135 * either NULL, string pointer, or pointer to a separately allocated
136 * string pointer list.
137 *
138 * This is good for low memory environments using a pool allocator: the
139 * top level allocation has a fixed size and the pointer lists have quite
140 * small allocation size, which further matches the typical pool sizes
141 * needed by objects, strings, property tables, etc.
142 */
143
144 #if defined(DUK_USE_STRTAB_CHAIN)
145
146 #if defined(DUK_USE_HEAPPTR16)
147 DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) {
148 duk_small_uint_t slotidx;
149 duk_strtab_entry *e;
150 duk_uint16_t *lst;
151 duk_uint16_t *new_lst;
152 duk_size_t i, n;
153 duk_uint16_t null16 = heap->heapptr_null16;
154 duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
155
156 DUK_ASSERT(heap != NULL);
157 DUK_ASSERT(h != NULL);
158
159 slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
160 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
161
162 e = heap->strtable + slotidx;
163 if (e->listlen == 0) {
164 if (e->u.str16 == null16) {
165 e->u.str16 = h16;
166 } else {
167 /* Now two entries in the same slot, alloc list */
168 lst = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * 2);
169 if (lst == NULL) {
170 return 1; /* fail */
171 }
172 lst[0] = e->u.str16;
173 lst[1] = h16;
174 e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) lst);
175 e->listlen = 2;
176 }
177 } else {
178 DUK_ASSERT(e->u.strlist16 != null16);
179 lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
180 DUK_ASSERT(lst != NULL);
181 for (i = 0, n = e->listlen; i < n; i++) {
182 if (lst[i] == null16) {
183 lst[i] = h16;
184 return 0;
185 }
186 }
187
188 if (e->listlen + 1 == 0) {
189 /* Overflow, relevant mainly when listlen is 16 bits. */
190 return 1; /* fail */
191 }
192
193 new_lst = (duk_uint16_t *) DUK_REALLOC(heap, lst, sizeof(duk_uint16_t) * (e->listlen + 1));
194 if (new_lst == NULL) {
195 return 1; /* fail */
196 }
197 new_lst[e->listlen++] = h16;
198 e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) new_lst);
199 }
200 return 0;
201 }
202 #else /* DUK_USE_HEAPPTR16 */
203 DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) {
204 duk_small_uint_t slotidx;
205 duk_strtab_entry *e;
206 duk_hstring **lst;
207 duk_hstring **new_lst;
208 duk_size_t i, n;
209
210 DUK_ASSERT(heap != NULL);
211 DUK_ASSERT(h != NULL);
212
213 slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
214 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
215
216 e = heap->strtable + slotidx;
217 if (e->listlen == 0) {
218 if (e->u.str == NULL) {
219 e->u.str = h;
220 } else {
221 /* Now two entries in the same slot, alloc list */
222 lst = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * 2);
223 if (lst == NULL) {
224 return 1; /* fail */
225 }
226 lst[0] = e->u.str;
227 lst[1] = h;
228 e->u.strlist = lst;
229 e->listlen = 2;
230 }
231 } else {
232 DUK_ASSERT(e->u.strlist != NULL);
233 lst = e->u.strlist;
234 for (i = 0, n = e->listlen; i < n; i++) {
235 if (lst[i] == NULL) {
236 lst[i] = h;
237 return 0;
238 }
239 }
240
241 if (e->listlen + 1 == 0) {
242 /* Overflow, relevant mainly when listlen is 16 bits. */
243 return 1; /* fail */
244 }
245
246 new_lst = (duk_hstring **) DUK_REALLOC(heap, e->u.strlist, sizeof(duk_hstring *) * (e->listlen + 1));
247 if (new_lst == NULL) {
248 return 1; /* fail */
249 }
250 new_lst[e->listlen++] = h;
251 e->u.strlist = new_lst;
252 }
253 return 0;
254 }
255 #endif /* DUK_USE_HEAPPTR16 */
256
257 #if defined(DUK_USE_HEAPPTR16)
258 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) {
259 duk_small_uint_t slotidx;
260 duk_strtab_entry *e;
261 duk_uint16_t *lst;
262 duk_size_t i, n;
263 duk_uint16_t null16 = heap->heapptr_null16;
264
265 DUK_ASSERT(heap != NULL);
266
267 slotidx = strhash % DUK_STRTAB_CHAIN_SIZE;
268 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
269
270 e = heap->strtable + slotidx;
271 if (e->listlen == 0) {
272 if (e->u.str16 != null16) {
273 duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16);
274 DUK_ASSERT(h != NULL);
275 if (DUK_HSTRING_GET_BYTELEN(h) == blen &&
276 DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) {
277 return h;
278 }
279 }
280 } else {
281 DUK_ASSERT(e->u.strlist16 != null16);
282 lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
283 DUK_ASSERT(lst != NULL);
284 for (i = 0, n = e->listlen; i < n; i++) {
285 if (lst[i] != null16) {
286 duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[i]);
287 DUK_ASSERT(h != NULL);
288 if (DUK_HSTRING_GET_BYTELEN(h) == blen &&
289 DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) {
290 return h;
291 }
292 }
293 }
294 }
295
296 return NULL;
297 }
298 #else /* DUK_USE_HEAPPTR16 */
299 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) {
300 duk_small_uint_t slotidx;
301 duk_strtab_entry *e;
302 duk_hstring **lst;
303 duk_size_t i, n;
304
305 DUK_ASSERT(heap != NULL);
306
307 slotidx = strhash % DUK_STRTAB_CHAIN_SIZE;
308 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
309
310 e = heap->strtable + slotidx;
311 if (e->listlen == 0) {
312 if (e->u.str != NULL &&
313 DUK_HSTRING_GET_BYTELEN(e->u.str) == blen &&
314 DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e->u.str), (size_t) blen) == 0) {
315 return e->u.str;
316 }
317 } else {
318 DUK_ASSERT(e->u.strlist != NULL);
319 lst = e->u.strlist;
320 for (i = 0, n = e->listlen; i < n; i++) {
321 if (lst[i] != NULL &&
322 DUK_HSTRING_GET_BYTELEN(lst[i]) == blen &&
323 DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(lst[i]), (size_t) blen) == 0) {
324 return lst[i];
325 }
326 }
327 }
328
329 return NULL;
330 }
331 #endif /* DUK_USE_HEAPPTR16 */
332
333 #if defined(DUK_USE_HEAPPTR16)
334 DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) {
335 duk_small_uint_t slotidx;
336 duk_strtab_entry *e;
337 duk_uint16_t *lst;
338 duk_size_t i, n;
339 duk_uint16_t h16;
340 duk_uint16_t null16 = heap->heapptr_null16;
341
342 DUK_ASSERT(heap != NULL);
343 DUK_ASSERT(h != NULL);
344
345 slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
346 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
347
348 DUK_ASSERT(h != NULL);
349 h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
350
351 e = heap->strtable + slotidx;
352 if (e->listlen == 0) {
353 if (e->u.str16 == h16) {
354 e->u.str16 = null16;
355 return;
356 }
357 } else {
358 DUK_ASSERT(e->u.strlist16 != null16);
359 lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
360 DUK_ASSERT(lst != NULL);
361 for (i = 0, n = e->listlen; i < n; i++) {
362 if (lst[i] == h16) {
363 lst[i] = null16;
364 return;
365 }
366 }
367 }
368
369 DUK_D(DUK_DPRINT("failed to find string that should be in stringtable"));
370 DUK_UNREACHABLE();
371 return;
372 }
373 #else /* DUK_USE_HEAPPTR16 */
374 DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) {
375 duk_small_uint_t slotidx;
376 duk_strtab_entry *e;
377 duk_hstring **lst;
378 duk_size_t i, n;
379
380 DUK_ASSERT(heap != NULL);
381 DUK_ASSERT(h != NULL);
382
383 slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE;
384 DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE);
385
386 e = heap->strtable + slotidx;
387 if (e->listlen == 0) {
388 DUK_ASSERT(h != NULL);
389 if (e->u.str == h) {
390 e->u.str = NULL;
391 return;
392 }
393 } else {
394 DUK_ASSERT(e->u.strlist != NULL);
395 lst = e->u.strlist;
396 for (i = 0, n = e->listlen; i < n; i++) {
397 DUK_ASSERT(h != NULL);
398 if (lst[i] == h) {
399 lst[i] = NULL;
400 return;
401 }
402 }
403 }
404
405 DUK_D(DUK_DPRINT("failed to find string that should be in stringtable"));
406 DUK_UNREACHABLE();
407 return;
408 }
409 #endif /* DUK_USE_HEAPPTR16 */
410
411 #if defined(DUK_USE_DEBUG)
412 DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
413 duk_strtab_entry *e;
414 duk_small_uint_t i;
415 duk_size_t j, n, used;
416 #if defined(DUK_USE_HEAPPTR16)
417 duk_uint16_t *lst;
418 duk_uint16_t null16 = heap->heapptr_null16;
419 #else
420 duk_hstring **lst;
421 #endif
422
423 DUK_ASSERT(heap != NULL);
424
425 for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
426 e = heap->strtable + i;
427
428 if (e->listlen == 0) {
429 #if defined(DUK_USE_HEAPPTR16)
430 DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0)));
431 #else
432 DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0)));
433 #endif
434 } else {
435 used = 0;
436 #if defined(DUK_USE_HEAPPTR16)
437 lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
438 #else
439 lst = e->u.strlist;
440 #endif
441 DUK_ASSERT(lst != NULL);
442 for (j = 0, n = e->listlen; j < n; j++) {
443 #if defined(DUK_USE_HEAPPTR16)
444 if (lst[j] != null16) {
445 #else
446 if (lst[j] != NULL) {
447 #endif
448 used++;
449 }
450 }
451 DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (int) i, (int) used, (int) e->listlen));
452 }
453 }
454 }
455 #endif /* DUK_USE_DEBUG */
456
457 #endif /* DUK_USE_STRTAB_CHAIN */
458
459 /*
460 * String table algorithm: closed hashing with a probe sequence
461 *
462 * This is the default algorithm and works fine for environments with
463 * minimal memory constraints.
464 */
465
466 #if defined(DUK_USE_STRTAB_PROBE)
467
468 /* Count actually used (non-NULL, non-DELETED) entries. */
469 DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) {
470 duk_int_t res = 0;
471 duk_uint_fast32_t i, n;
472 #if defined(DUK_USE_HEAPPTR16)
473 duk_uint16_t null16 = heap->heapptr_null16;
474 duk_uint16_t deleted16 = heap->heapptr_deleted16;
475 #endif
476
477 n = (duk_uint_fast32_t) heap->st_size;
478 for (i = 0; i < n; i++) {
479 #if defined(DUK_USE_HEAPPTR16)
480 if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) {
481 #else
482 if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) {
483 #endif
484 res++;
485 }
486 }
487 return res;
488 }
489
490 #if defined(DUK_USE_HEAPPTR16)
491 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) {
492 #else
493 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) {
494 #endif
495 duk_uint32_t i;
496 duk_uint32_t step;
497 #if defined(DUK_USE_HEAPPTR16)
498 duk_uint16_t null16 = heap->heapptr_null16;
499 duk_uint16_t deleted16 = heap->heapptr_deleted16;
500 #endif
501
502 DUK_ASSERT(size > 0);
503
504 i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size);
505 step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h));
506 for (;;) {
507 #if defined(DUK_USE_HEAPPTR16)
508 duk_uint16_t e16 = entries16[i];
509 #else
510 duk_hstring *e = entries[i];
511 #endif
512
513 #if defined(DUK_USE_HEAPPTR16)
514 /* XXX: could check for e16 == 0 because NULL is guaranteed to
515 * encode to zero.
516 */
517 if (e16 == null16) {
518 #else
519 if (e == NULL) {
520 #endif
521 DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i));
522 #if defined(DUK_USE_HEAPPTR16)
523 entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
524 #else
525 entries[i] = h;
526 #endif
527 (*p_used)++;
528 break;
529 #if defined(DUK_USE_HEAPPTR16)
530 } else if (e16 == deleted16) {
531 #else
532 } else if (e == DUK__DELETED_MARKER(heap)) {
533 #endif
534 /* st_used remains the same, DELETED is counted as used */
535 DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i));
536 #if defined(DUK_USE_HEAPPTR16)
537 entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
538 #else
539 entries[i] = h;
540 #endif
541 break;
542 }
543 DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i));
544 i = (i + step) % size;
545
546 /* looping should never happen */
547 DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size));
548 }
549 }
550
551 #if defined(DUK_USE_HEAPPTR16)
552 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) {
553 #else
554 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) {
555 #endif
556 duk_uint32_t i;
557 duk_uint32_t step;
558
559 DUK_ASSERT(size > 0);
560
561 i = DUK__HASH_INITIAL(strhash, size);
562 step = DUK__HASH_PROBE_STEP(strhash);
563 for (;;) {
564 duk_hstring *e;
565 #if defined(DUK_USE_HEAPPTR16)
566 e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]);
567 #else
568 e = entries[i];
569 #endif
570
571 if (!e) {
572 return NULL;
573 }
574 if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) {
575 if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) {
576 DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)",
577 (long) i, (long) step, (long) size));
578 return e;
579 }
580 }
581 DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)",
582 (long) i, (long) step, (long) size));
583 i = (i + step) % size;
584
585 /* looping should never happen */
586 DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size));
587 }
588 DUK_UNREACHABLE();
589 }
590
591 #if defined(DUK_USE_HEAPPTR16)
592 DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) {
593 #else
594 DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) {
595 #endif
596 duk_uint32_t i;
597 duk_uint32_t step;
598 duk_uint32_t hash;
599 #if defined(DUK_USE_HEAPPTR16)
600 duk_uint16_t null16 = heap->heapptr_null16;
601 duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h);
602 #endif
603
604 DUK_ASSERT(size > 0);
605
606 hash = DUK_HSTRING_GET_HASH(h);
607 i = DUK__HASH_INITIAL(hash, size);
608 step = DUK__HASH_PROBE_STEP(hash);
609 for (;;) {
610 #if defined(DUK_USE_HEAPPTR16)
611 duk_uint16_t e16 = entries16[i];
612 #else
613 duk_hstring *e = entries[i];
614 #endif
615
616 #if defined(DUK_USE_HEAPPTR16)
617 if (e16 == null16) {
618 #else
619 if (!e) {
620 #endif
621 DUK_UNREACHABLE();
622 break;
623 }
624 #if defined(DUK_USE_HEAPPTR16)
625 if (e16 == h16) {
626 #else
627 if (e == h) {
628 #endif
629 /* st_used remains the same, DELETED is counted as used */
630 DUK_DDD(DUK_DDDPRINT("free matching hit: %ld", (long) i));
631 #if defined(DUK_USE_HEAPPTR16)
632 entries16[i] = heap->heapptr_deleted16;
633 #else
634 entries[i] = DUK__DELETED_MARKER(heap);
635 #endif
636 break;
637 }
638
639 DUK_DDD(DUK_DDDPRINT("free matching miss: %ld", (long) i));
640 i = (i + step) % size;
641
642 /* looping should never happen */
643 DUK_ASSERT(i != DUK__HASH_INITIAL(hash, size));
644 }
645 }
646
647 DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) {
648 #if defined(DUK_USE_DEBUG)
649 duk_uint32_t old_used = heap->st_used;
650 #endif
651 duk_uint32_t old_size = heap->st_size;
652 #if defined(DUK_USE_HEAPPTR16)
653 duk_uint16_t *old_entries = heap->strtable16;
654 duk_uint16_t *new_entries = NULL;
655 #else
656 duk_hstring **old_entries = heap->strtable;
657 duk_hstring **new_entries = NULL;
658 #endif
659 duk_uint32_t new_used = 0;
660 duk_uint32_t i;
661
662 #if defined(DUK_USE_DEBUG)
663 DUK_UNREF(old_used); /* unused with some debug level combinations */
664 #endif
665
666 #ifdef DUK_USE_DDDPRINT
667 DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load",
668 (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used,
669 (long) (((double) old_used) / ((double) old_size) * 100.0),
670 (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used_probe(heap),
671 (long) (((double) duk__count_used_probe(heap)) / ((double) new_size) * 100.0)));
672 #endif
673
674 DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used_probe(heap)); /* required for rehash to succeed, equality not that useful */
675 DUK_ASSERT(old_entries);
676
677 /*
678 * The attempt to allocate may cause a GC. Such a GC must not attempt to resize
679 * the stringtable (though it can be swept); finalizer execution and object
680 * compaction must also be postponed to avoid the pressure to add strings to the
681 * string table. Call site must prevent these.
682 */
683
684 #if defined(DUK_USE_MARK_AND_SWEEP)
685 DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE);
686 DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_FINALIZERS);
687 DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_OBJECT_COMPACTION);
688 #endif
689
690 #if defined(DUK_USE_HEAPPTR16)
691 new_entries = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * new_size);
692 #else
693 new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size);
694 #endif
695
696 if (!new_entries) {
697 goto resize_error;
698 }
699
700 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
701 for (i = 0; i < new_size; i++) {
702 #if defined(DUK_USE_HEAPPTR16)
703 new_entries[i] = heap->heapptr_null16;
704 #else
705 new_entries[i] = NULL;
706 #endif
707 }
708 #else
709 #if defined(DUK_USE_HEAPPTR16)
710 /* Relies on NULL encoding to zero. */
711 DUK_MEMZERO(new_entries, sizeof(duk_uint16_t) * new_size);
712 #else
713 DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size);
714 #endif
715 #endif
716
717 /* Because new_size > duk__count_used_probe(heap), guaranteed to work */
718 for (i = 0; i < old_size; i++) {
719 duk_hstring *e;
720
721 #if defined(DUK_USE_HEAPPTR16)
722 e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, old_entries[i]);
723 #else
724 e = old_entries[i];
725 #endif
726 if (e == NULL || e == DUK__DELETED_MARKER(heap)) {
727 continue;
728 }
729 /* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */
730 duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e);
731 }
732
733 #ifdef DUK_USE_DDPRINT
734 DUK_DD(DUK_DDPRINT("resized stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load",
735 (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used,
736 (long) (((double) old_used) / ((double) old_size) * 100.0),
737 (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) new_used,
738 (long) (((double) new_used) / ((double) new_size) * 100.0)));
739 #endif
740
741 #if defined(DUK_USE_HEAPPTR16)
742 DUK_FREE(heap, heap->strtable16);
743 heap->strtable16 = new_entries;
744 #else
745 DUK_FREE(heap, heap->strtable);
746 heap->strtable = new_entries;
747 #endif
748 heap->st_size = new_size;
749 heap->st_used = new_used; /* may be less, since DELETED entries are NULLed by rehash */
750
751 return 0; /* OK */
752
753 resize_error:
754 DUK_FREE(heap, new_entries);
755 return 1; /* FAIL */
756 }
757
758 DUK_LOCAL duk_bool_t duk__resize_strtab_probe(duk_heap *heap) {
759 duk_uint32_t new_size;
760 duk_bool_t ret;
761
762 new_size = (duk_uint32_t) duk__count_used_probe(heap);
763 if (new_size >= 0x80000000UL) {
764 new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME;
765 } else {
766 new_size = duk_util_get_hash_prime(DUK_STRTAB_GROW_ST_SIZE(new_size));
767 new_size = duk_util_get_hash_prime(new_size);
768 }
769 DUK_ASSERT(new_size > 0);
770
771 /* rehash even if old and new sizes are the same to get rid of
772 * DELETED entries.
773 */
774
775 ret = duk__resize_strtab_raw_probe(heap, new_size);
776
777 return ret;
778 }
779
780 DUK_LOCAL duk_bool_t duk__recheck_strtab_size_probe(duk_heap *heap, duk_uint32_t new_used) {
781 duk_uint32_t new_free;
782 duk_uint32_t tmp1;
783 duk_uint32_t tmp2;
784
785 DUK_ASSERT(new_used <= heap->st_size); /* grow by at most one */
786 new_free = heap->st_size - new_used; /* unsigned intentionally */
787
788 /* new_free / size <= 1 / DIV <=> new_free <= size / DIV */
789 /* new_used / size <= 1 / DIV <=> new_used <= size / DIV */
790
791 tmp1 = heap->st_size / DUK_STRTAB_MIN_FREE_DIVISOR;
792 tmp2 = heap->st_size / DUK_STRTAB_MIN_USED_DIVISOR;
793
794 if (new_free <= tmp1 || new_used <= tmp2) {
795 /* load factor too low or high, count actually used entries and resize */
796 return duk__resize_strtab_probe(heap);
797 } else {
798 return 0; /* OK */
799 }
800 }
801
802 #if defined(DUK_USE_DEBUG)
803 DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) {
804 duk_uint32_t i;
805 duk_hstring *h;
806
807 DUK_ASSERT(heap != NULL);
808 #if defined(DUK_USE_HEAPPTR16)
809 DUK_ASSERT(heap->strtable16 != NULL);
810 #else
811 DUK_ASSERT(heap->strtable != NULL);
812 #endif
813 DUK_UNREF(h);
814
815 for (i = 0; i < heap->st_size; i++) {
816 #if defined(DUK_USE_HEAPPTR16)
817 h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]);
818 #else
819 h = heap->strtable[i];
820 #endif
821
822 DUK_DD(DUK_DDPRINT("[%03d] -> %p", (int) i, (void *) h));
823 }
824 }
825 #endif /* DUK_USE_DEBUG */
826
827 #endif /* DUK_USE_STRTAB_PROBE */
828
829 /*
830 * Raw intern and lookup
831 */
832
833 DUK_LOCAL duk_hstring *duk__do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) {
834 duk_hstring *res;
835 const duk_uint8_t *extdata;
836 #if defined(DUK_USE_MARK_AND_SWEEP)
837 duk_small_uint_t prev_mark_and_sweep_base_flags;
838 #endif
839
840 /* Prevent any side effects on the string table and the caller provided
841 * str/blen arguments while interning is in progress. For example, if
842 * the caller provided str/blen from a dynamic buffer, a finalizer might
843 * resize that dynamic buffer, invalidating the call arguments.
844 */
845 #if defined(DUK_USE_MARK_AND_SWEEP)
846 DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0);
847 prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags;
848 DUK__PREVENT_MS_SIDE_EFFECTS(heap);
849 #endif
850
851 #if defined(DUK_USE_STRTAB_PROBE)
852 if (duk__recheck_strtab_size_probe(heap, heap->st_used + 1)) {
853 goto failed;
854 }
855 #endif
856
857 /* For manual testing only. */
858 #if 0
859 {
860 duk_size_t i;
861 DUK_PRINTF("INTERN: \"");
862 for (i = 0; i < blen; i++) {
863 duk_uint8_t x = str[i];
864 if (x >= 0x20 && x <= 0x7e && x != '"' && x != '\\') {
865 DUK_PRINTF("%c", (int) x); /* char: use int cast */
866 } else {
867 DUK_PRINTF("\\x%02lx", (long) x);
868 }
869 }
870 DUK_PRINTF("\"\n");
871 }
872 #endif
873
874 #if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK)
875 extdata = (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen);
876 #else
877 extdata = (const duk_uint8_t *) NULL;
878 #endif
879 res = duk__alloc_init_hstring(heap, str, blen, strhash, extdata);
880 if (!res) {
881 goto failed;
882 }
883
884 #if defined(DUK_USE_STRTAB_CHAIN)
885 if (duk__insert_hstring_chain(heap, res)) {
886 /* failed */
887 DUK_FREE(heap, res);
888 goto failed;
889 }
890 #elif defined(DUK_USE_STRTAB_PROBE)
891 /* guaranteed to succeed */
892 duk__insert_hstring_probe(heap,
893 #if defined(DUK_USE_HEAPPTR16)
894 heap->strtable16,
895 #else
896 heap->strtable,
897 #endif
898 heap->st_size,
899 &heap->st_used,
900 res);
901 #else
902 #error internal error, invalid strtab options
903 #endif
904
905 /* Note: hstring is in heap but has refcount zero and is not strongly reachable.
906 * Caller should increase refcount and make the hstring reachable before any
907 * operations which require allocation (and possible gc).
908 */
909
910 done:
911 #if defined(DUK_USE_MARK_AND_SWEEP)
912 heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
913 #endif
914 return res;
915
916 failed:
917 res = NULL;
918 goto done;
919 }
920
921 DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t *out_strhash) {
922 duk_hstring *res;
923
924 DUK_ASSERT(out_strhash);
925
926 *out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen);
927
928 #if defined(DUK_USE_ROM_STRINGS)
929 {
930 duk_small_uint_t i;
931 /* XXX: This is VERY inefficient now, and should be e.g. a
932 * binary search or perfect hash, to be fixed.
933 */
934 for (i = 0; i < (duk_small_uint_t) (sizeof(duk_rom_strings) / sizeof(duk_hstring *)); i++) {
935 duk_hstring *romstr;
936 romstr = (duk_hstring *) DUK_LOSE_CONST(duk_rom_strings[i]);
937 if (blen == DUK_HSTRING_GET_BYTELEN(romstr) &&
938 DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(romstr), blen) == 0) {
939 DUK_DD(DUK_DDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx",
940 romstr, (unsigned long) *out_strhash, (unsigned long) DUK_HSTRING_GET_HASH(romstr)));
941 DUK_ASSERT(*out_strhash == DUK_HSTRING_GET_HASH(romstr));
942 *out_strhash = DUK_HSTRING_GET_HASH(romstr);
943 return romstr;
944 }
945 }
946 }
947 #endif /* DUK_USE_ROM_STRINGS */
948
949 #if defined(DUK_USE_STRTAB_CHAIN)
950 res = duk__find_matching_string_chain(heap, str, blen, *out_strhash);
951 #elif defined(DUK_USE_STRTAB_PROBE)
952 res = duk__find_matching_string_probe(heap,
953 #if defined(DUK_USE_HEAPPTR16)
954 heap->strtable16,
955 #else
956 heap->strtable,
957 #endif
958 heap->st_size,
959 str,
960 blen,
961 *out_strhash);
962 #else
963 #error internal error, invalid strtab options
964 #endif
965
966 return res;
967 }
968
969 /*
970 * Exposed calls
971 */
972
973 #if 0 /*unused*/
974 DUK_INTERNAL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) {
975 duk_uint32_t strhash; /* dummy */
976 return duk__do_lookup(heap, str, blen, &strhash);
977 }
978 #endif
979
980 DUK_INTERNAL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) {
981 duk_hstring *res;
982 duk_uint32_t strhash;
983
984 /* caller is responsible for ensuring this */
985 DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN);
986
987 res = duk__do_lookup(heap, str, blen, &strhash);
988 if (res) {
989 return res;
990 }
991
992 res = duk__do_intern(heap, str, blen, strhash);
993 return res; /* may be NULL */
994 }
995
996 DUK_INTERNAL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) {
997 duk_hstring *res = duk_heap_string_intern(thr->heap, str, blen);
998 if (!res) {
999 DUK_ERROR_ALLOC_DEFMSG(thr);
1000 }
1001 return res;
1002 }
1003
1004 #if 0 /*unused*/
1005 DUK_INTERNAL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val) {
1006 char buf[DUK_STRTAB_U32_MAX_STRLEN+1];
1007 DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val);
1008 buf[sizeof(buf) - 1] = (char) 0;
1009 DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */
1010 return duk_heap_string_lookup(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
1011 }
1012 #endif
1013
1014 DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val) {
1015 char buf[DUK_STRTAB_U32_MAX_STRLEN+1];
1016 DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val);
1017 buf[sizeof(buf) - 1] = (char) 0;
1018 DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */
1019 return duk_heap_string_intern(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf));
1020 }
1021
1022 DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) {
1023 duk_hstring *res = duk_heap_string_intern_u32(thr->heap, val);
1024 if (!res) {
1025 DUK_ERROR_ALLOC_DEFMSG(thr);
1026 }
1027 return res;
1028 }
1029
1030 /* find and remove string from stringtable; caller must free the string itself */
1031 #if defined(DUK_USE_REFERENCE_COUNTING)
1032 DUK_INTERNAL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h) {
1033 DUK_DDD(DUK_DDDPRINT("remove string from stringtable: %!O", (duk_heaphdr *) h));
1034
1035 #if defined(DUK_USE_STRTAB_CHAIN)
1036 duk__remove_matching_hstring_chain(heap, h);
1037 #elif defined(DUK_USE_STRTAB_PROBE)
1038 duk__remove_matching_hstring_probe(heap,
1039 #if defined(DUK_USE_HEAPPTR16)
1040 heap->strtable16,
1041 #else
1042 heap->strtable,
1043 #endif
1044 heap->st_size,
1045 h);
1046 #else
1047 #error internal error, invalid strtab options
1048 #endif
1049 }
1050 #endif
1051
1052 #if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE)
1053 DUK_INTERNAL void duk_heap_force_strtab_resize(duk_heap *heap) {
1054 duk_small_uint_t prev_mark_and_sweep_base_flags;
1055 /* Force a resize so that DELETED entries are eliminated.
1056 * Another option would be duk__recheck_strtab_size_probe();
1057 * but since that happens on every intern anyway, this whole
1058 * check can now be disabled.
1059 */
1060
1061 DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0);
1062 prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags;
1063 DUK__PREVENT_MS_SIDE_EFFECTS(heap);
1064
1065 #if defined(DUK_USE_STRTAB_CHAIN)
1066 DUK_UNREF(heap);
1067 #elif defined(DUK_USE_STRTAB_PROBE)
1068 (void) duk__resize_strtab_probe(heap);
1069 #endif
1070
1071 heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
1072 }
1073 #endif
1074
1075 #if defined(DUK_USE_STRTAB_CHAIN)
1076 DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) {
1077 /* Free strings in the stringtable and any allocations needed
1078 * by the stringtable itself.
1079 */
1080 duk_uint_fast32_t i, j;
1081 duk_strtab_entry *e;
1082 #if defined(DUK_USE_HEAPPTR16)
1083 duk_uint16_t *lst;
1084 duk_uint16_t null16 = heap->heapptr_null16;
1085 #else
1086 duk_hstring **lst;
1087 #endif
1088 duk_hstring *h;
1089
1090 for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) {
1091 e = heap->strtable + i;
1092 if (e->listlen > 0) {
1093 #if defined(DUK_USE_HEAPPTR16)
1094 lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16);
1095 #else
1096 lst = e->u.strlist;
1097 #endif
1098 DUK_ASSERT(lst != NULL);
1099
1100 for (j = 0; j < e->listlen; j++) {
1101 #if defined(DUK_USE_HEAPPTR16)
1102 h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[j]);
1103 lst[j] = null16;
1104 #else
1105 h = lst[j];
1106 lst[j] = NULL;
1107 #endif
1108 /* strings may have inner refs (extdata) in some cases */
1109 if (h != NULL) {
1110 duk_free_hstring_inner(heap, h);
1111 DUK_FREE(heap, h);
1112 }
1113 }
1114 #if defined(DUK_USE_HEAPPTR16)
1115 e->u.strlist16 = null16;
1116 #else
1117 e->u.strlist = NULL;
1118 #endif
1119 DUK_FREE(heap, lst);
1120 } else {
1121 #if defined(DUK_USE_HEAPPTR16)
1122 h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16);
1123 e->u.str16 = null16;
1124 #else
1125 h = e->u.str;
1126 e->u.str = NULL;
1127 #endif
1128 if (h != NULL) {
1129 duk_free_hstring_inner(heap, h);
1130 DUK_FREE(heap, h);
1131 }
1132 }
1133 e->listlen = 0;
1134 }
1135 }
1136 #endif /* DUK_USE_STRTAB_CHAIN */
1137
1138 #if defined(DUK_USE_STRTAB_PROBE)
1139 DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) {
1140 duk_uint_fast32_t i;
1141 duk_hstring *h;
1142
1143 #if defined(DUK_USE_HEAPPTR16)
1144 if (heap->strtable16) {
1145 #else
1146 if (heap->strtable) {
1147 #endif
1148 for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) {
1149 #if defined(DUK_USE_HEAPPTR16)
1150 h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]);
1151 #else
1152 h = heap->strtable[i];
1153 #endif
1154 if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) {
1155 continue;
1156 }
1157 DUK_ASSERT(h != NULL);
1158
1159 /* strings may have inner refs (extdata) in some cases */
1160 duk_free_hstring_inner(heap, h);
1161 DUK_FREE(heap, h);
1162 #if 0 /* not strictly necessary */
1163 heap->strtable[i] = NULL;
1164 #endif
1165 }
1166 #if defined(DUK_USE_HEAPPTR16)
1167 DUK_FREE(heap, heap->strtable16);
1168 #else
1169 DUK_FREE(heap, heap->strtable);
1170 #endif
1171 #if 0 /* not strictly necessary */
1172 heap->strtable = NULL;
1173 #endif
1174 }
1175 }
1176 #endif /* DUK_USE_STRTAB_PROBE */
1177
1178 /* Undefine local defines */
1179 #undef DUK__HASH_INITIAL
1180 #undef DUK__HASH_PROBE_STEP
1181 #undef DUK__DELETED_MARKER