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