blob: 554425958bac767a5fc5bf8ae5a91a45afe88445 [file] [log] [blame]
<
/*
* Copyright (C) Alexander Borisov
* Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
#define INT24_MAX 0x7FFFFF
#define INT40_MAX 0x7FFFFFFFFFULL
#define INT48_MAX 0x7FFFFFFFFFFFULL
#define njs_buffer_magic(size, sign, little) \
((size << 2) | (sign << 1) | little)
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 }
};
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,
const njs_value_t *fill, const njs_value_t *encoding, uint64_t offset,
uint64_t end);
static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, const 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,
const njs_value_t *value, njs_typed_array_t *array, uint8_t *start,
uint8_t *end);
static void njs_buffer_decode_destroy(njs_vm_t *vm, const 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_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;
proto = &vm->prototypes[NJS_OBJ_TYPE_BUFFER].object;
array->type = NJS_OBJ_TYPE_UINT8_ARRAY;
njs_lvlhsh_init(&array->object.hash);
njs_lvlhsh_init(&array->object.shared_hash);
array->object.__proto__ = proto;
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;
}
njs_typed_array_t *
njs_buffer_alloc(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;
}
njs_int_t
njs_buffer_new(njs_vm_t *vm, njs_value_t *value, const u_char *start,
uint32_t size)
{
njs_typed_array_t *buffer;
buffer = njs_buffer_alloc(vm, size, 0);
if (njs_slow_path(buffer == NULL)) {
return NJS_ERROR;
}
memcpy(njs_typed_array_buffer(buffer)->u.u8, start, size);
njs_set_typed_array(value, buffer);
return NJS_OK;
}
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_safe(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_value_t *fill;
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(vm, size, safe || nargs <= 2);
if (njs_slow_path(array == NULL)) {
return NJS_ERROR;
}
fill = njs_arg(args, nargs, 2);
if (safe && njs_is_defined(fill)) {
ret = njs_buffer_fill(vm, array, fill, njs_arg(args, nargs, 3), 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_encoding(vm, njs_arg(args, nargs, 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(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);
if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
length = njs_typed_array_length(array);
buffer = njs_buffer_alloc(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(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;
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:
encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2));
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)
{
uint64_t num_start, num_end;
njs_int_t ret;
njs_array_buffer_t *buffer;
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;
}
buffer = njs_typed_array_buffer(array);
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
*out_start = &buffer->u.u8[array->offset + num_start];
*out_end = &buffer->u.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(njs_is_detached_buffer(arr->buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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(njs_is_detached_buffer(arr->buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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(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)
{
const njs_value_t *value;
value = njs_arg(args, nargs, 1);
njs_set_boolean(&vm->retval, njs_is_defined(value)
&& njs_buffer_encoding(vm, value) != 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;
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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;
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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;
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;
}
}
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;
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);
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 fill;
}
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 fill;
}
ret = njs_value_to_index(vm, value_end, &end);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
}
fill:
ret = njs_buffer_fill(vm, array, value, encode, 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, const njs_value_t *fill,
const njs_value_t *encode, uint64_t offset, uint64_t end)
{
double num;
uint8_t *start, *stop;
njs_int_t ret;
njs_array_buffer_t *buffer;
const njs_buffer_encoding_t *encoding;
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 (fill->type) {
case NJS_STRING:
encoding = njs_buffer_encoding(vm, encode);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
return njs_buffer_fill_string(vm, fill, array, encoding, start, stop);
case NJS_TYPED_ARRAY:
return njs_buffer_fill_typed_array(vm, fill, array, start, stop);
default:
ret = njs_value_to_number(vm, njs_value_arg(fill), &num);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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, const 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, const 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, *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;
}
value_start = njs_arg(args, nargs, 2);
value_end = njs_arg(args, nargs, 3);
start = 0;
end = array->byte_length;
encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 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);
}
if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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 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;
const uint8_t *u8;
njs_typed_array_t *array, *src;
njs_array_buffer_t *buffer;
const njs_buffer_encoding_t *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;
}
}
}
encoding:
encoding = njs_buffer_encoding(vm, enc);
if (njs_slow_path(encoding == NULL)) {
return NJS_ERROR;
}
buffer = njs_typed_array_buffer(array);
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
u8 = &buffer->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;
}
if (njs_slow_path(njs_is_detached_buffer(src->buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
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));
}
break;
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;
njs_array_buffer_t *buffer;
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;
}
buffer = njs_typed_array_buffer(ta);
if (njs_slow_path(njs_is_detached_buffer(buffer))) {
njs_type_error(vm, "detached buffer");
return NJS_ERROR;
}
p = &buffer->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;
}
const njs_buffer_encoding_t *
njs_buffer_encoding(njs_vm_t *vm, const njs_value_t *value)
{
njs_str_t name;
njs_buffer_encoding_t *encoding;
if (njs_slow_path(!njs_is_string(value))) {
if (njs_is_defined(value)) {
njs_type_error(vm, "encoding must be a string");
return NULL;
}
return &njs_buffer_encodings[0];
}
njs_string_get(value, &name);
for (encoding = &njs_buffer_encodings[0];
encoding->name.length != 0;
encoding++)
{
if (njs_strstr_eq(&name, &encoding->name)) {
return encoding;
}
}
njs_type_error(vm, "\"%V\" encoding is not supported", &name);
return NULL;
}
njs_int_t
njs_buffer_decode_string(njs_vm_t *vm, const 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, const 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),