| /* |
| * Fast buffer writer with spare management. |
| */ |
| |
| #include "duk_internal.h" |
| |
| /* |
| * Macro support functions (use only macros in calling code) |
| */ |
| |
| DUK_LOCAL void duk__bw_update_ptrs(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t curr_offset, duk_size_t new_length) { |
| duk_uint8_t *p; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw_ctx != NULL); |
| DUK_UNREF(thr); |
| |
| p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, bw_ctx->buf); |
| DUK_ASSERT(p != NULL || (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0 && curr_offset == 0 && new_length == 0)); |
| bw_ctx->p = p + curr_offset; |
| bw_ctx->p_base = p; |
| bw_ctx->p_limit = p + new_length; |
| } |
| |
| DUK_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf) { |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw_ctx != NULL); |
| DUK_ASSERT(h_buf != NULL); |
| DUK_UNREF(thr); |
| |
| bw_ctx->buf = h_buf; |
| duk__bw_update_ptrs(thr, bw_ctx, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf)); |
| } |
| |
| DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size) { |
| duk_context *ctx; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw_ctx != NULL); |
| ctx = (duk_context *) thr; |
| |
| (void) duk_push_dynamic_buffer(ctx, buf_size); |
| bw_ctx->buf = (duk_hbuffer_dynamic *) duk_get_hbuffer(ctx, -1); |
| duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size); |
| } |
| |
| /* Resize target buffer for requested size. Called by the macro only when the |
| * fast path test (= there is space) fails. |
| */ |
| DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz) { |
| duk_size_t curr_off; |
| duk_size_t add_sz; |
| duk_size_t new_sz; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw_ctx != NULL); |
| |
| /* We could do this operation without caller updating bw_ctx->ptr, |
| * but by writing it back here we can share code better. |
| */ |
| |
| curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base); |
| add_sz = (curr_off >> DUK_BW_SPARE_SHIFT) + DUK_BW_SPARE_ADD; |
| new_sz = curr_off + sz + add_sz; |
| if (new_sz < curr_off) { |
| /* overflow */ |
| DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); |
| return NULL; /* not reachable */ |
| } |
| #if 0 /* for manual torture testing: tight allocation, useful with valgrind */ |
| new_sz = curr_off + sz; |
| #endif |
| |
| /* This is important to ensure dynamic buffer data pointer is not |
| * NULL (which is possible if buffer size is zero), which in turn |
| * causes portability issues with e.g. memmove() and memcpy(). |
| */ |
| DUK_ASSERT(new_sz >= 1); |
| |
| DUK_DD(DUK_DDPRINT("resize bufferwriter from %ld to %ld (add_sz=%ld)", (long) curr_off, (long) new_sz, (long) add_sz)); |
| |
| duk_hbuffer_resize(thr, bw_ctx->buf, new_sz); |
| duk__bw_update_ptrs(thr, bw_ctx, curr_off, new_sz); |
| return bw_ctx->p; |
| } |
| |
| /* Make buffer compact, matching current written size. */ |
| DUK_INTERNAL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) { |
| duk_size_t len; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw_ctx != NULL); |
| DUK_UNREF(thr); |
| |
| len = (duk_size_t) (bw_ctx->p - bw_ctx->p_base); |
| duk_hbuffer_resize(thr, bw_ctx->buf, len); |
| duk__bw_update_ptrs(thr, bw_ctx, len, len); |
| } |
| |
| DUK_INTERNAL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) { |
| duk_uint8_t *p_base; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| p_base = bw->p_base; |
| DUK_MEMCPY((void *) bw->p, |
| (const void *) (p_base + src_off), |
| (size_t) len); |
| bw->p += len; |
| } |
| |
| DUK_INTERNAL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) { |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| DUK_BW_ENSURE(thr, bw, len); |
| duk_bw_write_raw_slice(thr, bw, src_off, len); |
| } |
| |
| DUK_INTERNAL void duk_bw_insert_raw_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len) { |
| duk_uint8_t *p_base; |
| duk_size_t buf_sz, move_sz; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(buf != NULL); |
| DUK_UNREF(thr); |
| |
| p_base = bw->p_base; |
| buf_sz = bw->p - p_base; |
| move_sz = buf_sz - dst_off; |
| |
| DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ |
| DUK_MEMMOVE((void *) (p_base + dst_off + len), |
| (const void *) (p_base + dst_off), |
| (size_t) move_sz); |
| DUK_MEMCPY((void *) (p_base + dst_off), |
| (const void *) buf, |
| (size_t) len); |
| bw->p += len; |
| } |
| |
| DUK_INTERNAL void duk_bw_insert_ensure_bytes(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, const duk_uint8_t *buf, duk_size_t len) { |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(buf != NULL); |
| DUK_UNREF(thr); |
| |
| DUK_BW_ENSURE(thr, bw, len); |
| duk_bw_insert_raw_bytes(thr, bw, dst_off, buf, len); |
| } |
| |
| DUK_INTERNAL void duk_bw_insert_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len) { |
| duk_uint8_t *p_base; |
| duk_size_t buf_sz, move_sz; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| p_base = bw->p_base; |
| |
| /* Don't support "straddled" source now. */ |
| DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len); |
| |
| if (dst_off <= src_off) { |
| /* Target is before source. Source offset is expressed as |
| * a "before change" offset. Account for the memmove. |
| */ |
| src_off += len; |
| } |
| |
| buf_sz = bw->p - p_base; |
| move_sz = buf_sz - dst_off; |
| |
| DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ |
| DUK_MEMMOVE((void *) (p_base + dst_off + len), |
| (const void *) (p_base + dst_off), |
| (size_t) move_sz); |
| DUK_MEMCPY((void *) (p_base + dst_off), |
| (const void *) (p_base + src_off), |
| (size_t) len); |
| bw->p += len; |
| } |
| |
| DUK_INTERNAL void duk_bw_insert_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t dst_off, duk_size_t src_off, duk_size_t len) { |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| /* Don't support "straddled" source now. */ |
| DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len); |
| |
| DUK_BW_ENSURE(thr, bw, len); |
| duk_bw_insert_raw_slice(thr, bw, dst_off, src_off, len); |
| } |
| |
| DUK_INTERNAL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { |
| duk_uint8_t *p_base, *p_dst, *p_src; |
| duk_size_t buf_sz, move_sz; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| p_base = bw->p_base; |
| buf_sz = bw->p - p_base; |
| move_sz = buf_sz - off; |
| p_dst = p_base + off + len; |
| p_src = p_base + off; |
| DUK_MEMMOVE((void *) p_dst, (const void *) p_src, (size_t) move_sz); |
| return p_src; /* point to start of 'reserved area' */ |
| } |
| |
| DUK_INTERNAL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| DUK_BW_ENSURE(thr, bw, len); |
| return duk_bw_insert_raw_area(thr, bw, off, len); |
| } |
| |
| DUK_INTERNAL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { |
| duk_size_t move_sz; |
| |
| duk_uint8_t *p_base; |
| duk_uint8_t *p_src; |
| duk_uint8_t *p_dst; |
| |
| DUK_ASSERT(thr != NULL); |
| DUK_ASSERT(bw != NULL); |
| DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_ASSERT(off + len <= DUK_BW_GET_SIZE(thr, bw)); |
| DUK_UNREF(thr); |
| |
| p_base = bw->p_base; |
| p_dst = p_base + off; |
| p_src = p_dst + len; |
| move_sz = (duk_size_t) (bw->p - p_src); |
| DUK_MEMMOVE((void *) p_dst, |
| (const void *) p_src, |
| (size_t) move_sz); |
| bw->p -= len; |
| } |
| |
| /* |
| * Macro support functions for reading/writing raw data. |
| * |
| * These are done using mempcy to ensure they're valid even for unaligned |
| * reads/writes on platforms where alignment counts. On x86 at least gcc |
| * is able to compile these into a bswap+mov. "Always inline" is used to |
| * ensure these macros compile to minimal code. |
| * |
| * Not really bufwriter related, but currently used together. |
| */ |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p) { |
| union { |
| duk_uint8_t b[2]; |
| duk_uint16_t x; |
| } u; |
| |
| DUK_MEMCPY((void *) u.b, (const void *) (*p), (size_t) 2); |
| u.x = DUK_NTOH16(u.x); |
| *p += 2; |
| return u.x; |
| } |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p) { |
| union { |
| duk_uint8_t b[4]; |
| duk_uint32_t x; |
| } u; |
| |
| DUK_MEMCPY((void *) u.b, (const void *) (*p), (size_t) 4); |
| u.x = DUK_NTOH32(u.x); |
| *p += 4; |
| return u.x; |
| } |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_read_double_be(duk_uint8_t **p) { |
| duk_double_union du; |
| union { |
| duk_uint8_t b[4]; |
| duk_uint32_t x; |
| } u; |
| |
| DUK_MEMCPY((void *) u.b, (const void *) (*p), (size_t) 4); |
| u.x = DUK_NTOH32(u.x); |
| du.ui[DUK_DBL_IDX_UI0] = u.x; |
| DUK_MEMCPY((void *) u.b, (const void *) (*p + 4), (size_t) 4); |
| u.x = DUK_NTOH32(u.x); |
| du.ui[DUK_DBL_IDX_UI1] = u.x; |
| *p += 8; |
| |
| return du.d; |
| } |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val) { |
| union { |
| duk_uint8_t b[2]; |
| duk_uint16_t x; |
| } u; |
| |
| u.x = DUK_HTON16(val); |
| DUK_MEMCPY((void *) (*p), (const void *) u.b, (size_t) 2); |
| *p += 2; |
| } |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val) { |
| union { |
| duk_uint8_t b[4]; |
| duk_uint32_t x; |
| } u; |
| |
| u.x = DUK_HTON32(val); |
| DUK_MEMCPY((void *) (*p), (const void *) u.b, (size_t) 4); |
| *p += 4; |
| } |
| |
| DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val) { |
| duk_double_union du; |
| union { |
| duk_uint8_t b[4]; |
| duk_uint32_t x; |
| } u; |
| |
| du.d = val; |
| u.x = du.ui[DUK_DBL_IDX_UI0]; |
| u.x = DUK_HTON32(u.x); |
| DUK_MEMCPY((void *) (*p), (const void *) u.b, (size_t) 4); |
| u.x = du.ui[DUK_DBL_IDX_UI1]; |
| u.x = DUK_HTON32(u.x); |
| DUK_MEMCPY((void *) (*p + 4), (const void *) u.b, (size_t) 4); |
| *p += 8; |
| } |