]> git.kaiwu.me - njs.git/commitdiff
Introduced Buffer implementation.
authorAlexander Borisov <alexander.borisov@nginx.com>
Wed, 16 Sep 2020 14:22:01 +0000 (17:22 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Wed, 16 Sep 2020 14:22:01 +0000 (17:22 +0300)
21 files changed:
auto/sources
src/njs.h
src/njs_array_buffer.c
src/njs_array_buffer.h
src/njs_buffer.c [new file with mode: 0644]
src/njs_buffer.h [new file with mode: 0644]
src/njs_builtin.c
src/njs_encoding.c
src/njs_main.h
src/njs_object_hash.h
src/njs_string.c
src/njs_string.h
src/njs_typed_array.c
src/njs_typed_array.h
src/njs_utils.h
src/njs_value.c
src/njs_value.h
src/njs_vm.c
src/njs_vm.h
src/test/njs_externals_test.c
src/test/njs_unit_test.c

index b756bc9917395a705a5c6eb333799eeb51b863fa..418a4d61136af0c7a73c45ffc5de675318168262 100644 (file)
@@ -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=" \
index f486511e89890194bdad194a92075b41c980a0be..2ddaa0d0c863d7f8104add050d044b70153a78ab 100644 (file)
--- 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.
index ab104c7895fc3876c120b82b80c6b19d145cf248..8c09012f1e19a8f13bfe4bff6f60fe6f0e14df63 100644 (file)
@@ -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;
     }
index 6db271b50c31e9d920f16db0dd2a33b6bdf74d46..b1d6d755a83babca3aa14046a89c0b035be955d5 100644 (file)
@@ -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 (file)
index 0000000..dfa8a0e
--- /dev/null
@@ -0,0 +1,2944 @@
+
+/*
+ * 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)
+
+
+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 (file)
index 0000000..e9b94a6
--- /dev/null
@@ -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_ */
index 352d67a6fc50bf73ba81067be40af3349a706487..62947ebc8092fdd9ebcc31ba65550767d5e63956 100644 (file)
@@ -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"),
index 01d28af14c30540dd15759cfa0a7323495145000..145004917b6b841deac4b42445a86b330964de9b 100644 (file)
@@ -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;
     }
index 9884d2b10548bdeb324acda12ec2fc2e7ad761ab..3102c62bea3f754faf1c2382c9601187faa4de42 100644 (file)
@@ -75,6 +75,7 @@
 #include <njs_math.h>
 #include <njs_json.h>
 #include <njs_encoding.h>
+#include <njs_buffer.h>
 
 #include <njs_timer.h>
 #include <njs_module.h>
index 0cd25bcfeac4de228730345cb99ca22e3f279702..db2ef7faba2cb46d29b1948df057c6662fbf3141 100644 (file)
         '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_ */
index 689f54bb2be2a36524516a84df80ee5ac1e2ef50..f7271d71c956ec9d4b9bd07d51299a20167c4eec 100644 (file)
@@ -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)
 {
index 21f74faf107df3432d75da1563108024de88aa23..16afd14edc34e0cd53e090fd978d7dabb0b01915 100644 (file)
@@ -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,
index e23fd596e72b6979921ca856febabf1d193b73cf..accfb77e1a06df5c16d08aac6430a1aa72e73b13 100644 (file)
@@ -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)
index 78db766504f67bd3980eef4c182523871296c0fd..3f8b25366a656488aa1e6cf53c13e21e86f1f170 100644 (file)
@@ -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)
index 960954a7e5db7626f5d6bcbb7e801d62a26a135f..c729ca1365ed0ec8605ba740fba359587dafdc75 100644 (file)
@@ -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,
index c733aacec2fd0e89fc13ef9890cf34ea5bec9716..b2c2ad9713c7f9ac29bd06d89efa394d79a6743a 100644 (file)
@@ -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");
index 130abb7f2ccaa47e43273c6e40083f8d375e0526..967dcb5d983bec5d53e51346e22ca51174096545 100644 (file)
@@ -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;
index a8daf2a0fb551bb4cea22d53542c1d82b3b53aa9..0b1269a492a41880540645e2519d84e5d3074b84 100644 (file)
@@ -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);
 }
 
 
index cec67fe6f9d63ddc6c1fc72e1e9fc0f6b3f70522..02789e9a4d36fe6eb14cb1fb5173b19083d6d7b0 100644 (file)
@@ -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,
index 67f4cc64169c2f091111adb8fe3de9650982d4e0..9787be32a890bd7b504b7fd759f3b51f71e06f61 100644 (file)
@@ -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,
         }
     },
 
index 4da0bc0b9ca136efcfd5041d4e55294e050e31cc..6b4b766b8a276fb6c39682aecf0891b36ebfd521 100644 (file)
     "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") },
 };