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