2 * Fast buffer writer with spare management.
5 #include "duk_internal.h"
8 * Macro support functions (use only macros in calling code)
11 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
) {
14 DUK_ASSERT(thr
!= NULL
);
15 DUK_ASSERT(bw_ctx
!= NULL
);
18 p
= (duk_uint8_t
*) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr
->heap
, bw_ctx
->buf
);
19 DUK_ASSERT(p
!= NULL
|| (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx
->buf
) == 0 && curr_offset
== 0 && new_length
== 0));
20 bw_ctx
->p
= p
+ curr_offset
;
22 bw_ctx
->p_limit
= p
+ new_length
;
25 DUK_INTERNAL
void duk_bw_init(duk_hthread
*thr
, duk_bufwriter_ctx
*bw_ctx
, duk_hbuffer_dynamic
*h_buf
) {
27 DUK_ASSERT(thr
!= NULL
);
28 DUK_ASSERT(bw_ctx
!= NULL
);
29 DUK_ASSERT(h_buf
!= NULL
);
33 duk__bw_update_ptrs(thr
, bw_ctx
, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf
));
36 DUK_INTERNAL
void duk_bw_init_pushbuf(duk_hthread
*thr
, duk_bufwriter_ctx
*bw_ctx
, duk_size_t buf_size
) {
39 DUK_ASSERT(thr
!= NULL
);
40 DUK_ASSERT(bw_ctx
!= NULL
);
41 ctx
= (duk_context
*) thr
;
43 (void) duk_push_dynamic_buffer(ctx
, buf_size
);
44 bw_ctx
->buf
= (duk_hbuffer_dynamic
*) duk_get_hbuffer(ctx
, -1);
45 duk__bw_update_ptrs(thr
, bw_ctx
, 0, buf_size
);
48 /* Resize target buffer for requested size. Called by the macro only when the
49 * fast path test (= there is space) fails.
51 DUK_INTERNAL duk_uint8_t
*duk_bw_resize(duk_hthread
*thr
, duk_bufwriter_ctx
*bw_ctx
, duk_size_t sz
) {
56 DUK_ASSERT(thr
!= NULL
);
57 DUK_ASSERT(bw_ctx
!= NULL
);
59 /* We could do this operation without caller updating bw_ctx->ptr,
60 * but by writing it back here we can share code better.
63 curr_off
= (duk_size_t
) (bw_ctx
->p
- bw_ctx
->p_base
);
64 add_sz
= (curr_off
>> DUK_BW_SPARE_SHIFT
) + DUK_BW_SPARE_ADD
;
65 new_sz
= curr_off
+ sz
+ add_sz
;
66 if (new_sz
< curr_off
) {
68 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_BUFFER_TOO_LONG
);
69 return NULL
; /* not reachable */
71 #if 0 /* for manual torture testing: tight allocation, useful with valgrind */
72 new_sz
= curr_off
+ sz
;
75 /* This is important to ensure dynamic buffer data pointer is not
76 * NULL (which is possible if buffer size is zero), which in turn
77 * causes portability issues with e.g. memmove() and memcpy().
79 DUK_ASSERT(new_sz
>= 1);
81 DUK_DD(DUK_DDPRINT("resize bufferwriter from %ld to %ld (add_sz=%ld)", (long) curr_off
, (long) new_sz
, (long) add_sz
));
83 duk_hbuffer_resize(thr
, bw_ctx
->buf
, new_sz
);
84 duk__bw_update_ptrs(thr
, bw_ctx
, curr_off
, new_sz
);
88 /* Make buffer compact, matching current written size. */
89 DUK_INTERNAL
void duk_bw_compact(duk_hthread
*thr
, duk_bufwriter_ctx
*bw_ctx
) {
92 DUK_ASSERT(thr
!= NULL
);
93 DUK_ASSERT(bw_ctx
!= NULL
);
96 len
= (duk_size_t
) (bw_ctx
->p
- bw_ctx
->p_base
);
97 duk_hbuffer_resize(thr
, bw_ctx
->buf
, len
);
98 duk__bw_update_ptrs(thr
, bw_ctx
, len
, len
);
101 DUK_INTERNAL
void duk_bw_write_raw_slice(duk_hthread
*thr
, duk_bufwriter_ctx
*bw
, duk_size_t src_off
, duk_size_t len
) {
104 DUK_ASSERT(thr
!= NULL
);
105 DUK_ASSERT(bw
!= NULL
);
106 DUK_ASSERT(src_off
<= DUK_BW_GET_SIZE(thr
, bw
));
107 DUK_ASSERT(len
<= DUK_BW_GET_SIZE(thr
, bw
));
108 DUK_ASSERT(src_off
+ len
<= DUK_BW_GET_SIZE(thr
, bw
));
112 DUK_MEMCPY((void *) bw
->p
,
113 (const void *) (p_base
+ src_off
),
118 DUK_INTERNAL
void duk_bw_write_ensure_slice(duk_hthread
*thr
, duk_bufwriter_ctx
*bw
, duk_size_t src_off
, duk_size_t len
) {
119 DUK_ASSERT(thr
!= NULL
);
120 DUK_ASSERT(bw
!= NULL
);
121 DUK_ASSERT(src_off
<= DUK_BW_GET_SIZE(thr
, bw
));
122 DUK_ASSERT(len
<= DUK_BW_GET_SIZE(thr
, bw
));
123 DUK_ASSERT(src_off
+ len
<= DUK_BW_GET_SIZE(thr
, bw
));
126 DUK_BW_ENSURE(thr
, bw
, len
);
127 duk_bw_write_raw_slice(thr
, bw
, src_off
, len
);
130 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
) {
132 duk_size_t buf_sz
, move_sz
;
134 DUK_ASSERT(thr
!= NULL
);
135 DUK_ASSERT(bw
!= NULL
);
136 DUK_ASSERT(dst_off
<= DUK_BW_GET_SIZE(thr
, bw
));
137 DUK_ASSERT(buf
!= NULL
);
141 buf_sz
= bw
->p
- p_base
;
142 move_sz
= buf_sz
- dst_off
;
144 DUK_ASSERT(p_base
!= NULL
); /* buffer size is >= 1 */
145 DUK_MEMMOVE((void *) (p_base
+ dst_off
+ len
),
146 (const void *) (p_base
+ dst_off
),
147 (duk_size_t
) move_sz
);
148 DUK_MEMCPY((void *) (p_base
+ dst_off
),
154 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
) {
155 DUK_ASSERT(thr
!= NULL
);
156 DUK_ASSERT(bw
!= NULL
);
157 DUK_ASSERT(dst_off
<= DUK_BW_GET_SIZE(thr
, bw
));
158 DUK_ASSERT(buf
!= NULL
);
161 DUK_BW_ENSURE(thr
, bw
, len
);
162 duk_bw_insert_raw_bytes(thr
, bw
, dst_off
, buf
, len
);
165 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
) {
167 duk_size_t buf_sz
, move_sz
;
169 DUK_ASSERT(thr
!= NULL
);
170 DUK_ASSERT(bw
!= NULL
);
171 DUK_ASSERT(dst_off
<= DUK_BW_GET_SIZE(thr
, bw
));
172 DUK_ASSERT(src_off
<= DUK_BW_GET_SIZE(thr
, bw
));
173 DUK_ASSERT(len
<= DUK_BW_GET_SIZE(thr
, bw
));
174 DUK_ASSERT(src_off
+ len
<= DUK_BW_GET_SIZE(thr
, bw
));
179 /* Don't support "straddled" source now. */
180 DUK_ASSERT(dst_off
<= src_off
|| dst_off
>= src_off
+ len
);
182 if (dst_off
<= src_off
) {
183 /* Target is before source. Source offset is expressed as
184 * a "before change" offset. Account for the memmove.
189 buf_sz
= bw
->p
- p_base
;
190 move_sz
= buf_sz
- dst_off
;
192 DUK_ASSERT(p_base
!= NULL
); /* buffer size is >= 1 */
193 DUK_MEMMOVE((void *) (p_base
+ dst_off
+ len
),
194 (const void *) (p_base
+ dst_off
),
195 (duk_size_t
) move_sz
);
196 DUK_MEMCPY((void *) (p_base
+ dst_off
),
197 (const void *) (p_base
+ src_off
),
202 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
) {
203 DUK_ASSERT(thr
!= NULL
);
204 DUK_ASSERT(bw
!= NULL
);
205 DUK_ASSERT(dst_off
<= DUK_BW_GET_SIZE(thr
, bw
));
206 DUK_ASSERT(src_off
<= DUK_BW_GET_SIZE(thr
, bw
));
207 DUK_ASSERT(len
<= DUK_BW_GET_SIZE(thr
, bw
));
208 DUK_ASSERT(src_off
+ len
<= DUK_BW_GET_SIZE(thr
, bw
));
211 /* Don't support "straddled" source now. */
212 DUK_ASSERT(dst_off
<= src_off
|| dst_off
>= src_off
+ len
);
214 DUK_BW_ENSURE(thr
, bw
, len
);
215 duk_bw_insert_raw_slice(thr
, bw
, dst_off
, src_off
, len
);
218 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
) {
219 duk_uint8_t
*p_base
, *p_dst
, *p_src
;
220 duk_size_t buf_sz
, move_sz
;
222 DUK_ASSERT(thr
!= NULL
);
223 DUK_ASSERT(bw
!= NULL
);
224 DUK_ASSERT(off
<= DUK_BW_GET_SIZE(thr
, bw
));
228 buf_sz
= bw
->p
- p_base
;
229 move_sz
= buf_sz
- off
;
230 p_dst
= p_base
+ off
+ len
;
231 p_src
= p_base
+ off
;
232 DUK_MEMMOVE((void *) p_dst
, (const void *) p_src
, move_sz
);
233 return p_src
; /* point to start of 'reserved area' */
236 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
) {
237 DUK_ASSERT(thr
!= NULL
);
238 DUK_ASSERT(bw
!= NULL
);
239 DUK_ASSERT(off
<= DUK_BW_GET_SIZE(thr
, bw
));
242 DUK_BW_ENSURE(thr
, bw
, len
);
243 return duk_bw_insert_raw_area(thr
, bw
, off
, len
);
246 DUK_INTERNAL
void duk_bw_remove_raw_slice(duk_hthread
*thr
, duk_bufwriter_ctx
*bw
, duk_size_t off
, duk_size_t len
) {
253 DUK_ASSERT(thr
!= NULL
);
254 DUK_ASSERT(bw
!= NULL
);
255 DUK_ASSERT(off
<= DUK_BW_GET_SIZE(thr
, bw
));
256 DUK_ASSERT(len
<= DUK_BW_GET_SIZE(thr
, bw
));
257 DUK_ASSERT(off
+ len
<= DUK_BW_GET_SIZE(thr
, bw
));
261 p_dst
= p_base
+ off
;
263 move_sz
= (duk_size_t
) (bw
->p
- p_src
);
264 DUK_MEMMOVE((void *) p_dst
,
265 (const void *) p_src
,
271 * Macro support functions for reading/writing raw data.
273 * These are done using mempcy to ensure they're valid even for unaligned
274 * reads/writes on platforms where alignment counts. On x86 at least gcc
275 * is able to compile these into a bswap+mov. "Always inline" is used to
276 * ensure these macros compile to minimal code.
278 * Not really bufwriter related, but currently used together.
281 DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t
duk_raw_read_u16_be(duk_uint8_t
**p
) {
287 DUK_MEMCPY((void *) u
.b
, (const void *) (*p
), 2);
288 u
.x
= DUK_NTOH16(u
.x
);
293 DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t
duk_raw_read_u32_be(duk_uint8_t
**p
) {
299 DUK_MEMCPY((void *) u
.b
, (const void *) (*p
), 4);
300 u
.x
= DUK_NTOH32(u
.x
);
305 DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t
duk_raw_read_double_be(duk_uint8_t
**p
) {
312 DUK_MEMCPY((void *) u
.b
, (const void *) (*p
), 4);
313 u
.x
= DUK_NTOH32(u
.x
);
314 du
.ui
[DUK_DBL_IDX_UI0
] = u
.x
;
315 DUK_MEMCPY((void *) u
.b
, (const void *) (*p
+ 4), 4);
316 u
.x
= DUK_NTOH32(u
.x
);
317 du
.ui
[DUK_DBL_IDX_UI1
] = u
.x
;
323 DUK_INTERNAL DUK_ALWAYS_INLINE
void duk_raw_write_u16_be(duk_uint8_t
**p
, duk_uint16_t val
) {
329 u
.x
= DUK_HTON16(val
);
330 DUK_MEMCPY((void *) (*p
), (const void *) u
.b
, 2);
334 DUK_INTERNAL DUK_ALWAYS_INLINE
void duk_raw_write_u32_be(duk_uint8_t
**p
, duk_uint32_t val
) {
340 u
.x
= DUK_HTON32(val
);
341 DUK_MEMCPY((void *) (*p
), (const void *) u
.b
, 4);
345 DUK_INTERNAL DUK_ALWAYS_INLINE
void duk_raw_write_double_be(duk_uint8_t
**p
, duk_double_t val
) {
353 u
.x
= du
.ui
[DUK_DBL_IDX_UI0
];
354 u
.x
= DUK_HTON32(u
.x
);
355 DUK_MEMCPY((void *) (*p
), (const void *) u
.b
, 4);
356 u
.x
= du
.ui
[DUK_DBL_IDX_UI1
];
357 u
.x
= DUK_HTON32(u
.x
);
358 DUK_MEMCPY((void *) (*p
+ 4), (const void *) u
.b
, 4);