1 /*
2 * Fast buffer writer with slack management.
3 */
4
5 #include "duk_internal.h"
6
7 /* XXX: Avoid duk_{memcmp,memmove}_unsafe() by imposing a minimum length of
8 * >0 for the underlying dynamic buffer.
9 */
10
11 /*
12 * Macro support functions (use only macros in calling code)
13 */
14
duk__bw_update_ptrs(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx,duk_size_t curr_offset,duk_size_t new_length)15 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) {
16 duk_uint8_t *p;
17
18 DUK_ASSERT(thr != NULL);
19 DUK_ASSERT(bw_ctx != NULL);
20 DUK_UNREF(thr);
21
22 /* 'p' might be NULL when the underlying buffer is zero size. If so,
23 * the resulting pointers are not used unsafely.
24 */
25 p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, bw_ctx->buf);
26 DUK_ASSERT(p != NULL || (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0 && curr_offset == 0 && new_length == 0));
27 bw_ctx->p = p + curr_offset;
28 bw_ctx->p_base = p;
29 bw_ctx->p_limit = p + new_length;
30 }
31
duk_bw_init(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx,duk_hbuffer_dynamic * h_buf)32 DUK_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf) {
33 DUK_ASSERT(thr != NULL);
34 DUK_ASSERT(bw_ctx != NULL);
35 DUK_ASSERT(h_buf != NULL);
36
37 bw_ctx->buf = h_buf;
38 duk__bw_update_ptrs(thr, bw_ctx, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf));
39 }
40
duk_bw_init_pushbuf(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx,duk_size_t buf_size)41 DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size) {
42 DUK_ASSERT(thr != NULL);
43 DUK_ASSERT(bw_ctx != NULL);
44
45 (void) duk_push_dynamic_buffer(thr, buf_size);
46 bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1);
47 DUK_ASSERT(bw_ctx->buf != NULL);
48 duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size);
49 }
50
51 /* Resize target buffer for requested size. Called by the macro only when the
52 * fast path test (= there is space) fails.
53 */
duk_bw_resize(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx,duk_size_t sz)54 DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz) {
55 duk_size_t curr_off;
56 duk_size_t add_sz;
57 duk_size_t new_sz;
58
59 DUK_ASSERT(thr != NULL);
60 DUK_ASSERT(bw_ctx != NULL);
61
62 /* We could do this operation without caller updating bw_ctx->ptr,
63 * but by writing it back here we can share code better.
64 */
65
66 curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
67 add_sz = (curr_off >> DUK_BW_SLACK_SHIFT) + DUK_BW_SLACK_ADD;
68 new_sz = curr_off + sz + add_sz;
69 if (DUK_UNLIKELY(new_sz < curr_off)) {
70 /* overflow */
71 DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG);
72 DUK_WO_NORETURN(return NULL;);
73 }
74 #if 0 /* for manual torture testing: tight allocation, useful with valgrind */
75 new_sz = curr_off + sz;
76 #endif
77
78 /* This is important to ensure dynamic buffer data pointer is not
79 * NULL (which is possible if buffer size is zero), which in turn
80 * causes portability issues with e.g. memmove() and memcpy().
81 */
82 DUK_ASSERT(new_sz >= 1);
83
84 DUK_DD(DUK_DDPRINT("resize bufferwriter from %ld to %ld (add_sz=%ld)", (long) curr_off, (long) new_sz, (long) add_sz));
85
86 duk_hbuffer_resize(thr, bw_ctx->buf, new_sz);
87 duk__bw_update_ptrs(thr, bw_ctx, curr_off, new_sz);
88 return bw_ctx->p;
89 }
90
91 /* Make buffer compact, matching current written size. */
duk_bw_compact(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx)92 DUK_INTERNAL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) {
93 duk_size_t len;
94
95 DUK_ASSERT(thr != NULL);
96 DUK_ASSERT(bw_ctx != NULL);
97 DUK_UNREF(thr);
98
99 len = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
100 duk_hbuffer_resize(thr, bw_ctx->buf, len);
101 duk__bw_update_ptrs(thr, bw_ctx, len, len);
102 }
103
duk_bw_write_raw_slice(duk_hthread * thr,duk_bufwriter_ctx * bw,duk_size_t src_off,duk_size_t len)104 DUK_INTERNAL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) {
105 duk_uint8_t *p_base;
106
107 DUK_ASSERT(thr != NULL);
108 DUK_ASSERT(bw != NULL);
109 DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
110 DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
111 DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
112 DUK_UNREF(thr);
113
114 p_base = bw->p_base;
115 duk_memcpy_unsafe((void *) bw->p,
116 (const void *) (p_base + src_off),
117 (size_t) len);
118 bw->p += len;
119 }
120
duk_bw_write_ensure_slice(duk_hthread * thr,duk_bufwriter_ctx * bw,duk_size_t src_off,duk_size_t len)121 DUK_INTERNAL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) {
122 DUK_ASSERT(thr != NULL);
123 DUK_ASSERT(bw != NULL);
124 DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
125 DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
126 DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
127
128 DUK_BW_ENSURE(thr, bw, len);
129 duk_bw_write_raw_slice(thr, bw, src_off, len);
130 }
131
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_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) {
133 duk_uint8_t *p_base;
134 duk_size_t buf_sz, move_sz;
135
136 DUK_ASSERT(thr != NULL);
137 DUK_ASSERT(bw != NULL);
138 DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
139 DUK_ASSERT(buf != NULL);
140 DUK_UNREF(thr);
141
142 p_base = bw->p_base;
143 buf_sz = (duk_size_t) (bw->p - p_base); /* constrained by maximum buffer size */
144 move_sz = buf_sz - dst_off;
145
146 DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */
147 duk_memmove_unsafe((void *) (p_base + dst_off + len),
148 (const void *) (p_base + dst_off),
149 (size_t) move_sz);
150 duk_memcpy_unsafe((void *) (p_base + dst_off),
151 (const void *) buf,
152 (size_t) len);
153 bw->p += len;
154 }
155
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)156 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) {
157 DUK_ASSERT(thr != NULL);
158 DUK_ASSERT(bw != NULL);
159 DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
160 DUK_ASSERT(buf != NULL);
161
162 DUK_BW_ENSURE(thr, bw, len);
163 duk_bw_insert_raw_bytes(thr, bw, dst_off, buf, len);
164 }
165
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)166 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_uint8_t *p_base;
168 duk_size_t buf_sz, move_sz;
169
170 DUK_ASSERT(thr != NULL);
171 DUK_ASSERT(bw != NULL);
172 DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
173 DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
174 DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
175 DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
176 DUK_UNREF(thr);
177
178 p_base = bw->p_base;
179
180 /* Don't support "straddled" source now. */
181 DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len);
182
183 if (dst_off <= src_off) {
184 /* Target is before source. Source offset is expressed as
185 * a "before change" offset. Account for the memmove.
186 */
187 src_off += len;
188 }
189
190 buf_sz = (duk_size_t) (bw->p - p_base);
191 move_sz = buf_sz - dst_off;
192
193 DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */
194 duk_memmove_unsafe((void *) (p_base + dst_off + len),
195 (const void *) (p_base + dst_off),
196 (size_t) move_sz);
197 duk_memcpy_unsafe((void *) (p_base + dst_off),
198 (const void *) (p_base + src_off),
199 (size_t) len);
200 bw->p += len;
201 }
202
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_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) {
204 DUK_ASSERT(thr != NULL);
205 DUK_ASSERT(bw != NULL);
206 DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
207 DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
208 DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
209 DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
210
211 /* Don't support "straddled" source now. */
212 DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len);
213
214 DUK_BW_ENSURE(thr, bw, len);
215 duk_bw_insert_raw_slice(thr, bw, dst_off, src_off, len);
216 }
217
duk_bw_insert_raw_area(duk_hthread * thr,duk_bufwriter_ctx * bw,duk_size_t off,duk_size_t 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;
221
222 DUK_ASSERT(thr != NULL);
223 DUK_ASSERT(bw != NULL);
224 DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));
225 DUK_UNREF(thr);
226
227 p_base = bw->p_base;
228 buf_sz = (duk_size_t) (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_unsafe((void *) p_dst, (const void *) p_src, (size_t) move_sz);
233 return p_src; /* point to start of 'reserved area' */
234 }
235
duk_bw_insert_ensure_area(duk_hthread * thr,duk_bufwriter_ctx * bw,duk_size_t off,duk_size_t len)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));
240
241 DUK_BW_ENSURE(thr, bw, len);
242 return duk_bw_insert_raw_area(thr, bw, off, len);
243 }
244
duk_bw_remove_raw_slice(duk_hthread * thr,duk_bufwriter_ctx * bw,duk_size_t off,duk_size_t len)245 DUK_INTERNAL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) {
246 duk_size_t move_sz;
247
248 duk_uint8_t *p_base;
249 duk_uint8_t *p_src;
250 duk_uint8_t *p_dst;
251
252 DUK_ASSERT(thr != NULL);
253 DUK_ASSERT(bw != NULL);
254 DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));
255 DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
256 DUK_ASSERT(off + len <= DUK_BW_GET_SIZE(thr, bw));
257 DUK_UNREF(thr);
258
259 p_base = bw->p_base;
260 p_dst = p_base + off;
261 p_src = p_dst + len;
262 move_sz = (duk_size_t) (bw->p - p_src);
263 duk_memmove_unsafe((void *) p_dst,
264 (const void *) p_src,
265 (size_t) move_sz);
266 bw->p -= len;
267 }
268
269 /*
270 * Assertion helpers
271 */
272
273 #if defined(DUK_USE_ASSERTIONS)
duk_bw_assert_valid(duk_hthread * thr,duk_bufwriter_ctx * bw_ctx)274 DUK_INTERNAL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) {
275 DUK_UNREF(thr);
276 DUK_ASSERT(bw_ctx != NULL);
277 DUK_ASSERT(bw_ctx->buf != NULL);
278 DUK_ASSERT((DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0) ||
279 (bw_ctx->p != NULL &&
280 bw_ctx->p_base != NULL &&
281 bw_ctx->p_limit != NULL &&
282 bw_ctx->p_limit >= bw_ctx->p_base &&
283 bw_ctx->p >= bw_ctx->p_base &&
284 bw_ctx->p <= bw_ctx->p_limit));
285 }
286 #endif
287