From 2ceafd3fd2819fcaae99bb64bf1632bbe6abbbde Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 16 Sep 2020 17:22:01 +0300 Subject: [PATCH] Introduced Buffer implementation. --- auto/sources | 1 + src/njs.h | 6 +- src/njs_array_buffer.c | 16 +- src/njs_array_buffer.h | 5 +- src/njs_buffer.c | 2944 +++++++++++++++++++++++++++++++++ src/njs_buffer.h | 19 + src/njs_builtin.c | 14 + src/njs_encoding.c | 4 +- src/njs_main.h | 1 + src/njs_object_hash.h | 10 + src/njs_string.c | 42 + src/njs_string.h | 4 + src/njs_typed_array.c | 67 +- src/njs_typed_array.h | 7 +- src/njs_utils.h | 12 + src/njs_value.c | 1 + src/njs_value.h | 1 + src/njs_vm.c | 6 +- src/njs_vm.h | 1 + src/test/njs_externals_test.c | 9 +- src/test/njs_unit_test.c | 936 ++++++++++- 21 files changed, 4011 insertions(+), 95 deletions(-) create mode 100644 src/njs_buffer.c create mode 100644 src/njs_buffer.h diff --git a/auto/sources b/auto/sources index b756bc99..418a4d61 100644 --- a/auto/sources +++ b/auto/sources @@ -57,6 +57,7 @@ NJS_LIB_SRCS=" \ src/njs_promise.c \ src/njs_query_string.c \ src/njs_encoding.c \ + src/njs_buffer.c \ " NJS_LIB_TEST_SRCS=" \ diff --git a/src/njs.h b/src/njs.h index f486511e..2ddaa0d0 100644 --- a/src/njs.h +++ b/src/njs.h @@ -317,11 +317,11 @@ NJS_EXPORT njs_int_t njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value, uintptr_t *next); /* - * Sets a Uint8Array value. + * Sets a Buffer value. * start data is not copied and should not be freed. */ -NJS_EXPORT njs_int_t njs_vm_value_typed_array_uint8_set(njs_vm_t *vm, - njs_value_t *value, const u_char *start, uint32_t size); +NJS_EXPORT njs_int_t njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value, + const u_char *start, uint32_t size); /* * Converts a value to string. diff --git a/src/njs_array_buffer.c b/src/njs_array_buffer.c index ab104c78..8c09012f 100644 --- a/src/njs_array_buffer.c +++ b/src/njs_array_buffer.c @@ -8,7 +8,7 @@ njs_array_buffer_t * -njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size) +njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, njs_bool_t zeroing) { njs_object_t *proto; njs_array_buffer_t *array; @@ -22,11 +22,15 @@ njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size) goto memory_error; } - if (size > 0) { + if (zeroing) { array->u.data = njs_mp_zalloc(vm->mem_pool, size); - if (njs_slow_path(array->u.data == NULL)) { - goto memory_error; - } + + } else { + array->u.data = njs_mp_alloc(vm->mem_pool, size); + } + + if (njs_slow_path(array->u.data == NULL)) { + goto memory_error; } proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object; @@ -80,7 +84,7 @@ njs_array_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - array = njs_array_buffer_alloc(vm, size); + array = njs_array_buffer_alloc(vm, size, 1); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } diff --git a/src/njs_array_buffer.h b/src/njs_array_buffer.h index 6db271b5..b1d6d755 100644 --- a/src/njs_array_buffer.h +++ b/src/njs_array_buffer.h @@ -12,7 +12,8 @@ ((buffer)->size) -njs_array_buffer_t *njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size); +njs_array_buffer_t *njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, + njs_bool_t zeroing); njs_int_t njs_array_buffer_writable(njs_vm_t *vm, njs_array_buffer_t *buffer); njs_inline njs_array_buffer_t * @@ -29,7 +30,7 @@ njs_array_buffer_slice(njs_vm_t *vm, njs_array_buffer_t *this, int64_t start, new_len = njs_max(final - first, 0); - new_buffer = njs_array_buffer_alloc(vm, new_len); + new_buffer = njs_array_buffer_alloc(vm, new_len, 1); if (new_buffer == NULL) { return NULL; } diff --git a/src/njs_buffer.c b/src/njs_buffer.c new file mode 100644 index 00000000..dfa8a0ea --- /dev/null +++ b/src/njs_buffer.c @@ -0,0 +1,2944 @@ + +/* + * Copyright (C) Alexander Borisov + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + + +#include + + +#define INT24_MAX 0x7FFFFF +#define INT40_MAX 0x7FFFFFFFFFULL +#define INT48_MAX 0x7FFFFFFFFFFFULL + +#define njs_buffer_magic(size, sign, little) \ + ((size << 2) | (sign << 1) | little) + + +typedef njs_int_t (*njs_buffer_encode)(njs_vm_t *vm, njs_value_t *value, + const njs_str_t *src); +typedef size_t (*njs_buffer_encode_length)(const njs_str_t *src, + size_t *out_size); + +typedef struct { + njs_str_t name; + njs_buffer_encode encode; + njs_buffer_encode decode; + njs_buffer_encode_length decode_length; +} njs_buffer_encoding_t; + + +static njs_buffer_encoding_t njs_buffer_encodings[] = +{ + { + njs_str("utf-8"), + njs_string_decode_utf8, + njs_string_decode_utf8, + njs_decode_utf8_length + }, + + { + njs_str("utf8"), + njs_string_decode_utf8, + njs_string_decode_utf8, + njs_decode_utf8_length + }, + + { + njs_str("hex"), + njs_string_hex, + njs_string_decode_hex, + njs_decode_hex_length + }, + + { + njs_str("base64"), + njs_string_base64, + njs_string_decode_base64, + njs_decode_base64_length + }, + + { + njs_str("base64url"), + njs_string_base64url, + njs_string_decode_base64url, + njs_decode_base64url_length + }, + + { njs_null_str, 0, 0, 0 } + +#define njs_buffer_utf8_encoding() &njs_buffer_encodings[0] +}; + + +static njs_int_t njs_buffer_from_object(njs_vm_t *vm, njs_value_t *value); +static njs_int_t njs_buffer_from_array_buffer(njs_vm_t *vm, njs_value_t *value, + njs_value_t *length, njs_value_t *offset); +static njs_int_t njs_buffer_from_typed_array(njs_vm_t *vm, njs_value_t *value); +static njs_int_t njs_buffer_from_string(njs_vm_t *vm, njs_value_t *value, + const njs_buffer_encoding_t *encoding); +static njs_int_t njs_buffer_write_string(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, + uint64_t offset, uint64_t length); +static njs_int_t njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, + njs_value_t *value, const njs_buffer_encoding_t *encoding, uint64_t offset, + uint64_t end); +static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, + uint8_t *start, uint8_t *end); +static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, uint8_t *start, uint8_t *end); +static const njs_buffer_encoding_t *njs_buffer_encoding(njs_vm_t *vm, + njs_value_t *value); +static njs_int_t njs_buffer_decode_string(njs_vm_t *vm, njs_value_t *value, + njs_value_t *dst, const njs_buffer_encoding_t *encoding); +static void njs_buffer_decode_destroy(njs_vm_t *vm, njs_value_t *source, + njs_value_t *target); + + +njs_int_t +njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, + uint32_t size) +{ + njs_object_t *proto; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + array = njs_mp_alloc(vm->mem_pool, sizeof(njs_typed_array_t) + + sizeof(njs_array_buffer_t)); + if (njs_slow_path(array == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + buffer = (njs_array_buffer_t *) &array[1]; + + proto = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object; + + njs_lvlhsh_init(&buffer->object.hash); + njs_lvlhsh_init(&buffer->object.shared_hash); + buffer->object.__proto__ = proto; + buffer->object.slots = NULL; + buffer->object.type = NJS_ARRAY_BUFFER; + buffer->object.shared = 1; + buffer->object.extensible = 1; + buffer->object.error_data = 0; + buffer->object.fast_array = 0; + buffer->u.data = (void *) start; + buffer->size = size; + + array->type = NJS_OBJ_TYPE_UINT8_ARRAY; + njs_lvlhsh_init(&array->object.hash); + njs_lvlhsh_init(&array->object.shared_hash); + array->object.__proto__ = &vm->prototypes[array->type].object; + array->object.slots = NULL; + array->object.type = NJS_TYPED_ARRAY; + array->object.shared = 0; + array->object.extensible = 1; + array->object.error_data = 0; + array->object.fast_array = 1; + array->buffer = buffer; + array->offset = 0; + array->byte_length = size; + + njs_set_typed_array(value, array); + + return NJS_OK; +} + + +static njs_typed_array_t * +njs_buffer_alloc_array(njs_vm_t *vm, size_t size, njs_bool_t zeroing) +{ + njs_value_t value; + njs_typed_array_t *array; + + njs_set_number(&value, size); + + array = njs_typed_array_alloc(vm, &value, 1, zeroing, + NJS_OBJ_TYPE_UINT8_ARRAY); + if (njs_slow_path(array == NULL)) { + return NULL; + } + + array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object; + + return array; +} + + +static njs_int_t +njs_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_type_error(vm, "Buffer is not a constructor"); + + return NJS_ERROR; +} + + +static njs_int_t +njs_buffer_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t safe) +{ + double size; + njs_int_t ret; + njs_typed_array_t *array; + const njs_buffer_encoding_t *encoding; + + if (njs_slow_path(!njs_is_number(njs_arg(args, nargs, 1)))) { + njs_type_error(vm, "\"size\" argument must be of type number"); + return NJS_ERROR; + } + + size = njs_number(njs_argument(args, 1)); + if (njs_slow_path(size < 0 || size > INT32_MAX)) { + njs_range_error(vm, "invalid size"); + return NJS_ERROR; + } + + array = njs_buffer_alloc_array(vm, size, safe || nargs <= 2); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + if (safe && nargs > 2) { + encoding = njs_buffer_utf8_encoding(); + + if (nargs > 3 && njs_is_string(njs_argument(args, 2))) { + encoding = njs_buffer_encoding(vm, njs_argument(args, 3)); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + ret = njs_buffer_fill(vm, array, njs_argument(args, 2), encoding, 0, + array->byte_length); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + njs_set_typed_array(&vm->retval, array); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + njs_value_t *value; + const njs_buffer_encoding_t *encoding; + + value = njs_arg(args, nargs, 1); + + switch (value->type) { + case NJS_TYPED_ARRAY: + return njs_buffer_from_typed_array(vm, value); + + case NJS_ARRAY_BUFFER: + return njs_buffer_from_array_buffer(vm, value, njs_arg(args, nargs, 2), + njs_arg(args, nargs, 3)); + + case NJS_STRING: + encoding = njs_buffer_utf8_encoding(); + + if (nargs > 2) { + encoding = njs_buffer_encoding(vm, njs_argument(args, 2)); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + return njs_buffer_from_string(vm, value, encoding); + + default: + if (njs_is_object(value)) { + ret = njs_buffer_from_object(vm, value); + if (njs_slow_path(ret != NJS_DECLINED)) { + return ret; + } + } + + njs_type_error(vm, "first argument %s is not a string " + "or Buffer-like object", njs_type_string(value->type)); + } + + return NJS_ERROR; +} + + +static njs_int_t +njs_buffer_from_object(njs_vm_t *vm, njs_value_t *value) +{ + double num; + int64_t len; + uint8_t *p; + uint32_t i; + njs_str_t str; + njs_int_t ret; + njs_array_t *array; + njs_value_t retval, length; + njs_typed_array_t *buffer; + + static const njs_value_t string_length = njs_string("length"); + static const njs_str_t str_buffer = njs_str("Buffer"); + +next: + + ret = njs_value_property(vm, value, njs_value_arg(&string_length), + &length); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (ret == NJS_DECLINED) { + ret = njs_value_property(vm, value, njs_value_arg(&njs_string_type), + &retval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_DECLINED; + } + + ret = njs_value_to_string(vm, &retval, &retval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_DECLINED; + } + + njs_string_get(&retval, &str); + + if (!njs_strstr_eq(&str, &str_buffer)) { + return NJS_DECLINED; + } + + ret = njs_value_property(vm, value, njs_value_arg(&njs_string_data), + &retval); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_DECLINED; + } + + if (njs_is_object(&retval)) { + value = &retval; + goto next; + } + + return NJS_DECLINED; + } + + ret = njs_value_to_length(vm, &length, &len); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + buffer = njs_buffer_alloc_array(vm, len, 0); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + p = njs_typed_array_buffer(buffer)->u.u8; + + if (njs_is_fast_array(value)) { + array = njs_array(value); + + for (i = 0; i < array->length; i++) { + ret = njs_value_to_number(vm, &array->start[i], &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *p++ = njs_number_to_int32(num); + } + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; + } + + for (i = 0; i < len; i++) { + ret = njs_value_property_i64(vm, value, i, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + ret = njs_value_to_number(vm, &retval, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + *p++ = njs_number_to_int32(num); + } + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_from_array_buffer(njs_vm_t *vm, njs_value_t *value, + njs_value_t *offset, njs_value_t *length) +{ + int64_t off, len; + njs_int_t ret; + njs_value_t arg; + njs_typed_array_t *buffer; + njs_array_buffer_t *array; + + array = njs_array_buffer(value); + + ret = njs_value_to_index(vm, offset, (uint64_t *) &off); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if ((size_t) off > array->size) { + njs_range_error(vm, "\"offset\" is outside of buffer bounds"); + return NJS_ERROR; + } + + if (njs_is_defined(length)) { + ret = njs_value_to_length(vm, length, &len); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + } else { + len = array->size - off; + } + + if ((size_t) (off + len) > array->size) { + njs_range_error(vm, "\"length\" is outside of buffer bounds"); + return NJS_ERROR; + } + + njs_set_array_buffer(&arg, array); + + buffer = njs_typed_array_alloc(vm, &arg, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + buffer->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object; + + buffer->offset = off; + buffer->byte_length = len; + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_from_typed_array(njs_vm_t *vm, njs_value_t *value) +{ + uint8_t *p; + uint32_t i, length; + njs_typed_array_t *buffer, *array; + + array = njs_typed_array(value); + length = njs_typed_array_length(array); + + buffer = njs_buffer_alloc_array(vm, length, 0); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + p = njs_typed_array_buffer(buffer)->u.u8; + + for (i = 0; i < length; i++) { + *p++ = njs_number_to_int32(njs_typed_array_prop(array, i)); + } + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_from_string(njs_vm_t *vm, njs_value_t *value, + const njs_buffer_encoding_t *encoding) +{ + njs_int_t ret; + njs_str_t str; + njs_value_t dst; + njs_typed_array_t *buffer; + + ret = njs_buffer_decode_string(vm, value, &dst, encoding); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_string_get(&dst, &str); + + buffer = njs_buffer_alloc_array(vm, str.length, 0); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + memcpy(njs_typed_array_buffer(buffer)->u.u8, str.start, str.length); + + njs_buffer_decode_destroy(vm, value, &dst); + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; +} + + +static size_t +njs_buffer_decode_string_length(njs_value_t *value, + const njs_buffer_encoding_t *encoding) +{ + size_t size; + njs_str_t str; + njs_string_prop_t string; + + (void) njs_string_prop(&string, value); + + str.start = string.start; + str.length = string.size; + size = string.size; + + if (encoding->decode == njs_string_decode_utf8 && string.length != 0) { + return size; + } + + encoding->decode_length(&str, &size); + + return size; +} + + +static njs_int_t +njs_buffer_byte_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + size_t size; + njs_value_t *value, *enc; + const njs_buffer_encoding_t *encoding; + + value = njs_arg(args, nargs, 1); + + switch (value->type) { + case NJS_TYPED_ARRAY: + njs_set_number(&vm->retval, njs_typed_array(value)->byte_length); + return NJS_OK; + + case NJS_ARRAY_BUFFER: + njs_set_number(&vm->retval, njs_array_buffer(value)->size); + return NJS_OK; + + case NJS_DATA_VIEW: + njs_set_number(&vm->retval, njs_data_view(value)->byte_length); + return NJS_OK; + + case NJS_STRING: + enc = njs_arg(args, nargs, 2); + encoding = njs_buffer_utf8_encoding(); + + if (njs_is_defined(enc)) { + encoding = njs_buffer_encoding(vm, enc); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + size = njs_buffer_decode_string_length(value, encoding); + + njs_set_number(&vm->retval, size); + + return NJS_OK; + + default: + njs_type_error(vm, "first argument %s is not a string " + "or Buffer-like object", njs_type_string(value->type)); + } + + return NJS_ERROR; +} + + +static njs_typed_array_t * +njs_buffer_slot(njs_vm_t *vm, njs_value_t *value, const char *name) +{ + njs_typed_array_t *array; + + if (njs_slow_path(!njs_is_object(value))) { + goto failed; + } + + array = njs_object_proto_lookup(njs_object(value), NJS_TYPED_ARRAY, + njs_typed_array_t); + + if (njs_slow_path(array != NULL + && array->type != NJS_OBJ_TYPE_UINT8_ARRAY)) + { + goto failed; + } + + return array; + +failed: + + njs_type_error(vm, "\"%s\" argument must be an instance " + "of Buffer or Uint8Array", name); + return NULL; +} + + +static njs_int_t +njs_buffer_array_range(njs_vm_t *vm, njs_typed_array_t *array, + const njs_value_t *start, const njs_value_t *end, const char *name, + uint8_t **out_start, uint8_t **out_end) +{ + uint8_t *u8; + uint64_t num_start, num_end; + njs_int_t ret; + + num_start = 0; + + if (njs_is_defined(start)) { + ret = njs_value_to_index(vm, njs_value_arg(start), &num_start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + if (num_start > array->byte_length) { + njs_range_error(vm, "\"%sStart\" is out of range: %L", name, num_start); + return NJS_ERROR; + } + + num_end = array->byte_length; + + if (njs_is_defined(end)) { + ret = njs_value_to_index(vm, njs_value_arg(end), &num_end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + if (num_end > array->byte_length) { + njs_range_error(vm, "\"%sEnd\" is out of range: %L", name, num_end); + return NJS_ERROR; + } + + if (num_start > num_end) { + num_end = num_start; + } + + u8 = njs_typed_array_buffer(array)->u.u8; + *out_start = &u8[array->offset + num_start]; + *out_end = &u8[array->offset + num_end]; + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_compare_array(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2, + const njs_value_t *target_start, const njs_value_t *target_end, + const njs_value_t *source_start, const njs_value_t *source_end) +{ + size_t size, src_size, trg_size; + uint8_t *src, *src_end, *trg, *trg_end; + njs_int_t ret; + njs_typed_array_t *source, *target; + + source = njs_buffer_slot(vm , val1, "source"); + if (njs_slow_path(source == NULL)) { + return NJS_ERROR; + } + + target = njs_buffer_slot(vm , val2, "target"); + if (njs_slow_path(target == NULL)) { + return NJS_ERROR; + } + + ret = njs_buffer_array_range(vm, target, target_start, target_end, "target", + &trg, &trg_end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_buffer_array_range(vm, source, source_start, source_end, "source", + &src, &src_end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + trg_size = trg_end - trg; + src_size = src_end - src; + + size = njs_min(trg_size, src_size); + + ret = memcmp(trg, src, size); + + if (ret != 0) { + njs_set_number(&vm->retval, (ret < 0) ? 1 : -1); + return NJS_OK; + } + + if (trg_size > src_size) { + ret = -1; + + } else if (trg_size < src_size) { + ret = 1; + } + + njs_set_number(&vm->retval, ret); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_compare(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + return njs_buffer_compare_array(vm, njs_arg(args, nargs, 1), + njs_arg(args, nargs, 2), + &njs_value_undefined, &njs_value_undefined, + &njs_value_undefined, &njs_value_undefined); +} + + +static njs_int_t +njs_buffer_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + u_char *p; + size_t n; + int64_t i, len, list_len; + njs_int_t ret; + njs_value_t *list, *value, *length, retval; + njs_array_t *array; + njs_typed_array_t *buffer, *arr; + + list = njs_arg(args, nargs, 1); + + if (njs_slow_path(!njs_is_array(list))) { + njs_type_error(vm, "\"list\" argument must be an instance of Array"); + return NJS_ERROR; + } + + len = 0; + ret = njs_object_length(vm, list, &list_len); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (njs_is_fast_array(list)) { + array = njs_array(list); + for (i = 0; i < list_len; i++) { + value = &array->start[i]; + + if (njs_slow_path(!njs_is_typed_array_uint8(value))) { + njs_type_error(vm, "\"list[%L]\" argument must be an " + "instance of Buffer or Uint8Array", i); + return NJS_ERROR; + } + + arr = njs_typed_array(value); + + if (njs_slow_path((SIZE_MAX - len) < arr->byte_length)) { + njs_type_error(vm, "Invalid length"); + return NJS_ERROR; + } + + len += arr->byte_length; + } + + } else { + + for (i = 0; i < list_len; i++) { + ret = njs_value_property_i64(vm, list, i, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + if (njs_slow_path(!njs_is_typed_array(&retval))) { + njs_type_error(vm, "\"list[%L]\" argument must be an " + "instance of Buffer or Uint8Array", i); + return NJS_ERROR; + } + + arr = njs_typed_array(&retval); + + if (njs_slow_path((SIZE_MAX - len) < arr->byte_length)) { + njs_type_error(vm, "Invalid length"); + return NJS_ERROR; + } + + len += arr->byte_length; + } + } + + length = njs_arg(args, nargs, 2); + if (njs_is_defined(length)) { + if (njs_slow_path(!njs_is_number(length))) { + njs_type_error(vm, "\"length\" argument must be of type number"); + return NJS_ERROR; + } + + len = njs_number(length); + if (njs_slow_path(len < 0)) { + njs_range_error(vm, "\"length\" is out of range"); + return NJS_ERROR; + } + } + + buffer = njs_buffer_alloc_array(vm, len, 0); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + p = njs_typed_array_buffer(buffer)->u.u8; + + if (njs_is_fast_array(list)) { + array = njs_array(list); + + for (i = 0; len != 0 && i < list_len; i++) { + arr = njs_typed_array(&array->start[i]); + n = njs_min((size_t) len, arr->byte_length); + + p = njs_cpymem(p, njs_typed_array_buffer(arr)->u.u8, n); + + len -= n; + } + + } else { + for (i = 0; len != 0 && i < list_len; i++) { + ret = njs_value_property_i64(vm, list, i, &retval); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + arr = njs_typed_array(&retval); + n = njs_min((size_t) len, arr->byte_length); + + p = njs_cpymem(p, njs_typed_array_buffer(arr)->u.u8, n); + + len -= n; + } + } + + if (len != 0) { + njs_memzero(p, len); + } + + njs_set_typed_array(&vm->retval, buffer); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_is_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_bool_t is; + njs_typed_array_t *array; + + is = 0; + + array = njs_buffer_slot(vm , njs_arg(args, nargs, 1), "source"); + + if (njs_fast_path(array != NULL && array->object.__proto__ + == &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object)) + { + is = 1; + } + + njs_set_boolean(&vm->retval, is); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_is_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_set_boolean(&vm->retval, + njs_buffer_encoding(vm, njs_arg(args, nargs, 1)) != NULL); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_length(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_typed_array_t *array; + + array = njs_buffer_slot(vm, value, "this"); + if (njs_slow_path(array == NULL)) { + njs_set_undefined(retval); + return NJS_DECLINED; + } + + njs_set_number(retval, array->byte_length); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_read_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t magic) +{ + double v; + uint32_t u32; + uint64_t u64, index, size; + njs_int_t ret; + njs_bool_t little, swap, sign; + njs_value_t *this, *value; + const uint8_t *u8; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + size = magic >> 2; + + if (!size) { + value = njs_arg(args, nargs, 2); + if (njs_slow_path(!njs_is_number(value))) { + njs_type_error(vm, "\"byteLength\" is not a number"); + return NJS_ERROR; + } + + size = (size_t) njs_number(value); + if (njs_slow_path(size > 6)) { + njs_type_error(vm, "\"byteLength\" must be <= 6"); + return NJS_ERROR; + } + } + + if (njs_slow_path(size + index > array->byte_length)) { + njs_range_error(vm, "index %uL is outside the bound of the buffer", + index); + return NJS_ERROR; + } + + sign = (magic >> 1) & 1; + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + buffer = array->buffer; + u8 = &buffer->u.u8[index + array->offset]; + + switch (size) { + case 1: + if (sign) { + v = (int8_t) *u8; + + } else { + v = *u8; + } + + break; + + case 2: + u32 = *((uint16_t *) u8); + + if (swap) { + u32 = njs_bswap_u16(u32); + } + + if (sign) { + /* Sign extension. */ + + u32 |= (u32 & (INT16_MAX + 1ULL)) * UINT32_MAX; + v = (int16_t) u32; + + } else { + v = u32; + } + + break; + + case 3: + if (little) { + u32 = (u8[2] << 16) | (u8[1] << 8) | u8[0]; + + } else { + u32 = (u8[0] << 16) | (u8[1] << 8) | u8[2]; + } + + if (sign) { + /* Sign extension. */ + + u32 |= (u32 & (INT24_MAX + 1ULL)) * UINT32_MAX; + v = (int32_t) u32; + + } else { + v = u32; + } + + break; + + case 4: + u32 = *((uint32_t *) u8); + + if (swap) { + u32 = njs_bswap_u32(u32); + } + + if (sign) { + /* Sign extension. */ + + u32 |= (u32 & (INT32_MAX + 1ULL)) * UINT32_MAX; + v = (int32_t) u32; + + } else { + v = u32; + } + + break; + + case 5: + if (little) { + u64 = ((uint64_t) u8[4] << 32) + | ((uint64_t) u8[3] << 24) + | (u8[2] << 16) + | (u8[1] << 8) + | u8[0]; + + } else { + u64 = ((uint64_t) u8[0] << 32) + | ((uint64_t) u8[1] << 24) + | (u8[2] << 16) + | (u8[3] << 8) + | u8[4]; + } + + if (sign) { + /* Sign extension. */ + + u64 |= (u64 & (INT40_MAX + 1ULL)) * UINT64_MAX; + v = (int64_t) u64; + + } else { + v = u64; + } + + break; + + case 6: + default: + if (little) { + u64 = ((uint64_t) u8[5] << 40) + | ((uint64_t) u8[4] << 32) + |((uint64_t) u8[3] << 24) + | (u8[2] << 16) + | (u8[1] << 8) + | u8[0]; + + } else { + u64 = ((uint64_t) u8[0] << 40) + | ((uint64_t) u8[1] << 32) + | ((uint64_t) u8[2] << 24) + | (u8[3] << 16) + | (u8[4] << 8) + | u8[5]; + } + + if (sign) { + /* Sign extension. */ + + u64 |= (u64 & (INT48_MAX + 1ULL)) * UINT64_MAX; + v = (int64_t) u64; + + } else { + v = u64; + } + + break; + } + + njs_set_number(&vm->retval, v); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_read_float(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t magic) +{ + double v; + uint32_t u32; + uint64_t index, size; + njs_int_t ret; + njs_bool_t little, swap; + njs_value_t *this; + const uint8_t *u8; + njs_conv_f32_t conv_f32; + njs_conv_f64_t conv_f64; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + size = magic >> 2; + + if (njs_slow_path(size + index > array->byte_length)) { + njs_range_error(vm, "index %uL is outside the bound of the buffer", + index); + return NJS_ERROR; + } + + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + buffer = array->buffer; + u8 = &buffer->u.u8[index + array->offset]; + + switch (size) { + case 4: + u32 = *((uint32_t *) u8); + + if (swap) { + u32 = njs_bswap_u32(u32); + } + + conv_f32.u = u32; + v = conv_f32.f; + break; + + case 8: + default: + conv_f64.u = *((uint64_t *) u8); + + if (swap) { + conv_f64.u = njs_bswap_u64(conv_f64.u); + } + + v = conv_f64.f; + } + + njs_set_number(&vm->retval, v); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_write_int(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t magic) +{ + uint8_t *u8; + int64_t i64; + uint32_t u32; + uint64_t index, size; + njs_int_t ret; + njs_bool_t little, swap; + njs_value_t *this, *value; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &i64); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + index = 0; + + if (nargs > 2) { + ret = njs_value_to_index(vm, njs_argument(args, 2), &index); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + size = magic >> 2; + + if (!size) { + value = njs_arg(args, nargs, 3); + if (njs_slow_path(!njs_is_number(value))) { + njs_type_error(vm, "\"byteLength\" is not a number"); + return NJS_ERROR; + } + + size = (size_t) njs_number(value); + if (njs_slow_path(size > 6)) { + njs_type_error(vm, "\"byteLength\" must be <= 6"); + return NJS_ERROR; + } + } + + if (njs_slow_path(size + index > array->byte_length)) { + njs_range_error(vm, "index %uL is outside the bound of the buffer", + index); + return NJS_ERROR; + } + + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + buffer = njs_typed_array_writable(vm, array); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + u8 = &buffer->u.u8[index + array->offset]; + + switch (size) { + case 1: + *u8 = i64; + break; + + case 2: + u32 = (uint16_t) i64; + + if (swap) { + u32 = njs_bswap_u16(u32); + } + + *((uint16_t *) u8) = u32; + break; + + case 3: + if (little) { + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; + + } else { + u8 += 2; + + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8 = i64; + } + + break; + + case 4: + u32 = i64; + + if (swap) { + u32 = njs_bswap_u32(u32); + } + + *((uint32_t *) u8) = u32; + break; + + case 5: + if (little) { + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; + + } else { + u8 += 4; + + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8 = i64; + } + + break; + + case 6: + default: + if (little) { + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; i64 >>= 8; + *u8++ = i64; + + } else { + u8 += 5; + + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8-- = i64; i64 >>= 8; + *u8 = i64; + } + + break; + } + + njs_set_undefined(&vm->retval); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_write_float(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t magic) +{ + double v; + uint8_t *u8; + uint64_t index, size; + njs_int_t ret; + njs_bool_t little, swap; + njs_value_t *this; + njs_conv_f32_t conv_f32; + njs_conv_f64_t conv_f64; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &v); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + index = 0; + + if (nargs > 2) { + ret = njs_value_to_index(vm, njs_argument(args, 2), &index); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + size = magic >> 2; + + if (njs_slow_path(size + index > array->byte_length)) { + njs_range_error(vm, "index %uL is outside the bound of the buffer", + index); + return NJS_ERROR; + } + + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + buffer = njs_typed_array_writable(vm, array); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + u8 = &buffer->u.u8[index + array->offset]; + + switch (size) { + case 4: + conv_f32.f = (float) v; + + if (swap) { + conv_f32.u = njs_bswap_u32(conv_f32.u); + } + + *((uint32_t *) u8) = conv_f32.u; + break; + + case 8: + default: + conv_f64.f = v; + + if (swap) { + conv_f64.u = njs_bswap_u64(conv_f64.u); + } + + *((uint64_t *) u8) = conv_f64.u; + } + + njs_set_undefined(&vm->retval); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + uint64_t offset, length; + njs_int_t ret; + njs_value_t *this, *value, *value_offset, *value_length, + *enc; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + const njs_buffer_encoding_t *encoding; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + value = njs_arg(args, nargs, 1); + value_offset = njs_arg(args, nargs, 2); + value_length = njs_arg(args, nargs, 3); + enc = njs_arg(args, nargs, 4); + + offset = 0; + length = array->byte_length; + encoding = njs_buffer_utf8_encoding(); + + if (njs_slow_path(!njs_is_string(value))) { + njs_type_error(vm, "first argument must be a string"); + return NJS_ERROR; + } + + if (njs_is_defined(value_offset)) { + if (njs_is_string(value_offset)) { + enc = value_offset; + goto encoding; + } + + ret = njs_value_to_index(vm, value_offset, &offset); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + if (njs_is_defined(value_length)) { + if (njs_is_string(value_length)) { + enc = value_length; + goto encoding; + } + + ret = njs_value_to_index(vm, value_length, &length); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + } + + if (njs_is_defined(enc)) { + if (njs_slow_path(!njs_is_string(enc))) { + njs_type_error(vm, "\"encoding\" argument must be of type string"); + return NJS_ERROR; + } + + encoding: + + encoding = njs_buffer_encoding(vm, enc); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + buffer = njs_typed_array_writable(vm, array); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + if (offset >= array->byte_length) { + njs_range_error(vm, "\"offset\" is out of range"); + return NJS_ERROR; + } + + return njs_buffer_write_string(vm, value, array, encoding, offset, length); +} + + +static njs_int_t +njs_buffer_write_string(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, + uint64_t offset, uint64_t length) +{ + uint8_t *start; + njs_int_t ret; + njs_str_t str; + njs_value_t dst; + njs_array_buffer_t *buffer; + + buffer = njs_typed_array_buffer(array); + + ret = njs_buffer_decode_string(vm, value, &dst, encoding); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_string_get(&dst, &str); + + start = &buffer->u.u8[array->offset + offset]; + + if (length > array->byte_length - offset) { + length = array->byte_length - offset; + } + + if (str.length == 0) { + length = 0; + goto done; + } + + memcpy(start, str.start, length); + +done: + + njs_buffer_decode_destroy(vm, value, &dst); + + njs_set_number(&vm->retval, length); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + uint64_t offset, end; + njs_int_t ret; + njs_value_t *this, *value, *value_offset, *value_end, + *encode; + njs_typed_array_t *array; + const njs_buffer_encoding_t *encoding; + + this = njs_argument(args, 0); + if (njs_slow_path(nargs < 2)) { + goto done; + } + + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + value = njs_arg(args, nargs, 1); + value_offset = njs_arg(args, nargs, 2); + value_end = njs_arg(args, nargs, 3); + encode = njs_arg(args, nargs, 4); + encoding = njs_buffer_utf8_encoding(); + + + offset = 0; + end = array->byte_length; + + if (njs_is_defined(value_offset)) { + if (njs_is_string(value) && njs_is_string(value_offset)) { + encode = value_offset; + goto encoding; + } + + ret = njs_value_to_index(vm, value_offset, &offset); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + if (njs_is_defined(value_end)) { + if (njs_is_string(value) && njs_is_string(value_end)) { + encode = value_end; + goto encoding; + } + + ret = njs_value_to_index(vm, value_end, &end); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + if (njs_is_defined(encode)) { + if (njs_slow_path(!njs_is_string(encode))) { + njs_type_error(vm, "\"encoding\" argument must be of type string"); + return NJS_ERROR; + } + + encoding: + + encoding = njs_buffer_encoding(vm, encode); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + ret = njs_buffer_fill(vm, array, value, encoding, offset, end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + +done: + + njs_vm_retval_set(vm, this); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, njs_value_t *value, + const njs_buffer_encoding_t *encoding, uint64_t offset, uint64_t end) +{ + double num; + uint8_t *start, *stop; + njs_int_t ret; + njs_array_buffer_t *buffer; + + buffer = njs_typed_array_writable(vm, array); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + if (njs_slow_path(offset > array->byte_length)) { + njs_range_error(vm, "\"offset\" is out of range"); + return NJS_ERROR; + } + + if (njs_slow_path(end > array->byte_length)) { + njs_range_error(vm, "\"end\" is out of range"); + return NJS_ERROR; + } + + if (njs_slow_path(offset >= end)) { + return NJS_OK; + } + + start = &buffer->u.u8[array->offset + offset]; + stop = &buffer->u.u8[array->offset + end]; + + switch (value->type) { + case NJS_STRING: + return njs_buffer_fill_string(vm, value, array, encoding, start, stop); + + case NJS_TYPED_ARRAY: + return njs_buffer_fill_typed_array(vm, value, array, start, stop); + + default: + ret = njs_value_to_number(vm, value, &num); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + memset(start, njs_number_to_uint32(num) & 0xff, end - offset); + } + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_fill_string(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, + uint8_t *start, uint8_t *end) +{ + uint64_t n; + njs_int_t ret; + njs_str_t str; + njs_value_t dst; + + ret = njs_buffer_decode_string(vm, value, &dst, encoding); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_string_get(&dst, &str); + + if (str.length == 0) { + memset(start, 0, end - start); + goto done; + } + + while (start < end) { + n = njs_min(str.length, (size_t) (end - start)); + start = njs_cpymem(start, str.start, n); + } + +done: + + njs_buffer_decode_destroy(vm, value, &dst); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_fill_typed_array(njs_vm_t *vm, njs_value_t *value, + njs_typed_array_t *array, uint8_t *to, uint8_t *end) +{ + size_t byte_length; + uint8_t *from; + uint64_t n; + njs_typed_array_t *arr_from; + njs_array_buffer_t *buffer; + + buffer = njs_typed_array_buffer(array); + + arr_from = njs_typed_array(value); + byte_length = arr_from->byte_length; + from = &njs_typed_array_buffer(arr_from)->u.u8[arr_from->offset]; + + if (njs_typed_array_buffer(arr_from)->u.u8 == buffer->u.u8) { + while (to < end) { + n = njs_min(byte_length, (size_t) (end - to)); + memmove(to, from, n); + to += n; + } + + } else { + while (to < end) { + n = njs_min(byte_length, (size_t) (end - to)); + to = njs_cpymem(to, from, n); + } + } + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_to_string(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused) +{ + uint64_t start, end; + njs_int_t ret; + njs_str_t str; + njs_value_t *this, *enc, *value_start, *value_end; + njs_typed_array_t *array; + const njs_buffer_encoding_t *encoding; + + this = njs_argument(args, 0); + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + enc = njs_arg(args, nargs, 1); + value_start = njs_arg(args, nargs, 2); + value_end = njs_arg(args, nargs, 3); + + start = 0; + end = array->byte_length; + encoding = njs_buffer_utf8_encoding(); + + if (njs_is_defined(enc)) { + encoding = njs_buffer_encoding(vm, njs_argument(args, 1)); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + if (njs_is_defined(value_start)) { + ret = njs_value_to_index(vm, value_start, &start); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + start = njs_min(start, array->byte_length); + } + + if (njs_is_defined(value_end)) { + ret = njs_value_to_index(vm, value_end, &end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + end = njs_min(end, array->byte_length); + } + + str.start = &njs_typed_array_buffer(array)->u.u8[array->offset + start]; + str.length = end - start; + + if (njs_slow_path(str.length == 0)) { + njs_vm_retval_set(vm, &njs_string_empty); + return NJS_OK; + } + + return encoding->encode(vm, &vm->retval, &str); +} + + +static njs_int_t +njs_buffer_prototype_compare(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + return njs_buffer_compare_array(vm, njs_argument(args, 0), + njs_arg(args, nargs, 1), njs_arg(args, nargs, 2), + njs_arg(args, nargs, 3), njs_arg(args, nargs, 4), + njs_arg(args, nargs, 5)); +} + + +static njs_int_t +njs_buffer_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + size_t size; + uint8_t *src, *src_end, *trg, *trg_end; + njs_int_t ret; + njs_value_t *val1, *val2; + njs_typed_array_t *source, *target; + njs_array_buffer_t *buffer, *array; + + val1 = njs_argument(args, 0); + val2 = njs_arg(args, nargs, 1); + + source = njs_buffer_slot(vm, val1, "source"); + if (njs_slow_path(source == NULL)) { + return NJS_ERROR; + } + + target = njs_buffer_slot(vm, val2, "target"); + if (njs_slow_path(target == NULL)) { + return NJS_ERROR; + } + + ret = njs_buffer_array_range(vm, target, njs_arg(args, nargs, 2), + &njs_value_undefined, "target", &trg, + &trg_end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + ret = njs_buffer_array_range(vm, source, njs_arg(args, nargs, 3), + njs_arg(args, nargs, 4), "source", &src, + &src_end); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + buffer = njs_typed_array_writable(vm, target); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + array = njs_typed_array_buffer(source); + + size = njs_min(trg_end - trg, src_end - src); + + if (buffer->u.data != array->u.data) { + memcpy(trg, src, size); + + } else { + memmove(trg, src, size); + } + + njs_set_number(&vm->retval, size); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_equals(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + + ret = njs_buffer_compare_array(vm, njs_argument(args, 0), + njs_arg(args, nargs, 1), &njs_value_undefined, + &njs_value_undefined, &njs_value_undefined, + &njs_value_undefined); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_set_boolean(&vm->retval, njs_number(&vm->retval) == 0); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t last) +{ + uint8_t *u8, byte; + int64_t from, to, increment, length, offset, index, i; + njs_int_t ret; + njs_str_t str; + njs_value_t *this, *value, *value_from, *enc, dst; + njs_typed_array_t *array, *src; + const njs_buffer_encoding_t *encoding; + + encoding = njs_buffer_utf8_encoding(); + + this = njs_argument(args, 0); + value = njs_arg(args, nargs, 1); + value_from = njs_arg(args, nargs, 2); + enc = njs_arg(args, nargs, 3); + + array = njs_buffer_slot(vm, this, "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + index = -1; + + if (njs_slow_path(array->byte_length == 0)) { + goto done; + } + + length = array->byte_length; + + if (last) { + from = length - 1; + to = -1; + increment = -1; + + } else { + from = 0; + to = length; + increment = 1; + } + + if (njs_is_defined(value_from)) { + if (njs_is_string(value) && njs_is_string(value_from)) { + enc = value_from; + goto encoding; + } + + ret = njs_value_to_integer(vm, value_from, &from); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + if (last) { + if (from >= 0) { + from = njs_min(from, length - 1); + + } else if (from < 0) { + from += length; + } + + if (from <= to) { + goto done; + } + + } else { + if (from < 0) { + from += length; + + if (from < 0) { + from = 0; + } + } + + if (from >= to) { + goto done; + } + } + } + + if (njs_is_defined(enc)) { + + encoding: + + encoding = njs_buffer_encoding(vm, enc); + if (njs_slow_path(encoding == NULL)) { + return NJS_ERROR; + } + } + + u8 = &njs_typed_array_buffer(array)->u.u8[0]; + offset = array->offset; + + switch (value->type) { + case NJS_STRING: + case NJS_TYPED_ARRAY: + if (njs_is_string(value)) { + ret = njs_buffer_decode_string(vm, value, &dst, encoding); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_string_get(&dst, &str); + + } else { + src = njs_typed_array(value); + if (njs_slow_path(src->type != NJS_OBJ_TYPE_UINT8_ARRAY)) { + goto fail; + } + + str.start = &src->buffer->u.u8[src->offset]; + str.length = src->byte_length; + } + + if (njs_slow_path(str.length == 0)) { + index = (last) ? length : 0; + goto done; + } + + if (last) { + if (from - to < (int64_t) str.length) { + goto done; + } + + from -= str.length - 1; + + } else { + if (to - from < (int64_t) str.length) { + goto done; + } + + to -= str.length - 1; + } + + for (i = from; i != to; i += increment) { + if (memcmp(&u8[offset + i], str.start, str.length) == 0) { + index = i; + goto done; + } + } + + break; + + case NJS_NUMBER: + byte = njs_number_to_uint32(njs_number(value)); + + for (i = from; i != to; i += increment) { + if (u8[offset + i] == byte) { + index = i; + goto done; + } + } + + break; + + default: +fail: + njs_type_error(vm, "\"value\" argument %s is not a string " + "or Buffer-like object", njs_type_string(value->type)); + return NJS_ERROR; + } + +done: + + njs_set_number(&vm->retval, index); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + + ret = njs_buffer_prototype_index_of(vm, args, nargs, unused); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_set_boolean(&vm->retval, (njs_number(&vm->retval) != -1)); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + njs_int_t ret; + njs_typed_array_t *array; + + ret = njs_typed_array_prototype_slice(vm, args, nargs, unused); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + array = njs_typed_array(&vm->retval); + array->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object; + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_swap(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t size) +{ + uint8_t *p, *end; + njs_typed_array_t *array; + njs_array_buffer_t *buffer; + + array = njs_buffer_slot(vm, njs_argument(args, 0), "this"); + if (njs_slow_path(array == NULL)) { + return NJS_ERROR; + } + + if ((array->byte_length % size) != 0) { + njs_range_error(vm, "Buffer size must be a multiple of %d-bits", + (int) (size << 3)); + return NJS_ERROR; + } + + buffer = njs_typed_array_writable(vm, array); + if (njs_slow_path(buffer == NULL)) { + return NJS_ERROR; + } + + p = &buffer->u.u8[array->offset]; + end = p + array->byte_length; + + switch (size) { + case 2: + for (; p < end; p += 2) { + *((uint16_t *) p) = njs_bswap_u16(*((uint16_t *) p)); + } + + break; + + case 4: + for (; p < end; p += 4) { + *((uint32_t *) p) = njs_bswap_u32(*((uint32_t *) p)); + } + + case 8: + default: + for (; p < end; p += 8) { + *((uint64_t *) p) = njs_bswap_u64(*((uint64_t *) p)); + } + } + + njs_set_typed_array(&vm->retval, array); + + return NJS_OK; +} + + +static njs_int_t +njs_buffer_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) +{ + u_char *p, *end; + njs_int_t ret; + njs_value_t *value; + njs_value_t object, array; + njs_array_t *arr; + njs_object_t *obj; + njs_typed_array_t *ta; + + static const njs_value_t string_buffer = njs_string("Buffer"); + + ta = njs_buffer_slot(vm, njs_argument(args, 0), "this"); + if (njs_slow_path(ta == NULL)) { + return NJS_ERROR; + } + + obj = njs_object_alloc(vm); + if (njs_slow_path(obj == NULL)) { + return NJS_ERROR; + } + + njs_set_object(&object, obj); + + ret = njs_value_property_set(vm, &object, njs_value_arg(&njs_string_type), + njs_value_arg(&string_buffer)); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + arr = njs_array_alloc(vm, 1, ta->byte_length, 0); + if (njs_slow_path(arr == NULL)) { + return NJS_ERROR; + } + + p = &njs_typed_array_buffer(ta)->u.u8[ta->offset]; + end = p + ta->byte_length; + value = arr->start; + + while (p < end) { + njs_set_number(value++, *p++); + } + + njs_set_array(&array, arr); + + ret = njs_value_property_set(vm, &object, njs_value_arg(&njs_string_data), + &array); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + njs_set_object(&vm->retval, obj); + + return NJS_OK; +} + + +static const njs_buffer_encoding_t * +njs_buffer_encoding(njs_vm_t *vm, njs_value_t *value) +{ + njs_str_t str; + njs_int_t ret; + njs_buffer_encoding_t *encoding; + + if (njs_slow_path(!njs_is_string(value))) { + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + } + + njs_string_get(value, &str); + + for (encoding = &njs_buffer_encodings[0]; + encoding->name.length != 0; + encoding++) + { + if (njs_strstr_eq(&str, &encoding->name)) { + return encoding; + } + } + + njs_type_error(vm, "\"%V\" encoding is not supported", &str); + + return NULL; +} + + +static njs_int_t +njs_buffer_decode_string(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst, + const njs_buffer_encoding_t *encoding) +{ + njs_int_t ret; + njs_str_t str; + njs_string_prop_t string; + + (void) njs_string_prop(&string, value); + + str.start = string.start; + str.length = string.size; + + *dst = *value; + + if (encoding->decode == njs_string_decode_utf8 && string.length != 0) { + return NJS_OK; + } + + ret = encoding->decode(vm, dst, &str); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + return NJS_OK; +} + + +static void +njs_buffer_decode_destroy(njs_vm_t *vm, njs_value_t *source, + njs_value_t *target) +{ + njs_str_t src, trg; + + njs_string_get(source, &src); + njs_string_get(target, &trg); + + if (src.start != trg.start) { + njs_mp_free(vm->mem_pool, trg.start); + } +} + + +static const njs_object_prop_t njs_buffer_prototype_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), + .value = njs_string("Buffer"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("constructor"), + .value = njs_prop_handler(njs_object_prototype_create_constructor), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("length"), + .value = njs_prop_handler(njs_buffer_prototype_length), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readInt8"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(1, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUInt8"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(1, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readInt16LE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(2, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUInt16LE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(2, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readInt16BE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(2, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUInt16BE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(2, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readInt32LE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(4, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUInt32LE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(4, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readInt32BE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(4, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUInt32BE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 1, + njs_buffer_magic(4, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readIntLE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 2, + njs_buffer_magic(0, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUIntLE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 2, + njs_buffer_magic(0, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readIntBE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 2, + njs_buffer_magic(0, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readUIntBE"), + .value = njs_native_function2(njs_buffer_prototype_read_int, 2, + njs_buffer_magic(0, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readFloatLE"), + .value = njs_native_function2(njs_buffer_prototype_read_float, 1, + njs_buffer_magic(4, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readFloatBE"), + .value = njs_native_function2(njs_buffer_prototype_read_float, 1, + njs_buffer_magic(4, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readDoubleLE"), + .value = njs_native_function2(njs_buffer_prototype_read_float, 1, + njs_buffer_magic(8, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("readDoubleBE"), + .value = njs_native_function2(njs_buffer_prototype_read_float, 1, + njs_buffer_magic(8, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeInt8"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(1, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUInt8"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(1, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeInt16LE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(2, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUInt16LE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(2, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeInt16BE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(2, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUInt16BE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(2, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeInt32LE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(4, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUInt32LE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(4, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeInt32BE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(4, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUInt32BE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 1, + njs_buffer_magic(4, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeIntLE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 3, + njs_buffer_magic(0, 1, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUIntLE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 3, + njs_buffer_magic(0, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeIntBE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 3, + njs_buffer_magic(0, 1, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeUIntBE"), + .value = njs_native_function2(njs_buffer_prototype_write_int, 3, + njs_buffer_magic(0, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeFloatLE"), + .value = njs_native_function2(njs_buffer_prototype_write_float, 1, + njs_buffer_magic(4, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeFloatBE"), + .value = njs_native_function2(njs_buffer_prototype_write_float, 1, + njs_buffer_magic(4, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeDoubleLE"), + .value = njs_native_function2(njs_buffer_prototype_write_float, 1, + njs_buffer_magic(8, 0, 1)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeDoubleBE"), + .value = njs_native_function2(njs_buffer_prototype_write_float, 1, + njs_buffer_magic(8, 0, 0)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("write"), + .value = njs_native_function(njs_buffer_prototype_write, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("fill"), + .value = njs_native_function(njs_buffer_prototype_fill, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("toString"), + .value = njs_native_function(njs_buffer_prototype_to_string, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("compare"), + .value = njs_native_function(njs_buffer_prototype_compare, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("copy"), + .value = njs_native_function(njs_buffer_prototype_copy, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("equals"), + .value = njs_native_function(njs_buffer_prototype_equals, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("indexOf"), + .value = njs_native_function2(njs_buffer_prototype_index_of, 1, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("lastIndexOf"), + .value = njs_native_function2(njs_buffer_prototype_index_of, 1, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("includes"), + .value = njs_native_function(njs_buffer_prototype_includes, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("subarray"), + .value = njs_native_function2(njs_buffer_prototype_slice, 2, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("slice"), + .value = njs_native_function2(njs_buffer_prototype_slice, 2, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("swap16"), + .value = njs_native_function2(njs_buffer_prototype_swap, 0, 2), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("swap32"), + .value = njs_native_function2(njs_buffer_prototype_swap, 0, 4), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("swap64"), + .value = njs_native_function2(njs_buffer_prototype_swap, 0, 8), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("toJSON"), + .value = njs_native_function(njs_buffer_prototype_to_json, 0), + .writable = 1, + .configurable = 1, + }, +}; + + +const njs_object_init_t njs_buffer_prototype_init = { + njs_buffer_prototype_properties, + njs_nitems(njs_buffer_prototype_properties), +}; + + +static const njs_object_prop_t njs_buffer_constructor_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Buffer"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("length"), + .value = njs_value(NJS_NUMBER, 0, 0.0), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("alloc"), + .value = njs_native_function2(njs_buffer_alloc, 0, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("allocUnsafe"), + .value = njs_native_function2(njs_buffer_alloc, 1, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_long_string("allocUnsafeSlow"), + .value = njs_native_function2(njs_buffer_alloc, 1, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_long_string("byteLength"), + .value = njs_native_function(njs_buffer_byte_length, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_long_string("compare"), + .value = njs_native_function(njs_buffer_compare, 2), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_long_string("concat"), + .value = njs_native_function(njs_buffer_concat, 1), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("from"), + .value = njs_native_function(njs_buffer_from, 3), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("isBuffer"), + .value = njs_native_function(njs_buffer_is_buffer, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("isEncoding"), + .value = njs_native_function(njs_buffer_is_encoding, 0), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("prototype"), + .value = njs_prop_handler(njs_object_prototype_create), + }, +}; + + +const njs_object_init_t njs_buffer_constructor_init = { + njs_buffer_constructor_properties, + njs_nitems(njs_buffer_constructor_properties), +}; + + +const njs_object_type_init_t njs_buffer_type_init = { + .constructor = njs_native_ctor(njs_buffer_constructor, 0, 0), + .prototype_props = &njs_buffer_prototype_init, + .constructor_props = &njs_buffer_constructor_init, + .prototype_value = { .object = { .type = NJS_OBJECT } }, +}; + + +static const njs_object_prop_t njs_buffer_constants_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("MAX_LENGTH"), + .value = njs_value(NJS_NUMBER, 1, INT32_MAX), + .enumerable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_long_string("MAX_STRING_LENGTH"), + .value = njs_value(NJS_NUMBER, 1, NJS_STRING_MAX_LENGTH), + .enumerable = 1, + }, +}; + + +static const njs_object_init_t njs_buffer_constants_init = { + njs_buffer_constants_properties, + njs_nitems(njs_buffer_constants_properties), +}; + + +static njs_int_t +njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, + njs_value_t *unused, njs_value_t *retval) +{ + return njs_object_prop_init(vm, &njs_buffer_constants_init, prop, value, + retval); +} + + +static njs_int_t +njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, + njs_value_t *unused, njs_value_t *retval) +{ + return njs_object_prop_init(vm, &njs_buffer_constructor_init, prop, value, + retval); +} + + +static const njs_object_prop_t njs_buffer_object_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("buffer"), + .configurable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("constants"), + .value = njs_prop_handler(njs_buffer_constants), + .enumerable = 1, + }, + + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("Buffer"), + .value = njs_prop_handler(njs_buffer), + .enumerable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("kMaxLength"), + .value = njs_value(NJS_NUMBER, 1, INT32_MAX), + .enumerable = 1, + }, +}; + + +const njs_object_init_t njs_buffer_object_init = { + njs_buffer_object_properties, + njs_nitems(njs_buffer_object_properties), +}; diff --git a/src/njs_buffer.h b/src/njs_buffer.h new file mode 100644 index 00000000..e9b94a68 --- /dev/null +++ b/src/njs_buffer.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Alexander Borisov + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_BUFFER_H_INCLUDED_ +#define _NJS_BUFFER_H_INCLUDED_ + + +njs_int_t njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, + uint32_t size); + + +extern const njs_object_type_init_t njs_buffer_type_init; +extern const njs_object_init_t njs_buffer_object_init; + + +#endif /* _NJS_BUFFER_H_INCLUDED_ */ diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 352d67a6..62947ebc 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -51,6 +51,7 @@ static const njs_object_init_t *njs_module_init[] = { &njs_fs_object_init, &njs_crypto_object_init, &njs_query_string_object_init, + &njs_buffer_object_init, NULL }; @@ -74,6 +75,7 @@ static const njs_object_type_init_t *const &njs_data_view_type_init, &njs_text_decoder_type_init, &njs_text_encoder_type_init, + &njs_buffer_type_init, /* Hidden types. */ @@ -352,6 +354,9 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global) vm->prototypes[i].object.__proto__ = typed_array_prototype; } + vm->prototypes[NJS_OBJ_TYPE_BUFFER].object.__proto__ = + &vm->prototypes[NJS_OBJ_TYPE_UINT8_ARRAY].object; + error_prototype = &vm->prototypes[NJS_OBJ_TYPE_ERROR].object; error_prototype->__proto__ = object_prototype; @@ -1314,6 +1319,15 @@ static const njs_object_prop_t njs_global_this_object_properties[] = .configurable = 1, }, + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("Buffer"), + .value = njs_prop_handler2(njs_top_level_constructor, + NJS_OBJ_TYPE_BUFFER, NJS_BUFFER_HASH), + .writable = 1, + .configurable = 1, + }, + { .type = NJS_PROPERTY_HANDLER, .name = njs_string("Uint8Array"), diff --git a/src/njs_encoding.c b/src/njs_encoding.c index 01d28af1..14500491 100644 --- a/src/njs_encoding.c +++ b/src/njs_encoding.c @@ -130,7 +130,7 @@ njs_text_encoder_encode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_set_number(&value, size); - array = njs_typed_array_alloc(vm, &value, 1, NJS_OBJ_TYPE_UINT8_ARRAY); + array = njs_typed_array_alloc(vm, &value, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } @@ -154,7 +154,7 @@ njs_text_encoder_encode_utf8(njs_vm_t *vm, njs_string_prop_t *prop) njs_set_number(&value, prop->size); - array = njs_typed_array_alloc(vm, &value, 1, NJS_OBJ_TYPE_UINT8_ARRAY); + array = njs_typed_array_alloc(vm, &value, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } diff --git a/src/njs_main.h b/src/njs_main.h index 9884d2b1..3102c62b 100644 --- a/src/njs_main.h +++ b/src/njs_main.h @@ -75,6 +75,7 @@ #include #include #include +#include #include #include diff --git a/src/njs_object_hash.h b/src/njs_object_hash.h index 0cd25bcf..db2ef7fa 100644 --- a/src/njs_object_hash.h +++ b/src/njs_object_hash.h @@ -792,4 +792,14 @@ 'T'), 'e'), 'x'), 't'), 'E'), 'n'), 'c'), 'o'), 'd'), 'e'), 'r') +#define NJS_BUFFER_HASH \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add( \ + njs_djb_hash_add(NJS_DJB_HASH_INIT, \ + 'B'), 'u'), 'f'), 'f'), 'e'), 'r') + + #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */ diff --git a/src/njs_string.c b/src/njs_string.c index 689f54bb..f7271d71 100644 --- a/src/njs_string.c +++ b/src/njs_string.c @@ -1794,6 +1794,48 @@ njs_decode_hex(njs_str_t *dst, const njs_str_t *src) } +void +njs_decode_utf8(njs_str_t *dst, const njs_str_t *src) +{ + njs_unicode_decode_t ctx; + + njs_utf8_decode_init(&ctx); + + (void) njs_utf8_stream_encode(&ctx, src->start, src->start + src->length, + dst->start, 1, 0); +} + + +size_t +njs_decode_utf8_length(const njs_str_t *src, size_t *out_size) +{ + njs_unicode_decode_t ctx; + + njs_utf8_decode_init(&ctx); + + return njs_utf8_stream_length(&ctx, src->start, src->length, 1, 0, + out_size); +} + + +njs_int_t +njs_string_decode_utf8(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) +{ + size_t length; + njs_str_t dst; + + length = njs_decode_utf8_length(src, &dst.length); + dst.start = njs_string_alloc(vm, value, dst.length, length); + + if (njs_fast_path(dst.start != NULL)) { + njs_decode_utf8(&dst, src); + return NJS_OK; + } + + return NJS_ERROR; +} + + njs_int_t njs_string_decode_hex(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) { diff --git a/src/njs_string.h b/src/njs_string.h index 21f74faf..16afd14e 100644 --- a/src/njs_string.h +++ b/src/njs_string.h @@ -191,6 +191,8 @@ size_t njs_encode_hex_length(const njs_str_t *src, size_t *out_size); void njs_encode_base64(njs_str_t *dst, const njs_str_t *src); size_t njs_encode_base64_length(const njs_str_t *src, size_t *out_size); +void njs_decode_utf8(njs_str_t *dst, const njs_str_t *src); +size_t njs_decode_utf8_length(const njs_str_t *src, size_t *out_size); void njs_decode_hex(njs_str_t *dst, const njs_str_t *src); size_t njs_decode_hex_length(const njs_str_t *src, size_t *out_size); void njs_decode_base64(njs_str_t *dst, const njs_str_t *src); @@ -204,6 +206,8 @@ njs_int_t njs_string_base64(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_base64url(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); +njs_int_t njs_string_decode_utf8(njs_vm_t *vm, njs_value_t *value, + const njs_str_t *src); njs_int_t njs_string_decode_hex(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_decode_base64(njs_vm_t *vm, njs_value_t *value, diff --git a/src/njs_typed_array.c b/src/njs_typed_array.c index e23fd596..accfb77e 100644 --- a/src/njs_typed_array.c +++ b/src/njs_typed_array.c @@ -26,7 +26,7 @@ static void njs_typed_array_prop_set(njs_vm_t *vm, njs_typed_array_t *array, njs_typed_array_t * njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, - njs_object_type_t type) + njs_bool_t zeroing, njs_object_type_t type) { double num; int64_t i, length; @@ -121,7 +121,7 @@ njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } if (buffer == NULL) { - buffer = njs_array_buffer_alloc(vm, size); + buffer = njs_array_buffer_alloc(vm, size, zeroing); if (njs_slow_path(buffer == NULL)) { return NULL; } @@ -198,53 +198,6 @@ memory_error: } -njs_int_t -njs_typed_array_uint8_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, - uint32_t size) -{ - njs_object_t *proto; - njs_typed_array_t *array; - njs_array_buffer_t *buffer; - - array = njs_mp_zalloc(vm->mem_pool, sizeof(njs_typed_array_t) - + sizeof(njs_array_buffer_t)); - if (njs_slow_path(array == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } - - buffer = (njs_array_buffer_t *) &array[1]; - - proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object; - - njs_lvlhsh_init(&buffer->object.hash); - njs_lvlhsh_init(&buffer->object.shared_hash); - buffer->object.__proto__ = proto; - buffer->object.slots = NULL; - buffer->object.type = NJS_ARRAY_BUFFER; - buffer->object.shared = 1; - buffer->object.extensible = 1; - buffer->object.error_data = 0; - buffer->object.fast_array = 0; - buffer->u.data = (void *) start; - buffer->size = size; - - array->buffer = buffer; - array->byte_length = size; - array->type = NJS_OBJ_TYPE_UINT8_ARRAY; - njs_lvlhsh_init(&array->object.hash); - njs_lvlhsh_init(&array->object.shared_hash); - array->object.__proto__ = &vm->prototypes[array->type].object; - array->object.type = NJS_TYPED_ARRAY; - array->object.extensible = 1; - array->object.fast_array = 1; - - njs_set_typed_array(value, array); - - return NJS_OK; -} - - static njs_int_t njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic) @@ -256,7 +209,7 @@ njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - array = njs_typed_array_alloc(vm, &args[1], nargs - 1, magic); + array = njs_typed_array_alloc(vm, &args[1], nargs - 1, 1, magic); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } @@ -956,7 +909,7 @@ njs_typed_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, } -static njs_int_t +njs_int_t njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t copy) { @@ -2539,18 +2492,6 @@ static const njs_object_init_t njs_data_view_constructor_init = { }; -typedef union { - float f; - uint32_t u; -} njs_conv_f32_t; - - -typedef union { - double f; - uint64_t u; -} njs_conv_f64_t; - - static njs_int_t njs_data_view_prototype_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type) diff --git a/src/njs_typed_array.h b/src/njs_typed_array.h index 78db7665..3f8b2536 100644 --- a/src/njs_typed_array.h +++ b/src/njs_typed_array.h @@ -9,16 +9,15 @@ njs_typed_array_t *njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, - njs_uint_t nargs, njs_object_type_t type); -njs_int_t njs_typed_array_uint8_set(njs_vm_t *vm, njs_value_t *value, - const u_char *start, uint32_t size); + njs_uint_t nargs, njs_bool_t zeroing, njs_object_type_t type); njs_array_buffer_t *njs_typed_array_writable(njs_vm_t *vm, njs_typed_array_t *array); njs_int_t njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, njs_value_t *setval); njs_int_t njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_typed_array_t *array, njs_value_t *sep); - +njs_int_t njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t copy); njs_inline unsigned njs_typed_array_element_size(njs_object_type_t type) diff --git a/src/njs_utils.h b/src/njs_utils.h index 960954a7..c729ca13 100644 --- a/src/njs_utils.h +++ b/src/njs_utils.h @@ -8,6 +8,18 @@ #define _NJS_UTILS_H_INCLUDED_ +typedef union { + float f; + uint32_t u; +} njs_conv_f32_t; + + +typedef union { + double f; + uint64_t u; +} njs_conv_f64_t; + + typedef int (*njs_sort_cmp_t)(const void *, const void *, void *ctx); void njs_qsort(void *base, size_t n, size_t size, njs_sort_cmp_t cmp, diff --git a/src/njs_value.c b/src/njs_value.c index c733aace..b2c2ad97 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -47,6 +47,7 @@ const njs_value_t njs_string_symbol = njs_string("symbol"); const njs_value_t njs_string_string = njs_string("string"); const njs_value_t njs_string_name = njs_string("name"); const njs_value_t njs_string_data = njs_string("data"); +const njs_value_t njs_string_type = njs_string("type"); const njs_value_t njs_string_external = njs_string("external"); const njs_value_t njs_string_invalid = njs_string("invalid"); const njs_value_t njs_string_object = njs_string("object"); diff --git a/src/njs_value.h b/src/njs_value.h index 130abb7f..967dcb5d 100644 --- a/src/njs_value.h +++ b/src/njs_value.h @@ -831,6 +831,7 @@ extern const njs_value_t njs_string_nan; extern const njs_value_t njs_string_symbol; extern const njs_value_t njs_string_string; extern const njs_value_t njs_string_data; +extern const njs_value_t njs_string_type; extern const njs_value_t njs_string_name; extern const njs_value_t njs_string_external; extern const njs_value_t njs_string_invalid; diff --git a/src/njs_vm.c b/src/njs_vm.c index a8daf2a0..0b1269a4 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -697,10 +697,10 @@ njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, njs_int_t -njs_vm_value_typed_array_uint8_set(njs_vm_t *vm, njs_value_t *value, - const u_char *start, uint32_t size) +njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, + uint32_t size) { - return njs_typed_array_uint8_set(vm, value, start, size); + return njs_buffer_set(vm, value, start, size); } diff --git a/src/njs_vm.h b/src/njs_vm.h index cec67fe6..02789e9a 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -87,6 +87,7 @@ typedef enum { NJS_OBJ_TYPE_DATA_VIEW, NJS_OBJ_TYPE_TEXT_DECODER, NJS_OBJ_TYPE_TEXT_ENCODER, + NJS_OBJ_TYPE_BUFFER, #define NJS_OBJ_TYPE_HIDDEN_MIN (NJS_OBJ_TYPE_FS_DIRENT) NJS_OBJ_TYPE_FS_DIRENT, diff --git a/src/test/njs_externals_test.c b/src/test/njs_externals_test.c index 67f4cc64..9787be32 100644 --- a/src/test/njs_externals_test.c +++ b/src/test/njs_externals_test.c @@ -194,11 +194,10 @@ njs_unit_test_r_host(njs_vm_t *vm, njs_object_prop_t *prop, static njs_int_t -njs_unit_test_r_u8buffer(njs_vm_t *vm, njs_object_prop_t *prop, +njs_unit_test_r_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { - return njs_vm_value_typed_array_uint8_set(vm, retval, - (u_char *) "АБВГДЕЁЖЗИЙ", 22); + return njs_vm_value_buffer_set(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22); } @@ -564,10 +563,10 @@ static njs_external_t njs_unit_test_r_external[] = { { .flags = NJS_EXTERN_PROPERTY, - .name.string = njs_str("u8buffer"), + .name.string = njs_str("buffer"), .enumerable = 1, .u.property = { - .handler = njs_unit_test_r_u8buffer, + .handler = njs_unit_test_r_buffer, } }, diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 4da0bc0b..6b4b766b 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -25,6 +25,15 @@ "delete " nm "[0];" +#define njs_buffer_byte_map(func, sign, divisor) \ + "var buf = Buffer.alloc(6);" \ + "[1,2,3,4,5,6].map(byte => {" \ + " buf." func "(" sign "(2 ** (byte * 7)) / " \ + njs_stringify(divisor) ", 0, byte);" \ + " return njs.dump(buf);" \ + "})" + + typedef struct { njs_str_t script; njs_str_t ret; @@ -18344,6 +18353,919 @@ static njs_unit_test_t njs_test[] = "var en = new TextEncoder();" "njs.dump(en.encode(de.decode(buf)))"), njs_str("Uint8Array [2,0,0,0]") }, + + /* Buffer */ + + { njs_str("new Buffer();"), + njs_str("TypeError: Buffer is not a constructor") }, + + { njs_str("var buf = Buffer.alloc();"), + njs_str("TypeError: \"size\" argument must be of type number") }, + + { njs_str("var buf = Buffer.alloc('best buffer');"), + njs_str("TypeError: \"size\" argument must be of type number") }, + + { njs_str("var buf = Buffer.alloc(-1);"), + njs_str("RangeError: invalid size") }, + + { njs_str("var buf = Buffer.alloc(4); njs.dump(buf)"), + njs_str("Buffer [0,0,0,0]") }, + + { njs_str("var buf = Buffer.alloc(4, 88); buf"), + njs_str("XXXX") }, + + { njs_str("var buf = Buffer.alloc(4, 945); njs.dump(buf)"), + njs_str("Buffer [177,177,177,177]") }, + + { njs_str("var buf = Buffer.alloc(4, -1); njs.dump(buf)"), + njs_str("Buffer [255,255,255,255]") }, + + { njs_str("var buf = Buffer.alloc(4, -1, 'utf-128'); njs.dump(buf)"), + njs_str("Buffer [255,255,255,255]") }, + + { njs_str("var buf = Buffer.alloc(10, 'α'); buf"), + njs_str("ααααα") }, + + { njs_str("var buf = Buffer.alloc(4, 'α'); njs.dump(buf)"), + njs_str("Buffer [206,177,206,177]") }, + + { njs_str("var buf = Buffer.alloc(2, 'ααααα'); njs.dump(buf)"), + njs_str("Buffer [206,177]") }, + + { njs_str("var buf = Buffer.alloc(1, 'α'); njs.dump(buf)"), + njs_str("Buffer [206]") }, + + { njs_str("var buf = Buffer.alloc(4, 'ZXZpbA==', 'base64'); buf"), + njs_str("evil") }, + + { njs_str("var buf = Buffer.alloc(8, 'ZXZpbA==', 'base64'); buf"), + njs_str("evilevil") }, + + { njs_str("var buf = Buffer.alloc(8, 'evil', 'utf-128'); buf"), + njs_str("TypeError: \"utf-128\" encoding is not supported") }, + + { njs_str("var foo = new Uint8Array(10).fill(88);" + "var buf = Buffer.alloc(8, foo); buf"), + njs_str("XXXXXXXX") }, + + { njs_str("var foo = new Uint16Array(10).fill(0xB1CE);" + "var buf = Buffer.alloc(10, foo); buf"), + njs_str("ααααα") }, + + { njs_str("var foo = new Uint16Array(20).fill(0xB1CE);" + "var buf = Buffer.alloc(10, foo); buf"), + njs_str("ααααα") }, + + { njs_str("var foo = new Uint16Array(2).fill(0xB1CE);" + "var buf = Buffer.alloc(10, foo); buf"), + njs_str("ααααα") }, + + { njs_str("var foo = new Uint16Array(2).fill(0xB1CE);" + "var buf = Buffer.alloc(10, foo); buf"), + njs_str("ααααα") }, + + { njs_str("var foo = Buffer.alloc(10, 'α');" + "var buf = Buffer.alloc(4, foo); buf"), + njs_str("αα") }, + + { njs_str("var buf = Buffer.allocUnsafe(10).fill('α'); buf"), + njs_str("ααααα") }, + + { njs_str("var buf = Buffer.allocUnsafe(-1)"), + njs_str("RangeError: invalid size") }, + + { njs_str("[" + " ['6576696c', 'hex', 4]," + " ['6576696', 'hex', 3]," + " ['', 'hex', 0]," + " ['', 'base64', 0]," + " ['ZXZpbA==', 'base64', 4]," + " ['ZXZpbA', 'base64url', 4]," + " ['ααααα', undefined, 10]," + "].every(args => Buffer.byteLength(args[0], args[1]) == args[2])"), + njs_str("true") }, + + { njs_str("Buffer.from([])"), + njs_str("") }, + + { njs_str("Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])"), + njs_str("buffer") }, + + { njs_str(njs_declare_sparse_array("arr", 6) + "[0x62, 0x75, 0x66, 0x66, 0x65, 0x72].map((v, i) => {arr[i] = v;});" + "Buffer.from(arr)"), + njs_str("buffer") }, + + { njs_str("Buffer.from({length:3, 0:0x62, 1:0x75, 2:0x66})"), + njs_str("buf") }, + + { njs_str("njs.dump(Buffer.from([-1,1,255,22323,-Infinity,Infinity,NaN]))"), + njs_str("Buffer [255,1,255,51,0,0,0]") }, + + { njs_str("var buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); njs.dump(buf)"), + njs_str("Buffer [98,117,102,102,101,114]") }, + + { njs_str("var buf = Buffer.from([1,2,3]); njs.dump(Buffer.from(buf.toJSON()))"), + njs_str("Buffer [1,2,3]") }, + + { njs_str("[" + " {type: 'B'}," + " {type: undefined}," + " {type:'Buffer'}," + " {type:'Buffer', data:null}," + " {type:'Buffer', data:{}}," + "].every(v=>{ try { Buffer.from(v)} catch(e) {return e.name == 'TypeError'}})"), + njs_str("true") }, + + { njs_str("var foo = new Uint16Array(2);" + "foo[0] = 5000; foo[1] = 4000;" + "var buf = Buffer.from(foo.buffer);" + "foo[1] = 6000;" + "njs.dump(buf)"), + njs_str("Buffer [136,19,112,23]") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 1); njs.dump(buf)"), + njs_str("Buffer [3,182,3]") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, -1); njs.dump(buf)"), + njs_str("RangeError: invalid index") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 5); njs.dump(buf)"), + njs_str("RangeError: \"offset\" is outside of buffer bounds") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 2, 1); njs.dump(buf)"), + njs_str("Buffer [182]") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 2, -1); njs.dump(buf)"), + njs_str("Buffer []") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 2, 3); njs.dump(buf)"), + njs_str("RangeError: \"length\" is outside of buffer bounds") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 2, 0); njs.dump(buf)"), + njs_str("Buffer []") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, 2, 2); njs.dump(buf)"), + njs_str("Buffer [182,3]") }, + + { njs_str("var foo = new Uint16Array(2).fill(950);" + "var buf = Buffer.from(foo.buffer, '2', '2'); njs.dump(buf)"), + njs_str("Buffer [182,3]") }, + + { njs_str("var foo = new Uint32Array(1).fill(0xF1F2F3F4);" + "var buf = Buffer.from(foo); njs.dump(buf)"), + njs_str("Buffer [244]") }, + + { njs_str("var foo = new Uint32Array(2).fill(0xF1F2F3F4);" + "var buf = Buffer.from(foo); njs.dump(buf)"), + njs_str("Buffer [244,244]") }, + + { njs_str("var foo = new Uint8Array(5);" + "foo[0] = 1; foo[1] = 2; foo[2] = 3; foo[3] = 4; foo[4] = 5;" + "foo = foo.subarray(1, 3);" + "var buf = Buffer.from(foo); njs.dump(buf)"), + njs_str("Buffer [2,3]") }, + + { njs_str("var buf = Buffer.from(''); njs.dump(buf)"), + njs_str("Buffer []") }, + + { njs_str("var buf = Buffer.from('α'); njs.dump(buf)"), + njs_str("Buffer [206,177]") }, + + { njs_str("[" + " ['6576696c', 'hex']," + " ['ZXZpbA==', 'base64']," + " ['ZXZpbA==#', 'base64']," + " ['ZXZpbA', 'base64url']," + " ['ZXZpbA##', 'base64url']," + "].every(args => Buffer.from(args[0], args[1]) == 'evil')"), + njs_str("true") }, + + { njs_str("var buf = Buffer.from(String.bytesFrom([0xF3])); buf"), + njs_str("�") }, + + { njs_str("Buffer.from('', 'utf-128')"), + njs_str("TypeError: \"utf-128\" encoding is not supported") }, + + { njs_str("[Buffer.from('α'), new Uint8Array(10), {}, 1]" + ".map(v=>Buffer.isBuffer(v))"), + njs_str("true,false,false,false") }, + + { njs_str("['utf8', 'utf-8', 'hex', 'base64', 'base64url', 'utf-88', '1hex']" + ".map(v=>Buffer.isEncoding(v))"), + njs_str("true,true,true,true,true,false,false") }, + + { njs_str("[" + " ['ABC', 'ABCD', -1]," + " ['ABCD', 'ABC', 1]," + " ['ABC', 'ACB', -1]," + " ['ACB', 'ABC', 1]," + " ['ABC', 'ABC', 0]," + " ['', 'ABC', -1]," + " ['', '', 0]," + "].every(args => {" + " if (Buffer.compare(Buffer.from(args[0]), Buffer.from(args[1])) != args[2]) {" + " throw new TypeError(" + " `Buffer.compare(Buffer.from(${args[0]}), Buffer.from(${args[1]})) != ${args[2]}`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("[" + " ['ABC', 'ABCD', -1]," + " ['ABCD', 'ABC', 1]," + " ['ABC', 'ACB', -1]," + " ['ACB', 'ABC', 1]," + " ['ABC', 'ABC', 0]," + " ['', 'ABC', -1]," + " ['', '', 0]," + "].every(args => {" + " if (Buffer.from(args[0]).compare(Buffer.from(args[1])) != args[2]) {" + " throw new TypeError(" + " `Buffer.from(${args[0]}).compare(Buffer.from(${args[1]})) != ${args[2]}`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "[" + " [0,3,0,2, -1]," + " [0,2,0,3, 1]," + " [3,4,3,4, 0]," + " [undefined, undefined, undefined, undefined, 0]," + " [-1, undefined, undefined, undefined, 'invalid index']," + " [0, -1, undefined, undefined, 'invalid index']," + " [0, 0, -1, undefined, 'invalid index']," + " [0, 0, 0, -1, 'invalid index']," + "]" + ".every(as => {" + " try {" + " if (buf.compare(buf, as[0], as[1], as[2], as[3]) != as[4]) {" + " throw new TypeError(" + " `buf.compare(${as[0]}, ${as[1]}, ${as[2]}, ${as[3]}) != ${as[4]}`);" + " }" + " } catch (e) { return e.message == as[4]}" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.from('ABCD');" + "buf1.compare(buf2, 5)"), + njs_str("RangeError: \"targetStart\" is out of range: 5") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.from('ABCD');" + "buf1.compare(buf2, 0, 3, 5)"), + njs_str("RangeError: \"sourceStart\" is out of range: 5") }, + + { njs_str("var arr = new Uint8Array(4);" + "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" + "arr = arr.subarray(1, 4);" + "var buf = Buffer.from('ABCD');" + "buf.compare(arr, 0, 3, 1, 4)"), + njs_str("0") }, + + { njs_str("['123', 'abc', '124', '', 'AB', 'ABCD']" + ".map(v=>Buffer.from(v)).sort(Buffer.compare).map(v=>v.toString())"), + njs_str(",123,124,AB,ABCD,abc") }, + + { njs_str("Buffer.compare(Buffer.alloc(1), 'text')"), + njs_str("TypeError: \"target\" argument must be an instance of Buffer or Uint8Array") }, + + { njs_str("Buffer.compare('text', Buffer.from('ACB'))"), + njs_str("TypeError: \"source\" argument must be an instance of Buffer or Uint8Array") }, + + { njs_str("Buffer.concat()"), + njs_str("TypeError: \"list\" argument must be an instance of Array") }, + + { njs_str("Buffer.concat([])"), + njs_str("") }, + + { njs_str("Buffer.concat([new Uint16Array(10)])"), + njs_str("TypeError: \"list[0]\" argument must be an instance of Buffer or Uint8Array") }, + + { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)]).fill('abc')"), + njs_str("abc") }, + + { njs_str("Buffer.concat([Buffer.from('AB'), Buffer.from('CD')])"), + njs_str("ABCD") }, + + { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)], 2).fill('abc')"), + njs_str("ab") }, + + { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)], 6).fill('abc')"), + njs_str("abcabc") }, + + { njs_str(njs_declare_sparse_array("list", 2) + "list[0] = new Uint8Array(2); list[1] = new Uint8Array(3);" + "Buffer.concat(list).fill('ab');"), + njs_str("ababa") }, + + { njs_str("Buffer.concat([], '123')"), + njs_str("TypeError: \"length\" argument must be of type number") }, + + { njs_str("Buffer.concat([], -1)"), + njs_str("RangeError: \"length\" is out of range") }, + + { njs_str("var buf = Buffer.from('α'); buf[1]"), + njs_str("177") }, + + { njs_str("var buf = Buffer.from('α'); buf[1] = 1; njs.dump(buf)"), + njs_str("Buffer [206,1]") }, + + { njs_str("var arrBuf = new ArrayBuffer(16);" + "var buf = Buffer.from(arrBuf); buf.buffer === arrBuf"), + njs_str("true") }, + + { njs_str("[" + " [[0], 4, '65,66,67,68,0,0,0,0,0,0']," + " [[5], 4, '0,0,0,0,0,65,66,67,68,0']," + " [[8], 2, '0,0,0,0,0,0,0,0,65,66']," + " [[8,2,4], 2, '0,0,0,0,0,0,0,0,67,68']," + " [[10], 0, '0,0,0,0,0,0,0,0,0,0']," + "]" + ".every(args => {" + " var buf1 = Buffer.from('ABCD');" + " var buf2 = Buffer.alloc(10, 0);" + " var as = args[0];" + " var length = buf1.copy(buf2, as[0], as[1], as[2]);" + "" + " if (length != args[1]) {" + " throw new TypeError(`buf1.copy(buf2, ${as[0]}, ${as[1]}, ${as[2]}): ${length} != ${args[1]}`)" + " }" + "" + " if (njs.dump(buf2) != `Buffer [${args[2]}]`) {" + " throw new TypeError(" + " `buf1.copy(buf2, ${as[0]}, ${as[1]}, ${as[2]}): ${njs.dump(buf2)} != Buffer [${args[2]}]`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("[" + " [[0], 'ABCDEF']," + " [[0,2], 'CDEFEF']," + " [[0,2,6], 'CDEFEF']," + " [[1,2,4], 'ACDDEF']," + " [[1,2,3], 'ACCDEF']" + "]" + ".every(args => {" + " var buf = Buffer.from('ABCDEF');" + " var as = args[0];" + " buf.copy(buf, as[0], as[1], as[2]);" + "" + " if (buf.toString() != args[1]) {" + " throw new TypeError(" + " `buf.copy(buf, ${as[0]}, ${as[1]}, ${as[2]}): buf.toString() != ${args[1]}`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.alloc(10, 0);" + "buf1.copy(buf2, -1)"), + njs_str("RangeError: invalid index") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.alloc(10, 0);" + "buf1.copy(buf2, 0, -1)"), + njs_str("RangeError: invalid index") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.alloc(10, 0);" + "buf1.copy(buf2, 0, 5)"), + njs_str("RangeError: \"sourceStart\" is out of range: 5") }, + + { njs_str("var arr = new Uint8Array(4);" + "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" + "arr = arr.subarray(1, 4);" + "var buf1 = Buffer.from(arr);" + "var buf2 = Buffer.alloc(10, 0);" + "var length = buf1.copy(buf2, 1, 1, 2); [length, njs.dump(buf2)]"), + njs_str("1,Buffer [0,67,0,0,0,0,0,0,0,0]") }, + + { njs_str("var arr = new Uint8Array(4);" + "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" + "arr = arr.subarray(1, 4);" + "var buf1 = Buffer.from(arr);" + "var buf2 = Buffer.alloc(10, 0);" + "var length = buf1.copy(buf2, 1, 1, 2); [length, njs.dump(buf2)]"), + njs_str("1,Buffer [0,67,0,0,0,0,0,0,0,0]") }, + + { njs_str("[" + " ['ABC', 'ABCD', false]," + " ['ABCD', 'ABC', false]," + " ['ABC', 'ACB', false]," + " ['ACB', 'ABC', false]," + " ['ABC', 'ABC', true]," + " ['', 'ABC', false]," + " ['', '', true]," + "].every(args => {" + " if (Buffer.from(args[0]).equals(Buffer.from(args[1])) != args[2]) {" + " throw new TypeError(" + " `Buffer.from(${args[0]}).compare(Buffer.from(${args[1]})) != ${args[2]}`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("var buf = Buffer.alloc(4);" + "buf.fill('ZXZpbA==', 'base64')"), + njs_str("evil") }, + + { njs_str("var buf = Buffer.alloc(4);" + "buf.fill('6576696c', 'hex')"), + njs_str("evil") }, + + { njs_str("var buf = Buffer.alloc(4);" + "buf.fill('ZXZpbA==', '')"), + njs_str("TypeError: \"\" encoding is not supported") }, + + { njs_str("var buf = Buffer.alloc(8);" + "buf.fill('6576696c', 'hex')"), + njs_str("evilevil") }, + + { njs_str("var buf = Buffer.alloc(10);" + "buf.fill('evil')"), + njs_str("evilevilev") }, + + { njs_str("var buf = Buffer.allocUnsafe(5);" + "buf[3] = 1;" + "buf.fill(''); njs.dump(buf)"), + njs_str("Buffer [0,0,0,0,0]") }, + + { njs_str("var arr = new Uint8Array(4);" + "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" + "arr = arr.subarray(1, 4);" + "var buf = Buffer.allocUnsafe(6);" + "buf.fill(arr); njs.dump(buf)"), + njs_str("Buffer [66,67,68,66,67,68]") }, + + { njs_str("var buf = Buffer.alloc(6, 'ABCDEF');" + "buf.fill(buf, 2, 6)"), + njs_str("ABABCD") }, + + { njs_str("Buffer.alloc(6).fill(0x41)"), + njs_str("AAAAAA") }, + + { njs_str("Buffer.alloc(6).fill({valueOf(){return 0x42}})"), + njs_str("BBBBBB") }, + + { njs_str("njs.dump(Buffer.alloc(3).fill(-1))"), + njs_str("Buffer [255,255,255]") }, + + { njs_str("[NaN, Infinity, -Infinity, undefined, null, {}]" + ".every(v => njs.dump(Buffer.alloc(3).fill(v)) == 'Buffer [0,0,0]')"), + njs_str("true") }, + + { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 4,3))"), + njs_str("Buffer [0,0,0,0,0,0]") }, + + { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 3,3))"), + njs_str("Buffer [0,0,0,0,0,0]") }, + + { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 2,3))"), + njs_str("Oops") }, + + { njs_str("njs.dump(Buffer.alloc(5).fill('α'))"), + njs_str("Buffer [206,177,206,177,206]") }, + + { njs_str("Buffer.alloc(4).fill('ABCD', -1)"), + njs_str("RangeError: invalid index") }, + + { njs_str("Buffer.alloc(4).fill('ABCD', 5)"), + njs_str("RangeError: \"offset\" is out of range") }, + + { njs_str("Buffer.alloc(4).fill('ABCD', 0, -1)"), + njs_str("RangeError: invalid index") }, + + { njs_str("Buffer.alloc(4).fill('ABCD', 0, 5)"), + njs_str("RangeError: \"end\" is out of range") }, + + { njs_str("Buffer.alloc(513).fill('A'.repeat(512)).length"), + njs_str("513") }, + + { njs_str("njs.dump(Buffer.alloc(4).fill((new Uint8Array(5)).fill(1)))"), + njs_str("Buffer [1,1,1,1]") }, + + { njs_str("var src = new Uint8Array(10).fill(255);" + "var u8 = new Uint8Array(src.buffer, 1, 8);" + "u8.set([1,2,3,4,5,6,7,8]);" + "njs.dump(Buffer.alloc(9).fill(u8))"), + njs_str("Buffer [1,2,3,4,5,6,7,8,1]") }, + + { njs_str("Buffer.alloc(513).fill((new Uint8Array(512)).fill(1)).length"), + njs_str("513") }, + + { njs_str("Buffer.alloc(4).fill('ABCD', undefined, undefined, 'utf-128')"), + njs_str("TypeError: \"utf-128\" encoding is not supported") }, + + { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 3);" + "buf1.fill('B', 1, 2); njs.dump(buf1)"), + njs_str("Buffer [66,66]") }, + + { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 3);" + "buf1.fill(0x42, 1, 2); njs.dump(buf1)"), + njs_str("Buffer [66,66]") }, + + { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 2);" + "var buf2 = Buffer.from('ABCD').subarray(1, 3);" + "buf2.fill(buf1, 1, 2); njs.dump(buf2)"), + njs_str("Buffer [66,66]") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "['BC', 'CB', 'ABCD', 'ABCDE', ''].map(v=>buf.indexOf(v))"), + njs_str("1,-1,0,-1,0") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "[0,5,-2,-1].map(v=>buf.indexOf('C', v))"), + njs_str("2,-1,2,-1") }, + + { njs_str("var buf = Buffer.from('evil');" + "buf.indexOf('ZXZpbA==', undefined, 'base64')"), + njs_str("0") }, + + { njs_str("var buf = Buffer.from('evil');" + "buf.indexOf('6576696c', undefined, 'hex')"), + njs_str("0") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "buf.indexOf('C', undefined, 'utf-128')"), + njs_str("TypeError: \"utf-128\" encoding is not supported") }, + + { njs_str("var buf = Buffer.from('ABCDABC');" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.indexOf(Buffer.from(v)))"), + njs_str("1,-1,0,-1,0,2,-1") }, + + { njs_str("var buf = Buffer.from('ABCDABC');" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.includes(Buffer.from(v)))"), + njs_str("true,false,true,false,true,true,false") }, + + { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.indexOf(Buffer.from(v)))"), + njs_str("1,-1,0,-1,0,2,-1") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "buf.indexOf(0x43)"), + njs_str("2") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "buf.indexOf(0x43, -2)"), + njs_str("2") }, + + { njs_str("var buf = Buffer.from('ABCD');" + "buf.indexOf(0x43, -1)"), + njs_str("-1") }, + + { njs_str("var buf1 = Buffer.from('ABCD');" + "var buf2 = Buffer.from('XXCX').subarray(2, 3);" + "buf1.indexOf(buf2)"), + njs_str("2") }, + + { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 4);" + "buf1.indexOf(0x43)"), + njs_str("1") }, + + { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 4);" + "buf1.indexOf('C')"), + njs_str("1") }, + + { njs_str("var buf = Buffer.from('ABCDABC');" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(v))"), + njs_str("5,-1,0,-1,7,6,-1") }, + + { njs_str("var buf = Buffer.from('ABCDABC');" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(Buffer.from(v)))"), + njs_str("5,-1,0,-1,7,6,-1") }, + + { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(v))"), + njs_str("5,-1,0,-1,7,6,-1") }, + + { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" + "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(Buffer.from(v)))"), + njs_str("5,-1,0,-1,7,6,-1") }, + + { njs_str("var buf = Buffer.from('CABCD');" + "[2,-2,1,-10,10,-5,-4,0].map(v=>buf.lastIndexOf('C', v))"), + njs_str("0,3,0,-1,3,0,0,0") }, + + { njs_str("var buf = Buffer.from('CABCD');" + "[2,-2,1,-10,10,-5,-4,0].map(v=>buf.lastIndexOf(Buffer.from('CZ').subarray(0,1), v))"), + njs_str("0,3,0,-1,3,0,0,0") }, + + { njs_str("var buf = Buffer.from('CABCD');" + "buf.lastIndexOf(0x43)"), + njs_str("3") }, + + { njs_str("var buf = Buffer.from('CABCD');" + "[2,1,0,4,5,-1,-5].map(v=>buf.lastIndexOf(0x43, v))"), + njs_str("0,0,0,3,3,3,0") }, + + { njs_str("var buf1 = Buffer.from('ACBCD').subarray(1, 4);" + "var buf2 = Buffer.from('C');" + "buf1.lastIndexOf(buf2)"), + njs_str("2") }, + + { njs_str("var buf1 = Buffer.from('XXCXX').subarray(2,3);" + "buf1.lastIndexOf(Buffer.from('X'))"), + njs_str("-1") }, + + { njs_str("var buf = Buffer.from('ACBCD').subarray(1, 4);" + "buf.lastIndexOf(0x43)"), + njs_str("2") }, + + { njs_str("var buf = Buffer.from('ACBCD').subarray(1, 4);" + "buf.lastIndexOf('C')"), + njs_str("2") }, + + { njs_str("['swap16', 'swap32', 'swap64'].every(method => {" + " var buf = Buffer.from([]);" + " buf[method]();" + " return njs.dump(buf) === 'Buffer []';" + "})"), + njs_str("true") }, + + { njs_str("['swap16', 'swap32', 'swap64'].every(method => {" + " var buf = Buffer.from([1,2,3]);" + " try { buf[method]() } " + " catch(e) {return e.message === `Buffer size must be a multiple of ${method.substr(4)}-bits`};" + "})"), + njs_str("true") }, + + { njs_str("['swap16', 'swap32', 'swap64'].map(method => {" + " var buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);" + " buf[method]();" + " return njs.dump(buf);" + "})"), + njs_str("Buffer [2,1,4,3,6,5,8,7]," + "Buffer [4,3,2,1,8,7,6,5]," + "Buffer [8,7,6,5,4,3,2,1]") }, + + { njs_str("['swap16', 'swap32', 'swap64'].map(method => {" + " var u8 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8]);" + " var buf = Buffer.from(u8.buffer, 1);" + " buf[method]();" + " return njs.dump(buf);" + "})"), + njs_str("Buffer [2,1,4,3,6,5,8,7]," + "Buffer [4,3,2,1,8,7,6,5]," + "Buffer [8,7,6,5,4,3,2,1]") }, + + { njs_str("njs.dump(Buffer.from('αααα').toJSON())"), + njs_str("{type:'Buffer',data:[206,177,206,177,206,177,206,177]}") }, + + { njs_str("njs.dump(Buffer.from('').toJSON())"), + njs_str("{type:'Buffer',data:[]}") }, + + { njs_str("[" + " [['base64'], 'ZXZpbA==']," + " [['base64url'], 'ZXZpbA']," + " [['hex'], '6576696c']," + " [[undefined,1,3], 'vi']," + " [[undefined,5], '']," + " [[undefined,undefined,5], 'evil']," + " [[undefined,undefined,undefined], 'evil']," + "].every(args => {" + " var buf = Buffer.from('evil');" + " var as = args[0];" + " if (buf.toString(as[0], as[1], as[2]) != args[1]) {" + " throw new TypeError(" + " `buf.toString(${as[0]}, ${as[1]}, ${as[2]}) != ${args[1]}`);" + " }" + " return true;" + "})"), + njs_str("true") }, + + { njs_str("Buffer.from('evil').toString('utf-128')"), + njs_str("TypeError: \"utf-128\" encoding is not supported") }, + + { njs_str("var buf = Buffer.allocUnsafe(4);" + "var len = buf.write('ZXZpbA==', 'base64'); [len, buf]"), + njs_str("4,evil") }, + + { njs_str("var buf = Buffer.allocUnsafe(4);" + "var len = buf.write('ZXZpbA==', undefined, 'base64'); [len, buf]"), + njs_str("4,evil") }, + + { njs_str("var buf = Buffer.allocUnsafe(4);" + "var len = buf.write('ZXZpbA==', undefined, undefined, 'base64'); [len, buf]"), + njs_str("4,evil") }, + + { njs_str("Buffer.allocUnsafe(4).write()"), + njs_str("TypeError: first argument must be a string") }, + + { njs_str("Buffer.allocUnsafe(4).write({a: 1})"), + njs_str("TypeError: first argument must be a string") }, + + { njs_str("Buffer.alloc(4).write('evil', 4, 1);"), + njs_str("RangeError: \"offset\" is out of range") }, + + { njs_str("Buffer.alloc(4).write('evil', -1);"), + njs_str("RangeError: invalid index") }, + + { njs_str("var buf = Buffer.alloc(4);" + "var len = buf.write('evil', 3, 1); [len, njs.dump(buf)]"), + njs_str("1,Buffer [0,0,0,101]") }, + + { njs_str("var buf = Buffer.alloc(4);" + "var len = buf.write('evil', 0, 5); [len, buf]"), + njs_str("4,evil") }, + + { njs_str("Buffer.alloc(4).write('evil', undefined, -1);"), + njs_str("RangeError: invalid index") }, + + { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" + "[1,2,3,4,5,6].map(byte => [buf.readUIntLE(0, byte), buf.readUIntLE(1, byte)])"), + njs_str("250,251," + "64506,64763," + "16579578,16645371," + "4261215226,4278058235," + "1095182908410,1099494718715," + "281470647991290,274877890034939") }, + + { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" + "[1,2,3,4,5,6].map(byte => [buf.readIntLE(0, byte), buf.readIntLE(1, byte)])"), + njs_str("-6,-5," + "-1030,-773," + "-197638,-131845," + "-33752070,-16909061," + "-4328719366,-16909061," + "-4328719366,-6597086675717") }, + + { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" + "[1,2,3,4,5,6].map(byte => [buf.readUIntBE(0, byte), buf.readUIntBE(1, byte)])"), + njs_str("250,251," + "64251,64508," + "16448508,16514301," + "4210818301,4227661310," + "1077969485310,1082281295615," + "275960188239615,277064011677689") }, + + { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" + "[1,2,3,4,5,6].map(byte => [buf.readIntBE(0, byte), buf.readIntBE(1, byte)])"), + njs_str("-6,-5," + "-1285,-1028," + "-328708,-262915," + "-84148995,-67305986," + "-21542142466,-17230332161," + "-5514788471041,-4410965032967") }, + + { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" + "function t(sign, endianness, offset) { " + " return [1,2,4].every(size => {" + " var method = `read${sign}Int${size * 8}`;" + " if (size > 1) { method += endianness};" + " var gmethod = `read${sign}Int${endianness}`;" + " var gv = buf[gmethod](offset, size);" + " var sv = buf[method](offset);" + " if (gv != sv) {throw Error(`${gmethod}(${offset},${size}):${gv} != ${method}(${offset}):${sv}`)}" + " return true;" + " });" + "}; " + "t('U', 'LE', 0) && t('U', 'LE', 1)" + "&& t('', 'LE', 0) && t('', 'LE', 1)" + "&& t('U', 'BE', 0) && t('U', 'BE', 1)" + "&& t('', 'BE', 0) && t('', 'BE', 1)"), + njs_str("true") }, + + { njs_str("var buf = Buffer.alloc(9);" + "function t(sign, endianness, offset) { " + " return [1,2,4].every(size => {" + " var rgmethod = `read${sign}Int${endianness}`;" + " var wgmethod = `write${sign}Int${endianness}`;" + " var rmethod = `read${sign}Int${size * 8}`;" + " var wmethod = `write${sign}Int${size * 8}`;" + " if (size > 1) { rmethod += endianness; wmethod += endianness; };" + " var v = 0x7abbccddeeff & (size * 8 - 1);" + "" + " buf[wgmethod](v, offset, size);" + " var gv = buf[rgmethod](offset, size);" + "" + " buf.fill(0);" + " buf[wmethod](v, offset);" + " var sv = buf[rmethod](offset);" + " if (gv != sv) {throw Error(`${wmethod}(${v}, ${offset}):${sv} != ${wgmethod}(${v},${offset}):${gv}`)}" + " return true;" + " });" + "}; " + "t('U', 'LE', 0) && t('U', 'LE', 1)" + "&& t('', 'LE', 0) && t('', 'LE', 1)" + "&& t('U', 'BE', 0) && t('U', 'BE', 1)" + "&& t('', 'BE', 0) && t('', 'BE', 1)"), + njs_str("true") }, + + { njs_str(njs_buffer_byte_map("writeUIntLE", "+", 1)), + njs_str("Buffer [128,0,0,0,0,0]," + "Buffer [0,64,0,0,0,0]," + "Buffer [0,0,32,0,0,0]," + "Buffer [0,0,0,16,0,0]," + "Buffer [0,0,0,0,8,0]," + "Buffer [0,0,0,0,0,4]") }, + + { njs_str(njs_buffer_byte_map("writeUIntBE", "+", 1)), + njs_str("Buffer [128,0,0,0,0,0]," + "Buffer [64,0,0,0,0,0]," + "Buffer [32,0,0,0,0,0]," + "Buffer [16,0,0,0,0,0]," + "Buffer [8,0,0,0,0,0]," + "Buffer [4,0,0,0,0,0]") }, + + { njs_str(njs_buffer_byte_map("writeIntLE", "-", 2)), + njs_str("Buffer [192,0,0,0,0,0]," + "Buffer [0,224,0,0,0,0]," + "Buffer [0,0,240,0,0,0]," + "Buffer [0,0,0,248,0,0]," + "Buffer [0,0,0,0,252,0]," + "Buffer [0,0,0,0,0,254]") }, + + { njs_str(njs_buffer_byte_map("writeIntBE", "-", 2)), + njs_str("Buffer [192,0,0,0,0,0]," + "Buffer [224,0,0,0,0,0]," + "Buffer [240,0,0,0,0,0]," + "Buffer [248,0,0,0,0,0]," + "Buffer [252,0,0,0,0,0]," + "Buffer [254,0,0,0,0,0]") }, + + { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleBE()"), + njs_str("8.20788039913184e-304") }, + + { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleLE()"), + njs_str("5.447603722011605e-270") }, + + { njs_str("Buffer.from([1, 2, 3, 4]).readFloatBE()"), + njs_str("2.387939260590663e-38") }, + + { njs_str("Buffer.from([1, 2, 3, 4]).readFloatLE()"), + njs_str("1.539989614439558e-36") }, + + { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleBE(1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleLE(1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("Buffer.from([1, 2, 3, 4]).readFloatBE(1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("Buffer.from([1, 2, 3, 4]).readFloatLE(1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("var buf = Buffer.allocUnsafe(8);" + "buf.writeDoubleBE(123.456); njs.dump(buf)"), + njs_str("Buffer [64,94,221,47,26,159,190,119]") }, + + { njs_str("var buf = Buffer.allocUnsafe(8);" + "buf.writeDoubleLE(123.456); njs.dump(buf)"), + njs_str("Buffer [119,190,159,26,47,221,94,64]") }, + + { njs_str("var buf = Buffer.allocUnsafe(4);" + "buf.writeFloatBE(123.456); njs.dump(buf)"), + njs_str("Buffer [66,246,233,121]") }, + + { njs_str("var buf = Buffer.allocUnsafe(4);" + "buf.writeFloatLE(123.456); njs.dump(buf)"), + njs_str("Buffer [121,233,246,66]") }, + + { njs_str("var buf = Buffer.allocUnsafe(8).writeDoubleBE(123.456, 1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("var buf = Buffer.allocUnsafe(8).writeDoubleLE(123.456, 1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("var buf = Buffer.allocUnsafe(4).writeFloatBE(123.456, 1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("var buf = Buffer.allocUnsafe(4).writeFloatLE(123.456, 1)"), + njs_str("RangeError: index 1 is outside the bound of the buffer") }, + + { njs_str("var buffer = require('buffer');" + "buffer.Buffer.alloc(5).fill('ABC')"), + njs_str("ABCAB") }, + + { njs_str("var buffer = require('buffer');" + "typeof buffer.kMaxLength === 'number' "), + njs_str("true") }, + + { njs_str("var buffer = require('buffer');" + "typeof buffer.constants.MAX_LENGTH === 'number' "), + njs_str("true") }, + + { njs_str("var buffer = require('buffer');" + "typeof buffer.constants.MAX_STRING_LENGTH === 'number' "), + njs_str("true") }, }; @@ -18690,35 +19612,35 @@ static njs_unit_test_t njs_externals_test[] = "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"), njs_str("АБВ,АБВГДЕЁЖЗИЙ,1,{},{},02|АБВ") }, - { njs_str("var s = (new TextDecoder()).decode($r.u8buffer); [s, s.length]"), + { njs_str("var s = (new TextDecoder()).decode($r.buffer); [s, s.length]"), njs_str("АБВГДЕЁЖЗИЙ,11") }, - { njs_str("var b = $r.u8buffer; " + { njs_str("var b = $r.buffer; " "b[4] = '@'.codePointAt(0); b[5] = '#'.codePointAt(0);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБ@#ГДЕЁЖЗИЙ,12") }, - { njs_str("var b = $r.u8buffer; " + { njs_str("var b = $r.buffer; " "b.copyWithin(16,0,6);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБВГДЕЁЖАБВ,11") }, - { njs_str("var b = $r.u8buffer; " + { njs_str("var b = $r.buffer; " "b.fill('#'.codePointAt(0));" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("######################,22") }, - { njs_str("var b = $r.u8buffer; " + { njs_str("var b = $r.buffer; " "b.set(['@'.codePointAt(0), '#'.codePointAt(0)], 4);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБ@#ГДЕЁЖЗИЙ,12") }, - { njs_str("var b = $r.u8buffer; " + { njs_str("var b = $r.buffer; " "var u16 = new Uint16Array(b.buffer); u16.reverse();" "var s = (new TextDecoder()).decode(u16); [s, s.length]"), njs_str("ЙИЗЖЁЕДГВБА,11") }, - { njs_str("$r.u8buffer.sort().slice(0,3)"), + { njs_str("$r.buffer.sort().slice(0,3)"), njs_str("129,144,145") }, }; -- 2.47.3