void duk_hbuffer_remove_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, size_t length) { char *p; size_t end_offset; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT(offset >= 0); /* always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT(length >= 0); /* always true */ DUK_ASSERT(offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); end_offset = offset + length; if (end_offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not strictly from end of buffer; need to shuffle data */ DUK_MEMMOVE(p + offset, p + end_offset, DUK_HBUFFER_GET_SIZE(buf) - end_offset); /* always > 0 */ } DUK_MEMSET(p + DUK_HBUFFER_GET_SIZE(buf) - length, 0, length); /* always > 0 */ buf->size -= length; /* Note: no shrink check, intentional */ }
void duk_hbuffer_insert_bytes(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint8_t *data, size_t length) { char *p; /* XXX: allow inserts with offset > curr_size? i.e., insert zeroes automatically? */ DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(offset >= 0); /* unsigned, so always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* equality is OK (= append) */ DUK_ASSERT(data != NULL); DUK_ASSERT_DISABLE(length >= 0); /* unsigned, so always true */ if (length == 0) { return; } if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) { duk_hbuffer_resize(thr, buf, DUK_HBUFFER_GET_SIZE(buf), duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length)); } DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length); p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); if (offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not an append */ DUK_ASSERT(DUK_HBUFFER_GET_SIZE(buf) - offset > 0); /* not a zero byte memmove */ DUK_MEMMOVE((void *) (p + offset + length), (void *) (p + offset), DUK_HBUFFER_GET_SIZE(buf) - offset); } DUK_MEMCPY((void *) (p + offset), data, length); buf->size += length; }
void duk_hbuffer_remove_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, size_t length) { char *p; size_t end_offset; DUK_UNREF(thr); DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(offset >= 0); /* always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(length >= 0); /* always true */ DUK_ASSERT(offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); end_offset = offset + length; if (end_offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not strictly from end of buffer; need to shuffle data */ DUK_MEMMOVE(p + offset, p + end_offset, DUK_HBUFFER_GET_SIZE(buf) - end_offset); /* always > 0 */ } /* Here we want to zero data even with automatic buffer zeroing * disabled as we depend on this internally too. */ DUK_MEMZERO(p + DUK_HBUFFER_GET_SIZE(buf) - length, length); /* always > 0 */ buf->size -= length; /* Note: no shrink check, intentional */ }
static void duk__sort_array_indices(duk_hobject *h_obj) { duk_hstring **keys; duk_hstring **p_curr, **p_insert, **p_end; duk_hstring *h_curr; duk_uarridx_t val_highest, val_curr, val_insert; DUK_ASSERT(h_obj != NULL); DUK_ASSERT(h_obj->e_next >= 2); /* control props */ if (h_obj->e_next <= 1 + DUK__ENUM_START_INDEX) { return; } keys = DUK_HOBJECT_E_GET_KEY_BASE(h_obj); p_end = keys + h_obj->e_next; keys += DUK__ENUM_START_INDEX; DUK_DDD(DUK_DDDPRINT("keys=%p, p_end=%p (after skipping enum props)", (void *) keys, (void *) p_end)); #ifdef DUK_USE_DDDPRINT { duk_uint_fast32_t i; for (i = 0; i < (duk_uint_fast32_t) h_obj->e_next; i++) { DUK_DDD(DUK_DDDPRINT("initial: %ld %p -> %!O", (long) i, (void *) DUK_HOBJECT_E_GET_KEY_PTR(h_obj, i), (duk_heaphdr *) DUK_HOBJECT_E_GET_KEY(h_obj, i))); } } #endif val_highest = DUK_HSTRING_GET_ARRIDX_SLOW(keys[0]); for (p_curr = keys + 1; p_curr < p_end; p_curr++) { DUK_ASSERT(*p_curr != NULL); val_curr = DUK_HSTRING_GET_ARRIDX_SLOW(*p_curr); if (val_curr >= val_highest) { DUK_DDD(DUK_DDDPRINT("p_curr=%p, p_end=%p, val_highest=%ld, val_curr=%ld -> " "already in correct order, next", (void *) p_curr, (void *) p_end, (long) val_highest, (long) val_curr)); val_highest = val_curr; continue; } DUK_DDD(DUK_DDDPRINT("p_curr=%p, p_end=%p, val_highest=%ld, val_curr=%ld -> " "needs to be inserted", (void *) p_curr, (void *) p_end, (long) val_highest, (long) val_curr)); /* Needs to be inserted; scan backwards, since we optimize * for the case where elements are nearly in order. */ p_insert = p_curr - 1; for (;;) { val_insert = DUK_HSTRING_GET_ARRIDX_SLOW(*p_insert); if (val_insert < val_curr) { DUK_DDD(DUK_DDDPRINT("p_insert=%p, val_insert=%ld, val_curr=%ld -> insert after this", (void *) p_insert, (long) val_insert, (long) val_curr)); p_insert++; break; } if (p_insert == keys) { DUK_DDD(DUK_DDDPRINT("p_insert=%p -> out of keys, insert to beginning", (void *) p_insert)); break; } DUK_DDD(DUK_DDDPRINT("p_insert=%p, val_insert=%ld, val_curr=%ld -> search backwards", (void *) p_insert, (long) val_insert, (long) val_curr)); p_insert--; } DUK_DDD(DUK_DDDPRINT("final p_insert=%p", (void *) p_insert)); /* .-- p_insert .-- p_curr * v v * | ... | insert | ... | curr */ h_curr = *p_curr; DUK_DDD(DUK_DDDPRINT("memmove: dest=%p, src=%p, size=%ld, h_curr=%p", (void *) (p_insert + 1), (void *) p_insert, (long) (p_curr - p_insert), (void *) h_curr)); DUK_MEMMOVE((void *) (p_insert + 1), (void *) p_insert, (size_t) ((p_curr - p_insert) * sizeof(duk_hstring *))); *p_insert = h_curr; /* keep val_highest */ } #ifdef DUK_USE_DDDPRINT { duk_uint_fast32_t i; for (i = 0; i < (duk_uint_fast32_t) h_obj->e_next; i++) { DUK_DDD(DUK_DDDPRINT("final: %ld %p -> %!O", (long) i, (void *) DUK_HOBJECT_E_GET_KEY_PTR(h_obj, i), (duk_heaphdr *) DUK_HOBJECT_E_GET_KEY(h_obj, i))); } } #endif }
DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset) { duk_heap *heap; duk_strcache *sce; duk_uint_fast32_t byte_offset; duk_small_int_t i; duk_bool_t use_cache; duk_uint_fast32_t dist_start, dist_end, dist_sce; duk_uint8_t *p_start; duk_uint8_t *p_end; duk_uint8_t *p_found; if (char_offset > DUK_HSTRING_GET_CHARLEN(h)) { goto error; } /* * For ASCII strings, the answer is simple. */ if (DUK_HSTRING_IS_ASCII(h)) { /* clen == blen -> pure ascii */ return char_offset; } /* * For non-ASCII strings, we need to scan forwards or backwards * from some starting point. The starting point may be the start * or end of the string, or some cached midpoint in the string * cache. * * For "short" strings we simply scan without checking or updating * the cache. For longer strings we check and update the cache as * necessary, inserting a new cache entry if none exists. */ DUK_DDD(DUK_DDDPRINT("non-ascii string %p, char_offset=%ld, clen=%ld, blen=%ld", (void *) h, (long) char_offset, (long) DUK_HSTRING_GET_CHARLEN(h), (long) DUK_HSTRING_GET_BYTELEN(h))); heap = thr->heap; sce = NULL; use_cache = (DUK_HSTRING_GET_CHARLEN(h) > DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT); if (use_cache) { #ifdef DUK_USE_DDDPRINT DUK_DDD(DUK_DDDPRINT("stringcache before char2byte (using cache):")); for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { duk_strcache *c = heap->strcache + i; DUK_DDD(DUK_DDDPRINT(" [%ld] -> h=%p, cidx=%ld, bidx=%ld", (long) i, (void *) c->h, (long) c->cidx, (long) c->bidx)); } #endif for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { duk_strcache *c = heap->strcache + i; if (c->h == h) { sce = c; break; } } } /* * Scan from shortest distance: * - start of string * - end of string * - cache entry (if exists) */ DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h) >= char_offset); dist_start = char_offset; dist_end = DUK_HSTRING_GET_CHARLEN(h) - char_offset; dist_sce = 0; DUK_UNREF(dist_sce); /* initialize for debug prints, needed if sce==NULL */ p_start = (duk_uint8_t *) DUK_HSTRING_GET_DATA(h); p_end = (duk_uint8_t *) (p_start + DUK_HSTRING_GET_BYTELEN(h)); p_found = NULL; if (sce) { if (char_offset >= sce->cidx) { dist_sce = char_offset - sce->cidx; if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) { DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " "scan forwards from sce", (long) use_cache, (void *) (sce ? sce->h : NULL), (sce ? (long) sce->cidx : (long) -1), (sce ? (long) sce->bidx : (long) -1), (long) dist_start, (long) dist_end, (long) dist_sce)); p_found = duk__scan_forwards(p_start + sce->bidx, p_end, dist_sce); goto scan_done; } } else { dist_sce = sce->cidx - char_offset; if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) { DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " "scan backwards from sce", (long) use_cache, (void *) (sce ? sce->h : NULL), (sce ? (long) sce->cidx : (long) -1), (sce ? (long) sce->bidx : (long) -1), (long) dist_start, (long) dist_end, (long) dist_sce)); p_found = duk__scan_backwards(p_start + sce->bidx, p_start, dist_sce); goto scan_done; } } } /* no sce, or sce scan not best */ if (dist_start <= dist_end) { DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " "scan forwards from string start", (long) use_cache, (void *) (sce ? sce->h : NULL), (sce ? (long) sce->cidx : (long) -1), (sce ? (long) sce->bidx : (long) -1), (long) dist_start, (long) dist_end, (long) dist_sce)); p_found = duk__scan_forwards(p_start, p_end, dist_start); } else { DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " "scan backwards from string end", (long) use_cache, (void *) (sce ? sce->h : NULL), (sce ? (long) sce->cidx : (long) -1), (sce ? (long) sce->bidx : (long) -1), (long) dist_start, (long) dist_end, (long) dist_sce)); p_found = duk__scan_backwards(p_end, p_start, dist_end); } scan_done: if (!p_found) { /* Scan error: this shouldn't normally happen; it could happen if * string is not valid UTF-8 data, and clen/blen are not consistent * with the scanning algorithm. */ goto error; } DUK_ASSERT(p_found >= p_start); DUK_ASSERT(p_found <= p_end); /* may be equal */ byte_offset = (duk_uint32_t) (p_found - p_start); DUK_DDD(DUK_DDDPRINT("-> string %p, cidx %ld -> bidx %ld", (void *) h, (long) char_offset, (long) byte_offset)); /* * Update cache entry (allocating if necessary), and move the * cache entry to the first place (in an "LRU" policy). */ if (use_cache) { /* update entry, allocating if necessary */ if (!sce) { sce = heap->strcache + DUK_HEAP_STRCACHE_SIZE - 1; /* take last entry */ sce->h = h; } DUK_ASSERT(sce != NULL); sce->bidx = (duk_uint32_t) (p_found - p_start); sce->cidx = (duk_uint32_t) char_offset; /* LRU: move our entry to first */ if (sce > &heap->strcache[0]) { /* * A C * B A * C <- sce ==> B * D D */ duk_strcache tmp; tmp = *sce; DUK_MEMMOVE((void *) (&heap->strcache[1]), (void *) (&heap->strcache[0]), (size_t) (((char *) sce) - ((char *) &heap->strcache[0]))); heap->strcache[0] = tmp; /* 'sce' points to the wrong entry here, but is no longer used */ } #ifdef DUK_USE_DDDPRINT DUK_DDD(DUK_DDDPRINT("stringcache after char2byte (using cache):")); for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { duk_strcache *c = heap->strcache + i; DUK_DDD(DUK_DDDPRINT(" [%ld] -> h=%p, cidx=%ld, bidx=%ld", (long) i, (void *) c->h, (long) c->cidx, (long) c->bidx)); } #endif } return byte_offset; error: DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, "string scan error"); return 0; }
void duk_hbuffer_insert_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t dst_offset, size_t src_offset, size_t length) { char *p; size_t src_end_offset; /* source end (exclusive) in initial buffer */ size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(dst_offset >= 0); /* always true */ DUK_ASSERT(dst_offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(src_offset >= 0); /* always true */ DUK_ASSERT(src_offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(length >= 0); /* always true */ DUK_ASSERT(src_offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) { duk_hbuffer_resize(thr, buf, DUK_HBUFFER_GET_SIZE(buf), duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length)); } DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length); p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); /* * src_offset and dst_offset refer to the state of the buffer * before any changes are made. This must be taken into account * when moving data around; in particular, the source data may * "straddle" the dst_offset, so the insert may need to be handled * in two pieces. */ src_end_offset = src_offset + length; /* create a hole for the insert */ len = DUK_HBUFFER_GET_SIZE(buf) - dst_offset; if (len > 0) { DUK_MEMMOVE(p + dst_offset + length, p + dst_offset, len); } if (src_offset < dst_offset) { if (src_end_offset <= dst_offset) { /* entire source is before 'dst_offset' */ DUK_MEMCPY(p + dst_offset, p + src_offset, length); } else { /* part of the source is before 'dst_offset'; straddles */ len = dst_offset - src_offset; DUK_ASSERT(len >= 1 && len < length); DUK_ASSERT(length - len >= 1); DUK_MEMCPY(p + dst_offset, p + src_offset, len); DUK_MEMCPY(p + dst_offset + len, p + src_offset + length + len, /* take above memmove() into account */ length - len); } } else { /* entire source is after 'dst_offset' */ DUK_MEMCPY(p + dst_offset, p + src_offset + length, /* take above memmove() into account */ length); } buf->size += length; }