[njs] Introduced Buffer implementation.

Alexander Borisov alexander.borisov at nginx.com
Wed Sep 16 14:23:05 UTC 2020


details:   https://hg.nginx.org/njs/rev/27bb9caf186c
branches:  
changeset: 1523:27bb9caf186c
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Wed Sep 16 17:22:01 2020 +0300
description:
Introduced Buffer implementation.

diffstat:

 auto/sources                  |     1 +
 src/njs.h                     |     6 +-
 src/njs_array_buffer.c        |    16 +-
 src/njs_array_buffer.h        |     5 +-
 src/njs_buffer.c              |  2944 +++++++++++++++++++++++++++++++++++++++++
 src/njs_buffer.h              |    19 +
 src/njs_builtin.c             |    14 +
 src/njs_encoding.c            |     4 +-
 src/njs_main.h                |     1 +
 src/njs_object_hash.h         |    10 +
 src/njs_string.c              |    42 +
 src/njs_string.h              |     4 +
 src/njs_typed_array.c         |    67 +-
 src/njs_typed_array.h         |     7 +-
 src/njs_utils.h               |    12 +
 src/njs_value.c               |     1 +
 src/njs_value.h               |     1 +
 src/njs_vm.c                  |     6 +-
 src/njs_vm.h                  |     1 +
 src/test/njs_externals_test.c |     9 +-
 src/test/njs_unit_test.c      |   936 ++++++++++++-
 21 files changed, 4011 insertions(+), 95 deletions(-)

diffs (truncated from 4450 to 1000 lines):

diff -r e1dccc5a2f05 -r 27bb9caf186c auto/sources
--- a/auto/sources	Fri Sep 11 13:54:57 2020 +0000
+++ b/auto/sources	Wed Sep 16 17:22:01 2020 +0300
@@ -57,6 +57,7 @@ NJS_LIB_SRCS=" \
    src/njs_promise.c \
    src/njs_query_string.c \
    src/njs_encoding.c \
+   src/njs_buffer.c \
 "
 
 NJS_LIB_TEST_SRCS=" \
diff -r e1dccc5a2f05 -r 27bb9caf186c src/njs.h
--- a/src/njs.h	Fri Sep 11 13:54:57 2020 +0000
+++ b/src/njs.h	Wed Sep 16 17:22:01 2020 +0300
@@ -317,11 +317,11 @@ NJS_EXPORT njs_int_t njs_vm_value_string
     njs_value_t *value, uintptr_t *next);
 
 /*
- * Sets a Uint8Array value.
+ * Sets a Buffer value.
  *   start data is not copied and should not be freed.
  */
-NJS_EXPORT njs_int_t njs_vm_value_typed_array_uint8_set(njs_vm_t *vm,
-    njs_value_t *value, const u_char *start, uint32_t size);
+NJS_EXPORT njs_int_t njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value,
+    const u_char *start, uint32_t size);
 
 /*
  * Converts a value to string.
diff -r e1dccc5a2f05 -r 27bb9caf186c src/njs_array_buffer.c
--- a/src/njs_array_buffer.c	Fri Sep 11 13:54:57 2020 +0000
+++ b/src/njs_array_buffer.c	Wed Sep 16 17:22:01 2020 +0300
@@ -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, uin
         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 *v
         return NJS_ERROR;
     }
 
-    array = njs_array_buffer_alloc(vm, size);
+    array = njs_array_buffer_alloc(vm, size, 1);
     if (njs_slow_path(array == NULL)) {
         return NJS_ERROR;
     }
diff -r e1dccc5a2f05 -r 27bb9caf186c src/njs_array_buffer.h
--- a/src/njs_array_buffer.h	Fri Sep 11 13:54:57 2020 +0000
+++ b/src/njs_array_buffer.h	Wed Sep 16 17:22:01 2020 +0300
@@ -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
 
     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 -r e1dccc5a2f05 -r 27bb9caf186c src/njs_buffer.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_buffer.c	Wed Sep 16 17:22:01 2020 +0300
@@ -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;


More information about the nginx-devel mailing list