2 * Duktape.Buffer, Node.js Buffer, and Khronos/ES6 TypedArray built-ins
5 #include "duk_internal.h"
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.
15 static const duk_uint8_t duk__buffer_class_from_elemtype
[9] = {
16 DUK_HOBJECT_CLASS_UINT8ARRAY
,
17 DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY
,
18 DUK_HOBJECT_CLASS_INT8ARRAY
,
19 DUK_HOBJECT_CLASS_UINT16ARRAY
,
20 DUK_HOBJECT_CLASS_INT16ARRAY
,
21 DUK_HOBJECT_CLASS_UINT32ARRAY
,
22 DUK_HOBJECT_CLASS_INT32ARRAY
,
23 DUK_HOBJECT_CLASS_FLOAT32ARRAY
,
24 DUK_HOBJECT_CLASS_FLOAT64ARRAY
26 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
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.
32 static const duk_uint8_t duk__buffer_proto_from_elemtype
[9] = {
33 DUK_BIDX_UINT8ARRAY_PROTOTYPE
,
34 DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE
,
35 DUK_BIDX_INT8ARRAY_PROTOTYPE
,
36 DUK_BIDX_UINT16ARRAY_PROTOTYPE
,
37 DUK_BIDX_INT16ARRAY_PROTOTYPE
,
38 DUK_BIDX_UINT32ARRAY_PROTOTYPE
,
39 DUK_BIDX_INT32ARRAY_PROTOTYPE
,
40 DUK_BIDX_FLOAT32ARRAY_PROTOTYPE
,
41 DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
43 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
45 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
46 /* Map DUK__FLX_xxx to byte size.
48 static const duk_uint8_t duk__buffer_nbytes_from_fldtype
[6] = {
49 1, /* DUK__FLD_8BIT */
50 2, /* DUK__FLD_16BIT */
51 4, /* DUK__FLD_32BIT */
52 4, /* DUK__FLD_FLOAT */
53 8, /* DUK__FLD_DOUBLE */
54 0 /* DUK__FLD_VARINT; not relevant here */
56 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
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.
65 static duk_uint16_t duk__buffer_elemtype_copy_compatible
[9] = {
66 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8 */
67 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8
) |
68 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
) |
69 (1U << DUK_HBUFFEROBJECT_ELEM_INT8
),
71 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
72 * Note: INT8 is -not- copy compatible, e.g. -1 would coerce to 0x00.
74 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8
) |
75 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
),
77 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT8 */
78 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8
) |
79 (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
) |
80 (1U << DUK_HBUFFEROBJECT_ELEM_INT8
),
82 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT16 */
83 (1U << DUK_HBUFFEROBJECT_ELEM_UINT16
) |
84 (1U << DUK_HBUFFEROBJECT_ELEM_INT16
),
86 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT16 */
87 (1U << DUK_HBUFFEROBJECT_ELEM_UINT16
) |
88 (1U << DUK_HBUFFEROBJECT_ELEM_INT16
),
90 /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT32 */
91 (1U << DUK_HBUFFEROBJECT_ELEM_UINT32
) |
92 (1U << DUK_HBUFFEROBJECT_ELEM_INT32
),
94 /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT32 */
95 (1U << DUK_HBUFFEROBJECT_ELEM_UINT32
) |
96 (1U << DUK_HBUFFEROBJECT_ELEM_INT32
),
98 /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT32 */
99 (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT32
),
101 /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT64 */
102 (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT64
)
104 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
106 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
108 DUK_LOCAL duk_hbufferobject
*duk__getrequire_bufobj_this(duk_context
*ctx
, duk_bool_t throw_flag
) {
111 duk_hbufferobject
*h_this
;
113 DUK_ASSERT(ctx
!= NULL
);
114 thr
= (duk_hthread
*) ctx
;
116 tv
= duk_get_borrowed_this_tval(ctx
);
117 DUK_ASSERT(tv
!= NULL
);
118 if (DUK_TVAL_IS_OBJECT(tv
)) {
119 h_this
= (duk_hbufferobject
*) DUK_TVAL_GET_OBJECT(tv
);
120 DUK_ASSERT(h_this
!= NULL
);
121 if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject
*) h_this
)) {
122 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this
);
128 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_BUFFER
);
132 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
134 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
135 /* Check that 'this' is a duk_hbufferobject and return a pointer to it. */
136 DUK_LOCAL duk_hbufferobject
*duk__get_bufobj_this(duk_context
*ctx
) {
137 return duk__getrequire_bufobj_this(ctx
, 0);
139 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
141 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
142 /* Check that 'this' is a duk_hbufferobject and return a pointer to it
145 DUK_LOCAL duk_hbufferobject
*duk__require_bufobj_this(duk_context
*ctx
) {
146 return duk__getrequire_bufobj_this(ctx
, 1);
148 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
150 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
151 /* Check that value is a duk_hbufferobject and return a pointer to it. */
152 DUK_LOCAL duk_hbufferobject
*duk__require_bufobj_value(duk_context
*ctx
, duk_idx_t index
) {
155 duk_hbufferobject
*h_obj
;
157 thr
= (duk_hthread
*) ctx
;
159 /* Don't accept relative indices now. */
160 DUK_ASSERT(index
>= 0);
162 tv
= duk_require_tval(ctx
, index
);
163 DUK_ASSERT(tv
!= NULL
);
164 if (DUK_TVAL_IS_OBJECT(tv
)) {
165 h_obj
= (duk_hbufferobject
*) DUK_TVAL_GET_OBJECT(tv
);
166 DUK_ASSERT(h_obj
!= NULL
);
167 if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject
*) h_obj
)) {
168 DUK_ASSERT_HBUFFEROBJECT_VALID(h_obj
);
173 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_BUFFER
);
174 return NULL
; /* not reachable */
176 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
178 DUK_LOCAL
void duk__set_bufobj_buffer(duk_context
*ctx
, duk_hbufferobject
*h_bufobj
, duk_hbuffer
*h_val
) {
181 thr
= (duk_hthread
*) ctx
;
184 DUK_ASSERT(ctx
!= NULL
);
185 DUK_ASSERT(h_bufobj
!= NULL
);
186 DUK_ASSERT(h_bufobj
->buf
== NULL
); /* no need to decref */
187 DUK_ASSERT(h_val
!= NULL
);
188 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
190 h_bufobj
->buf
= h_val
;
191 DUK_HBUFFER_INCREF(thr
, h_val
);
192 h_bufobj
->length
= (duk_uint_t
) DUK_HBUFFER_GET_SIZE(h_val
);
193 DUK_ASSERT(h_bufobj
->shift
== 0);
194 DUK_ASSERT(h_bufobj
->elem_type
== DUK_HBUFFEROBJECT_ELEM_UINT8
);
196 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
199 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
200 DUK_LOCAL duk_hbufferobject
*duk__push_arraybuffer_with_length(duk_context
*ctx
, duk_uint_t len
) {
202 duk_hbufferobject
*h_bufobj
;
204 (void) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
205 h_val
= (duk_hbuffer
*) duk_get_hbuffer(ctx
, -1);
206 DUK_ASSERT(h_val
!= NULL
);
208 h_bufobj
= duk_push_bufferobject_raw(ctx
,
209 DUK_HOBJECT_FLAG_EXTENSIBLE
|
210 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
211 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
),
212 DUK_BIDX_ARRAYBUFFER_PROTOTYPE
);
213 DUK_ASSERT(h_bufobj
!= NULL
);
215 duk__set_bufobj_buffer(ctx
, h_bufobj
, h_val
);
216 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
220 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
222 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
223 /* Shared offset/length coercion helper. */
224 DUK_LOCAL
void duk__resolve_offset_opt_length(duk_context
*ctx
,
225 duk_hbufferobject
*h_bufarg
,
226 duk_idx_t idx_offset
,
227 duk_idx_t idx_length
,
228 duk_uint_t
*out_offset
,
229 duk_uint_t
*out_length
,
230 duk_bool_t throw_flag
) {
232 duk_int_t offset_signed
;
233 duk_int_t length_signed
;
237 thr
= (duk_hthread
*) ctx
;
240 offset_signed
= duk_to_int(ctx
, idx_offset
);
241 if (offset_signed
< 0) {
244 offset
= (duk_uint_t
) offset_signed
;
245 if (offset
> h_bufarg
->length
) {
248 DUK_ASSERT_DISABLE(offset
>= 0); /* unsigned */
249 DUK_ASSERT(offset
<= h_bufarg
->length
);
251 if (duk_is_undefined(ctx
, idx_length
)) {
252 DUK_ASSERT(h_bufarg
->length
>= offset
);
253 length
= h_bufarg
->length
- offset
; /* >= 0 */
255 length_signed
= duk_to_int(ctx
, idx_length
);
256 if (length_signed
< 0) {
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.
268 length
= h_bufarg
->length
- offset
;
272 DUK_ASSERT_DISABLE(length
>= 0); /* unsigned */
273 DUK_ASSERT(offset
+ length
<= h_bufarg
->length
);
275 *out_offset
= offset
;
276 *out_length
= length
;
280 duk_error(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
282 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
284 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
285 /* Shared lenient buffer length clamping helper. No negative indices, no
286 * element/byte shifting.
288 DUK_LOCAL
void duk__clamp_startend_nonegidx_noshift(duk_context
*ctx
,
289 duk_hbufferobject
*h_bufobj
,
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
;
298 DUK_ASSERT(out_start_offset
!= NULL
);
299 DUK_ASSERT(out_end_offset
!= NULL
);
301 buffer_length
= (duk_int_t
) h_bufobj
->length
;
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
;
308 end_offset
= duk_to_int_clamped(ctx
, idx_end
, start_offset
, buffer_length
);
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
);
317 *out_start_offset
= start_offset
;
318 *out_end_offset
= end_offset
;
320 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
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().
330 DUK_LOCAL
void duk__clamp_startend_negidx_shifted(duk_context
*ctx
,
331 duk_hbufferobject
*h_bufobj
,
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
;
340 DUK_ASSERT(out_start_offset
!= NULL
);
341 DUK_ASSERT(out_end_offset
!= NULL
);
343 buffer_length
= (duk_int_t
) h_bufobj
->length
;
344 buffer_length
>>= h_bufobj
->shift
; /* as elements */
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.
351 start_offset
= duk_to_int(ctx
, idx_start
);
352 if (start_offset
< 0) {
353 start_offset
= buffer_length
+ start_offset
;
355 if (duk_is_undefined(ctx
, idx_end
)) {
356 end_offset
= buffer_length
;
358 end_offset
= duk_to_int(ctx
, idx_end
);
359 if (end_offset
< 0) {
360 end_offset
= buffer_length
+ end_offset
;
363 /* Note: start_offset/end_offset can still be < 0 here. */
365 if (start_offset
< 0) {
367 } else if (start_offset
> buffer_length
) {
368 start_offset
= buffer_length
;
370 if (end_offset
< start_offset
) {
371 end_offset
= start_offset
;
372 } else if (end_offset
> buffer_length
) {
373 end_offset
= buffer_length
;
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
);
381 /* Convert indices to byte offsets. */
382 start_offset
<<= h_bufobj
->shift
;
383 end_offset
<<= h_bufobj
->shift
;
385 *out_start_offset
= start_offset
;
386 *out_end_offset
= end_offset
;
388 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
391 * Indexed read/write helpers (also used from outside this file)
394 DUK_INTERNAL
void duk_hbufferobject_push_validated_read(duk_context
*ctx
, duk_hbufferobject
*h_bufobj
, duk_uint8_t
*p
, duk_small_uint_t elem_size
) {
397 DUK_MEMCPY((void *) du
.uc
, (const void *) p
, elem_size
);
399 switch (h_bufobj
->elem_type
) {
400 case DUK_HBUFFEROBJECT_ELEM_UINT8
:
401 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
402 case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
:
404 duk_push_uint(ctx
, (duk_uint_t
) du
.uc
[0]);
406 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
407 /* These are not needed when only Duktape.Buffer is supported. */
408 case DUK_HBUFFEROBJECT_ELEM_INT8
:
409 duk_push_int(ctx
, (duk_int_t
) (duk_int8_t
) du
.uc
[0]);
411 case DUK_HBUFFEROBJECT_ELEM_UINT16
:
412 duk_push_uint(ctx
, (duk_uint_t
) du
.us
[0]);
414 case DUK_HBUFFEROBJECT_ELEM_INT16
:
415 duk_push_int(ctx
, (duk_int_t
) (duk_int16_t
) du
.us
[0]);
417 case DUK_HBUFFEROBJECT_ELEM_UINT32
:
418 duk_push_uint(ctx
, (duk_uint_t
) du
.ui
[0]);
420 case DUK_HBUFFEROBJECT_ELEM_INT32
:
421 duk_push_int(ctx
, (duk_int_t
) (duk_int32_t
) du
.ui
[0]);
423 case DUK_HBUFFEROBJECT_ELEM_FLOAT32
:
424 duk_push_number(ctx
, (duk_double_t
) du
.f
[0]);
426 case DUK_HBUFFEROBJECT_ELEM_FLOAT64
:
427 duk_push_number(ctx
, (duk_double_t
) du
.d
);
429 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
435 DUK_INTERNAL
void duk_hbufferobject_validated_write(duk_context
*ctx
, duk_hbufferobject
*h_bufobj
, duk_uint8_t
*p
, duk_small_uint_t elem_size
) {
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.
446 switch (h_bufobj
->elem_type
) {
447 case DUK_HBUFFEROBJECT_ELEM_UINT8
:
448 du
.uc
[0] = (duk_uint8_t
) duk_to_uint32(ctx
, -1);
450 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
451 /* These are not needed when only Duktape.Buffer is supported. */
452 case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
:
453 du
.uc
[0] = (duk_uint8_t
) duk_to_uint8clamped(ctx
, -1);
455 case DUK_HBUFFEROBJECT_ELEM_INT8
:
456 du
.uc
[0] = (duk_uint8_t
) duk_to_int32(ctx
, -1);
458 case DUK_HBUFFEROBJECT_ELEM_UINT16
:
459 du
.us
[0] = (duk_uint16_t
) duk_to_uint32(ctx
, -1);
461 case DUK_HBUFFEROBJECT_ELEM_INT16
:
462 du
.us
[0] = (duk_uint16_t
) duk_to_int32(ctx
, -1);
464 case DUK_HBUFFEROBJECT_ELEM_UINT32
:
465 du
.ui
[0] = (duk_uint32_t
) duk_to_uint32(ctx
, -1);
467 case DUK_HBUFFEROBJECT_ELEM_INT32
:
468 du
.ui
[0] = (duk_uint32_t
) duk_to_int32(ctx
, -1);
470 case DUK_HBUFFEROBJECT_ELEM_FLOAT32
:
471 du
.f
[0] = (duk_float_t
) duk_to_number(ctx
, -1);
473 case DUK_HBUFFEROBJECT_ELEM_FLOAT64
:
474 du
.d
= (duk_double_t
) duk_to_number(ctx
, -1);
476 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
481 DUK_MEMCPY((void *) p
, (const void *) du
.uc
, elem_size
);
485 * Duktape.Buffer: constructor
488 DUK_INTERNAL duk_ret_t
duk_bi_buffer_constructor(duk_context
*ctx
) {
491 duk_small_int_t buf_dynamic
;
492 duk_uint8_t
*buf_data
;
493 const duk_uint8_t
*src_data
;
495 thr
= (duk_hthread
*) ctx
;
499 * Constructor arguments are currently somewhat compatible with
500 * (keep it that way if possible):
502 * http://nodejs.org/api/buffer.html
504 * Note that the ToBuffer() coercion (duk_to_buffer()) does NOT match
505 * the constructor behavior.
508 buf_dynamic
= duk_get_boolean(ctx
, 1); /* default to false */
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
);
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
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
);
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.
539 * If called as a constructor, a new Duktape.Buffer object
540 * pointing to the same plain buffer is created below.
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
;
548 if (h_bufobj
->buf
== NULL
) {
549 return DUK_RET_TYPE_ERROR
;
551 duk_push_hbuffer(ctx
, h_bufobj
->buf
);
556 return DUK_RET_TYPE_ERROR
;
559 DUK_ASSERT(duk_is_buffer(ctx
, -1));
561 /* stack is unbalanced, but: [ <something> buf ] */
563 if (duk_is_constructor_call(ctx
)) {
564 duk_hbufferobject
*h_bufobj
;
567 h_val
= duk_get_hbuffer(ctx
, -1);
568 DUK_ASSERT(h_val
!= NULL
);
570 h_bufobj
= duk_push_bufferobject_raw(ctx
,
571 DUK_HOBJECT_FLAG_EXTENSIBLE
|
572 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
573 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER
),
574 DUK_BIDX_BUFFER_PROTOTYPE
);
575 DUK_ASSERT(h_bufobj
!= NULL
);
577 duk__set_bufobj_buffer(ctx
, h_bufobj
, h_val
);
579 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
581 /* Note: unbalanced stack on purpose */
587 * Node.js Buffer: constructor
590 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
591 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_constructor(duk_context
*ctx
) {
592 /* Internal class is Object: Object.prototype.toString.call(new Buffer(0))
593 * prints "[object Object]".
599 duk_hbufferobject
*h_bufobj
;
602 switch (duk_get_type(ctx
, 0)) {
603 case DUK_TYPE_BUFFER
: {
604 /* Custom behavior: plain buffer is used as internal buffer
605 * without making a copy (matches Duktape.Buffer).
607 duk_set_top(ctx
, 1); /* -> [ buffer ] */
610 case DUK_TYPE_NUMBER
: {
611 len
= duk_to_int_clamped(ctx
, 0, 0, DUK_INT_MAX
);
612 buf
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
615 case DUK_TYPE_OBJECT
: {
616 (void) duk_get_prop_string(ctx
, 0, "length");
617 len
= duk_to_int_clamped(ctx
, -1, 0, DUK_INT_MAX
);
619 buf
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
620 for (i
= 0; i
< len
; i
++) {
621 /* XXX: fast path for array arguments? */
622 duk_get_prop_index(ctx
, 0, (duk_uarridx_t
) i
);
623 buf
[i
] = (duk_uint8_t
) (duk_to_uint32(ctx
, -1) & 0xffU
);
628 case DUK_TYPE_STRING
: {
629 /* ignore encoding for now */
631 buf
= (duk_uint8_t
*) duk_to_buffer(ctx
, -1, &buf_size
);
635 return DUK_RET_TYPE_ERROR
;
638 DUK_ASSERT(duk_is_buffer(ctx
, -1));
639 h_buf
= duk_get_hbuffer(ctx
, -1);
640 DUK_ASSERT(h_buf
!= NULL
);
642 h_bufobj
= duk_push_bufferobject_raw(ctx
,
643 DUK_HOBJECT_FLAG_EXTENSIBLE
|
644 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
645 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER
),
646 DUK_BIDX_NODEJS_BUFFER_PROTOTYPE
);
647 DUK_ASSERT(h_bufobj
!= NULL
);
649 h_bufobj
->buf
= h_buf
;
650 DUK_HBUFFER_INCREF(thr
, h_buf
);
651 DUK_ASSERT(h_bufobj
->offset
== 0);
652 h_bufobj
->length
= (duk_int_t
) DUK_HBUFFER_GET_SIZE(h_buf
);
653 DUK_ASSERT(h_bufobj
->elem_type
== DUK_HBUFFEROBJECT_ELEM_UINT8
);
655 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
659 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
660 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_constructor(duk_context
*ctx
) {
662 return DUK_RET_UNSUPPORTED_ERROR
;
664 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
667 * ArrayBuffer, DataView, and TypedArray constructors
670 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
671 DUK_INTERNAL duk_ret_t
duk_bi_arraybuffer_constructor(duk_context
*ctx
) {
672 duk_hbufferobject
*h_bufobj
;
675 /* XXX: function flag to make this automatic? */
676 if (!duk_is_constructor_call(ctx
)) {
677 return DUK_RET_TYPE_ERROR
;
680 if (duk_is_buffer(ctx
, 0)) {
681 /* Custom behavior: plain buffer is used as internal buffer
682 * without making a copy (matches Duktape.Buffer).
685 h_val
= duk_get_hbuffer(ctx
, 0);
686 DUK_ASSERT(h_val
!= NULL
);
688 /* XXX: accept any duk_hbufferobject type as an input also? */
691 len
= duk_to_int(ctx
, 0);
695 (void) duk_push_fixed_buffer(ctx
, (duk_size_t
) len
);
696 h_val
= (duk_hbuffer
*) duk_get_hbuffer(ctx
, -1);
697 DUK_ASSERT(h_val
!= NULL
);
700 h_bufobj
= duk_push_bufferobject_raw(ctx
,
701 DUK_HOBJECT_FLAG_EXTENSIBLE
|
702 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
703 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
),
704 DUK_BIDX_ARRAYBUFFER_PROTOTYPE
);
705 DUK_ASSERT(h_bufobj
!= NULL
);
707 duk__set_bufobj_buffer(ctx
, h_bufobj
, h_val
);
708 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
713 return DUK_RET_RANGE_ERROR
;
715 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
716 DUK_INTERNAL duk_ret_t
duk_bi_arraybuffer_constructor(duk_context
*ctx
) {
718 return DUK_RET_UNSUPPORTED_ERROR
;
720 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
723 /* Format of magic, bits:
724 * 0...1: elem size shift (0-3)
725 * 2...5: elem type (DUK_HBUFFEROBJECT_ELEM_xxx)
728 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
729 DUK_INTERNAL duk_ret_t
duk_bi_typedarray_constructor(duk_context
*ctx
) {
733 duk_hbufferobject
*h_bufobj
= NULL
;
734 duk_hbufferobject
*h_bufarr
= NULL
;
735 duk_hbufferobject
*h_bufarg
= NULL
;
737 duk_small_uint_t magic
;
738 duk_small_uint_t shift
;
739 duk_small_uint_t elem_type
;
740 duk_small_uint_t elem_size
;
741 duk_small_uint_t class_num
;
742 duk_small_uint_t proto_bidx
;
743 duk_uint_t align_mask
;
744 duk_uint_t elem_length
;
745 duk_int_t elem_length_signed
;
746 duk_uint_t byte_length
;
747 duk_small_uint_t copy_mode
;
749 thr
= (duk_hthread
*) ctx
;
752 /* XXX: function flag to make this automatic? */
753 if (!duk_is_constructor_call(ctx
)) {
754 return DUK_RET_TYPE_ERROR
;
757 /* We could fit built-in index into magic but that'd make the magic
758 * number dependent on built-in numbering (genbuiltins.py doesn't
759 * handle that yet). So map both class and prototype from the
762 magic
= duk_get_current_magic(ctx
);
763 shift
= magic
& 0x03; /* bits 0...1: shift */
764 elem_type
= (magic
>> 2) & 0x0f; /* bits 2...5: type */
765 elem_size
= 1 << shift
;
766 align_mask
= elem_size
- 1;
767 DUK_ASSERT(elem_type
< sizeof(duk__buffer_proto_from_elemtype
) / sizeof(duk_uint8_t
));
768 proto_bidx
= duk__buffer_proto_from_elemtype
[elem_type
];
769 DUK_ASSERT(proto_bidx
< DUK_NUM_BUILTINS
);
770 DUK_ASSERT(elem_type
< sizeof(duk__buffer_class_from_elemtype
) / sizeof(duk_uint8_t
));
771 class_num
= duk__buffer_class_from_elemtype
[elem_type
];
773 DUK_DD(DUK_DDPRINT("typedarray constructor, magic=%d, shift=%d, elem_type=%d, "
774 "elem_size=%d, proto_bidx=%d, class_num=%d",
775 (int) magic
, (int) shift
, (int) elem_type
, (int) elem_size
,
776 (int) proto_bidx
, (int) class_num
));
778 /* Argument variants. When the argument is an ArrayBuffer a view to
779 * the same buffer is created; otherwise a new ArrayBuffer is always
783 tv
= duk_get_tval(ctx
, 0);
784 DUK_ASSERT(tv
!= NULL
); /* arg count */
785 if (DUK_TVAL_IS_OBJECT(tv
)) {
786 h_obj
= DUK_TVAL_GET_OBJECT(tv
);
787 DUK_ASSERT(h_obj
!= NULL
);
789 if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj
) == DUK_HOBJECT_CLASS_ARRAYBUFFER
) {
790 /* ArrayBuffer: unlike any other argument variant, create
791 * a view into the existing buffer.
794 duk_int_t byte_offset_signed
;
795 duk_uint_t byte_offset
;
797 h_bufarg
= (duk_hbufferobject
*) h_obj
;
799 byte_offset_signed
= duk_to_int(ctx
, 1);
800 if (byte_offset_signed
< 0) {
803 byte_offset
= (duk_uint_t
) byte_offset_signed
;
804 if (byte_offset
> h_bufarg
->length
||
805 (byte_offset
& align_mask
) != 0) {
806 /* Must be >= 0 and multiple of element size. */
809 if (duk_is_undefined(ctx
, 2)) {
810 DUK_ASSERT(h_bufarg
->length
>= byte_offset
);
811 byte_length
= h_bufarg
->length
- byte_offset
;
812 if ((byte_length
& align_mask
) != 0) {
813 /* Must be element size multiple from
814 * start offset to end of buffer.
818 elem_length
= (byte_length
>> shift
);
820 elem_length_signed
= duk_to_int(ctx
, 2);
821 if (elem_length_signed
< 0) {
824 elem_length
= (duk_uint_t
) elem_length_signed
;
825 byte_length
= elem_length
<< shift
;
826 if ((byte_length
>> shift
) != elem_length
) {
827 /* Byte length would overflow. */
828 /* XXX: easier check with less code? */
831 DUK_ASSERT(h_bufarg
->length
>= byte_offset
);
832 if (byte_length
> h_bufarg
->length
- byte_offset
) {
833 /* Not enough data. */
837 DUK_ASSERT_DISABLE(byte_offset
>= 0);
838 DUK_ASSERT(byte_offset
<= h_bufarg
->length
);
839 DUK_ASSERT_DISABLE(byte_length
>= 0);
840 DUK_ASSERT(byte_offset
+ byte_length
<= h_bufarg
->length
);
841 DUK_ASSERT((elem_length
<< shift
) == byte_length
);
843 h_bufobj
= duk_push_bufferobject_raw(ctx
,
844 DUK_HOBJECT_FLAG_EXTENSIBLE
|
845 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
846 DUK_HOBJECT_CLASS_AS_FLAGS(class_num
),
848 h_val
= h_bufarg
->buf
;
850 return DUK_RET_TYPE_ERROR
;
852 h_bufobj
->buf
= h_val
;
853 DUK_HBUFFER_INCREF(thr
, h_val
);
854 h_bufobj
->offset
= h_bufarg
->offset
+ byte_offset
;
855 h_bufobj
->length
= byte_length
;
856 h_bufobj
->shift
= shift
;
857 h_bufobj
->elem_type
= elem_type
;
858 h_bufobj
->is_view
= 1;
859 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
861 /* Set .buffer to the argument ArrayBuffer. */
863 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
864 duk_compact(ctx
, -1);
866 } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj
)) {
867 /* TypedArray (or other non-ArrayBuffer duk_hbufferobject).
868 * Conceptually same behavior as for an Array-like argument,
869 * with a few fast paths.
872 h_bufarg
= (duk_hbufferobject
*) h_obj
;
873 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg
);
874 elem_length_signed
= (duk_int_t
) (h_bufarg
->length
>> h_bufarg
->shift
);
875 if (h_bufarg
->buf
== NULL
) {
876 return DUK_RET_TYPE_ERROR
;
879 /* Select copy mode. Must take into account element
880 * compatibility and validity of the underlying source
884 DUK_DDD(DUK_DDDPRINT("selecting copy mode for bufobj arg, "
885 "src byte_length=%ld, src shift=%d, "
886 "src/dst elem_length=%ld; "
887 "dst shift=%d -> dst byte_length=%ld",
888 (long) h_bufarg
->length
, (int) h_bufarg
->shift
,
889 (long) elem_length_signed
, (int) shift
,
890 (long) (elem_length_signed
<< shift
)));
892 copy_mode
= 2; /* default is explicit index read/write copy */
893 DUK_ASSERT(elem_type
< sizeof(duk__buffer_elemtype_copy_compatible
) / sizeof(duk_uint16_t
));
894 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg
)) {
895 if ((duk__buffer_elemtype_copy_compatible
[elem_type
] & (1 << h_bufarg
->elem_type
)) != 0) {
896 DUK_DDD(DUK_DDDPRINT("source/target are copy compatible, memcpy"));
897 DUK_ASSERT(shift
== h_bufarg
->shift
); /* byte sizes will match */
900 DUK_DDD(DUK_DDDPRINT("source/target not copy compatible but valid, fast copy"));
905 /* Array or Array-like */
906 elem_length_signed
= (duk_int_t
) duk_get_length(ctx
, 0);
910 /* Non-object argument is simply int coerced, matches
911 * V8 behavior (except for "null", which we coerce to
912 * 0 but V8 TypeErrors).
914 elem_length_signed
= duk_to_int(ctx
, 0);
917 if (elem_length_signed
< 0) {
920 elem_length
= (duk_uint_t
) elem_length_signed
;
921 byte_length
= (duk_uint_t
) (elem_length
<< shift
);
922 if ((byte_length
>> shift
) != elem_length
) {
923 /* Byte length would overflow. */
924 /* XXX: easier check with less code? */
928 DUK_DDD(DUK_DDDPRINT("elem_length=%ld, byte_length=%ld",
929 (long) elem_length
, (long) byte_length
));
931 /* ArrayBuffer argument is handled specially above; the rest of the
932 * argument variants are handled by shared code below.
935 /* Push a new ArrayBuffer (becomes view .buffer) */
936 h_bufarr
= duk__push_arraybuffer_with_length(ctx
, byte_length
);
937 DUK_ASSERT(h_bufarr
!= NULL
);
938 h_val
= h_bufarr
->buf
;
939 DUK_ASSERT(h_val
!= NULL
);
941 /* Push the resulting view object and attach the ArrayBuffer. */
942 h_bufobj
= duk_push_bufferobject_raw(ctx
,
943 DUK_HOBJECT_FLAG_EXTENSIBLE
|
944 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
945 DUK_HOBJECT_CLASS_AS_FLAGS(class_num
),
948 h_bufobj
->buf
= h_val
;
949 DUK_HBUFFER_INCREF(thr
, h_val
);
950 DUK_ASSERT(h_bufobj
->offset
== 0);
951 h_bufobj
->length
= byte_length
;
952 h_bufobj
->shift
= shift
;
953 h_bufobj
->elem_type
= elem_type
;
954 h_bufobj
->is_view
= 1;
955 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
959 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
960 duk_compact(ctx
, -1);
962 /* Copy values, the copy method depends on the arguments.
964 * Copy mode decision may depend on the validity of the underlying
965 * buffer of the source argument; there must be no harmful side effects
966 * from there to here for copy_mode to still be valid.
968 DUK_DDD(DUK_DDDPRINT("copy mode: %d", (int) copy_mode
));
976 DUK_ASSERT(h_bufobj
!= NULL
);
977 DUK_ASSERT(h_bufobj
->buf
!= NULL
);
978 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj
));
979 DUK_ASSERT(h_bufarg
!= NULL
);
980 DUK_ASSERT(h_bufarg
->buf
!= NULL
);
981 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg
));
983 p_dst
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufobj
);
984 p_src
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufarg
);
986 DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld",
987 (void *) p_src
, (void *) p_dst
, (long) byte_length
));
989 DUK_MEMCPY((void *) p_dst
, (const void *) p_src
, (size_t) byte_length
);
993 /* Copy values through direct validated reads and writes. */
995 duk_small_uint_t src_elem_size
;
996 duk_small_uint_t dst_elem_size
;
998 duk_uint8_t
*p_src_end
;
1001 DUK_ASSERT(h_bufobj
!= NULL
);
1002 DUK_ASSERT(h_bufobj
->buf
!= NULL
);
1003 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj
));
1004 DUK_ASSERT(h_bufarg
!= NULL
);
1005 DUK_ASSERT(h_bufarg
->buf
!= NULL
);
1006 DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg
));
1008 src_elem_size
= 1 << h_bufarg
->shift
;
1009 dst_elem_size
= elem_size
;
1011 p_src
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufarg
);
1012 p_dst
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufobj
);
1013 p_src_end
= p_src
+ h_bufarg
->length
;
1015 DUK_DDD(DUK_DDDPRINT("using fast copy: p_src=%p, p_src_end=%p, p_dst=%p, "
1016 "src_elem_size=%d, dst_elem_size=%d",
1017 (void *) p_src
, (void *) p_src_end
, (void *) p_dst
,
1018 (int) src_elem_size
, (int) dst_elem_size
));
1020 while (p_src
!= p_src_end
) {
1021 DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
1022 "p_src=%p, p_src_end=%p, p_dst=%p",
1023 (void *) p_src
, (void *) p_src_end
, (void *) p_dst
));
1024 /* A validated read() is always a number, so it's write coercion
1025 * is always side effect free an won't invalidate pointers etc.
1027 duk_hbufferobject_push_validated_read(ctx
, h_bufarg
, p_src
, src_elem_size
);
1028 duk_hbufferobject_validated_write(ctx
, h_bufobj
, p_dst
, dst_elem_size
);
1030 p_src
+= src_elem_size
;
1031 p_dst
+= dst_elem_size
;
1036 /* Copy values by index reads and writes. Let virtual
1037 * property handling take care of coercion.
1041 DUK_DDD(DUK_DDDPRINT("using slow copy"));
1043 for (i
= 0; i
< elem_length
; i
++) {
1044 duk_get_prop_index(ctx
, 0, (duk_uarridx_t
) i
);
1045 duk_put_prop_index(ctx
, -2, (duk_uarridx_t
) i
);
1051 /* No copy, leave zero bytes in the buffer. There's no
1052 * ambiguity with Float32/Float64 because zero bytes also
1056 DUK_DDD(DUK_DDDPRINT("using no copy"));
1064 return DUK_RET_RANGE_ERROR
;
1066 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1067 DUK_INTERNAL duk_ret_t
duk_bi_typedarray_constructor(duk_context
*ctx
) {
1069 return DUK_RET_UNSUPPORTED_ERROR
;
1071 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1073 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1074 DUK_INTERNAL duk_ret_t
duk_bi_dataview_constructor(duk_context
*ctx
) {
1075 duk_hbufferobject
*h_bufarg
;
1076 duk_hbufferobject
*h_bufobj
;
1081 /* XXX: function flag to make this automatic? */
1082 if (!duk_is_constructor_call(ctx
)) {
1083 return DUK_RET_TYPE_ERROR
;
1086 h_bufarg
= duk__require_bufobj_value(ctx
, 0);
1087 DUK_ASSERT(h_bufarg
!= NULL
);
1089 duk__resolve_offset_opt_length(ctx
, h_bufarg
, 1, 2, &offset
, &length
, 1 /*throw_flag*/);
1090 DUK_ASSERT(offset
<= h_bufarg
->length
);
1091 DUK_ASSERT(offset
+ length
<= h_bufarg
->length
);
1093 h_bufobj
= duk_push_bufferobject_raw(ctx
,
1094 DUK_HOBJECT_FLAG_EXTENSIBLE
|
1095 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
1096 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW
),
1097 DUK_BIDX_DATAVIEW_PROTOTYPE
);
1099 h_val
= h_bufarg
->buf
;
1100 if (h_val
== NULL
) {
1101 return DUK_RET_TYPE_ERROR
;
1103 h_bufobj
->buf
= h_val
;
1104 DUK_HBUFFER_INCREF(thr
, h_val
);
1105 h_bufobj
->offset
= h_bufarg
->offset
+ offset
;
1106 h_bufobj
->length
= length
;
1107 DUK_ASSERT(h_bufobj
->shift
== 0);
1108 DUK_ASSERT(h_bufobj
->elem_type
== DUK_HBUFFEROBJECT_ELEM_UINT8
);
1109 h_bufobj
->is_view
= 1;
1111 /* The DataView .buffer property is ordinarily set to the argument
1112 * which is an ArrayBuffer. We accept any duk_hbufferobject as
1113 * an argument and .buffer will be set to the argument regardless
1114 * of what it is. This may be a bit confusing if the argument
1115 * is e.g. a DataView or another TypedArray view.
1117 * XXX: Copy .buffer property from a DataView/TypedArray argument?
1118 * Create a fresh ArrayBuffer for Duktape.Buffer and Node.js Buffer
1119 * arguments? See: test-bug-dataview-buffer-prop.js.
1123 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
1124 duk_compact(ctx
, -1);
1126 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
1129 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1130 DUK_INTERNAL duk_ret_t
duk_bi_dataview_constructor(duk_context
*ctx
) {
1132 return DUK_RET_UNSUPPORTED_ERROR
;
1134 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1137 * ArrayBuffer.isView()
1140 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1141 DUK_INTERNAL duk_ret_t
duk_bi_arraybuffer_isview(duk_context
*ctx
) {
1145 h_obj
= duk_get_hobject(ctx
, 0);
1146 if (h_obj
!= NULL
&& DUK_HOBJECT_IS_BUFFEROBJECT(h_obj
)) {
1147 ret
= ((duk_hbufferobject
*) h_obj
)->is_view
;
1149 duk_push_boolean(ctx
, ret
);
1152 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1153 DUK_INTERNAL duk_ret_t
duk_bi_arraybuffer_isview(duk_context
*ctx
) {
1155 return DUK_RET_UNSUPPORTED_ERROR
;
1157 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1160 * Node.js Buffer: toString([encoding], [start], [end])
1163 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1164 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_tostring(duk_context
*ctx
) {
1166 duk_hbufferobject
*h_this
;
1167 duk_int_t start_offset
, end_offset
;
1168 duk_uint8_t
*buf_slice
;
1169 duk_size_t slice_length
;
1171 thr
= (duk_hthread
*) ctx
;
1174 h_this
= duk__get_bufobj_this(ctx
);
1175 if (h_this
== NULL
) {
1176 /* XXX: happens e.g. when evaluating: String(Buffer.prototype). */
1177 duk_push_string(ctx
, "[object Object]");
1180 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this
);
1182 /* ignore encoding for now */
1184 duk__clamp_startend_nonegidx_noshift(ctx
, h_this
, 1 /*idx_start*/, 2 /*idx_end*/, &start_offset
, &end_offset
);
1186 slice_length
= (duk_size_t
) (end_offset
- start_offset
);
1187 buf_slice
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, slice_length
);
1188 DUK_ASSERT(buf_slice
!= NULL
);
1190 if (h_this
->buf
== NULL
) {
1194 if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this
, start_offset
+ slice_length
)) {
1195 DUK_MEMCPY((void *) buf_slice
,
1196 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + start_offset
),
1199 /* not covered, return all zeroes */
1203 duk_to_string(ctx
, -1);
1207 return DUK_RET_TYPE_ERROR
;
1209 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1210 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_tostring(duk_context
*ctx
) {
1212 return DUK_RET_UNSUPPORTED_ERROR
;
1214 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1217 * Duktape.Buffer: toString(), valueOf()
1220 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1221 DUK_INTERNAL duk_ret_t
duk_bi_buffer_prototype_tostring_shared(duk_context
*ctx
) {
1224 duk_small_int_t to_string
= duk_get_current_magic(ctx
);
1226 thr
= (duk_hthread
*) ctx
;
1229 tv
= duk_get_borrowed_this_tval(ctx
);
1230 DUK_ASSERT(tv
!= NULL
);
1232 if (DUK_TVAL_IS_BUFFER(tv
)) {
1234 h_buf
= DUK_TVAL_GET_BUFFER(tv
);
1235 DUK_ASSERT(h_buf
!= NULL
);
1236 duk_push_hbuffer(ctx
, h_buf
);
1237 } else if (DUK_TVAL_IS_OBJECT(tv
)) {
1239 duk_hbufferobject
*h_bufobj
;
1241 /* Accept any duk_hbufferobject, though we're only normally
1242 * called for Duktape.Buffer values.
1244 h
= DUK_TVAL_GET_OBJECT(tv
);
1245 DUK_ASSERT(h
!= NULL
);
1246 if (!DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
1247 DUK_DD(DUK_DDPRINT("toString/valueOf() called for a non-bufferobject object"));
1250 h_bufobj
= (duk_hbufferobject
*) h
;
1251 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
1253 if (h_bufobj
->buf
== NULL
) {
1254 DUK_DD(DUK_DDPRINT("toString/valueOf() called for a bufferobject with NULL buf"));
1257 duk_push_hbuffer(ctx
, h_bufobj
->buf
);
1263 duk_to_string(ctx
, -1);
1268 return DUK_RET_TYPE_ERROR
;
1270 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1271 DUK_INTERNAL duk_ret_t
duk_bi_buffer_prototype_tostring_shared(duk_context
*ctx
) {
1273 return DUK_RET_UNSUPPORTED_ERROR
;
1275 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1278 * Node.js Buffer.prototype: toJSON()
1281 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1282 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_tojson(duk_context
*ctx
) {
1284 duk_hbufferobject
*h_this
;
1288 thr
= (duk_hthread
*) ctx
;
1290 h_this
= duk__require_bufobj_this(ctx
);
1291 DUK_ASSERT(h_this
!= NULL
);
1293 if (h_this
->buf
== NULL
|| !DUK_HBUFFEROBJECT_VALID_SLICE(h_this
)) {
1294 /* Serialize uncovered backing buffer as a null; doesn't
1295 * really matter as long we're memory safe.
1301 duk_push_object(ctx
);
1302 duk_push_hstring_stridx(ctx
, DUK_STRIDX_UC_BUFFER
);
1303 duk_put_prop_stridx(ctx
, -2, DUK_STRIDX_TYPE
);
1305 duk_push_array(ctx
);
1306 for (i
= 0; i
< h_this
->length
; i
++) {
1307 /* XXX: regetting the pointer may be overkill - we're writing
1308 * to a side-effect free array here.
1310 DUK_ASSERT(h_this
->buf
!= NULL
);
1311 buf
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
);
1312 duk_push_uint(ctx
, (duk_uint_t
) buf
[i
]);
1313 duk_put_prop_index(ctx
, -2, (duk_idx_t
) i
);
1315 duk_put_prop_stridx(ctx
, -2, DUK_STRIDX_DATA
);
1319 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1320 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_tojson(duk_context
*ctx
) {
1322 return DUK_RET_UNSUPPORTED_ERROR
;
1324 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1327 * Node.js Buffer.prototype.equals()
1328 * Node.js Buffer.prototype.compare()
1329 * Node.js Buffer.compare()
1332 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1333 DUK_INTERNAL duk_ret_t
duk_bi_buffer_compare_shared(duk_context
*ctx
) {
1335 duk_small_uint_t magic
;
1336 duk_hbufferobject
*h_bufarg1
;
1337 duk_hbufferobject
*h_bufarg2
;
1338 duk_small_int_t comp_res
;
1340 thr
= (duk_hthread
*) ctx
;
1343 magic
= duk_get_current_magic(ctx
);
1345 /* Static call style. */
1346 h_bufarg1
= duk__require_bufobj_value(ctx
, 0);
1347 h_bufarg2
= duk__require_bufobj_value(ctx
, 1);
1349 h_bufarg1
= duk__require_bufobj_this(ctx
);
1350 h_bufarg2
= duk__require_bufobj_value(ctx
, 0);
1352 DUK_ASSERT(h_bufarg1
!= NULL
);
1353 DUK_ASSERT(h_bufarg2
!= NULL
);
1355 /* We want to compare the slice/view areas of the arguments.
1356 * If either slice/view is invalid (underlying buffer is shorter)
1357 * ensure equals() is false, but otherwise the only thing that
1358 * matters is to be memory safe.
1361 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg1
) &&
1362 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg2
)) {
1363 comp_res
= duk_js_data_compare((const duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_bufarg1
->buf
) + h_bufarg1
->offset
,
1364 (const duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_bufarg2
->buf
) + h_bufarg2
->offset
,
1365 (duk_size_t
) h_bufarg1
->length
,
1366 (duk_size_t
) h_bufarg2
->length
);
1368 comp_res
= -1; /* either nonzero value is ok */
1372 /* compare: similar to string comparison but for buffer data. */
1373 duk_push_int(ctx
, comp_res
);
1376 duk_push_boolean(ctx
, (comp_res
== 0));
1381 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1382 DUK_INTERNAL duk_ret_t
duk_bi_buffer_compare_shared(duk_context
*ctx
) {
1384 return DUK_RET_UNSUPPORTED_ERROR
;
1386 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1389 * Node.js Buffer.prototype.fill()
1392 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1393 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_fill(duk_context
*ctx
) {
1395 duk_hbufferobject
*h_this
;
1396 const duk_uint8_t
*fill_str_ptr
;
1397 duk_size_t fill_str_len
;
1398 duk_uint8_t fill_value
;
1399 duk_int_t fill_offset
;
1401 duk_size_t fill_length
;
1404 thr
= (duk_hthread
*) ctx
;
1407 h_this
= duk__require_bufobj_this(ctx
);
1408 DUK_ASSERT(h_this
!= NULL
);
1409 if (h_this
->buf
== NULL
) {
1410 return DUK_RET_TYPE_ERROR
;
1413 /* [ value offset end ] */
1415 if (duk_is_string(ctx
, 0)) {
1416 fill_str_ptr
= (const duk_uint8_t
*) duk_get_lstring(ctx
, 0, &fill_str_len
);
1417 DUK_ASSERT(fill_str_ptr
!= NULL
);
1419 fill_value
= (duk_uint8_t
) duk_to_uint32(ctx
, 0);
1420 fill_str_ptr
= (const duk_uint8_t
*) &fill_value
;
1424 /* Fill offset handling is more lenient than in Node.js. */
1426 duk__clamp_startend_nonegidx_noshift(ctx
, h_this
, 1 /*idx_start*/, 2 /*idx_end*/, &fill_offset
, &fill_end
);
1428 DUK_DDD(DUK_DDDPRINT("fill: fill_value=%02x, fill_offset=%ld, fill_end=%ld, view length=%ld",
1429 (unsigned int) fill_value
, (long) fill_offset
, (long) fill_end
, (long) h_this
->length
));
1431 DUK_ASSERT(fill_end
- fill_offset
>= 0);
1432 DUK_ASSERT(h_this
->buf
!= NULL
);
1434 p
= (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + fill_offset
);
1435 fill_length
= (duk_size_t
) (fill_end
- fill_offset
);
1436 if (fill_str_len
== 1) {
1437 /* Handle single character fills as memset() even when
1438 * the fill data comes from a one-char argument.
1440 DUK_MEMSET((void *) p
, (int) fill_str_ptr
[0], (size_t) fill_length
);
1441 } else if (fill_str_len
> 1) {
1444 for (i
= 0, n
= (fill_end
- fill_offset
), t
= 0; i
< n
; i
++) {
1445 p
[i
] = fill_str_ptr
[t
++];
1446 if (t
>= fill_str_len
) {
1451 DUK_DDD(DUK_DDDPRINT("zero size fill pattern, ignore silently"));
1454 /* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */
1458 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1459 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_fill(duk_context
*ctx
) {
1461 return DUK_RET_UNSUPPORTED_ERROR
;
1463 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1466 * Node.js Buffer.prototype.write(string, [offset], [length], [encoding])
1469 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1470 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_write(duk_context
*ctx
) {
1472 duk_hbufferobject
*h_this
;
1475 const duk_uint8_t
*str_data
;
1478 thr
= (duk_hthread
*) ctx
;
1481 h_this
= duk__require_bufobj_this(ctx
);
1482 DUK_ASSERT(h_this
!= NULL
);
1484 /* Argument must be a string, e.g. a buffer is not allowed. */
1485 str_data
= (const duk_uint8_t
*) duk_require_lstring(ctx
, 0, &str_len
);
1487 duk__resolve_offset_opt_length(ctx
, h_this
, 1, 2, &offset
, &length
, 0 /*throw_flag*/);
1488 DUK_ASSERT(offset
<= h_this
->length
);
1489 DUK_ASSERT(offset
+ length
<= h_this
->length
);
1491 /* XXX: encoding is ignored now. */
1493 if (length
> str_len
) {
1494 length
= (duk_uint_t
) str_len
;
1497 if (DUK_HBUFFEROBJECT_VALID_SLICE(h_this
)) {
1498 /* Cannot overlap. */
1499 DUK_MEMCPY((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + offset
),
1500 (const void *) str_data
,
1503 DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore"));
1506 duk_push_uint(ctx
, length
);
1509 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1510 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_write(duk_context
*ctx
) {
1512 return DUK_RET_UNSUPPORTED_ERROR
;
1514 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1517 * Node.js Buffer.prototype.copy()
1520 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1521 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_copy(duk_context
*ctx
) {
1523 duk_hbufferobject
*h_this
;
1524 duk_hbufferobject
*h_bufarg
;
1525 duk_int_t source_length
;
1526 duk_int_t target_length
;
1527 duk_int_t target_start
, source_start
, source_end
;
1528 duk_uint_t target_ustart
, source_ustart
, source_uend
;
1529 duk_uint_t copy_size
= 0;
1531 /* [ targetBuffer targetStart sourceStart sourceEnd ] */
1533 thr
= (duk_hthread
*) ctx
;
1536 h_this
= duk__require_bufobj_this(ctx
);
1537 h_bufarg
= duk__require_bufobj_value(ctx
, 0);
1538 DUK_ASSERT(h_this
!= NULL
);
1539 DUK_ASSERT(h_bufarg
!= NULL
);
1540 source_length
= (duk_int_t
) h_this
->length
;
1541 target_length
= (duk_int_t
) h_bufarg
->length
;
1543 target_start
= duk_to_int(ctx
, 1);
1544 source_start
= duk_to_int(ctx
, 2);
1545 if (duk_is_undefined(ctx
, 3)) {
1546 source_end
= source_length
;
1548 source_end
= duk_to_int(ctx
, 3);
1551 DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, "
1552 "source_start=%ld, source_end=%ld, source_length=%ld",
1553 (long) target_start
, (long) h_bufarg
->length
,
1554 (long) source_start
, (long) source_end
, (long) source_length
));
1556 /* This behavior mostly mimics Node.js now. */
1558 if (source_start
< 0 || source_end
< 0 || target_start
< 0) {
1559 /* Negative offsets cause a RangeError. */
1562 source_ustart
= (duk_uint_t
) source_start
;
1563 source_uend
= (duk_uint_t
) source_end
;
1564 target_ustart
= (duk_uint_t
) target_start
;
1565 if (source_ustart
>= source_uend
|| /* crossed offsets or zero size */
1566 source_ustart
>= (duk_uint_t
) source_length
|| /* source out-of-bounds (but positive) */
1567 target_ustart
>= (duk_uint_t
) target_length
) { /* target out-of-bounds (but positive) */
1570 if (source_uend
>= (duk_uint_t
) source_length
) {
1571 /* Source end clamped silently to available length. */
1572 source_uend
= source_length
;
1574 copy_size
= source_uend
- source_ustart
;
1575 if (target_ustart
+ copy_size
> (duk_uint_t
) target_length
) {
1576 /* Clamp to target's end if too long.
1578 * NOTE: there's no overflow possibility in the comparison;
1579 * both target_ustart and copy_size are >= 0 and based on
1580 * values in duk_int_t range. Adding them as duk_uint_t
1581 * values is then guaranteed not to overflow.
1583 DUK_ASSERT(target_ustart
+ copy_size
>= target_ustart
); /* no overflow */
1584 DUK_ASSERT(target_ustart
+ copy_size
>= copy_size
); /* no overflow */
1585 copy_size
= (duk_uint_t
) target_length
- target_ustart
;
1588 DUK_DDD(DUK_DDDPRINT("making copy: target_ustart=%lu source_ustart=%lu copy_size=%lu",
1589 (unsigned long) target_ustart
, (unsigned long) source_ustart
,
1590 (unsigned long) copy_size
));
1592 DUK_ASSERT(copy_size
>= 1);
1593 DUK_ASSERT(source_ustart
<= (duk_uint_t
) source_length
);
1594 DUK_ASSERT(source_ustart
+ copy_size
<= (duk_uint_t
) source_length
);
1595 DUK_ASSERT(target_ustart
<= (duk_uint_t
) target_length
);
1596 DUK_ASSERT(target_ustart
+ copy_size
<= (duk_uint_t
) target_length
);
1598 /* Ensure copy is covered by underlying buffers. */
1599 DUK_ASSERT(h_bufarg
->buf
!= NULL
); /* length check */
1600 DUK_ASSERT(h_this
->buf
!= NULL
); /* length check */
1601 if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufarg
, target_ustart
+ copy_size
) &&
1602 DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this
, source_ustart
+ copy_size
)) {
1603 /* Must use memmove() because copy area may overlap (source and target
1604 * buffer may be the same, or from different slices.
1606 DUK_MEMMOVE((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufarg
) + target_ustart
),
1607 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + source_ustart
),
1608 (size_t) copy_size
);
1610 DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring"));
1614 /* Return value is like write(), number of bytes written.
1615 * The return value matters because of code like:
1616 * "off += buf.copy(...)".
1618 duk_push_uint(ctx
, copy_size
);
1622 return DUK_RET_RANGE_ERROR
;
1624 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1625 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_copy(duk_context
*ctx
) {
1627 return DUK_RET_UNSUPPORTED_ERROR
;
1629 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1632 * TypedArray.prototype.set()
1634 * TypedArray set() is pretty interesting to implement because:
1636 * - The source argument may be a plain array or a typedarray. If the
1637 * source is a TypedArray, values are decoded and re-encoded into the
1638 * target (not as a plain byte copy). This may happen even when the
1639 * element byte size is the same, e.g. integer values may be re-encoded
1642 * - Source and target may refer to the same underlying buffer, so that
1643 * the set() operation may overlap. The specification requires that this
1644 * must work as if a copy was made before the operation. Note that this
1645 * is NOT a simple memmove() situation because the source and target
1646 * byte sizes may be different -- e.g. a 4-byte source (Int8Array) may
1647 * expand to a 16-byte target (Uint32Array) so that the target overlaps
1648 * the source both from beginning and the end (unlike in typical memmove).
1650 * - Even if 'buf' pointers of the source and target differ, there's no
1651 * guarantee that their memory areas don't overlap. This may be the
1652 * case with external buffers.
1654 * Even so, it is nice to optimize for the common case:
1656 * - Source and target separate buffers or non-overlapping.
1658 * - Source and target have a compatible type so that a plain byte copy
1659 * is possible. Note that while e.g. uint8 and int8 are compatible
1660 * (coercion one way or another doesn't change the byte representation),
1661 * e.g. int8 and uint8clamped are NOT compatible when writing int8
1662 * values into uint8clamped typedarray (-1 would clamp to 0 for instance).
1664 * See test-bi-typedarray-proto-set.js.
1667 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1668 DUK_INTERNAL duk_ret_t
duk_bi_typedarray_set(duk_context
*ctx
) {
1670 duk_hbufferobject
*h_this
;
1673 duk_int_t offset_signed
;
1674 duk_uint_t offset_elems
;
1675 duk_uint_t offset_bytes
;
1677 thr
= (duk_hthread
*) ctx
;
1680 h_this
= duk__require_bufobj_this(ctx
);
1681 DUK_ASSERT(h_this
!= NULL
);
1682 DUK_ASSERT_HBUFFEROBJECT_VALID(h_this
);
1684 if (h_this
->buf
== NULL
) {
1685 DUK_DDD(DUK_DDDPRINT("source neutered, skip copy"));
1689 h_obj
= duk_require_hobject(ctx
, 0);
1690 DUK_ASSERT(h_obj
!= NULL
);
1692 /* XXX: V8 throws a TypeError for negative values. Would it
1693 * be more useful to interpret negative offsets here from the
1694 * end of the buffer too?
1696 offset_signed
= duk_to_int(ctx
, 1);
1697 if (offset_signed
< 0) {
1698 return DUK_RET_TYPE_ERROR
;
1700 offset_elems
= (duk_uint_t
) offset_signed
;
1701 offset_bytes
= offset_elems
<< h_this
->shift
;
1702 if ((offset_bytes
>> h_this
->shift
) != offset_elems
) {
1703 /* Byte length would overflow. */
1704 /* XXX: easier check with less code? */
1705 return DUK_RET_RANGE_ERROR
;
1707 if (offset_bytes
> h_this
->length
) {
1708 /* Equality may be OK but >length not. Checking
1709 * this explicitly avoids some overflow cases
1712 return DUK_RET_RANGE_ERROR
;
1714 DUK_ASSERT(offset_bytes
<= h_this
->length
);
1716 /* Fast path: source is a TypedArray (or any bufferobject). */
1718 if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj
)) {
1719 duk_hbufferobject
*h_bufarg
;
1720 duk_uint16_t comp_mask
;
1721 duk_small_int_t no_overlap
= 0;
1722 duk_uint_t src_length
;
1723 duk_uint_t dst_length
;
1724 duk_uint_t dst_length_elems
;
1725 duk_uint8_t
*p_src_base
;
1726 duk_uint8_t
*p_src_end
;
1728 duk_uint8_t
*p_dst_base
;
1730 duk_small_uint_t src_elem_size
;
1731 duk_small_uint_t dst_elem_size
;
1733 h_bufarg
= (duk_hbufferobject
*) h_obj
;
1734 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg
);
1736 if (h_bufarg
->buf
== NULL
) {
1737 DUK_DDD(DUK_DDDPRINT("target neutered, skip copy"));
1741 /* Nominal size check. */
1742 src_length
= h_bufarg
->length
; /* bytes in source */
1743 dst_length_elems
= (src_length
>> h_bufarg
->shift
); /* elems in source and dest */
1744 dst_length
= dst_length_elems
<< h_this
->shift
; /* bytes in dest */
1745 if ((dst_length
>> h_this
->shift
) != dst_length_elems
) {
1746 /* Byte length would overflow. */
1747 /* XXX: easier check with less code? */
1748 return DUK_RET_RANGE_ERROR
;
1750 DUK_DDD(DUK_DDDPRINT("nominal size check: src_length=%ld, dst_length=%ld",
1751 (long) src_length
, (long) dst_length
));
1752 DUK_ASSERT(offset_bytes
<= h_this
->length
);
1753 if (dst_length
> h_this
->length
- offset_bytes
) {
1754 /* Overflow not an issue because subtraction is used on the right
1755 * side and guaranteed to be >= 0.
1757 DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
1758 return DUK_RET_RANGE_ERROR
;
1760 if (!DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this
, offset_bytes
+ dst_length
)) {
1761 DUK_DDD(DUK_DDDPRINT("copy not covered by underlying target buffer, ignore"));
1765 p_src_base
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufarg
);
1766 p_dst_base
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + offset_bytes
;
1768 /* Check actual underlying buffers for validity and that they
1769 * cover the copy. No side effects are allowed after the check
1770 * so that the validity status doesn't change.
1772 if (!DUK_HBUFFEROBJECT_VALID_SLICE(h_this
) ||
1773 !DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg
)) {
1774 /* The condition could be more narrow and check for the
1775 * copy area only, but there's no need for fine grained
1776 * behavior when the underlying buffer is misconfigured.
1778 DUK_DDD(DUK_DDDPRINT("source and/or target not covered by underlying buffer, skip copy"));
1782 /* We want to do a straight memory copy if possible: this is
1783 * an important operation because .set() is the TypedArray
1784 * way to copy chunks of memory. However, because set()
1785 * conceptually works in terms of elements, not all views are
1786 * compatible with direct byte copying.
1788 * If we do manage a direct copy, the "overlap issue" handled
1789 * below can just be solved using memmove() because the source
1790 * and destination element sizes are necessarily equal.
1793 DUK_ASSERT(h_this
->elem_type
< sizeof(duk__buffer_elemtype_copy_compatible
) / sizeof(duk_uint16_t
));
1794 comp_mask
= duk__buffer_elemtype_copy_compatible
[h_this
->elem_type
];
1795 if (comp_mask
& (1 << h_bufarg
->elem_type
)) {
1796 DUK_ASSERT(src_length
== dst_length
);
1798 DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible"));
1799 DUK_MEMMOVE((void *) p_dst_base
, (const void *) p_src_base
, (size_t) dst_length
);
1802 DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item"));
1804 /* We want to avoid making a copy to process set() but that's
1805 * not always possible: the source and the target may overlap
1806 * and because element sizes are different, the overlap cannot
1807 * always be handled with a memmove() or choosing the copy
1808 * direction in a certain way. For example, if source type is
1809 * uint8 and target type is uint32, the target area may exceed
1810 * the source area from both ends!
1812 * Note that because external buffers may point to the same
1813 * memory areas, we must ultimately make this check using
1816 * NOTE: careful with side effects: any side effect may cause
1817 * a buffer resize (or external buffer pointer/length update)!
1820 DUK_DDD(DUK_DDDPRINT("overlap check: p_src_base=%p, src_length=%ld, "
1821 "p_dst_base=%p, dst_length=%ld",
1822 (void *) p_src_base
, (long) src_length
,
1823 (void *) p_dst_base
, (long) dst_length
));
1825 if (p_src_base
>= p_dst_base
+ dst_length
|| /* source starts after dest ends */
1826 p_src_base
+ src_length
<= p_dst_base
) { /* source ends before dest starts */
1831 /* There's overlap: the desired end result is that
1832 * conceptually a copy is made to avoid "trampling"
1833 * of source data by destination writes. We make
1834 * an actual temporary copy to handle this case.
1836 duk_uint8_t
*p_src_copy
;
1838 DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source"));
1839 p_src_copy
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, src_length
);
1840 DUK_ASSERT(p_src_copy
!= NULL
);
1841 DUK_MEMCPY((void *) p_src_copy
, (const void *) p_src_base
, (size_t) src_length
);
1843 p_src_base
= p_src_copy
; /* use p_src_base from now on */
1845 /* Value stack intentionally mixed size here. */
1847 DUK_DDD(DUK_DDDPRINT("after overlap check: p_src_base=%p, src_length=%ld, "
1848 "p_dst_base=%p, dst_length=%ld, valstack top=%ld",
1849 (void *) p_src_base
, (long) src_length
,
1850 (void *) p_dst_base
, (long) dst_length
,
1851 (long) duk_get_top(ctx
)));
1853 /* Ready to make the copy. We must proceed element by element
1854 * and must avoid any side effects that might cause the buffer
1855 * validity check above to become invalid.
1857 * Although we work through the value stack here, only plain
1858 * numbers are handled which should be side effect safe.
1861 src_elem_size
= 1 << h_bufarg
->shift
;
1862 dst_elem_size
= 1 << h_this
->shift
;
1865 p_src_end
= p_src_base
+ src_length
;
1867 while (p_src
!= p_src_end
) {
1868 DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: "
1869 "p_src=%p, p_src_end=%p, p_dst=%p",
1870 (void *) p_src
, (void *) p_src_end
, (void *) p_dst
));
1871 /* A validated read() is always a number, so it's write coercion
1872 * is always side effect free an won't invalidate pointers etc.
1874 duk_hbufferobject_push_validated_read(ctx
, h_bufarg
, p_src
, src_elem_size
);
1875 duk_hbufferobject_validated_write(ctx
, h_this
, p_dst
, dst_elem_size
);
1877 p_src
+= src_elem_size
;
1878 p_dst
+= dst_elem_size
;
1883 /* Slow path: quite slow, but we save space by using the property code
1884 * to write coerce target values. We don't need to worry about overlap
1885 * here because the source is not a TypedArray.
1887 * We could use the bufferobject write coercion helper but since the
1888 * property read may have arbitrary side effects, full validity checks
1889 * would be needed for every element anyway.
1892 n
= (duk_uarridx_t
) duk_get_length(ctx
, 0);
1893 DUK_ASSERT(offset_bytes
<= h_this
->length
);
1894 if ((n
<< h_this
->shift
) > h_this
->length
- offset_bytes
) {
1895 /* Overflow not an issue because subtraction is used on the right
1896 * side and guaranteed to be >= 0.
1898 DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length"));
1899 return DUK_RET_RANGE_ERROR
;
1902 /* There's no need to check for buffer validity status for the
1903 * target here: the property access code will do that for each
1904 * element. Moreover, if we did check the validity here, side
1905 * effects from reading the source argument might invalidate
1906 * the results anyway.
1909 DUK_ASSERT_TOP(ctx
, 2);
1912 for (i
= 0; i
< n
; i
++) {
1913 duk_get_prop_index(ctx
, 0, i
);
1914 duk_put_prop_index(ctx
, 2, offset_elems
+ i
);
1920 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
1921 DUK_INTERNAL duk_ret_t
duk_bi_typedarray_set(duk_context
*ctx
) {
1923 return DUK_RET_UNSUPPORTED_ERROR
;
1925 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
1928 * Node.js Buffer.prototype.slice([start], [end])
1929 * ArrayBuffer.prototype.slice(begin, [end])
1930 * TypedArray.prototype.slice(begin, [end])
1932 * The API calls are almost identical; negative indices are counted from end
1933 * of buffer, and final indices are clamped (allowing crossed indices). Main
1936 * - Copy/view behavior; Node.js .slice() and TypedArray .subarray() create
1937 * views, ArrayBuffer .slice() creates a copy
1939 * - Resulting object has a different class and prototype depending on the
1940 * call (or 'this' argument)
1942 * - TypedArray .subarray() arguments are element indices, not byte offsets
1945 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
1946 DUK_INTERNAL duk_ret_t
duk_bi_buffer_slice_shared(duk_context
*ctx
) {
1948 duk_small_int_t magic
;
1949 duk_small_uint_t res_class_num
;
1950 duk_hobject
*res_proto
;
1951 duk_hbufferobject
*h_this
;
1952 duk_hbufferobject
*h_bufobj
;
1954 duk_int_t start_offset
, end_offset
;
1955 duk_uint_t slice_length
;
1957 thr
= (duk_hthread
*) ctx
;
1962 magic
= duk_get_current_magic(ctx
);
1963 h_this
= duk__require_bufobj_this(ctx
);
1965 /* Slice offsets are element (not byte) offsets, which only matters
1966 * for TypedArray views, Node.js Buffer and ArrayBuffer have shift
1967 * zero so byte and element offsets are the same. Negative indices
1968 * are counted from end of slice, crossed indices are allowed (and
1969 * result in zero length result), and final values are clamped
1970 * against the current slice. There's intentionally no check
1971 * against the underlying buffer here.
1974 duk__clamp_startend_negidx_shifted(ctx
, h_this
, 0 /*idx_start*/, 1 /*idx_end*/, &start_offset
, &end_offset
);
1975 DUK_ASSERT(end_offset
>= start_offset
);
1976 slice_length
= (duk_uint_t
) (end_offset
- start_offset
);
1978 /* The resulting buffer object gets the same class and prototype as
1979 * the buffer in 'this', e.g. if the input is a Node.js Buffer the
1980 * result is a Node.js Buffer; if the input is a Float32Array, the
1981 * result is a Float32Array.
1983 * For the class number this seems correct. The internal prototype
1984 * is not so clear: if 'this' is a bufferobject with a non-standard
1985 * prototype object, that value gets copied over into the result
1986 * (instead of using the standard prototype for that object type).
1989 res_class_num
= DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject
*) h_this
);
1990 h_bufobj
= duk_push_bufferobject_raw(ctx
,
1991 DUK_HOBJECT_FLAG_EXTENSIBLE
|
1992 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
1993 DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num
),
1994 DUK_BIDX_OBJECT_PROTOTYPE
); /* replaced */
1995 DUK_ASSERT(h_bufobj
!= NULL
);
1996 res_proto
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, (duk_hobject
*) h_this
); /* may be NULL */
1997 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) h_bufobj
, res_proto
);
1999 h_bufobj
->length
= slice_length
;
2000 h_bufobj
->shift
= h_this
->shift
; /* inherit */
2001 h_bufobj
->elem_type
= h_this
->elem_type
; /* inherit */
2002 h_bufobj
->is_view
= magic
& 0x01;
2003 DUK_ASSERT(h_bufobj
->is_view
== 0 || h_bufobj
->is_view
== 1);
2005 h_val
= h_this
->buf
;
2006 if (h_val
== NULL
) {
2007 return DUK_RET_TYPE_ERROR
;
2011 /* non-zero: make copy */
2012 duk_uint8_t
*p_copy
;
2013 duk_size_t copy_length
;
2015 p_copy
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, (duk_size_t
) slice_length
);
2016 DUK_ASSERT(p_copy
!= NULL
);
2018 /* Copy slice, respecting underlying buffer limits; remainder
2021 copy_length
= DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this
, slice_length
);
2022 DUK_MEMCPY((void *) p_copy
,
2023 (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
) + start_offset
),
2026 h_val
= duk_get_hbuffer(ctx
, -1);
2027 DUK_ASSERT(h_val
!= NULL
);
2029 h_bufobj
->buf
= h_val
;
2030 DUK_HBUFFER_INCREF(thr
, h_val
);
2031 DUK_ASSERT(h_bufobj
->offset
== 0);
2033 duk_pop(ctx
); /* reachable so pop OK */
2035 h_bufobj
->buf
= h_val
;
2036 DUK_HBUFFER_INCREF(thr
, h_val
);
2037 h_bufobj
->offset
= (duk_uint_t
) (h_this
->offset
+ start_offset
);
2039 /* Copy the .buffer property, needed for TypedArray.prototype.subarray().
2041 * XXX: limit copy only for TypedArray classes specifically?
2045 if (duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_LC_BUFFER
)) {
2046 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
2052 /* unbalanced stack on purpose */
2054 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
2057 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2058 DUK_INTERNAL duk_ret_t
duk_bi_buffer_slice_shared(duk_context
*ctx
) {
2060 return DUK_RET_UNSUPPORTED_ERROR
;
2062 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2065 * Node.js Buffer.isEncoding()
2068 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2069 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_is_encoding(duk_context
*ctx
) {
2070 const char *encoding
;
2072 /* only accept lowercase 'utf8' now. */
2074 encoding
= duk_to_string(ctx
, 0);
2075 DUK_ASSERT(duk_is_string(ctx
, 0)); /* guaranteed by duk_to_string() */
2076 duk_push_boolean(ctx
, DUK_STRCMP(encoding
, "utf8") == 0);
2079 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2080 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_is_encoding(duk_context
*ctx
) {
2082 return DUK_RET_UNSUPPORTED_ERROR
;
2084 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2087 * Node.js Buffer.isBuffer()
2090 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2091 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_is_buffer(duk_context
*ctx
) {
2095 duk_hobject
*h_proto
;
2098 thr
= (duk_hthread
*) ctx
;
2100 DUK_ASSERT(duk_get_top(ctx
) >= 1); /* nargs */
2101 tv
= duk_get_tval(ctx
, 0);
2102 DUK_ASSERT(tv
!= NULL
);
2104 if (DUK_TVAL_IS_OBJECT(tv
)) {
2105 h
= DUK_TVAL_GET_OBJECT(tv
);
2106 DUK_ASSERT(h
!= NULL
);
2108 h_proto
= thr
->builtins
[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE
];
2109 DUK_ASSERT(h_proto
!= NULL
);
2111 h
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
);
2113 ret
= duk_hobject_prototype_chain_contains(thr
, h
, h_proto
, 0 /*ignore_loop*/);
2117 duk_push_boolean(ctx
, ret
);
2120 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2121 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_is_buffer(duk_context
*ctx
) {
2123 return DUK_RET_UNSUPPORTED_ERROR
;
2125 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2128 * Node.js Buffer.byteLength()
2131 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2132 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_byte_length(duk_context
*ctx
) {
2136 /* At the moment Buffer(<str>) will just use the string bytes as
2137 * is (ignoring encoding), so we return the string length here
2141 str
= duk_to_lstring(ctx
, 0, &len
);
2143 duk_push_size_t(ctx
, len
);
2146 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2147 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_byte_length(duk_context
*ctx
) {
2149 return DUK_RET_UNSUPPORTED_ERROR
;
2151 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2154 * Node.js Buffer.concat()
2157 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2158 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_concat(duk_context
*ctx
) {
2161 duk_int_t total_length
= 0;
2162 duk_hbufferobject
*h_bufobj
;
2163 duk_hbufferobject
*h_bufres
;
2167 duk_size_t space_left
;
2168 duk_size_t copy_size
;
2170 thr
= (duk_hthread
*) ctx
;
2173 /* Node.js accepts only actual Arrays. */
2174 h_arg
= duk_require_hobject(ctx
, 0);
2175 if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg
) != DUK_HOBJECT_CLASS_ARRAY
) {
2176 return DUK_RET_TYPE_ERROR
;
2179 /* Compute result length and validate argument buffers. */
2180 n
= (duk_uint_t
) duk_get_length(ctx
, 0);
2181 for (i
= 0; i
< n
; i
++) {
2182 /* Neutered checks not necessary here: neutered buffers have
2183 * zero 'length' so we'll effectively skip them.
2185 DUK_ASSERT_TOP(ctx
, 2); /* [ array totalLength ] */
2186 duk_get_prop_index(ctx
, 0, (duk_uarridx_t
) i
); /* -> [ array totalLength buf ] */
2187 h_bufobj
= duk__require_bufobj_value(ctx
, 2);
2188 DUK_ASSERT(h_bufobj
!= NULL
);
2189 total_length
+= h_bufobj
->length
;
2193 /* For the case n==1 Node.js doesn't seem to type check
2194 * the sole member but we do it before returning it.
2195 * For this case only the original buffer object is
2196 * returned (not a copy).
2198 duk_get_prop_index(ctx
, 0, 0);
2202 /* User totalLength overrides a computed length, but we'll check
2203 * every copy in the copy loop. Note that duk_to_uint() can
2204 * technically have arbitrary side effects so we need to recheck
2205 * the buffers in the copy loop.
2207 if (!duk_is_undefined(ctx
, 1) && n
> 0) {
2208 /* For n == 0, Node.js ignores totalLength argument and
2209 * returns a zero length buffer.
2211 total_length
= duk_to_int(ctx
, 1);
2213 if (total_length
< 0) {
2214 return DUK_RET_RANGE_ERROR
;
2217 h_bufres
= duk_push_bufferobject_raw(ctx
,
2218 DUK_HOBJECT_FLAG_EXTENSIBLE
|
2219 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
2220 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER
),
2221 DUK_BIDX_NODEJS_BUFFER_PROTOTYPE
);
2222 DUK_ASSERT(h_bufres
!= NULL
);
2224 p
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, total_length
);
2225 DUK_ASSERT(p
!= NULL
);
2226 space_left
= total_length
;
2228 for (i
= 0; i
< n
; i
++) {
2229 DUK_ASSERT_TOP(ctx
, 4); /* [ array totalLength bufres buf ] */
2231 duk_get_prop_index(ctx
, 0, (duk_uarridx_t
) i
);
2232 h_bufobj
= duk__require_bufobj_value(ctx
, 4);
2233 DUK_ASSERT(h_bufobj
!= NULL
);
2235 copy_size
= h_bufobj
->length
;
2236 if (copy_size
> space_left
) {
2237 copy_size
= space_left
;
2240 if (h_bufobj
->buf
!= NULL
&&
2241 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj
)) {
2242 DUK_MEMCPY((void *) p
,
2243 (const void *) DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_bufobj
),
2246 /* Just skip, leaving zeroes in the result. */
2250 space_left
-= copy_size
;
2255 h_val
= duk_get_hbuffer(ctx
, -1);
2256 DUK_ASSERT(h_val
!= NULL
);
2258 duk__set_bufobj_buffer(ctx
, h_bufres
, h_val
);
2259 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufres
);
2261 duk_pop(ctx
); /* pop plain buffer, now reachable through h_bufres */
2263 return 1; /* return h_bufres */
2265 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2266 DUK_INTERNAL duk_ret_t
duk_bi_nodejs_buffer_concat(duk_context
*ctx
) {
2268 return DUK_RET_UNSUPPORTED_ERROR
;
2270 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2273 * Shared readfield and writefield methods
2275 * The readfield/writefield methods need support for endianness and field
2276 * types. All offsets are byte based so no offset shifting is needed.
2279 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2280 /* Format of magic, bits:
2281 * 0...1: field type; 0=uint8, 1=uint16, 2=uint32, 3=float, 4=double, 5=unused, 6=unused, 7=unused
2282 * 3: endianness: 0=little, 1=big
2283 * 4: signed: 1=yes, 0=no
2284 * 5: typedarray: 1=yes, 0=no
2286 #define DUK__FLD_8BIT 0
2287 #define DUK__FLD_16BIT 1
2288 #define DUK__FLD_32BIT 2
2289 #define DUK__FLD_FLOAT 3
2290 #define DUK__FLD_DOUBLE 4
2291 #define DUK__FLD_VARINT 5
2292 #define DUK__FLD_BIGENDIAN (1 << 3)
2293 #define DUK__FLD_SIGNED (1 << 4)
2294 #define DUK__FLD_TYPEDARRAY (1 << 5)
2296 /* XXX: split into separate functions for each field type? */
2297 DUK_INTERNAL duk_ret_t
duk_bi_buffer_readfield(duk_context
*ctx
) {
2299 duk_small_int_t magic
= (duk_small_int_t
) duk_get_current_magic(ctx
);
2300 duk_small_int_t magic_ftype
;
2301 duk_small_int_t magic_bigendian
;
2302 duk_small_int_t magic_signed
;
2303 duk_small_int_t magic_typedarray
;
2304 duk_small_int_t endswap
;
2305 duk_hbufferobject
*h_this
;
2306 duk_bool_t no_assert
;
2307 duk_int_t offset_signed
;
2309 duk_uint_t buffer_length
;
2310 duk_uint_t check_length
;
2312 duk_double_union du
;
2314 thr
= (duk_hthread
*) ctx
;
2317 magic_ftype
= magic
& 0x0007;
2318 magic_bigendian
= magic
& 0x0008;
2319 magic_signed
= magic
& 0x0010;
2320 magic_typedarray
= magic
& 0x0020;
2322 h_this
= duk__require_bufobj_this(ctx
);
2323 DUK_ASSERT(h_this
!= NULL
);
2324 buffer_length
= h_this
->length
;
2326 /* [ offset noAssert ], when ftype != DUK__FLD_VARINT */
2327 /* [ offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
2328 /* [ offset littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */
2330 /* Handle TypedArray vs. Node.js Buffer arg differences */
2331 if (magic_typedarray
) {
2333 #if defined(DUK_USE_INTEGER_LE)
2334 endswap
= !duk_to_boolean(ctx
, 1); /* 1=little endian */
2336 endswap
= duk_to_boolean(ctx
, 1); /* 1=little endian */
2339 no_assert
= duk_to_boolean(ctx
, (magic_ftype
== DUK__FLD_VARINT
) ? 2 : 1);
2340 #if defined(DUK_USE_INTEGER_LE)
2341 endswap
= magic_bigendian
;
2343 endswap
= !magic_bigendian
;
2347 /* Offset is coerced first to signed integer range and then to unsigned.
2348 * This ensures we can add a small byte length (1-8) to the offset in
2349 * bound checks and not wrap.
2351 offset_signed
= duk_to_int(ctx
, 0);
2352 offset
= (duk_uint_t
) offset_signed
;
2353 if (offset_signed
< 0) {
2357 DUK_DDD(DUK_DDDPRINT("readfield, buffer_length=%ld, offset=%ld, no_assert=%d, "
2358 "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
2360 (long) buffer_length
, (long) offset
, (int) no_assert
,
2361 (unsigned int) magic
, (int) magic_ftype
, (int) (magic_bigendian
>> 3),
2362 (int) (magic_signed
>> 4), (int) endswap
));
2364 /* Update 'buffer_length' to be the effective, safe limit which
2365 * takes into account the underlying buffer. This value will be
2366 * potentially invalidated by any side effect.
2368 check_length
= DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this
, buffer_length
);
2369 DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
2370 (long) buffer_length
, (long) check_length
));
2373 buf
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
);
2375 /* neutered, value doesn't matter because check_length is 0. */
2376 DUK_ASSERT(check_length
== 0);
2380 switch (magic_ftype
) {
2381 case DUK__FLD_8BIT
: {
2383 if (offset
+ 1U > check_length
) {
2388 duk_push_int(ctx
, (duk_int_t
) ((duk_int8_t
) tmp
));
2390 duk_push_uint(ctx
, (duk_uint_t
) tmp
);
2394 case DUK__FLD_16BIT
: {
2396 if (offset
+ 2U > check_length
) {
2399 DUK_MEMCPY((void *) du
.uc
, (const void *) (buf
+ offset
), 2);
2402 tmp
= DUK_BSWAP16(tmp
);
2405 duk_push_int(ctx
, (duk_int_t
) ((duk_int16_t
) tmp
));
2407 duk_push_uint(ctx
, (duk_uint_t
) tmp
);
2411 case DUK__FLD_32BIT
: {
2413 if (offset
+ 4U > check_length
) {
2416 DUK_MEMCPY((void *) du
.uc
, (const void *) (buf
+ offset
), 4);
2419 tmp
= DUK_BSWAP32(tmp
);
2422 duk_push_int(ctx
, (duk_int_t
) ((duk_int32_t
) tmp
));
2424 duk_push_uint(ctx
, (duk_uint_t
) tmp
);
2428 case DUK__FLD_FLOAT
: {
2430 if (offset
+ 4U > check_length
) {
2433 DUK_MEMCPY((void *) du
.uc
, (const void *) (buf
+ offset
), 4);
2436 tmp
= DUK_BSWAP32(tmp
);
2439 duk_push_number(ctx
, (duk_double_t
) du
.f
[0]);
2442 case DUK__FLD_DOUBLE
: {
2443 if (offset
+ 8U > check_length
) {
2446 DUK_MEMCPY((void *) du
.uc
, (const void *) (buf
+ offset
), 8);
2448 DUK_DBLUNION_BSWAP64(&du
);
2450 duk_push_number(ctx
, (duk_double_t
) du
.d
);
2453 case DUK__FLD_VARINT
: {
2454 /* Node.js Buffer variable width integer field. We don't really
2455 * care about speed here, so aim for shortest algorithm.
2457 duk_int_t field_bytelen
;
2458 duk_int_t i
, i_step
, i_end
;
2459 #if defined(DUK_USE_64BIT_OPS)
2461 duk_small_uint_t shift_tmp
;
2464 duk_small_int_t highbyte
;
2466 const duk_uint8_t
*p
;
2468 field_bytelen
= duk_get_int(ctx
, 1); /* avoid side effects! */
2469 if (field_bytelen
< 1 || field_bytelen
> 6) {
2470 goto fail_field_length
;
2472 if (offset
+ (duk_uint_t
) field_bytelen
> check_length
) {
2475 p
= (const duk_uint8_t
*) (buf
+ offset
);
2477 /* Slow gathering of value using either 64-bit arithmetic
2478 * or IEEE doubles if 64-bit types not available. Handling
2479 * of negative numbers is a bit non-obvious in both cases.
2482 if (magic_bigendian
) {
2483 /* Gather in big endian */
2486 i_end
= field_bytelen
; /* one i_step over */
2488 /* Gather in little endian */
2489 i
= field_bytelen
- 1;
2491 i_end
= -1; /* one i_step over */
2494 #if defined(DUK_USE_64BIT_OPS)
2497 DUK_ASSERT(i
>= 0 && i
< field_bytelen
);
2498 tmp
= (tmp
<< 8) + (duk_int64_t
) p
[i
];
2500 } while (i
!= i_end
);
2503 /* Shift to sign extend. */
2504 shift_tmp
= 64 - (field_bytelen
* 8);
2505 tmp
= (tmp
<< shift_tmp
) >> shift_tmp
;
2508 duk_push_i64(ctx
, tmp
);
2511 if (magic_signed
&& (highbyte
& 0x80) != 0) {
2512 /* 0xff => 255 - 256 = -1; 0x80 => 128 - 256 = -128 */
2513 tmp
= (duk_double_t
) (highbyte
- 256);
2515 tmp
= (duk_double_t
) highbyte
;
2522 DUK_ASSERT(i
>= 0 && i
< field_bytelen
);
2523 tmp
= (tmp
* 256.0) + (duk_double_t
) p
[i
];
2526 duk_push_number(ctx
, tmp
);
2530 default: { /* should never happen but default here */
2540 /* Node.js return value for noAssert out-of-bounds reads is
2541 * usually (but not always) NaN. Return NaN consistently.
2547 return DUK_RET_RANGE_ERROR
;
2549 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2550 DUK_INTERNAL duk_ret_t
duk_bi_buffer_readfield(duk_context
*ctx
) {
2552 return DUK_RET_UNSUPPORTED_ERROR
;
2554 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2556 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2557 /* XXX: split into separate functions for each field type? */
2558 DUK_INTERNAL duk_ret_t
duk_bi_buffer_writefield(duk_context
*ctx
) {
2560 duk_small_int_t magic
= (duk_small_int_t
) duk_get_current_magic(ctx
);
2561 duk_small_int_t magic_ftype
;
2562 duk_small_int_t magic_bigendian
;
2563 duk_small_int_t magic_signed
;
2564 duk_small_int_t magic_typedarray
;
2565 duk_small_int_t endswap
;
2566 duk_hbufferobject
*h_this
;
2567 duk_bool_t no_assert
;
2568 duk_int_t offset_signed
;
2570 duk_uint_t buffer_length
;
2571 duk_uint_t check_length
;
2573 duk_double_union du
;
2574 duk_int_t nbytes
= 0;
2576 thr
= (duk_hthread
*) ctx
;
2579 magic_ftype
= magic
& 0x0007;
2580 magic_bigendian
= magic
& 0x0008;
2581 magic_signed
= magic
& 0x0010;
2582 magic_typedarray
= magic
& 0x0020;
2583 DUK_UNREF(magic_signed
);
2585 h_this
= duk__require_bufobj_this(ctx
);
2586 DUK_ASSERT(h_this
!= NULL
);
2587 buffer_length
= h_this
->length
;
2589 /* [ value offset noAssert ], when ftype != DUK__FLD_VARINT */
2590 /* [ value offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */
2591 /* [ offset value littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */
2593 /* Handle TypedArray vs. Node.js Buffer arg differences */
2594 if (magic_typedarray
) {
2596 #if defined(DUK_USE_INTEGER_LE)
2597 endswap
= !duk_to_boolean(ctx
, 2); /* 1=little endian */
2599 endswap
= duk_to_boolean(ctx
, 2); /* 1=little endian */
2601 duk_swap(ctx
, 0, 1); /* offset/value order different from Node.js */
2603 no_assert
= duk_to_boolean(ctx
, (magic_ftype
== DUK__FLD_VARINT
) ? 3 : 2);
2604 #if defined(DUK_USE_INTEGER_LE)
2605 endswap
= magic_bigendian
;
2607 endswap
= !magic_bigendian
;
2611 /* Offset is coerced first to signed integer range and then to unsigned.
2612 * This ensures we can add a small byte length (1-8) to the offset in
2613 * bound checks and not wrap.
2615 offset_signed
= duk_to_int(ctx
, 1);
2616 offset
= (duk_uint_t
) offset_signed
;
2618 /* We need 'nbytes' even for a failed offset; return value must be
2619 * (offset + nbytes) even when write fails due to invalid offset.
2621 if (magic_ftype
!= DUK__FLD_VARINT
) {
2622 DUK_ASSERT(magic_ftype
>= 0 && magic_ftype
< (duk_small_int_t
) (sizeof(duk__buffer_nbytes_from_fldtype
) / sizeof(duk_uint8_t
)));
2623 nbytes
= duk__buffer_nbytes_from_fldtype
[magic_ftype
];
2625 nbytes
= duk_get_int(ctx
, 2);
2626 if (nbytes
< 1 || nbytes
> 6) {
2627 goto fail_field_length
;
2630 DUK_ASSERT(nbytes
>= 1 && nbytes
<= 8);
2632 /* Now we can check offset validity. */
2633 if (offset_signed
< 0) {
2637 DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, "
2638 "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
2640 duk_get_tval(ctx
, 0), (long) buffer_length
, (long) offset
, (int) no_assert
,
2641 (unsigned int) magic
, (int) magic_ftype
, (int) (magic_bigendian
>> 3),
2642 (int) (magic_signed
>> 4), (int) endswap
));
2644 /* Coerce value to a number before computing check_length, so that
2645 * the field type specific coercion below can't have side effects
2646 * that would invalidate check_length.
2648 duk_to_number(ctx
, 0);
2650 /* Update 'buffer_length' to be the effective, safe limit which
2651 * takes into account the underlying buffer. This value will be
2652 * potentially invalidated by any side effect.
2654 check_length
= DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this
, buffer_length
);
2655 DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld",
2656 (long) buffer_length
, (long) check_length
));
2659 buf
= DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr
->heap
, h_this
);
2661 /* neutered, value doesn't matter because check_length is 0. */
2662 DUK_ASSERT(check_length
== 0);
2666 switch (magic_ftype
) {
2667 case DUK__FLD_8BIT
: {
2668 if (offset
+ 1U > check_length
) {
2671 /* sign doesn't matter when writing */
2672 buf
[offset
] = (duk_uint8_t
) duk_to_uint32(ctx
, 0);
2675 case DUK__FLD_16BIT
: {
2677 if (offset
+ 2U > check_length
) {
2680 tmp
= (duk_uint16_t
) duk_to_uint32(ctx
, 0);
2682 tmp
= DUK_BSWAP16(tmp
);
2685 /* sign doesn't matter when writing */
2686 DUK_MEMCPY((void *) (buf
+ offset
), (const void *) du
.uc
, 2);
2689 case DUK__FLD_32BIT
: {
2691 if (offset
+ 4U > check_length
) {
2694 tmp
= (duk_uint32_t
) duk_to_uint32(ctx
, 0);
2696 tmp
= DUK_BSWAP32(tmp
);
2699 /* sign doesn't matter when writing */
2700 DUK_MEMCPY((void *) (buf
+ offset
), (const void *) du
.uc
, 4);
2703 case DUK__FLD_FLOAT
: {
2705 if (offset
+ 4U > check_length
) {
2708 du
.f
[0] = (duk_float_t
) duk_to_number(ctx
, 0);
2711 tmp
= DUK_BSWAP32(tmp
);
2714 /* sign doesn't matter when writing */
2715 DUK_MEMCPY((void *) (buf
+ offset
), (const void *) du
.uc
, 4);
2718 case DUK__FLD_DOUBLE
: {
2719 if (offset
+ 8U > check_length
) {
2722 du
.d
= (duk_double_t
) duk_to_number(ctx
, 0);
2724 DUK_DBLUNION_BSWAP64(&du
);
2726 /* sign doesn't matter when writing */
2727 DUK_MEMCPY((void *) (buf
+ offset
), (const void *) du
.uc
, 8);
2730 case DUK__FLD_VARINT
: {
2731 /* Node.js Buffer variable width integer field. We don't really
2732 * care about speed here, so aim for shortest algorithm.
2734 duk_int_t field_bytelen
;
2735 duk_int_t i
, i_step
, i_end
;
2736 #if defined(DUK_USE_64BIT_OPS)
2743 field_bytelen
= (duk_int_t
) nbytes
;
2744 if (offset
+ (duk_uint_t
) field_bytelen
> check_length
) {
2748 /* Slow writing of value using either 64-bit arithmetic
2749 * or IEEE doubles if 64-bit types not available. There's
2750 * no special sign handling when writing varints.
2753 if (magic_bigendian
) {
2754 /* Write in big endian */
2755 i
= field_bytelen
; /* one i_step added at top of loop */
2759 /* Write in little endian */
2760 i
= -1; /* one i_step added at top of loop */
2762 i_end
= field_bytelen
- 1;
2765 /* XXX: The duk_to_number() cast followed by integer coercion
2766 * is platform specific so NaN, +/- Infinity, and out-of-bounds
2767 * values result in platform specific output now.
2768 * See: test-bi-nodejs-buffer-proto-varint-special.js
2771 #if defined(DUK_USE_64BIT_OPS)
2772 tmp
= (duk_int64_t
) duk_to_number(ctx
, 0);
2773 p
= (duk_uint8_t
*) (buf
+ offset
);
2776 DUK_ASSERT(i
>= 0 && i
< field_bytelen
);
2777 p
[i
] = (duk_uint8_t
) (tmp
& 0xff);
2778 tmp
= tmp
>> 8; /* unnecessary shift for last byte */
2779 } while (i
!= i_end
);
2781 tmp
= duk_to_number(ctx
, 0);
2782 p
= (duk_uint8_t
*) (buf
+ offset
);
2785 tmp
= DUK_FLOOR(tmp
);
2786 DUK_ASSERT(i
>= 0 && i
< field_bytelen
);
2787 p
[i
] = (duk_uint8_t
) (DUK_FMOD(tmp
, 256.0));
2788 tmp
= tmp
/ 256.0; /* unnecessary div for last byte */
2789 } while (i
!= i_end
);
2793 default: { /* should never happen but default here */
2798 /* Node.js Buffer: return offset + #bytes written (i.e. next
2801 if (magic_typedarray
) {
2802 /* For TypedArrays 'undefined' return value is specified
2803 * by ES6 (matches V8).
2807 duk_push_uint(ctx
, offset
+ nbytes
);
2813 /* Node.js return value for failed writes is offset + #bytes
2814 * that would have been written.
2816 /* XXX: for negative input offsets, 'offset' will be a large
2817 * positive value so the result here is confusing.
2819 if (magic_typedarray
) {
2822 duk_push_uint(ctx
, offset
+ nbytes
);
2825 return DUK_RET_RANGE_ERROR
;
2827 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2828 DUK_INTERNAL duk_ret_t
duk_bi_buffer_writefield(duk_context
*ctx
) {
2830 return DUK_RET_UNSUPPORTED_ERROR
;
2832 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2834 #undef DUK__FLD_8BIT
2835 #undef DUK__FLD_16BIT
2836 #undef DUK__FLD_32BIT
2837 #undef DUK__FLD_FLOAT
2838 #undef DUK__FLD_DOUBLE
2839 #undef DUK__FLD_VARINT
2840 #undef DUK__FLD_BIGENDIAN
2841 #undef DUK__FLD_SIGNED
2842 #undef DUK__FLD_TYPEDARRAY