]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_bi_buffer.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_bi_buffer.c
1 /*
2 * Duktape.Buffer, Node.js Buffer, and Khronos/ES6 TypedArray built-ins
3 */
4
5 #include "duk_internal.h"
6
7 /*
8 * Misc helpers
9 */
10
11 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
12 /* Map DUK_HBUFFEROBJECT_ELEM_xxx to duk_hobject class number.
13 * Sync with duk_hbufferobject.h and duk_hobject.h.
14 */
15 static const duk_uint8_t duk__buffer_class_from_elemtype[9] = {
16 DUK_HOBJECT_CLASS_UINT8ARRAY,
17 DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY,
18 DUK_HOBJECT_CLASS_INT8ARRAY,
19 DUK_HOBJECT_CLASS_UINT16ARRAY,
20 DUK_HOBJECT_CLASS_INT16ARRAY,
21 DUK_HOBJECT_CLASS_UINT32ARRAY,
22 DUK_HOBJECT_CLASS_INT32ARRAY,
23 DUK_HOBJECT_CLASS_FLOAT32ARRAY,
24 DUK_HOBJECT_CLASS_FLOAT64ARRAY
25 };
26 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
27
28 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
29 /* Map DUK_HBUFFEROBJECT_ELEM_xxx to prototype object built-in index.
30 * Sync with duk_hbufferobject.h.
31 */
32 static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = {
33 DUK_BIDX_UINT8ARRAY_PROTOTYPE,
34 DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE,
35 DUK_BIDX_INT8ARRAY_PROTOTYPE,
36 DUK_BIDX_UINT16ARRAY_PROTOTYPE,
37 DUK_BIDX_INT16ARRAY_PROTOTYPE,
38 DUK_BIDX_UINT32ARRAY_PROTOTYPE,
39 DUK_BIDX_INT32ARRAY_PROTOTYPE,
40 DUK_BIDX_FLOAT32ARRAY_PROTOTYPE,
41 DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
42 };
43 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
44
45 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
46 /* Map DUK__FLX_xxx to byte size.
47 */
48 static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = {
49 1, /* DUK__FLD_8BIT */
50 2, /* DUK__FLD_16BIT */
51 4, /* DUK__FLD_32BIT */
52 4, /* DUK__FLD_FLOAT */
53 8, /* DUK__FLD_DOUBLE */
54 0 /* DUK__FLD_VARINT; not relevant here */
55 };
56 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
57
58 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
59 /* Bitfield for each DUK_HBUFFEROBJECT_ELEM_xxx indicating which element types
60 * are compatible with a blind byte copy for the TypedArray set() method (also
61 * used for TypedArray constructor). Array index is target buffer elem type,
62 * bitfield indicates compatible source types. The types must have same byte
63 * size and they must be coercion compatible.
64 */
65 static duk_uint16_t duk__buffer_elemtype_copy_compatible[9] = {
66 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8 */
67 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) |
68 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED) |
69 (1U << DUK_HBUFFEROBJECT_ELEM_INT8),
70
71 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
72 * Note: INT8 is -not- copy compatible, e.g. -1 would coerce to 0x00.
73 */
74 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) |
75 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED),
76
77 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT8 */
78 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) |
79 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED) |
80 (1U << DUK_HBUFFEROBJECT_ELEM_INT8),
81
82 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT16 */
83 (1U << DUK_HBUFFEROBJECT_ELEM_UINT16) |
84 (1U << DUK_HBUFFEROBJECT_ELEM_INT16),
85
86 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT16 */
87 (1U << DUK_HBUFFEROBJECT_ELEM_UINT16) |
88 (1U << DUK_HBUFFEROBJECT_ELEM_INT16),
89
90 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT32 */
91 (1U << DUK_HBUFFEROBJECT_ELEM_UINT32) |
92 (1U << DUK_HBUFFEROBJECT_ELEM_INT32),
93
94 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT32 */
95 (1U << DUK_HBUFFEROBJECT_ELEM_UINT32) |
96 (1U << DUK_HBUFFEROBJECT_ELEM_INT32),
97
98 /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT32 */
99 (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT32),
100
101 /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT64 */
102 (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT64)
103 };
104 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
105
106 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
107 /* Shared helper. */
108 DUK_LOCAL duk_hbufferobject *duk__getrequire_bufobj_this(duk_context *ctx, duk_bool_t throw_flag) {
109 duk_hthread *thr;
110 duk_tval *tv;
111 duk_hbufferobject *h_this;
112
113 DUK_ASSERT(ctx != NULL);
114 thr = (duk_hthread *) ctx;
115
116 tv = duk_get_borrowed_this_tval(ctx);
117 DUK_ASSERT(tv != NULL);
118 if (DUK_TVAL_IS_OBJECT(tv)) {
119 h_this = (duk_hbufferobject *) DUK_TVAL_GET_OBJECT(tv);
120 DUK_ASSERT(h_this != NULL);
121 if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_this)) {
122 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this);
123 return h_this;
124 }
125 }
126
127 if (throw_flag) {
128 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_BUFFER);
129 }
130 return NULL;
131 }
132 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
133
134 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
135 /* Check that 'this' is a duk_hbufferobject and return a pointer to it. */
136 DUK_LOCAL duk_hbufferobject *duk__get_bufobj_this(duk_context *ctx) {
137 return duk__getrequire_bufobj_this(ctx, 0);
138 }
139 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
140
141 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
142 /* Check that 'this' is a duk_hbufferobject and return a pointer to it
143 * (NULL if not).
144 */
145 DUK_LOCAL duk_hbufferobject *duk__require_bufobj_this(duk_context *ctx) {
146 return duk__getrequire_bufobj_this(ctx, 1);
147 }
148 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
149
150 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
151 /* Check that value is a duk_hbufferobject and return a pointer to it. */
152 DUK_LOCAL duk_hbufferobject *duk__require_bufobj_value(duk_context *ctx, duk_idx_t index) {
153 duk_hthread *thr;
154 duk_tval *tv;
155 duk_hbufferobject *h_obj;
156
157 thr = (duk_hthread *) ctx;
158
159 /* Don't accept relative indices now. */
160 DUK_ASSERT(index >= 0);
161
162 tv = duk_require_tval(ctx, index);
163 DUK_ASSERT(tv != NULL);
164 if (DUK_TVAL_IS_OBJECT(tv)) {
165 h_obj = (duk_hbufferobject *) DUK_TVAL_GET_OBJECT(tv);
166 DUK_ASSERT(h_obj != NULL);
167 if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_obj)) {
168 DUK_ASSERT_HBUFFEROBJECT_VALID(h_obj);
169 return h_obj;
170 }
171 }
172
173 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_BUFFER);
174 return NULL; /* not reachable */
175 }
176 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
177
178 DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_hbuffer *h_val) {
179 duk_hthread *thr;
180
181 thr = (duk_hthread *) ctx;
182 DUK_UNREF(thr);
183
184 DUK_ASSERT(ctx != NULL);
185 DUK_ASSERT(h_bufobj != NULL);
186 DUK_ASSERT(h_bufobj->buf == NULL); /* no need to decref */
187 DUK_ASSERT(h_val != NULL);
188 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
189
190 h_bufobj->buf = h_val;
191 DUK_HBUFFER_INCREF(thr, h_val);
192 h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
193 DUK_ASSERT(h_bufobj->shift == 0);
194 DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
195
196 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
197 }
198
199 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
200 DUK_LOCAL duk_hbufferobject *duk__push_arraybuffer_with_length(duk_context *ctx, duk_uint_t len) {
201 duk_hbuffer *h_val;
202 duk_hbufferobject *h_bufobj;
203
204 (void) duk_push_fixed_buffer(ctx, (duk_size_t) len);
205 h_val = (duk_hbuffer *) duk_get_hbuffer(ctx, -1);
206 DUK_ASSERT(h_val != NULL);
207
208 h_bufobj = duk_push_bufferobject_raw(ctx,
209 DUK_HOBJECT_FLAG_EXTENSIBLE |
210 DUK_HOBJECT_FLAG_BUFFEROBJECT |
211 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
212 DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
213 DUK_ASSERT(h_bufobj != NULL);
214
215 duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
216 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
217
218 return h_bufobj;
219 }
220 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
221
222 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
223 /* Shared offset/length coercion helper. */
224 DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx,
225 duk_hbufferobject *h_bufarg,
226 duk_idx_t idx_offset,
227 duk_idx_t idx_length,
228 duk_uint_t *out_offset,
229 duk_uint_t *out_length,
230 duk_bool_t throw_flag) {
231 duk_hthread *thr;
232 duk_int_t offset_signed;
233 duk_int_t length_signed;
234 duk_uint_t offset;
235 duk_uint_t length;
236
237 thr = (duk_hthread *) ctx;
238 DUK_UNREF(thr);
239
240 offset_signed = duk_to_int(ctx, idx_offset);
241 if (offset_signed < 0) {
242 goto fail_range;
243 }
244 offset = (duk_uint_t) offset_signed;
245 if (offset > h_bufarg->length) {
246 goto fail_range;
247 }
248 DUK_ASSERT_DISABLE(offset >= 0); /* unsigned */
249 DUK_ASSERT(offset <= h_bufarg->length);
250
251 if (duk_is_undefined(ctx, idx_length)) {
252 DUK_ASSERT(h_bufarg->length >= offset);
253 length = h_bufarg->length - offset; /* >= 0 */
254 } else {
255 length_signed = duk_to_int(ctx, idx_length);
256 if (length_signed < 0) {
257 goto fail_range;
258 }
259 length = (duk_uint_t) length_signed;
260 DUK_ASSERT(h_bufarg->length >= offset);
261 if (length > h_bufarg->length - offset) {
262 /* Unlike for negative arguments, some call sites
263 * want length to be clamped if it's positive.
264 */
265 if (throw_flag) {
266 goto fail_range;
267 } else {
268 length = h_bufarg->length - offset;
269 }
270 }
271 }
272 DUK_ASSERT_DISABLE(length >= 0); /* unsigned */
273 DUK_ASSERT(offset + length <= h_bufarg->length);
274
275 *out_offset = offset;
276 *out_length = length;
277 return;
278
279 fail_range:
280 duk_error(thr, DUK_ERR_RANGE_ERROR, DUK_STR_INVALID_CALL_ARGS);
281 }
282 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
283
284 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
285 /* Shared lenient buffer length clamping helper. No negative indices, no
286 * element/byte shifting.
287 */
288 DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx,
289 duk_hbufferobject *h_bufobj,
290 duk_idx_t idx_start,
291 duk_idx_t idx_end,
292 duk_int_t *out_start_offset,
293 duk_int_t *out_end_offset) {
294 duk_int_t buffer_length;
295 duk_int_t start_offset;
296 duk_int_t end_offset;
297
298 DUK_ASSERT(out_start_offset != NULL);
299 DUK_ASSERT(out_end_offset != NULL);
300
301 buffer_length = (duk_int_t) h_bufobj->length;
302
303 /* undefined coerces to zero which is correct */
304 start_offset = duk_to_int_clamped(ctx, idx_start, 0, buffer_length);
305 if (duk_is_undefined(ctx, idx_end)) {
306 end_offset = buffer_length;
307 } else {
308 end_offset = duk_to_int_clamped(ctx, idx_end, start_offset, buffer_length);
309 }
310
311 DUK_ASSERT(start_offset >= 0);
312 DUK_ASSERT(start_offset <= buffer_length);
313 DUK_ASSERT(end_offset >= 0);
314 DUK_ASSERT(end_offset <= buffer_length);
315 DUK_ASSERT(start_offset <= end_offset);
316
317 *out_start_offset = start_offset;
318 *out_end_offset = end_offset;
319 }
320 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
321
322 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
323 /* Shared lenient buffer length clamping helper. Indices are treated as
324 * element indices (though output values are byte offsets) which only
325 * really matters for TypedArray views as other buffer object have a zero
326 * shift. Negative indices are counted from end of input slice; crossed
327 * indices are clamped to zero length; and final indices are clamped
328 * against input slice. Used for e.g. ArrayBuffer slice().
329 */
330 DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx,
331 duk_hbufferobject *h_bufobj,
332 duk_idx_t idx_start,
333 duk_idx_t idx_end,
334 duk_int_t *out_start_offset,
335 duk_int_t *out_end_offset) {
336 duk_int_t buffer_length;
337 duk_int_t start_offset;
338 duk_int_t end_offset;
339
340 DUK_ASSERT(out_start_offset != NULL);
341 DUK_ASSERT(out_end_offset != NULL);
342
343 buffer_length = (duk_int_t) h_bufobj->length;
344 buffer_length >>= h_bufobj->shift; /* as elements */
345
346 /* Resolve start/end offset as element indices first; arguments
347 * at idx_start/idx_end are element offsets. Working with element
348 * indices first also avoids potential for wrapping.
349 */
350
351 start_offset = duk_to_int(ctx, idx_start);
352 if (start_offset < 0) {
353 start_offset = buffer_length + start_offset;
354 }
355 if (duk_is_undefined(ctx, idx_end)) {
356 end_offset = buffer_length;
357 } else {
358 end_offset = duk_to_int(ctx, idx_end);
359 if (end_offset < 0) {
360 end_offset = buffer_length + end_offset;
361 }
362 }
363 /* Note: start_offset/end_offset can still be < 0 here. */
364
365 if (start_offset < 0) {
366 start_offset = 0;
367 } else if (start_offset > buffer_length) {
368 start_offset = buffer_length;
369 }
370 if (end_offset < start_offset) {
371 end_offset = start_offset;
372 } else if (end_offset > buffer_length) {
373 end_offset = buffer_length;
374 }
375 DUK_ASSERT(start_offset >= 0);
376 DUK_ASSERT(start_offset <= buffer_length);
377 DUK_ASSERT(end_offset >= 0);
378 DUK_ASSERT(end_offset <= buffer_length);
379 DUK_ASSERT(start_offset <= end_offset);
380
381 /* Convert indices to byte offsets. */
382 start_offset <<= h_bufobj->shift;
383 end_offset <<= h_bufobj->shift;
384
385 *out_start_offset = start_offset;
386 *out_end_offset = end_offset;
387 }
388 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
389
390 /*
391 * Indexed read/write helpers (also used from outside this file)
392 */
393
394 DUK_INTERNAL void duk_hbufferobject_push_validated_read(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
395 duk_double_union du;
396
397 DUK_MEMCPY((void *) du.uc, (const void *) p, elem_size);
398
399 switch (h_bufobj->elem_type) {
400 case DUK_HBUFFEROBJECT_ELEM_UINT8:
401 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
402 case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED:
403 #endif
404 duk_push_uint(ctx, (duk_uint_t) du.uc[0]);
405 break;
406 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
407 /* These are not needed when only Duktape.Buffer is supported. */
408 case DUK_HBUFFEROBJECT_ELEM_INT8:
409 duk_push_int(ctx, (duk_int_t) (duk_int8_t) du.uc[0]);
410 break;
411 case DUK_HBUFFEROBJECT_ELEM_UINT16:
412 duk_push_uint(ctx, (duk_uint_t) du.us[0]);
413 break;
414 case DUK_HBUFFEROBJECT_ELEM_INT16:
415 duk_push_int(ctx, (duk_int_t) (duk_int16_t) du.us[0]);
416 break;
417 case DUK_HBUFFEROBJECT_ELEM_UINT32:
418 duk_push_uint(ctx, (duk_uint_t) du.ui[0]);
419 break;
420 case DUK_HBUFFEROBJECT_ELEM_INT32:
421 duk_push_int(ctx, (duk_int_t) (duk_int32_t) du.ui[0]);
422 break;
423 case DUK_HBUFFEROBJECT_ELEM_FLOAT32:
424 duk_push_number(ctx, (duk_double_t) du.f[0]);
425 break;
426 case DUK_HBUFFEROBJECT_ELEM_FLOAT64:
427 duk_push_number(ctx, (duk_double_t) du.d);
428 break;
429 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
430 default:
431 DUK_UNREACHABLE();
432 }
433 }
434
435 DUK_INTERNAL void duk_hbufferobject_validated_write(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
436 duk_double_union du;
437
438 /* NOTE! Caller must ensure that any side effects from the
439 * coercions below are safe. If that cannot be guaranteed
440 * (which is normally the case), caller must coerce the
441 * argument using duk_to_number() before any pointer
442 * validations; the result of duk_to_number() always coerces
443 * without side effects here.
444 */
445
446 switch (h_bufobj->elem_type) {
447 case DUK_HBUFFEROBJECT_ELEM_UINT8:
448 du.uc[0] = (duk_uint8_t) duk_to_uint32(ctx, -1);
449 break;
450 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
451 /* These are not needed when only Duktape.Buffer is supported. */
452 case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED:
453 du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(ctx, -1);
454 break;
455 case DUK_HBUFFEROBJECT_ELEM_INT8:
456 du.uc[0] = (duk_uint8_t) duk_to_int32(ctx, -1);
457 break;
458 case DUK_HBUFFEROBJECT_ELEM_UINT16:
459 du.us[0] = (duk_uint16_t) duk_to_uint32(ctx, -1);
460 break;
461 case DUK_HBUFFEROBJECT_ELEM_INT16:
462 du.us[0] = (duk_uint16_t) duk_to_int32(ctx, -1);
463 break;
464 case DUK_HBUFFEROBJECT_ELEM_UINT32:
465 du.ui[0] = (duk_uint32_t) duk_to_uint32(ctx, -1);
466 break;
467 case DUK_HBUFFEROBJECT_ELEM_INT32:
468 du.ui[0] = (duk_uint32_t) duk_to_int32(ctx, -1);
469 break;
470 case DUK_HBUFFEROBJECT_ELEM_FLOAT32:
471 du.f[0] = (duk_float_t) duk_to_number(ctx, -1);
472 break;
473 case DUK_HBUFFEROBJECT_ELEM_FLOAT64:
474 du.d = (duk_double_t) duk_to_number(ctx, -1);
475 break;
476 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
477 default:
478 DUK_UNREACHABLE();
479 }
480
481 DUK_MEMCPY((void *) p, (const void *) du.uc, elem_size);
482 }
483
484 /*
485 * Duktape.Buffer: constructor
486 */
487
488 DUK_INTERNAL duk_ret_t duk_bi_buffer_constructor(duk_context *ctx) {
489 duk_hthread *thr;
490 duk_size_t buf_size;
491 duk_small_int_t buf_dynamic;
492 duk_uint8_t *buf_data;
493 const duk_uint8_t *src_data;
494
495 thr = (duk_hthread *) ctx;
496 DUK_UNREF(thr);
497
498 /*
499 * Constructor arguments are currently somewhat compatible with
500 * (keep it that way if possible):
501 *
502 * http://nodejs.org/api/buffer.html
503 *
504 * Note that the ToBuffer() coercion (duk_to_buffer()) does NOT match
505 * the constructor behavior.
506 */
507
508 buf_dynamic = duk_get_boolean(ctx, 1); /* default to false */
509
510 switch (duk_get_type(ctx, 0)) {
511 case DUK_TYPE_NUMBER: {
512 /* new buffer of specified size */
513 buf_size = (duk_size_t) duk_to_int(ctx, 0);
514 (void) duk_push_buffer(ctx, buf_size, buf_dynamic);
515 break;
516 }
517 case DUK_TYPE_BUFFER: {
518 /* return input buffer, converted to a Duktape.Buffer object
519 * if called as a constructor (no change if called as a
520 * function).
521 */
522 duk_set_top(ctx, 1);
523 break;
524 }
525 case DUK_TYPE_STRING: {
526 /* new buffer with string contents */
527 src_data = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &buf_size);
528 DUK_ASSERT(src_data != NULL); /* even for zero-length string */
529 buf_data = (duk_uint8_t *) duk_push_buffer(ctx, buf_size, buf_dynamic);
530 DUK_MEMCPY((void *) buf_data, (const void *) src_data, (size_t) buf_size);
531 break;
532 }
533 case DUK_TYPE_OBJECT: {
534 /* For all duk_hbufferobjects, get the plain buffer inside
535 * without making a copy. This is compatible with Duktape 1.2
536 * but means that a slice/view information is ignored and the
537 * full underlying buffer is returned.
538 *
539 * If called as a constructor, a new Duktape.Buffer object
540 * pointing to the same plain buffer is created below.
541 */
542 duk_hbufferobject *h_bufobj;
543 h_bufobj = (duk_hbufferobject *) duk_get_hobject(ctx, 0);
544 DUK_ASSERT(h_bufobj != NULL);
545 if (!DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj)) {
546 return DUK_RET_TYPE_ERROR;
547 }
548 if (h_bufobj->buf == NULL) {
549 return DUK_RET_TYPE_ERROR;
550 }
551 duk_push_hbuffer(ctx, h_bufobj->buf);
552 break;
553 }
554 case DUK_TYPE_NONE:
555 default: {
556 return DUK_RET_TYPE_ERROR;
557 }
558 }
559 DUK_ASSERT(duk_is_buffer(ctx, -1));
560
561 /* stack is unbalanced, but: [ <something> buf ] */
562
563 if (duk_is_constructor_call(ctx)) {
564 duk_hbufferobject *h_bufobj;
565 duk_hbuffer *h_val;
566
567 h_val = duk_get_hbuffer(ctx, -1);
568 DUK_ASSERT(h_val != NULL);
569
570 h_bufobj = duk_push_bufferobject_raw(ctx,
571 DUK_HOBJECT_FLAG_EXTENSIBLE |
572 DUK_HOBJECT_FLAG_BUFFEROBJECT |
573 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
574 DUK_BIDX_BUFFER_PROTOTYPE);
575 DUK_ASSERT(h_bufobj != NULL);
576
577 duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
578
579 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
580 }
581 /* Note: unbalanced stack on purpose */
582
583 return 1;
584 }
585
586 /*
587 * Node.js Buffer: constructor
588 */
589
590 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
591 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) {
592 /* Internal class is Object: Object.prototype.toString.call(new Buffer(0))
593 * prints "[object Object]".
594 */
595 duk_int_t len;
596 duk_int_t i;
597 duk_uint8_t *buf;
598 duk_hbuffer *h_buf;
599 duk_hbufferobject *h_bufobj;
600 duk_size_t buf_size;
601
602 switch (duk_get_type(ctx, 0)) {
603 case DUK_TYPE_BUFFER: {
604 /* Custom behavior: plain buffer is used as internal buffer
605 * without making a copy (matches Duktape.Buffer).
606 */
607 duk_set_top(ctx, 1); /* -> [ buffer ] */
608 break;
609 }
610 case DUK_TYPE_NUMBER: {
611 len = duk_to_int_clamped(ctx, 0, 0, DUK_INT_MAX);
612 buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len);
613 break;
614 }
615 case DUK_TYPE_OBJECT: {
616 (void) duk_get_prop_string(ctx, 0, "length");
617 len = duk_to_int_clamped(ctx, -1, 0, DUK_INT_MAX);
618 duk_pop(ctx);
619 buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len);
620 for (i = 0; i < len; i++) {
621 /* XXX: fast path for array arguments? */
622 duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
623 buf[i] = (duk_uint8_t) (duk_to_uint32(ctx, -1) & 0xffU);
624 duk_pop(ctx);
625 }
626 break;
627 }
628 case DUK_TYPE_STRING: {
629 /* ignore encoding for now */
630 duk_dup(ctx, 0);
631 buf = (duk_uint8_t *) duk_to_buffer(ctx, -1, &buf_size);
632 break;
633 }
634 default:
635 return DUK_RET_TYPE_ERROR;
636 }
637
638 DUK_ASSERT(duk_is_buffer(ctx, -1));
639 h_buf = duk_get_hbuffer(ctx, -1);
640 DUK_ASSERT(h_buf != NULL);
641
642 h_bufobj = duk_push_bufferobject_raw(ctx,
643 DUK_HOBJECT_FLAG_EXTENSIBLE |
644 DUK_HOBJECT_FLAG_BUFFEROBJECT |
645 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
646 DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
647 DUK_ASSERT(h_bufobj != NULL);
648
649 h_bufobj->buf = h_buf;
650 DUK_HBUFFER_INCREF(thr, h_buf);
651 DUK_ASSERT(h_bufobj->offset == 0);
652 h_bufobj->length = (duk_int_t) DUK_HBUFFER_GET_SIZE(h_buf);
653 DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
654
655 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
656
657 return 1;
658 }
659 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
660 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) {
661 DUK_UNREF(ctx);
662 return DUK_RET_UNSUPPORTED_ERROR;
663 }
664 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
665
666 /*
667 * ArrayBuffer, DataView, and TypedArray constructors
668 */
669
670 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
671 DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) {
672 duk_hbufferobject *h_bufobj;
673 duk_hbuffer *h_val;
674
675 /* XXX: function flag to make this automatic? */
676 if (!duk_is_constructor_call(ctx)) {
677 return DUK_RET_TYPE_ERROR;
678 }
679
680 if (duk_is_buffer(ctx, 0)) {
681 /* Custom behavior: plain buffer is used as internal buffer
682 * without making a copy (matches Duktape.Buffer).
683 */
684
685 h_val = duk_get_hbuffer(ctx, 0);
686 DUK_ASSERT(h_val != NULL);
687
688 /* XXX: accept any duk_hbufferobject type as an input also? */
689 } else {
690 duk_int_t len;
691 len = duk_to_int(ctx, 0);
692 if (len < 0) {
693 goto fail_length;
694 }
695 (void) duk_push_fixed_buffer(ctx, (duk_size_t) len);
696 h_val = (duk_hbuffer *) duk_get_hbuffer(ctx, -1);
697 DUK_ASSERT(h_val != NULL);
698 }
699
700 h_bufobj = duk_push_bufferobject_raw(ctx,
701 DUK_HOBJECT_FLAG_EXTENSIBLE |
702 DUK_HOBJECT_FLAG_BUFFEROBJECT |
703 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
704 DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
705 DUK_ASSERT(h_bufobj != NULL);
706
707 duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
708 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
709
710 return 1;
711
712 fail_length:
713 return DUK_RET_RANGE_ERROR;
714 }
715 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
716 DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) {
717 DUK_UNREF(ctx);
718 return DUK_RET_UNSUPPORTED_ERROR;
719 }
720 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
721
722
723 /* Format of magic, bits:
724 * 0...1: elem size shift (0-3)
725 * 2...5: elem type (DUK_HBUFFEROBJECT_ELEM_xxx)
726 */
727
728 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
729 DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
730 duk_hthread *thr;
731 duk_tval *tv;
732 duk_hobject *h_obj;
733 duk_hbufferobject *h_bufobj = NULL;
734 duk_hbufferobject *h_bufarr = NULL;
735 duk_hbufferobject *h_bufarg = NULL;
736 duk_hbuffer *h_val;
737 duk_small_uint_t magic;
738 duk_small_uint_t shift;
739 duk_small_uint_t elem_type;
740 duk_small_uint_t elem_size;
741 duk_small_uint_t class_num;
742 duk_small_uint_t proto_bidx;
743 duk_uint_t align_mask;
744 duk_uint_t elem_length;
745 duk_int_t elem_length_signed;
746 duk_uint_t byte_length;
747 duk_small_uint_t copy_mode;
748
749 thr = (duk_hthread *) ctx;
750 DUK_UNREF(thr);
751
752 /* XXX: function flag to make this automatic? */
753 if (!duk_is_constructor_call(ctx)) {
754 return DUK_RET_TYPE_ERROR;
755 }
756
757 /* We could fit built-in index into magic but that'd make the magic
758 * number dependent on built-in numbering (genbuiltins.py doesn't
759 * handle that yet). So map both class and prototype from the
760 * element type.
761 */
762 magic = duk_get_current_magic(ctx);
763 shift = magic & 0x03; /* bits 0...1: shift */
764 elem_type = (magic >> 2) & 0x0f; /* bits 2...5: type */
765 elem_size = 1 << shift;
766 align_mask = elem_size - 1;
767 DUK_ASSERT(elem_type < sizeof(duk__buffer_proto_from_elemtype) / sizeof(duk_uint8_t));
768 proto_bidx = duk__buffer_proto_from_elemtype[elem_type];
769 DUK_ASSERT(proto_bidx < DUK_NUM_BUILTINS);
770 DUK_ASSERT(elem_type < sizeof(duk__buffer_class_from_elemtype) / sizeof(duk_uint8_t));
771 class_num = duk__buffer_class_from_elemtype[elem_type];
772
773 DUK_DD(DUK_DDPRINT("typedarray constructor, magic=%d, shift=%d, elem_type=%d, "
774 "elem_size=%d, proto_bidx=%d, class_num=%d",
775 (int) magic, (int) shift, (int) elem_type, (int) elem_size,
776 (int) proto_bidx, (int) class_num));
777
778 /* Argument variants. When the argument is an ArrayBuffer a view to
779 * the same buffer is created; otherwise a new ArrayBuffer is always
780 * created.
781 */
782
783 tv = duk_get_tval(ctx, 0);
784 DUK_ASSERT(tv != NULL); /* arg count */
785 if (DUK_TVAL_IS_OBJECT(tv)) {
786 h_obj = DUK_TVAL_GET_OBJECT(tv);
787 DUK_ASSERT(h_obj != NULL);
788
789 if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_ARRAYBUFFER) {
790 /* ArrayBuffer: unlike any other argument variant, create
791 * a view into the existing buffer.
792 */
793
794 duk_int_t byte_offset_signed;
795 duk_uint_t byte_offset;
796
797 h_bufarg = (duk_hbufferobject *) h_obj;
798
799 byte_offset_signed = duk_to_int(ctx, 1);
800 if (byte_offset_signed < 0) {
801 goto fail_arguments;
802 }
803 byte_offset = (duk_uint_t) byte_offset_signed;
804 if (byte_offset > h_bufarg->length ||
805 (byte_offset & align_mask) != 0) {
806 /* Must be >= 0 and multiple of element size. */
807 goto fail_arguments;
808 }
809 if (duk_is_undefined(ctx, 2)) {
810 DUK_ASSERT(h_bufarg->length >= byte_offset);
811 byte_length = h_bufarg->length - byte_offset;
812 if ((byte_length & align_mask) != 0) {
813 /* Must be element size multiple from
814 * start offset to end of buffer.
815 */
816 goto fail_arguments;
817 }
818 elem_length = (byte_length >> shift);
819 } else {
820 elem_length_signed = duk_to_int(ctx, 2);
821 if (elem_length_signed < 0) {
822 goto fail_arguments;
823 }
824 elem_length = (duk_uint_t) elem_length_signed;
825 byte_length = elem_length << shift;
826 if ((byte_length >> shift) != elem_length) {
827 /* Byte length would overflow. */
828 /* XXX: easier check with less code? */
829 goto fail_arguments;
830 }
831 DUK_ASSERT(h_bufarg->length >= byte_offset);
832 if (byte_length > h_bufarg->length - byte_offset) {
833 /* Not enough data. */
834 goto fail_arguments;
835 }
836 }
837 DUK_ASSERT_DISABLE(byte_offset >= 0);
838 DUK_ASSERT(byte_offset <= h_bufarg->length);
839 DUK_ASSERT_DISABLE(byte_length >= 0);
840 DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length);
841 DUK_ASSERT((elem_length << shift) == byte_length);
842
843 h_bufobj = duk_push_bufferobject_raw(ctx,
844 DUK_HOBJECT_FLAG_EXTENSIBLE |
845 DUK_HOBJECT_FLAG_BUFFEROBJECT |
846 DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
847 proto_bidx);
848 h_val = h_bufarg->buf;
849 if (h_val == NULL) {
850 return DUK_RET_TYPE_ERROR;
851 }
852 h_bufobj->buf = h_val;
853 DUK_HBUFFER_INCREF(thr, h_val);
854 h_bufobj->offset = h_bufarg->offset + byte_offset;
855 h_bufobj->length = byte_length;
856 h_bufobj->shift = shift;
857 h_bufobj->elem_type = elem_type;
858 h_bufobj->is_view = 1;
859 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
860
861 /* Set .buffer to the argument ArrayBuffer. */
862 duk_dup(ctx, 0);
863 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
864 duk_compact(ctx, -1);
865 return 1;
866 } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
867 /* TypedArray (or other non-ArrayBuffer duk_hbufferobject).
868 * Conceptually same behavior as for an Array-like argument,
869 * with a few fast paths.
870 */
871
872 h_bufarg = (duk_hbufferobject *) h_obj;
873 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg);
874 elem_length_signed = (duk_int_t) (h_bufarg->length >> h_bufarg->shift);
875 if (h_bufarg->buf == NULL) {
876 return DUK_RET_TYPE_ERROR;
877 }
878
879 /* Select copy mode. Must take into account element
880 * compatibility and validity of the underlying source
881 * buffer.
882 */
883
884 DUK_DDD(DUK_DDDPRINT("selecting copy mode for bufobj arg, "
885 "src byte_length=%ld, src shift=%d, "
886 "src/dst elem_length=%ld; "
887 "dst shift=%d -> dst byte_length=%ld",
888 (long) h_bufarg->length, (int) h_bufarg->shift,
889 (long) elem_length_signed, (int) shift,
890 (long) (elem_length_signed << shift)));
891
892 copy_mode = 2; /* default is explicit index read/write copy */
893 DUK_ASSERT(elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t));
894 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)) {
895 if ((duk__buffer_elemtype_copy_compatible[elem_type] & (1 << h_bufarg->elem_type)) != 0) {
896 DUK_DDD(DUK_DDDPRINT("source/target are copy compatible, memcpy"));
897 DUK_ASSERT(shift == h_bufarg->shift); /* byte sizes will match */
898 copy_mode = 0;
899 } else {
900 DUK_DDD(DUK_DDDPRINT("source/target not copy compatible but valid, fast copy"));
901 copy_mode = 1;
902 }
903 }
904 } else {
905 /* Array or Array-like */
906 elem_length_signed = (duk_int_t) duk_get_length(ctx, 0);
907 copy_mode = 2;
908 }
909 } else {
910 /* Non-object argument is simply int coerced, matches
911 * V8 behavior (except for "null", which we coerce to
912 * 0 but V8 TypeErrors).
913 */
914 elem_length_signed = duk_to_int(ctx, 0);
915 copy_mode = 3;
916 }
917 if (elem_length_signed < 0) {
918 goto fail_arguments;
919 }
920 elem_length = (duk_uint_t) elem_length_signed;
921 byte_length = (duk_uint_t) (elem_length << shift);
922 if ((byte_length >> shift) != elem_length) {
923 /* Byte length would overflow. */
924 /* XXX: easier check with less code? */
925 goto fail_arguments;
926 }
927
928 DUK_DDD(DUK_DDDPRINT("elem_length=%ld, byte_length=%ld",
929 (long) elem_length, (long) byte_length));
930
931 /* ArrayBuffer argument is handled specially above; the rest of the
932 * argument variants are handled by shared code below.
933 */
934
935 /* Push a new ArrayBuffer (becomes view .buffer) */
936 h_bufarr = duk__push_arraybuffer_with_length(ctx, byte_length);
937 DUK_ASSERT(h_bufarr != NULL);
938 h_val = h_bufarr->buf;
939 DUK_ASSERT(h_val != NULL);
940
941 /* Push the resulting view object and attach the ArrayBuffer. */
942 h_bufobj = duk_push_bufferobject_raw(ctx,
943 DUK_HOBJECT_FLAG_EXTENSIBLE |
944 DUK_HOBJECT_FLAG_BUFFEROBJECT |
945 DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
946 proto_bidx);
947
948 h_bufobj->buf = h_val;
949 DUK_HBUFFER_INCREF(thr, h_val);
950 DUK_ASSERT(h_bufobj->offset == 0);
951 h_bufobj->length = byte_length;
952 h_bufobj->shift = shift;
953 h_bufobj->elem_type = elem_type;
954 h_bufobj->is_view = 1;
955 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
956
957 /* Set .buffer */
958 duk_dup(ctx, -2);
959 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
960 duk_compact(ctx, -1);
961
962 /* Copy values, the copy method depends on the arguments.
963 *
964 * Copy mode decision may depend on the validity of the underlying
965 * buffer of the source argument; there must be no harmful side effects
966 * from there to here for copy_mode to still be valid.
967 */
968 DUK_DDD(DUK_DDDPRINT("copy mode: %d", (int) copy_mode));
969 switch (copy_mode) {
970 case 0: {
971 /* Use byte copy. */
972
973 duk_uint8_t *p_src;
974 duk_uint8_t *p_dst;
975
976 DUK_ASSERT(h_bufobj != NULL);
977 DUK_ASSERT(h_bufobj->buf != NULL);
978 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj));
979 DUK_ASSERT(h_bufarg != NULL);
980 DUK_ASSERT(h_bufarg->buf != NULL);
981 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg));
982
983 p_dst = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj);
984 p_src = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg);
985
986 DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld",
987 (void *) p_src, (void *) p_dst, (long) byte_length));
988
989 DUK_MEMCPY((void *) p_dst, (const void *) p_src, (size_t) byte_length);
990 break;
991 }
992 case 1: {
993 /* Copy values through direct validated reads and writes. */
994
995 duk_small_uint_t src_elem_size;
996 duk_small_uint_t dst_elem_size;
997 duk_uint8_t *p_src;
998 duk_uint8_t *p_src_end;
999 duk_uint8_t *p_dst;
1000
1001 DUK_ASSERT(h_bufobj != NULL);
1002 DUK_ASSERT(h_bufobj->buf != NULL);
1003 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj));
1004 DUK_ASSERT(h_bufarg != NULL);
1005 DUK_ASSERT(h_bufarg->buf != NULL);
1006 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg));
1007
1008 src_elem_size = 1 << h_bufarg->shift;
1009 dst_elem_size = elem_size;
1010
1011 p_src = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg);
1012 p_dst = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj);
1013 p_src_end = p_src + h_bufarg->length;
1014
1015 DUK_DDD(DUK_DDDPRINT("using fast copy: p_src=%p, p_src_end=%p, p_dst=%p, "
1016 "src_elem_size=%d, dst_elem_size=%d",
1017 (void *) p_src, (void *) p_src_end, (void *) p_dst,
1018 (int) src_elem_size, (int) dst_elem_size));
1019
1020 while (p_src != p_src_end) {
1021 DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
1022 "p_src=%p, p_src_end=%p, p_dst=%p",
1023 (void *) p_src, (void *) p_src_end, (void *) p_dst));
1024 /* A validated read() is always a number, so it's write coercion
1025 * is always side effect free an won't invalidate pointers etc.
1026 */
1027 duk_hbufferobject_push_validated_read(ctx, h_bufarg, p_src, src_elem_size);
1028 duk_hbufferobject_validated_write(ctx, h_bufobj, p_dst, dst_elem_size);
1029 duk_pop(ctx);
1030 p_src += src_elem_size;
1031 p_dst += dst_elem_size;
1032 }
1033 break;
1034 }
1035 case 2: {
1036 /* Copy values by index reads and writes. Let virtual
1037 * property handling take care of coercion.
1038 */
1039 duk_uint_t i;
1040
1041 DUK_DDD(DUK_DDDPRINT("using slow copy"));
1042
1043 for (i = 0; i < elem_length; i++) {
1044 duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
1045 duk_put_prop_index(ctx, -2, (duk_uarridx_t) i);
1046 }
1047 break;
1048 }
1049 default:
1050 case 3: {
1051 /* No copy, leave zero bytes in the buffer. There's no
1052 * ambiguity with Float32/Float64 because zero bytes also
1053 * represent 0.0.
1054 */
1055
1056 DUK_DDD(DUK_DDDPRINT("using no copy"));
1057 break;
1058 }
1059 }
1060
1061 return 1;
1062
1063 fail_arguments:
1064 return DUK_RET_RANGE_ERROR;
1065 }
1066 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1067 DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
1068 DUK_UNREF(ctx);
1069 return DUK_RET_UNSUPPORTED_ERROR;
1070 }
1071 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1072
1073 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1074 DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) {
1075 duk_hbufferobject *h_bufarg;
1076 duk_hbufferobject *h_bufobj;
1077 duk_hbuffer *h_val;
1078 duk_uint_t offset;
1079 duk_uint_t length;
1080
1081 /* XXX: function flag to make this automatic? */
1082 if (!duk_is_constructor_call(ctx)) {
1083 return DUK_RET_TYPE_ERROR;
1084 }
1085
1086 h_bufarg = duk__require_bufobj_value(ctx, 0);
1087 DUK_ASSERT(h_bufarg != NULL);
1088
1089 duk__resolve_offset_opt_length(ctx, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/);
1090 DUK_ASSERT(offset <= h_bufarg->length);
1091 DUK_ASSERT(offset + length <= h_bufarg->length);
1092
1093 h_bufobj = duk_push_bufferobject_raw(ctx,
1094 DUK_HOBJECT_FLAG_EXTENSIBLE |
1095 DUK_HOBJECT_FLAG_BUFFEROBJECT |
1096 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW),
1097 DUK_BIDX_DATAVIEW_PROTOTYPE);
1098
1099 h_val = h_bufarg->buf;
1100 if (h_val == NULL) {
1101 return DUK_RET_TYPE_ERROR;
1102 }
1103 h_bufobj->buf = h_val;
1104 DUK_HBUFFER_INCREF(thr, h_val);
1105 h_bufobj->offset = h_bufarg->offset + offset;
1106 h_bufobj->length = length;
1107 DUK_ASSERT(h_bufobj->shift == 0);
1108 DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
1109 h_bufobj->is_view = 1;
1110
1111 /* The DataView .buffer property is ordinarily set to the argument
1112 * which is an ArrayBuffer. We accept any duk_hbufferobject as
1113 * an argument and .buffer will be set to the argument regardless
1114 * of what it is. This may be a bit confusing if the argument
1115 * is e.g. a DataView or another TypedArray view.
1116 *
1117 * XXX: Copy .buffer property from a DataView/TypedArray argument?
1118 * Create a fresh ArrayBuffer for Duktape.Buffer and Node.js Buffer
1119 * arguments? See: test-bug-dataview-buffer-prop.js.
1120 */
1121
1122 duk_dup(ctx, 0);
1123 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
1124 duk_compact(ctx, -1);
1125
1126 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
1127 return 1;
1128 }
1129 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1130 DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) {
1131 DUK_UNREF(ctx);
1132 return DUK_RET_UNSUPPORTED_ERROR;
1133 }
1134 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1135
1136 /*
1137 * ArrayBuffer.isView()
1138 */
1139
1140 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1141 DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) {
1142 duk_hobject *h_obj;
1143 duk_bool_t ret = 0;
1144
1145 h_obj = duk_get_hobject(ctx, 0);
1146 if (h_obj != NULL && DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
1147 ret = ((duk_hbufferobject *) h_obj)->is_view;
1148 }
1149 duk_push_boolean(ctx, ret);
1150 return 1;
1151 }
1152 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1153 DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) {
1154 DUK_UNREF(ctx);
1155 return DUK_RET_UNSUPPORTED_ERROR;
1156 }
1157 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1158
1159 /*
1160 * Node.js Buffer: toString([encoding], [start], [end])
1161 */
1162
1163 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1164 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) {
1165 duk_hthread *thr;
1166 duk_hbufferobject *h_this;
1167 duk_int_t start_offset, end_offset;
1168 duk_uint8_t *buf_slice;
1169 duk_size_t slice_length;
1170
1171 thr = (duk_hthread *) ctx;
1172 DUK_UNREF(thr);
1173
1174 h_this = duk__get_bufobj_this(ctx);
1175 if (h_this == NULL) {
1176 /* XXX: happens e.g. when evaluating: String(Buffer.prototype). */
1177 duk_push_string(ctx, "[object Object]");
1178 return 1;
1179 }
1180 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this);
1181
1182 /* ignore encoding for now */
1183
1184 duk__clamp_startend_nonegidx_noshift(ctx, h_this, 1 /*idx_start*/, 2 /*idx_end*/, &start_offset, &end_offset);
1185
1186 slice_length = (duk_size_t) (end_offset - start_offset);
1187 buf_slice = (duk_uint8_t *) duk_push_fixed_buffer(ctx, slice_length);
1188 DUK_ASSERT(buf_slice != NULL);
1189
1190 if (h_this->buf == NULL) {
1191 goto type_error;
1192 }
1193
1194 if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, start_offset + slice_length)) {
1195 DUK_MEMCPY((void *) buf_slice,
1196 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
1197 slice_length);
1198 } else {
1199 /* not covered, return all zeroes */
1200 ;
1201 }
1202
1203 duk_to_string(ctx, -1);
1204 return 1;
1205
1206 type_error:
1207 return DUK_RET_TYPE_ERROR;
1208 }
1209 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1210 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) {
1211 DUK_UNREF(ctx);
1212 return DUK_RET_UNSUPPORTED_ERROR;
1213 }
1214 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1215
1216 /*
1217 * Duktape.Buffer: toString(), valueOf()
1218 */
1219
1220 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1221 DUK_INTERNAL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx) {
1222 duk_hthread *thr;
1223 duk_tval *tv;
1224 duk_small_int_t to_string = duk_get_current_magic(ctx);
1225
1226 thr = (duk_hthread *) ctx;
1227 DUK_UNREF(thr);
1228
1229 tv = duk_get_borrowed_this_tval(ctx);
1230 DUK_ASSERT(tv != NULL);
1231
1232 if (DUK_TVAL_IS_BUFFER(tv)) {
1233 duk_hbuffer *h_buf;
1234 h_buf = DUK_TVAL_GET_BUFFER(tv);
1235 DUK_ASSERT(h_buf != NULL);
1236 duk_push_hbuffer(ctx, h_buf);
1237 } else if (DUK_TVAL_IS_OBJECT(tv)) {
1238 duk_hobject *h;
1239 duk_hbufferobject *h_bufobj;
1240
1241 /* Accept any duk_hbufferobject, though we're only normally
1242 * called for Duktape.Buffer values.
1243 */
1244 h = DUK_TVAL_GET_OBJECT(tv);
1245 DUK_ASSERT(h != NULL);
1246 if (!DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
1247 DUK_DD(DUK_DDPRINT("toString/valueOf() called for a non-bufferobject object"));
1248 goto type_error;
1249 }
1250 h_bufobj = (duk_hbufferobject *) h;
1251 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
1252
1253 if (h_bufobj->buf == NULL) {
1254 DUK_DD(DUK_DDPRINT("toString/valueOf() called for a bufferobject with NULL buf"));
1255 goto type_error;
1256 }
1257 duk_push_hbuffer(ctx, h_bufobj->buf);
1258 } else {
1259 goto type_error;
1260 }
1261
1262 if (to_string) {
1263 duk_to_string(ctx, -1);
1264 }
1265 return 1;
1266
1267 type_error:
1268 return DUK_RET_TYPE_ERROR;
1269 }
1270 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1271 DUK_INTERNAL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx) {
1272 DUK_UNREF(ctx);
1273 return DUK_RET_UNSUPPORTED_ERROR;
1274 }
1275 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1276
1277 /*
1278 * Node.js Buffer.prototype: toJSON()
1279 */
1280
1281 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1282 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) {
1283 duk_hthread *thr;
1284 duk_hbufferobject *h_this;
1285 duk_uint8_t *buf;
1286 duk_uint_t i;
1287
1288 thr = (duk_hthread *) ctx;
1289 DUK_UNREF(thr);
1290 h_this = duk__require_bufobj_this(ctx);
1291 DUK_ASSERT(h_this != NULL);
1292
1293 if (h_this->buf == NULL || !DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) {
1294 /* Serialize uncovered backing buffer as a null; doesn't
1295 * really matter as long we're memory safe.
1296 */
1297 duk_push_null(ctx);
1298 return 1;
1299 }
1300
1301 duk_push_object(ctx);
1302 duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_BUFFER);
1303 duk_put_prop_stridx(ctx, -2, DUK_STRIDX_TYPE);
1304
1305 duk_push_array(ctx);
1306 for (i = 0; i < h_this->length; i++) {
1307 /* XXX: regetting the pointer may be overkill - we're writing
1308 * to a side-effect free array here.
1309 */
1310 DUK_ASSERT(h_this->buf != NULL);
1311 buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this);
1312 duk_push_uint(ctx, (duk_uint_t) buf[i]);
1313 duk_put_prop_index(ctx, -2, (duk_idx_t) i);
1314 }
1315 duk_put_prop_stridx(ctx, -2, DUK_STRIDX_DATA);
1316
1317 return 1;
1318 }
1319 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1320 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) {
1321 DUK_UNREF(ctx);
1322 return DUK_RET_UNSUPPORTED_ERROR;
1323 }
1324 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1325
1326 /*
1327 * Node.js Buffer.prototype.equals()
1328 * Node.js Buffer.prototype.compare()
1329 * Node.js Buffer.compare()
1330 */
1331
1332 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1333 DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) {
1334 duk_hthread *thr;
1335 duk_small_uint_t magic;
1336 duk_hbufferobject *h_bufarg1;
1337 duk_hbufferobject *h_bufarg2;
1338 duk_small_int_t comp_res;
1339
1340 thr = (duk_hthread *) ctx;
1341 DUK_UNREF(thr);
1342
1343 magic = duk_get_current_magic(ctx);
1344 if (magic & 0x02) {
1345 /* Static call style. */
1346 h_bufarg1 = duk__require_bufobj_value(ctx, 0);
1347 h_bufarg2 = duk__require_bufobj_value(ctx, 1);
1348 } else {
1349 h_bufarg1 = duk__require_bufobj_this(ctx);
1350 h_bufarg2 = duk__require_bufobj_value(ctx, 0);
1351 }
1352 DUK_ASSERT(h_bufarg1 != NULL);
1353 DUK_ASSERT(h_bufarg2 != NULL);
1354
1355 /* We want to compare the slice/view areas of the arguments.
1356 * If either slice/view is invalid (underlying buffer is shorter)
1357 * ensure equals() is false, but otherwise the only thing that
1358 * matters is to be memory safe.
1359 */
1360
1361 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg1) &&
1362 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg2)) {
1363 comp_res = duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg1->buf) + h_bufarg1->offset,
1364 (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg2->buf) + h_bufarg2->offset,
1365 (duk_size_t) h_bufarg1->length,
1366 (duk_size_t) h_bufarg2->length);
1367 } else {
1368 comp_res = -1; /* either nonzero value is ok */
1369 }
1370
1371 if (magic & 0x01) {
1372 /* compare: similar to string comparison but for buffer data. */
1373 duk_push_int(ctx, comp_res);
1374 } else {
1375 /* equals */
1376 duk_push_boolean(ctx, (comp_res == 0));
1377 }
1378
1379 return 1;
1380 }
1381 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1382 DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) {
1383 DUK_UNREF(ctx);
1384 return DUK_RET_UNSUPPORTED_ERROR;
1385 }
1386 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1387
1388 /*
1389 * Node.js Buffer.prototype.fill()
1390 */
1391
1392 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1393 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) {
1394 duk_hthread *thr;
1395 duk_hbufferobject *h_this;
1396 const duk_uint8_t *fill_str_ptr;
1397 duk_size_t fill_str_len;
1398 duk_uint8_t fill_value;
1399 duk_int_t fill_offset;
1400 duk_int_t fill_end;
1401 duk_size_t fill_length;
1402 duk_uint8_t *p;
1403
1404 thr = (duk_hthread *) ctx;
1405 DUK_UNREF(thr);
1406
1407 h_this = duk__require_bufobj_this(ctx);
1408 DUK_ASSERT(h_this != NULL);
1409 if (h_this->buf == NULL) {
1410 return DUK_RET_TYPE_ERROR;
1411 }
1412
1413 /* [ value offset end ] */
1414
1415 if (duk_is_string(ctx, 0)) {
1416 fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &fill_str_len);
1417 DUK_ASSERT(fill_str_ptr != NULL);
1418 } else {
1419 fill_value = (duk_uint8_t) duk_to_uint32(ctx, 0);
1420 fill_str_ptr = (const duk_uint8_t *) &fill_value;
1421 fill_str_len = 1;
1422 }
1423
1424 /* Fill offset handling is more lenient than in Node.js. */
1425
1426 duk__clamp_startend_nonegidx_noshift(ctx, h_this, 1 /*idx_start*/, 2 /*idx_end*/, &fill_offset, &fill_end);
1427
1428 DUK_DDD(DUK_DDDPRINT("fill: fill_value=%02x, fill_offset=%ld, fill_end=%ld, view length=%ld",
1429 (unsigned int) fill_value, (long) fill_offset, (long) fill_end, (long) h_this->length));
1430
1431 DUK_ASSERT(fill_end - fill_offset >= 0);
1432 DUK_ASSERT(h_this->buf != NULL);
1433
1434 p = (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + fill_offset);
1435 fill_length = (duk_size_t) (fill_end - fill_offset);
1436 if (fill_str_len == 1) {
1437 /* Handle single character fills as memset() even when
1438 * the fill data comes from a one-char argument.
1439 */
1440 DUK_MEMSET((void *) p, (int) fill_str_ptr[0], (size_t) fill_length);
1441 } else if (fill_str_len > 1) {
1442 duk_size_t i, n, t;
1443
1444 for (i = 0, n = (fill_end - fill_offset), t = 0; i < n; i++) {
1445 p[i] = fill_str_ptr[t++];
1446 if (t >= fill_str_len) {
1447 t = 0;
1448 }
1449 }
1450 } else {
1451 DUK_DDD(DUK_DDDPRINT("zero size fill pattern, ignore silently"));
1452 }
1453
1454 /* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */
1455 duk_push_this(ctx);
1456 return 1;
1457 }
1458 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1459 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) {
1460 DUK_UNREF(ctx);
1461 return DUK_RET_UNSUPPORTED_ERROR;
1462 }
1463 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1464
1465 /*
1466 * Node.js Buffer.prototype.write(string, [offset], [length], [encoding])
1467 */
1468
1469 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1470 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) {
1471 duk_hthread *thr;
1472 duk_hbufferobject *h_this;
1473 duk_uint_t offset;
1474 duk_uint_t length;
1475 const duk_uint8_t *str_data;
1476 duk_size_t str_len;
1477
1478 thr = (duk_hthread *) ctx;
1479 DUK_UNREF(thr);
1480
1481 h_this = duk__require_bufobj_this(ctx);
1482 DUK_ASSERT(h_this != NULL);
1483
1484 /* Argument must be a string, e.g. a buffer is not allowed. */
1485 str_data = (const duk_uint8_t *) duk_require_lstring(ctx, 0, &str_len);
1486
1487 duk__resolve_offset_opt_length(ctx, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/);
1488 DUK_ASSERT(offset <= h_this->length);
1489 DUK_ASSERT(offset + length <= h_this->length);
1490
1491 /* XXX: encoding is ignored now. */
1492
1493 if (length > str_len) {
1494 length = (duk_uint_t) str_len;
1495 }
1496
1497 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) {
1498 /* Cannot overlap. */
1499 DUK_MEMCPY((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + offset),
1500 (const void *) str_data,
1501 (size_t) length);
1502 } else {
1503 DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore"));
1504 }
1505
1506 duk_push_uint(ctx, length);
1507 return 1;
1508 }
1509 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1510 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) {
1511 DUK_UNREF(ctx);
1512 return DUK_RET_UNSUPPORTED_ERROR;
1513 }
1514 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1515
1516 /*
1517 * Node.js Buffer.prototype.copy()
1518 */
1519
1520 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1521 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) {
1522 duk_hthread *thr;
1523 duk_hbufferobject *h_this;
1524 duk_hbufferobject *h_bufarg;
1525 duk_int_t source_length;
1526 duk_int_t target_length;
1527 duk_int_t target_start, source_start, source_end;
1528 duk_uint_t target_ustart, source_ustart, source_uend;
1529 duk_uint_t copy_size = 0;
1530
1531 /* [ targetBuffer targetStart sourceStart sourceEnd ] */
1532
1533 thr = (duk_hthread *) ctx;
1534 DUK_UNREF(thr);
1535
1536 h_this = duk__require_bufobj_this(ctx);
1537 h_bufarg = duk__require_bufobj_value(ctx, 0);
1538 DUK_ASSERT(h_this != NULL);
1539 DUK_ASSERT(h_bufarg != NULL);
1540 source_length = (duk_int_t) h_this->length;
1541 target_length = (duk_int_t) h_bufarg->length;
1542
1543 target_start = duk_to_int(ctx, 1);
1544 source_start = duk_to_int(ctx, 2);
1545 if (duk_is_undefined(ctx, 3)) {
1546 source_end = source_length;
1547 } else {
1548 source_end = duk_to_int(ctx, 3);
1549 }
1550
1551 DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, "
1552 "source_start=%ld, source_end=%ld, source_length=%ld",
1553 (long) target_start, (long) h_bufarg->length,
1554 (long) source_start, (long) source_end, (long) source_length));
1555
1556 /* This behavior mostly mimics Node.js now. */
1557
1558 if (source_start < 0 || source_end < 0 || target_start < 0) {
1559 /* Negative offsets cause a RangeError. */
1560 goto fail_bounds;
1561 }
1562 source_ustart = (duk_uint_t) source_start;
1563 source_uend = (duk_uint_t) source_end;
1564 target_ustart = (duk_uint_t) target_start;
1565 if (source_ustart >= source_uend || /* crossed offsets or zero size */
1566 source_ustart >= (duk_uint_t) source_length || /* source out-of-bounds (but positive) */
1567 target_ustart >= (duk_uint_t) target_length) { /* target out-of-bounds (but positive) */
1568 goto silent_ignore;
1569 }
1570 if (source_uend >= (duk_uint_t) source_length) {
1571 /* Source end clamped silently to available length. */
1572 source_uend = source_length;
1573 }
1574 copy_size = source_uend - source_ustart;
1575 if (target_ustart + copy_size > (duk_uint_t) target_length) {
1576 /* Clamp to target's end if too long.
1577 *
1578 * NOTE: there's no overflow possibility in the comparison;
1579 * both target_ustart and copy_size are >= 0 and based on
1580 * values in duk_int_t range. Adding them as duk_uint_t
1581 * values is then guaranteed not to overflow.
1582 */
1583 DUK_ASSERT(target_ustart + copy_size >= target_ustart); /* no overflow */
1584 DUK_ASSERT(target_ustart + copy_size >= copy_size); /* no overflow */
1585 copy_size = (duk_uint_t) target_length - target_ustart;
1586 }
1587
1588 DUK_DDD(DUK_DDDPRINT("making copy: target_ustart=%lu source_ustart=%lu copy_size=%lu",
1589 (unsigned long) target_ustart, (unsigned long) source_ustart,
1590 (unsigned long) copy_size));
1591
1592 DUK_ASSERT(copy_size >= 1);
1593 DUK_ASSERT(source_ustart <= (duk_uint_t) source_length);
1594 DUK_ASSERT(source_ustart + copy_size <= (duk_uint_t) source_length);
1595 DUK_ASSERT(target_ustart <= (duk_uint_t) target_length);
1596 DUK_ASSERT(target_ustart + copy_size <= (duk_uint_t) target_length);
1597
1598 /* Ensure copy is covered by underlying buffers. */
1599 DUK_ASSERT(h_bufarg->buf != NULL); /* length check */
1600 DUK_ASSERT(h_this->buf != NULL); /* length check */
1601 if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufarg, target_ustart + copy_size) &&
1602 DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, source_ustart + copy_size)) {
1603 /* Must use memmove() because copy area may overlap (source and target
1604 * buffer may be the same, or from different slices.
1605 */
1606 DUK_MEMMOVE((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart),
1607 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + source_ustart),
1608 (size_t) copy_size);
1609 } else {
1610 DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring"));
1611 }
1612
1613 silent_ignore:
1614 /* Return value is like write(), number of bytes written.
1615 * The return value matters because of code like:
1616 * "off += buf.copy(...)".
1617 */
1618 duk_push_uint(ctx, copy_size);
1619 return 1;
1620
1621 fail_bounds:
1622 return DUK_RET_RANGE_ERROR;
1623 }
1624 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1625 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) {
1626 DUK_UNREF(ctx);
1627 return DUK_RET_UNSUPPORTED_ERROR;
1628 }
1629 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1630
1631 /*
1632 * TypedArray.prototype.set()
1633 *
1634 * TypedArray set() is pretty interesting to implement because:
1635 *
1636 * - The source argument may be a plain array or a typedarray. If the
1637 * source is a TypedArray, values are decoded and re-encoded into the
1638 * target (not as a plain byte copy). This may happen even when the
1639 * element byte size is the same, e.g. integer values may be re-encoded
1640 * into floats.
1641 *
1642 * - Source and target may refer to the same underlying buffer, so that
1643 * the set() operation may overlap. The specification requires that this
1644 * must work as if a copy was made before the operation. Note that this
1645 * is NOT a simple memmove() situation because the source and target
1646 * byte sizes may be different -- e.g. a 4-byte source (Int8Array) may
1647 * expand to a 16-byte target (Uint32Array) so that the target overlaps
1648 * the source both from beginning and the end (unlike in typical memmove).
1649 *
1650 * - Even if 'buf' pointers of the source and target differ, there's no
1651 * guarantee that their memory areas don't overlap. This may be the
1652 * case with external buffers.
1653 *
1654 * Even so, it is nice to optimize for the common case:
1655 *
1656 * - Source and target separate buffers or non-overlapping.
1657 *
1658 * - Source and target have a compatible type so that a plain byte copy
1659 * is possible. Note that while e.g. uint8 and int8 are compatible
1660 * (coercion one way or another doesn't change the byte representation),
1661 * e.g. int8 and uint8clamped are NOT compatible when writing int8
1662 * values into uint8clamped typedarray (-1 would clamp to 0 for instance).
1663 *
1664 * See test-bi-typedarray-proto-set.js.
1665 */
1666
1667 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1668 DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) {
1669 duk_hthread *thr;
1670 duk_hbufferobject *h_this;
1671 duk_hobject *h_obj;
1672 duk_uarridx_t i, n;
1673 duk_int_t offset_signed;
1674 duk_uint_t offset_elems;
1675 duk_uint_t offset_bytes;
1676
1677 thr = (duk_hthread *) ctx;
1678 DUK_UNREF(thr);
1679
1680 h_this = duk__require_bufobj_this(ctx);
1681 DUK_ASSERT(h_this != NULL);
1682 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this);
1683
1684 if (h_this->buf == NULL) {
1685 DUK_DDD(DUK_DDDPRINT("source neutered, skip copy"));
1686 return 0;
1687 }
1688
1689 h_obj = duk_require_hobject(ctx, 0);
1690 DUK_ASSERT(h_obj != NULL);
1691
1692 /* XXX: V8 throws a TypeError for negative values. Would it
1693 * be more useful to interpret negative offsets here from the
1694 * end of the buffer too?
1695 */
1696 offset_signed = duk_to_int(ctx, 1);
1697 if (offset_signed < 0) {
1698 return DUK_RET_TYPE_ERROR;
1699 }
1700 offset_elems = (duk_uint_t) offset_signed;
1701 offset_bytes = offset_elems << h_this->shift;
1702 if ((offset_bytes >> h_this->shift) != offset_elems) {
1703 /* Byte length would overflow. */
1704 /* XXX: easier check with less code? */
1705 return DUK_RET_RANGE_ERROR;
1706 }
1707 if (offset_bytes > h_this->length) {
1708 /* Equality may be OK but >length not. Checking
1709 * this explicitly avoids some overflow cases
1710 * below.
1711 */
1712 return DUK_RET_RANGE_ERROR;
1713 }
1714 DUK_ASSERT(offset_bytes <= h_this->length);
1715
1716 /* Fast path: source is a TypedArray (or any bufferobject). */
1717
1718 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
1719 duk_hbufferobject *h_bufarg;
1720 duk_uint16_t comp_mask;
1721 duk_small_int_t no_overlap = 0;
1722 duk_uint_t src_length;
1723 duk_uint_t dst_length;
1724 duk_uint_t dst_length_elems;
1725 duk_uint8_t *p_src_base;
1726 duk_uint8_t *p_src_end;
1727 duk_uint8_t *p_src;
1728 duk_uint8_t *p_dst_base;
1729 duk_uint8_t *p_dst;
1730 duk_small_uint_t src_elem_size;
1731 duk_small_uint_t dst_elem_size;
1732
1733 h_bufarg = (duk_hbufferobject *) h_obj;
1734 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg);
1735
1736 if (h_bufarg->buf == NULL) {
1737 DUK_DDD(DUK_DDDPRINT("target neutered, skip copy"));
1738 return 0;
1739 }
1740
1741 /* Nominal size check. */
1742 src_length = h_bufarg->length; /* bytes in source */
1743 dst_length_elems = (src_length >> h_bufarg->shift); /* elems in source and dest */
1744 dst_length = dst_length_elems << h_this->shift; /* bytes in dest */
1745 if ((dst_length >> h_this->shift) != dst_length_elems) {
1746 /* Byte length would overflow. */
1747 /* XXX: easier check with less code? */
1748 return DUK_RET_RANGE_ERROR;
1749 }
1750 DUK_DDD(DUK_DDDPRINT("nominal size check: src_length=%ld, dst_length=%ld",
1751 (long) src_length, (long) dst_length));
1752 DUK_ASSERT(offset_bytes <= h_this->length);
1753 if (dst_length > h_this->length - offset_bytes) {
1754 /* Overflow not an issue because subtraction is used on the right
1755 * side and guaranteed to be >= 0.
1756 */
1757 DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
1758 return DUK_RET_RANGE_ERROR;
1759 }
1760 if (!DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, offset_bytes + dst_length)) {
1761 DUK_DDD(DUK_DDDPRINT("copy not covered by underlying target buffer, ignore"));
1762 return 0;
1763 }
1764
1765 p_src_base = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg);
1766 p_dst_base = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + offset_bytes;
1767
1768 /* Check actual underlying buffers for validity and that they
1769 * cover the copy. No side effects are allowed after the check
1770 * so that the validity status doesn't change.
1771 */
1772 if (!DUK_HBUFFEROBJECT_VALID_SLICE(h_this) ||
1773 !DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)) {
1774 /* The condition could be more narrow and check for the
1775 * copy area only, but there's no need for fine grained
1776 * behavior when the underlying buffer is misconfigured.
1777 */
1778 DUK_DDD(DUK_DDDPRINT("source and/or target not covered by underlying buffer, skip copy"));
1779 return 0;
1780 }
1781
1782 /* We want to do a straight memory copy if possible: this is
1783 * an important operation because .set() is the TypedArray
1784 * way to copy chunks of memory. However, because set()
1785 * conceptually works in terms of elements, not all views are
1786 * compatible with direct byte copying.
1787 *
1788 * If we do manage a direct copy, the "overlap issue" handled
1789 * below can just be solved using memmove() because the source
1790 * and destination element sizes are necessarily equal.
1791 */
1792
1793 DUK_ASSERT(h_this->elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t));
1794 comp_mask = duk__buffer_elemtype_copy_compatible[h_this->elem_type];
1795 if (comp_mask & (1 << h_bufarg->elem_type)) {
1796 DUK_ASSERT(src_length == dst_length);
1797
1798 DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible"));
1799 DUK_MEMMOVE((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length);
1800 return 0;
1801 }
1802 DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item"));
1803
1804 /* We want to avoid making a copy to process set() but that's
1805 * not always possible: the source and the target may overlap
1806 * and because element sizes are different, the overlap cannot
1807 * always be handled with a memmove() or choosing the copy
1808 * direction in a certain way. For example, if source type is
1809 * uint8 and target type is uint32, the target area may exceed
1810 * the source area from both ends!
1811 *
1812 * Note that because external buffers may point to the same
1813 * memory areas, we must ultimately make this check using
1814 * pointers.
1815 *
1816 * NOTE: careful with side effects: any side effect may cause
1817 * a buffer resize (or external buffer pointer/length update)!
1818 */
1819
1820 DUK_DDD(DUK_DDDPRINT("overlap check: p_src_base=%p, src_length=%ld, "
1821 "p_dst_base=%p, dst_length=%ld",
1822 (void *) p_src_base, (long) src_length,
1823 (void *) p_dst_base, (long) dst_length));
1824
1825 if (p_src_base >= p_dst_base + dst_length || /* source starts after dest ends */
1826 p_src_base + src_length <= p_dst_base) { /* source ends before dest starts */
1827 no_overlap = 1;
1828 }
1829
1830 if (!no_overlap) {
1831 /* There's overlap: the desired end result is that
1832 * conceptually a copy is made to avoid "trampling"
1833 * of source data by destination writes. We make
1834 * an actual temporary copy to handle this case.
1835 */
1836 duk_uint8_t *p_src_copy;
1837
1838 DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source"));
1839 p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer(ctx, src_length);
1840 DUK_ASSERT(p_src_copy != NULL);
1841 DUK_MEMCPY((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length);
1842
1843 p_src_base = p_src_copy; /* use p_src_base from now on */
1844 }
1845 /* Value stack intentionally mixed size here. */
1846
1847 DUK_DDD(DUK_DDDPRINT("after overlap check: p_src_base=%p, src_length=%ld, "
1848 "p_dst_base=%p, dst_length=%ld, valstack top=%ld",
1849 (void *) p_src_base, (long) src_length,
1850 (void *) p_dst_base, (long) dst_length,
1851 (long) duk_get_top(ctx)));
1852
1853 /* Ready to make the copy. We must proceed element by element
1854 * and must avoid any side effects that might cause the buffer
1855 * validity check above to become invalid.
1856 *
1857 * Although we work through the value stack here, only plain
1858 * numbers are handled which should be side effect safe.
1859 */
1860
1861 src_elem_size = 1 << h_bufarg->shift;
1862 dst_elem_size = 1 << h_this->shift;
1863 p_src = p_src_base;
1864 p_dst = p_dst_base;
1865 p_src_end = p_src_base + src_length;
1866
1867 while (p_src != p_src_end) {
1868 DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
1869 "p_src=%p, p_src_end=%p, p_dst=%p",
1870 (void *) p_src, (void *) p_src_end, (void *) p_dst));
1871 /* A validated read() is always a number, so it's write coercion
1872 * is always side effect free an won't invalidate pointers etc.
1873 */
1874 duk_hbufferobject_push_validated_read(ctx, h_bufarg, p_src, src_elem_size);
1875 duk_hbufferobject_validated_write(ctx, h_this, p_dst, dst_elem_size);
1876 duk_pop(ctx);
1877 p_src += src_elem_size;
1878 p_dst += dst_elem_size;
1879 }
1880
1881 return 0;
1882 } else {
1883 /* Slow path: quite slow, but we save space by using the property code
1884 * to write coerce target values. We don't need to worry about overlap
1885 * here because the source is not a TypedArray.
1886 *
1887 * We could use the bufferobject write coercion helper but since the
1888 * property read may have arbitrary side effects, full validity checks
1889 * would be needed for every element anyway.
1890 */
1891
1892 n = (duk_uarridx_t) duk_get_length(ctx, 0);
1893 DUK_ASSERT(offset_bytes <= h_this->length);
1894 if ((n << h_this->shift) > h_this->length - offset_bytes) {
1895 /* Overflow not an issue because subtraction is used on the right
1896 * side and guaranteed to be >= 0.
1897 */
1898 DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
1899 return DUK_RET_RANGE_ERROR;
1900 }
1901
1902 /* There's no need to check for buffer validity status for the
1903 * target here: the property access code will do that for each
1904 * element. Moreover, if we did check the validity here, side
1905 * effects from reading the source argument might invalidate
1906 * the results anyway.
1907 */
1908
1909 DUK_ASSERT_TOP(ctx, 2);
1910 duk_push_this(ctx);
1911
1912 for (i = 0; i < n; i++) {
1913 duk_get_prop_index(ctx, 0, i);
1914 duk_put_prop_index(ctx, 2, offset_elems + i);
1915 }
1916 }
1917
1918 return 0;
1919 }
1920 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1921 DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) {
1922 DUK_UNREF(ctx);
1923 return DUK_RET_UNSUPPORTED_ERROR;
1924 }
1925 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1926
1927 /*
1928 * Node.js Buffer.prototype.slice([start], [end])
1929 * ArrayBuffer.prototype.slice(begin, [end])
1930 * TypedArray.prototype.slice(begin, [end])
1931 *
1932 * The API calls are almost identical; negative indices are counted from end
1933 * of buffer, and final indices are clamped (allowing crossed indices). Main
1934 * differences:
1935 *
1936 * - Copy/view behavior; Node.js .slice() and TypedArray .subarray() create
1937 * views, ArrayBuffer .slice() creates a copy
1938 *
1939 * - Resulting object has a different class and prototype depending on the
1940 * call (or 'this' argument)
1941 *
1942 * - TypedArray .subarray() arguments are element indices, not byte offsets
1943 */
1944
1945 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1946 DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) {
1947 duk_hthread *thr;
1948 duk_small_int_t magic;
1949 duk_small_uint_t res_class_num;
1950 duk_hobject *res_proto;
1951 duk_hbufferobject *h_this;
1952 duk_hbufferobject *h_bufobj;
1953 duk_hbuffer *h_val;
1954 duk_int_t start_offset, end_offset;
1955 duk_uint_t slice_length;
1956
1957 thr = (duk_hthread *) ctx;
1958 DUK_UNREF(thr);
1959
1960 /* [ start end ] */
1961
1962 magic = duk_get_current_magic(ctx);
1963 h_this = duk__require_bufobj_this(ctx);
1964
1965 /* Slice offsets are element (not byte) offsets, which only matters
1966 * for TypedArray views, Node.js Buffer and ArrayBuffer have shift
1967 * zero so byte and element offsets are the same. Negative indices
1968 * are counted from end of slice, crossed indices are allowed (and
1969 * result in zero length result), and final values are clamped
1970 * against the current slice. There's intentionally no check
1971 * against the underlying buffer here.
1972 */
1973
1974 duk__clamp_startend_negidx_shifted(ctx, h_this, 0 /*idx_start*/, 1 /*idx_end*/, &start_offset, &end_offset);
1975 DUK_ASSERT(end_offset >= start_offset);
1976 slice_length = (duk_uint_t) (end_offset - start_offset);
1977
1978 /* The resulting buffer object gets the same class and prototype as
1979 * the buffer in 'this', e.g. if the input is a Node.js Buffer the
1980 * result is a Node.js Buffer; if the input is a Float32Array, the
1981 * result is a Float32Array.
1982 *
1983 * For the class number this seems correct. The internal prototype
1984 * is not so clear: if 'this' is a bufferobject with a non-standard
1985 * prototype object, that value gets copied over into the result
1986 * (instead of using the standard prototype for that object type).
1987 */
1988
1989 res_class_num = DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_this);
1990 h_bufobj = duk_push_bufferobject_raw(ctx,
1991 DUK_HOBJECT_FLAG_EXTENSIBLE |
1992 DUK_HOBJECT_FLAG_BUFFEROBJECT |
1993 DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num),
1994 DUK_BIDX_OBJECT_PROTOTYPE); /* replaced */
1995 DUK_ASSERT(h_bufobj != NULL);
1996 res_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_this); /* may be NULL */
1997 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_bufobj, res_proto);
1998
1999 h_bufobj->length = slice_length;
2000 h_bufobj->shift = h_this->shift; /* inherit */
2001 h_bufobj->elem_type = h_this->elem_type; /* inherit */
2002 h_bufobj->is_view = magic & 0x01;
2003 DUK_ASSERT(h_bufobj->is_view == 0 || h_bufobj->is_view == 1);
2004
2005 h_val = h_this->buf;
2006 if (h_val == NULL) {
2007 return DUK_RET_TYPE_ERROR;
2008 }
2009
2010 if (magic & 0x02) {
2011 /* non-zero: make copy */
2012 duk_uint8_t *p_copy;
2013 duk_size_t copy_length;
2014
2015 p_copy = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) slice_length);
2016 DUK_ASSERT(p_copy != NULL);
2017
2018 /* Copy slice, respecting underlying buffer limits; remainder
2019 * is left as zero.
2020 */
2021 copy_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, slice_length);
2022 DUK_MEMCPY((void *) p_copy,
2023 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
2024 copy_length);
2025
2026 h_val = duk_get_hbuffer(ctx, -1);
2027 DUK_ASSERT(h_val != NULL);
2028
2029 h_bufobj->buf = h_val;
2030 DUK_HBUFFER_INCREF(thr, h_val);
2031 DUK_ASSERT(h_bufobj->offset == 0);
2032
2033 duk_pop(ctx); /* reachable so pop OK */
2034 } else {
2035 h_bufobj->buf = h_val;
2036 DUK_HBUFFER_INCREF(thr, h_val);
2037 h_bufobj->offset = (duk_uint_t) (h_this->offset + start_offset);
2038
2039 /* Copy the .buffer property, needed for TypedArray.prototype.subarray().
2040 *
2041 * XXX: limit copy only for TypedArray classes specifically?
2042 */
2043
2044 duk_push_this(ctx);
2045 if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LC_BUFFER)) {
2046 duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
2047 duk_pop(ctx);
2048 } else {
2049 duk_pop_2(ctx);
2050 }
2051 }
2052 /* unbalanced stack on purpose */
2053
2054 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
2055 return 1;
2056 }
2057 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2058 DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) {
2059 DUK_UNREF(ctx);
2060 return DUK_RET_UNSUPPORTED_ERROR;
2061 }
2062 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2063
2064 /*
2065 * Node.js Buffer.isEncoding()
2066 */
2067
2068 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2069 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) {
2070 const char *encoding;
2071
2072 /* only accept lowercase 'utf8' now. */
2073
2074 encoding = duk_to_string(ctx, 0);
2075 DUK_ASSERT(duk_is_string(ctx, 0)); /* guaranteed by duk_to_string() */
2076 duk_push_boolean(ctx, DUK_STRCMP(encoding, "utf8") == 0);
2077 return 1;
2078 }
2079 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2080 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) {
2081 DUK_UNREF(ctx);
2082 return DUK_RET_UNSUPPORTED_ERROR;
2083 }
2084 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2085
2086 /*
2087 * Node.js Buffer.isBuffer()
2088 */
2089
2090 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2091 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) {
2092 duk_hthread *thr;
2093 duk_tval *tv;
2094 duk_hobject *h;
2095 duk_hobject *h_proto;
2096 duk_bool_t ret = 0;
2097
2098 thr = (duk_hthread *) ctx;
2099
2100 DUK_ASSERT(duk_get_top(ctx) >= 1); /* nargs */
2101 tv = duk_get_tval(ctx, 0);
2102 DUK_ASSERT(tv != NULL);
2103
2104 if (DUK_TVAL_IS_OBJECT(tv)) {
2105 h = DUK_TVAL_GET_OBJECT(tv);
2106 DUK_ASSERT(h != NULL);
2107
2108 h_proto = thr->builtins[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE];
2109 DUK_ASSERT(h_proto != NULL);
2110
2111 h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
2112 if (h) {
2113 ret = duk_hobject_prototype_chain_contains(thr, h, h_proto, 0 /*ignore_loop*/);
2114 }
2115 }
2116
2117 duk_push_boolean(ctx, ret);
2118 return 1;
2119 }
2120 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2121 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) {
2122 DUK_UNREF(ctx);
2123 return DUK_RET_UNSUPPORTED_ERROR;
2124 }
2125 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2126
2127 /*
2128 * Node.js Buffer.byteLength()
2129 */
2130
2131 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2132 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) {
2133 const char *str;
2134 duk_size_t len;
2135
2136 /* At the moment Buffer(<str>) will just use the string bytes as
2137 * is (ignoring encoding), so we return the string length here
2138 * unconditionally.
2139 */
2140
2141 str = duk_to_lstring(ctx, 0, &len);
2142 DUK_UNREF(str);
2143 duk_push_size_t(ctx, len);
2144 return 1;
2145 }
2146 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2147 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) {
2148 DUK_UNREF(ctx);
2149 return DUK_RET_UNSUPPORTED_ERROR;
2150 }
2151 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2152
2153 /*
2154 * Node.js Buffer.concat()
2155 */
2156
2157 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2158 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) {
2159 duk_hthread *thr;
2160 duk_hobject *h_arg;
2161 duk_int_t total_length = 0;
2162 duk_hbufferobject *h_bufobj;
2163 duk_hbufferobject *h_bufres;
2164 duk_hbuffer *h_val;
2165 duk_uint_t i, n;
2166 duk_uint8_t *p;
2167 duk_size_t space_left;
2168 duk_size_t copy_size;
2169
2170 thr = (duk_hthread *) ctx;
2171 DUK_UNREF(thr);
2172
2173 /* Node.js accepts only actual Arrays. */
2174 h_arg = duk_require_hobject(ctx, 0);
2175 if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg) != DUK_HOBJECT_CLASS_ARRAY) {
2176 return DUK_RET_TYPE_ERROR;
2177 }
2178
2179 /* Compute result length and validate argument buffers. */
2180 n = (duk_uint_t) duk_get_length(ctx, 0);
2181 for (i = 0; i < n; i++) {
2182 /* Neutered checks not necessary here: neutered buffers have
2183 * zero 'length' so we'll effectively skip them.
2184 */
2185 DUK_ASSERT_TOP(ctx, 2); /* [ array totalLength ] */
2186 duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); /* -> [ array totalLength buf ] */
2187 h_bufobj = duk__require_bufobj_value(ctx, 2);
2188 DUK_ASSERT(h_bufobj != NULL);
2189 total_length += h_bufobj->length;
2190 duk_pop(ctx);
2191 }
2192 if (n == 1) {
2193 /* For the case n==1 Node.js doesn't seem to type check
2194 * the sole member but we do it before returning it.
2195 * For this case only the original buffer object is
2196 * returned (not a copy).
2197 */
2198 duk_get_prop_index(ctx, 0, 0);
2199 return 1;
2200 }
2201
2202 /* User totalLength overrides a computed length, but we'll check
2203 * every copy in the copy loop. Note that duk_to_uint() can
2204 * technically have arbitrary side effects so we need to recheck
2205 * the buffers in the copy loop.
2206 */
2207 if (!duk_is_undefined(ctx, 1) && n > 0) {
2208 /* For n == 0, Node.js ignores totalLength argument and
2209 * returns a zero length buffer.
2210 */
2211 total_length = duk_to_int(ctx, 1);
2212 }
2213 if (total_length < 0) {
2214 return DUK_RET_RANGE_ERROR;
2215 }
2216
2217 h_bufres = duk_push_bufferobject_raw(ctx,
2218 DUK_HOBJECT_FLAG_EXTENSIBLE |
2219 DUK_HOBJECT_FLAG_BUFFEROBJECT |
2220 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
2221 DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
2222 DUK_ASSERT(h_bufres != NULL);
2223
2224 p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, total_length);
2225 DUK_ASSERT(p != NULL);
2226 space_left = total_length;
2227
2228 for (i = 0; i < n; i++) {
2229 DUK_ASSERT_TOP(ctx, 4); /* [ array totalLength bufres buf ] */
2230
2231 duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
2232 h_bufobj = duk__require_bufobj_value(ctx, 4);
2233 DUK_ASSERT(h_bufobj != NULL);
2234
2235 copy_size = h_bufobj->length;
2236 if (copy_size > space_left) {
2237 copy_size = space_left;
2238 }
2239
2240 if (h_bufobj->buf != NULL &&
2241 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) {
2242 DUK_MEMCPY((void *) p,
2243 (const void *) DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj),
2244 copy_size);
2245 } else {
2246 /* Just skip, leaving zeroes in the result. */
2247 ;
2248 }
2249 p += copy_size;
2250 space_left -= copy_size;
2251
2252 duk_pop(ctx);
2253 }
2254
2255 h_val = duk_get_hbuffer(ctx, -1);
2256 DUK_ASSERT(h_val != NULL);
2257
2258 duk__set_bufobj_buffer(ctx, h_bufres, h_val);
2259 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufres);
2260
2261 duk_pop(ctx); /* pop plain buffer, now reachable through h_bufres */
2262
2263 return 1; /* return h_bufres */
2264 }
2265 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2266 DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) {
2267 DUK_UNREF(ctx);
2268 return DUK_RET_UNSUPPORTED_ERROR;
2269 }
2270 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2271
2272 /*
2273 * Shared readfield and writefield methods
2274 *
2275 * The readfield/writefield methods need support for endianness and field
2276 * types. All offsets are byte based so no offset shifting is needed.
2277 */
2278
2279 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2280 /* Format of magic, bits:
2281 * 0...1: field type; 0=uint8, 1=uint16, 2=uint32, 3=float, 4=double, 5=unused, 6=unused, 7=unused
2282 * 3: endianness: 0=little, 1=big
2283 * 4: signed: 1=yes, 0=no
2284 * 5: typedarray: 1=yes, 0=no
2285 */
2286 #define DUK__FLD_8BIT 0
2287 #define DUK__FLD_16BIT 1
2288 #define DUK__FLD_32BIT 2
2289 #define DUK__FLD_FLOAT 3
2290 #define DUK__FLD_DOUBLE 4
2291 #define DUK__FLD_VARINT 5
2292 #define DUK__FLD_BIGENDIAN (1 << 3)
2293 #define DUK__FLD_SIGNED (1 << 4)
2294 #define DUK__FLD_TYPEDARRAY (1 << 5)
2295
2296 /* XXX: split into separate functions for each field type? */
2297 DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) {
2298 duk_hthread *thr;
2299 duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx);
2300 duk_small_int_t magic_ftype;
2301 duk_small_int_t magic_bigendian;
2302 duk_small_int_t magic_signed;
2303 duk_small_int_t magic_typedarray;
2304 duk_small_int_t endswap;
2305 duk_hbufferobject *h_this;
2306 duk_bool_t no_assert;
2307 duk_int_t offset_signed;
2308 duk_uint_t offset;
2309 duk_uint_t buffer_length;
2310 duk_uint_t check_length;
2311 duk_uint8_t *buf;
2312 duk_double_union du;
2313
2314 thr = (duk_hthread *) ctx;
2315 DUK_UNREF(thr);
2316
2317 magic_ftype = magic & 0x0007;
2318 magic_bigendian = magic & 0x0008;
2319 magic_signed = magic & 0x0010;
2320 magic_typedarray = magic & 0x0020;
2321
2322 h_this = duk__require_bufobj_this(ctx);
2323 DUK_ASSERT(h_this != NULL);
2324 buffer_length = h_this->length;
2325
2326 /* [ offset noAssert ], when ftype != DUK__FLD_VARINT */
2327 /* [ offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
2328 /* [ offset littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */
2329
2330 /* Handle TypedArray vs. Node.js Buffer arg differences */
2331 if (magic_typedarray) {
2332 no_assert = 0;
2333 #if defined(DUK_USE_INTEGER_LE)
2334 endswap = !duk_to_boolean(ctx, 1); /* 1=little endian */
2335 #else
2336 endswap = duk_to_boolean(ctx, 1); /* 1=little endian */
2337 #endif
2338 } else {
2339 no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1);
2340 #if defined(DUK_USE_INTEGER_LE)
2341 endswap = magic_bigendian;
2342 #else
2343 endswap = !magic_bigendian;
2344 #endif
2345 }
2346
2347 /* Offset is coerced first to signed integer range and then to unsigned.
2348 * This ensures we can add a small byte length (1-8) to the offset in
2349 * bound checks and not wrap.
2350 */
2351 offset_signed = duk_to_int(ctx, 0);
2352 offset = (duk_uint_t) offset_signed;
2353 if (offset_signed < 0) {
2354 goto fail_bounds;
2355 }
2356
2357 DUK_DDD(DUK_DDDPRINT("readfield, buffer_length=%ld, offset=%ld, no_assert=%d, "
2358 "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
2359 "endswap=%d",
2360 (long) buffer_length, (long) offset, (int) no_assert,
2361 (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3),
2362 (int) (magic_signed >> 4), (int) endswap));
2363
2364 /* Update 'buffer_length' to be the effective, safe limit which
2365 * takes into account the underlying buffer. This value will be
2366 * potentially invalidated by any side effect.
2367 */
2368 check_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, buffer_length);
2369 DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
2370 (long) buffer_length, (long) check_length));
2371
2372 if (h_this->buf) {
2373 buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this);
2374 } else {
2375 /* neutered, value doesn't matter because check_length is 0. */
2376 DUK_ASSERT(check_length == 0);
2377 buf = NULL;
2378 }
2379
2380 switch (magic_ftype) {
2381 case DUK__FLD_8BIT: {
2382 duk_uint8_t tmp;
2383 if (offset + 1U > check_length) {
2384 goto fail_bounds;
2385 }
2386 tmp = buf[offset];
2387 if (magic_signed) {
2388 duk_push_int(ctx, (duk_int_t) ((duk_int8_t) tmp));
2389 } else {
2390 duk_push_uint(ctx, (duk_uint_t) tmp);
2391 }
2392 break;
2393 }
2394 case DUK__FLD_16BIT: {
2395 duk_uint16_t tmp;
2396 if (offset + 2U > check_length) {
2397 goto fail_bounds;
2398 }
2399 DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 2);
2400 tmp = du.us[0];
2401 if (endswap) {
2402 tmp = DUK_BSWAP16(tmp);
2403 }
2404 if (magic_signed) {
2405 duk_push_int(ctx, (duk_int_t) ((duk_int16_t) tmp));
2406 } else {
2407 duk_push_uint(ctx, (duk_uint_t) tmp);
2408 }
2409 break;
2410 }
2411 case DUK__FLD_32BIT: {
2412 duk_uint32_t tmp;
2413 if (offset + 4U > check_length) {
2414 goto fail_bounds;
2415 }
2416 DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 4);
2417 tmp = du.ui[0];
2418 if (endswap) {
2419 tmp = DUK_BSWAP32(tmp);
2420 }
2421 if (magic_signed) {
2422 duk_push_int(ctx, (duk_int_t) ((duk_int32_t) tmp));
2423 } else {
2424 duk_push_uint(ctx, (duk_uint_t) tmp);
2425 }
2426 break;
2427 }
2428 case DUK__FLD_FLOAT: {
2429 duk_uint32_t tmp;
2430 if (offset + 4U > check_length) {
2431 goto fail_bounds;
2432 }
2433 DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 4);
2434 if (endswap) {
2435 tmp = du.ui[0];
2436 tmp = DUK_BSWAP32(tmp);
2437 du.ui[0] = tmp;
2438 }
2439 duk_push_number(ctx, (duk_double_t) du.f[0]);
2440 break;
2441 }
2442 case DUK__FLD_DOUBLE: {
2443 if (offset + 8U > check_length) {
2444 goto fail_bounds;
2445 }
2446 DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 8);
2447 if (endswap) {
2448 DUK_DBLUNION_BSWAP64(&du);
2449 }
2450 duk_push_number(ctx, (duk_double_t) du.d);
2451 break;
2452 }
2453 case DUK__FLD_VARINT: {
2454 /* Node.js Buffer variable width integer field. We don't really
2455 * care about speed here, so aim for shortest algorithm.
2456 */
2457 duk_int_t field_bytelen;
2458 duk_int_t i, i_step, i_end;
2459 #if defined(DUK_USE_64BIT_OPS)
2460 duk_int64_t tmp;
2461 duk_small_uint_t shift_tmp;
2462 #else
2463 duk_double_t tmp;
2464 duk_small_int_t highbyte;
2465 #endif
2466 const duk_uint8_t *p;
2467
2468 field_bytelen = duk_get_int(ctx, 1); /* avoid side effects! */
2469 if (field_bytelen < 1 || field_bytelen > 6) {
2470 goto fail_field_length;
2471 }
2472 if (offset + (duk_uint_t) field_bytelen > check_length) {
2473 goto fail_bounds;
2474 }
2475 p = (const duk_uint8_t *) (buf + offset);
2476
2477 /* Slow gathering of value using either 64-bit arithmetic
2478 * or IEEE doubles if 64-bit types not available. Handling
2479 * of negative numbers is a bit non-obvious in both cases.
2480 */
2481
2482 if (magic_bigendian) {
2483 /* Gather in big endian */
2484 i = 0;
2485 i_step = 1;
2486 i_end = field_bytelen; /* one i_step over */
2487 } else {
2488 /* Gather in little endian */
2489 i = field_bytelen - 1;
2490 i_step = -1;
2491 i_end = -1; /* one i_step over */
2492 }
2493
2494 #if defined(DUK_USE_64BIT_OPS)
2495 tmp = 0;
2496 do {
2497 DUK_ASSERT(i >= 0 && i < field_bytelen);
2498 tmp = (tmp << 8) + (duk_int64_t) p[i];
2499 i += i_step;
2500 } while (i != i_end);
2501
2502 if (magic_signed) {
2503 /* Shift to sign extend. */
2504 shift_tmp = 64 - (field_bytelen * 8);
2505 tmp = (tmp << shift_tmp) >> shift_tmp;
2506 }
2507
2508 duk_push_i64(ctx, tmp);
2509 #else
2510 highbyte = p[i];
2511 if (magic_signed && (highbyte & 0x80) != 0) {
2512 /* 0xff => 255 - 256 = -1; 0x80 => 128 - 256 = -128 */
2513 tmp = (duk_double_t) (highbyte - 256);
2514 } else {
2515 tmp = (duk_double_t) highbyte;
2516 }
2517 for (;;) {
2518 i += i_step;
2519 if (i == i_end) {
2520 break;
2521 }
2522 DUK_ASSERT(i >= 0 && i < field_bytelen);
2523 tmp = (tmp * 256.0) + (duk_double_t) p[i];
2524 }
2525
2526 duk_push_number(ctx, tmp);
2527 #endif
2528 break;
2529 }
2530 default: { /* should never happen but default here */
2531 goto fail_bounds;
2532 }
2533 }
2534
2535 return 1;
2536
2537 fail_field_length:
2538 fail_bounds:
2539 if (no_assert) {
2540 /* Node.js return value for noAssert out-of-bounds reads is
2541 * usually (but not always) NaN. Return NaN consistently.
2542 */
2543 duk_push_nan(ctx);
2544 return 1;
2545 }
2546
2547 return DUK_RET_RANGE_ERROR;
2548 }
2549 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2550 DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) {
2551 DUK_UNREF(ctx);
2552 return DUK_RET_UNSUPPORTED_ERROR;
2553 }
2554 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2555
2556 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2557 /* XXX: split into separate functions for each field type? */
2558 DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) {
2559 duk_hthread *thr;
2560 duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx);
2561 duk_small_int_t magic_ftype;
2562 duk_small_int_t magic_bigendian;
2563 duk_small_int_t magic_signed;
2564 duk_small_int_t magic_typedarray;
2565 duk_small_int_t endswap;
2566 duk_hbufferobject *h_this;
2567 duk_bool_t no_assert;
2568 duk_int_t offset_signed;
2569 duk_uint_t offset;
2570 duk_uint_t buffer_length;
2571 duk_uint_t check_length;
2572 duk_uint8_t *buf;
2573 duk_double_union du;
2574 duk_int_t nbytes = 0;
2575
2576 thr = (duk_hthread *) ctx;
2577 DUK_UNREF(thr);
2578
2579 magic_ftype = magic & 0x0007;
2580 magic_bigendian = magic & 0x0008;
2581 magic_signed = magic & 0x0010;
2582 magic_typedarray = magic & 0x0020;
2583 DUK_UNREF(magic_signed);
2584
2585 h_this = duk__require_bufobj_this(ctx);
2586 DUK_ASSERT(h_this != NULL);
2587 buffer_length = h_this->length;
2588
2589 /* [ value offset noAssert ], when ftype != DUK__FLD_VARINT */
2590 /* [ value offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
2591 /* [ offset value littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */
2592
2593 /* Handle TypedArray vs. Node.js Buffer arg differences */
2594 if (magic_typedarray) {
2595 no_assert = 0;
2596 #if defined(DUK_USE_INTEGER_LE)
2597 endswap = !duk_to_boolean(ctx, 2); /* 1=little endian */
2598 #else
2599 endswap = duk_to_boolean(ctx, 2); /* 1=little endian */
2600 #endif
2601 duk_swap(ctx, 0, 1); /* offset/value order different from Node.js */
2602 } else {
2603 no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2);
2604 #if defined(DUK_USE_INTEGER_LE)
2605 endswap = magic_bigendian;
2606 #else
2607 endswap = !magic_bigendian;
2608 #endif
2609 }
2610
2611 /* Offset is coerced first to signed integer range and then to unsigned.
2612 * This ensures we can add a small byte length (1-8) to the offset in
2613 * bound checks and not wrap.
2614 */
2615 offset_signed = duk_to_int(ctx, 1);
2616 offset = (duk_uint_t) offset_signed;
2617
2618 /* We need 'nbytes' even for a failed offset; return value must be
2619 * (offset + nbytes) even when write fails due to invalid offset.
2620 */
2621 if (magic_ftype != DUK__FLD_VARINT) {
2622 DUK_ASSERT(magic_ftype >= 0 && magic_ftype < (duk_small_int_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t)));
2623 nbytes = duk__buffer_nbytes_from_fldtype[magic_ftype];
2624 } else {
2625 nbytes = duk_get_int(ctx, 2);
2626 if (nbytes < 1 || nbytes > 6) {
2627 goto fail_field_length;
2628 }
2629 }
2630 DUK_ASSERT(nbytes >= 1 && nbytes <= 8);
2631
2632 /* Now we can check offset validity. */
2633 if (offset_signed < 0) {
2634 goto fail_bounds;
2635 }
2636
2637 DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, "
2638 "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
2639 "endswap=%d",
2640 duk_get_tval(ctx, 0), (long) buffer_length, (long) offset, (int) no_assert,
2641 (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3),
2642 (int) (magic_signed >> 4), (int) endswap));
2643
2644 /* Coerce value to a number before computing check_length, so that
2645 * the field type specific coercion below can't have side effects
2646 * that would invalidate check_length.
2647 */
2648 duk_to_number(ctx, 0);
2649
2650 /* Update 'buffer_length' to be the effective, safe limit which
2651 * takes into account the underlying buffer. This value will be
2652 * potentially invalidated by any side effect.
2653 */
2654 check_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, buffer_length);
2655 DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
2656 (long) buffer_length, (long) check_length));
2657
2658 if (h_this->buf) {
2659 buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this);
2660 } else {
2661 /* neutered, value doesn't matter because check_length is 0. */
2662 DUK_ASSERT(check_length == 0);
2663 buf = NULL;
2664 }
2665
2666 switch (magic_ftype) {
2667 case DUK__FLD_8BIT: {
2668 if (offset + 1U > check_length) {
2669 goto fail_bounds;
2670 }
2671 /* sign doesn't matter when writing */
2672 buf[offset] = (duk_uint8_t) duk_to_uint32(ctx, 0);
2673 break;
2674 }
2675 case DUK__FLD_16BIT: {
2676 duk_uint16_t tmp;
2677 if (offset + 2U > check_length) {
2678 goto fail_bounds;
2679 }
2680 tmp = (duk_uint16_t) duk_to_uint32(ctx, 0);
2681 if (endswap) {
2682 tmp = DUK_BSWAP16(tmp);
2683 }
2684 du.us[0] = tmp;
2685 /* sign doesn't matter when writing */
2686 DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 2);
2687 break;
2688 }
2689 case DUK__FLD_32BIT: {
2690 duk_uint32_t tmp;
2691 if (offset + 4U > check_length) {
2692 goto fail_bounds;
2693 }
2694 tmp = (duk_uint32_t) duk_to_uint32(ctx, 0);
2695 if (endswap) {
2696 tmp = DUK_BSWAP32(tmp);
2697 }
2698 du.ui[0] = tmp;
2699 /* sign doesn't matter when writing */
2700 DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 4);
2701 break;
2702 }
2703 case DUK__FLD_FLOAT: {
2704 duk_uint32_t tmp;
2705 if (offset + 4U > check_length) {
2706 goto fail_bounds;
2707 }
2708 du.f[0] = (duk_float_t) duk_to_number(ctx, 0);
2709 if (endswap) {
2710 tmp = du.ui[0];
2711 tmp = DUK_BSWAP32(tmp);
2712 du.ui[0] = tmp;
2713 }
2714 /* sign doesn't matter when writing */
2715 DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 4);
2716 break;
2717 }
2718 case DUK__FLD_DOUBLE: {
2719 if (offset + 8U > check_length) {
2720 goto fail_bounds;
2721 }
2722 du.d = (duk_double_t) duk_to_number(ctx, 0);
2723 if (endswap) {
2724 DUK_DBLUNION_BSWAP64(&du);
2725 }
2726 /* sign doesn't matter when writing */
2727 DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 8);
2728 break;
2729 }
2730 case DUK__FLD_VARINT: {
2731 /* Node.js Buffer variable width integer field. We don't really
2732 * care about speed here, so aim for shortest algorithm.
2733 */
2734 duk_int_t field_bytelen;
2735 duk_int_t i, i_step, i_end;
2736 #if defined(DUK_USE_64BIT_OPS)
2737 duk_int64_t tmp;
2738 #else
2739 duk_double_t tmp;
2740 #endif
2741 duk_uint8_t *p;
2742
2743 field_bytelen = (duk_int_t) nbytes;
2744 if (offset + (duk_uint_t) field_bytelen > check_length) {
2745 goto fail_bounds;
2746 }
2747
2748 /* Slow writing of value using either 64-bit arithmetic
2749 * or IEEE doubles if 64-bit types not available. There's
2750 * no special sign handling when writing varints.
2751 */
2752
2753 if (magic_bigendian) {
2754 /* Write in big endian */
2755 i = field_bytelen; /* one i_step added at top of loop */
2756 i_step = -1;
2757 i_end = 0;
2758 } else {
2759 /* Write in little endian */
2760 i = -1; /* one i_step added at top of loop */
2761 i_step = 1;
2762 i_end = field_bytelen - 1;
2763 }
2764
2765 /* XXX: The duk_to_number() cast followed by integer coercion
2766 * is platform specific so NaN, +/- Infinity, and out-of-bounds
2767 * values result in platform specific output now.
2768 * See: test-bi-nodejs-buffer-proto-varint-special.js
2769 */
2770
2771 #if defined(DUK_USE_64BIT_OPS)
2772 tmp = (duk_int64_t) duk_to_number(ctx, 0);
2773 p = (duk_uint8_t *) (buf + offset);
2774 do {
2775 i += i_step;
2776 DUK_ASSERT(i >= 0 && i < field_bytelen);
2777 p[i] = (duk_uint8_t) (tmp & 0xff);
2778 tmp = tmp >> 8; /* unnecessary shift for last byte */
2779 } while (i != i_end);
2780 #else
2781 tmp = duk_to_number(ctx, 0);
2782 p = (duk_uint8_t *) (buf + offset);
2783 do {
2784 i += i_step;
2785 tmp = DUK_FLOOR(tmp);
2786 DUK_ASSERT(i >= 0 && i < field_bytelen);
2787 p[i] = (duk_uint8_t) (DUK_FMOD(tmp, 256.0));
2788 tmp = tmp / 256.0; /* unnecessary div for last byte */
2789 } while (i != i_end);
2790 #endif
2791 break;
2792 }
2793 default: { /* should never happen but default here */
2794 goto fail_bounds;
2795 }
2796 }
2797
2798 /* Node.js Buffer: return offset + #bytes written (i.e. next
2799 * write offset).
2800 */
2801 if (magic_typedarray) {
2802 /* For TypedArrays 'undefined' return value is specified
2803 * by ES6 (matches V8).
2804 */
2805 return 0;
2806 }
2807 duk_push_uint(ctx, offset + nbytes);
2808 return 1;
2809
2810 fail_field_length:
2811 fail_bounds:
2812 if (no_assert) {
2813 /* Node.js return value for failed writes is offset + #bytes
2814 * that would have been written.
2815 */
2816 /* XXX: for negative input offsets, 'offset' will be a large
2817 * positive value so the result here is confusing.
2818 */
2819 if (magic_typedarray) {
2820 return 0;
2821 }
2822 duk_push_uint(ctx, offset + nbytes);
2823 return 1;
2824 }
2825 return DUK_RET_RANGE_ERROR;
2826 }
2827 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2828 DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) {
2829 DUK_UNREF(ctx);
2830 return DUK_RET_UNSUPPORTED_ERROR;
2831 }
2832 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2833
2834 #undef DUK__FLD_8BIT
2835 #undef DUK__FLD_16BIT
2836 #undef DUK__FLD_32BIT
2837 #undef DUK__FLD_FLOAT
2838 #undef DUK__FLD_DOUBLE
2839 #undef DUK__FLD_VARINT
2840 #undef DUK__FLD_BIGENDIAN
2841 #undef DUK__FLD_SIGNED
2842 #undef DUK__FLD_TYPEDARRAY