[njs] Introduced TextEncoder/TextDecoder implementation.
Alexander Borisov
alexander.borisov at nginx.com
Thu Jul 30 18:34:52 UTC 2020
details: https://hg.nginx.org/njs/rev/bcd1a41c6a67
branches:
changeset: 1485:bcd1a41c6a67
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Tue Jul 28 16:58:59 2020 +0300
description:
Introduced TextEncoder/TextDecoder implementation.
According to WHATWG encoding spec.
diffstat:
auto/sources | 1 +
src/njs_builtin.c | 22 +
src/njs_encoding.c | 804 +++++++++++++++++++++++++++++++++++++++++++++++
src/njs_encoding.h | 14 +
src/njs_main.h | 1 +
src/njs_object_hash.h | 30 +
src/njs_typed_array.c | 73 ++-
src/njs_typed_array.h | 2 +
src/njs_unicode.h | 1 +
src/njs_value.h | 7 +
src/njs_vm.h | 4 +
src/test/njs_unit_test.c | 143 ++++++++
12 files changed, 1072 insertions(+), 30 deletions(-)
diffs (truncated from 1336 to 1000 lines):
diff -r 0fad09ddb37a -r bcd1a41c6a67 auto/sources
--- a/auto/sources Thu Jul 30 17:47:05 2020 +0000
+++ b/auto/sources Tue Jul 28 16:58:59 2020 +0300
@@ -56,6 +56,7 @@ NJS_LIB_SRCS=" \
src/njs_typed_array.c \
src/njs_promise.c \
src/njs_query_string.c \
+ src/njs_encoding.c \
"
NJS_LIB_TEST_SRCS=" \
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_builtin.c
--- a/src/njs_builtin.c Thu Jul 30 17:47:05 2020 +0000
+++ b/src/njs_builtin.c Tue Jul 28 16:58:59 2020 +0300
@@ -71,6 +71,8 @@ static const njs_object_type_init_t *con
&njs_date_type_init,
&njs_promise_type_init,
&njs_array_buffer_type_init,
+ &njs_text_decoder_type_init,
+ &njs_text_encoder_type_init,
/* Hidden types. */
@@ -1283,6 +1285,26 @@ static const njs_object_prop_t njs_glob
{
.type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("TextDecoder"),
+ .value = njs_prop_handler2(njs_top_level_constructor,
+ NJS_OBJ_TYPE_TEXT_DECODER,
+ NJS_TEXT_DECODER_HASH),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("TextEncoder"),
+ .value = njs_prop_handler2(njs_top_level_constructor,
+ NJS_OBJ_TYPE_TEXT_ENCODER,
+ NJS_TEXT_ENCODER_HASH),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
.name = njs_string("Uint8Array"),
.value = njs_prop_handler2(njs_top_level_constructor,
NJS_OBJ_TYPE_UINT8_ARRAY,
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_encoding.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_encoding.c Tue Jul 28 16:58:59 2020 +0300
@@ -0,0 +1,804 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+typedef enum {
+ NJS_ENCODING_UTF8,
+} njs_encoding_t;
+
+
+typedef struct {
+ njs_encoding_t encoding;
+ njs_bool_t fatal;
+ njs_bool_t ignore_bom;
+
+ uint32_t codepoint;
+ njs_unicode_decode_t ctx;
+} njs_encoding_decode_t;
+
+
+typedef struct {
+ njs_str_t name;
+ njs_encoding_t encoding;
+} njs_encoding_label_t;
+
+
+static njs_encoding_label_t njs_encoding_labels[] =
+{
+ { njs_str("utf-8"), NJS_ENCODING_UTF8 },
+ { njs_str("utf8") , NJS_ENCODING_UTF8 },
+ { njs_null_str, 0 }
+};
+
+
+static njs_int_t njs_text_encoder_encode_utf8(njs_vm_t *vm,
+ njs_string_prop_t *prop);
+static njs_int_t njs_text_decoder_arg_encoding(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_encoding_decode_t *data);
+static njs_int_t njs_text_decoder_arg_options(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_encoding_decode_t *data);
+
+
+static njs_int_t
+njs_text_encoder_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ njs_object_t *proto;
+ njs_object_value_t *ov;
+
+ if (!vm->top_frame->ctor) {
+ njs_type_error(vm, "Constructor of TextEncoder requires 'new'");
+ return NJS_ERROR;
+ }
+
+ ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t));
+ if (njs_slow_path(ov == NULL)) {
+ njs_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ proto = &vm->prototypes[NJS_OBJ_TYPE_TEXT_ENCODER].object;
+
+ njs_lvlhsh_init(&ov->object.hash);
+ njs_lvlhsh_init(&ov->object.shared_hash);
+ ov->object.type = NJS_OBJECT_VALUE;
+ ov->object.shared = 0;
+ ov->object.extensible = 1;
+ ov->object.error_data = 0;
+ ov->object.fast_array = 0;
+ ov->object.__proto__ = proto;
+ ov->object.slots = NULL;
+
+ njs_set_data(&ov->value, NULL, NJS_DATA_TAG_TEXT_ENCODER);
+ njs_set_object_value(&vm->retval, ov);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_encoder_encode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *dst;
+ int64_t size;
+ uint32_t cp;
+ njs_int_t ret;
+ njs_value_t *this, *input, value;
+ const u_char *p, *start, *end;
+ njs_string_prop_t prop;
+ njs_typed_array_t *array;
+ njs_unicode_decode_t ctx;
+
+ this = njs_argument(args, 0);
+
+ if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_ENCODER))) {
+ njs_type_error(vm, "\"this\" is not a TextEncoder");
+ return NJS_ERROR;
+ }
+
+ start = NULL;
+ end = NULL;
+
+ if (nargs > 1) {
+ input = njs_argument(args, 1);
+
+ if (njs_slow_path(!njs_is_string(input))) {
+ ret = njs_value_to_string(vm, input, input);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ (void) njs_string_prop(&prop, input);
+
+ if (prop.length != 0) {
+ return njs_text_encoder_encode_utf8(vm, &prop);
+ }
+
+ start = prop.start;
+ end = start + prop.size;
+ }
+
+ p = start;
+
+ cp = 0;
+ size = 0;
+
+ njs_utf8_decode_init(&ctx);
+
+ while (p < end) {
+ cp = njs_utf8_decode(&ctx, &p, end);
+
+ if (cp > NJS_UNICODE_MAX_CODEPOINT) {
+ if (cp == NJS_UNICODE_CONTINUE) {
+ continue;
+ }
+
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ size += njs_utf8_size(cp);
+ }
+
+ if (cp == NJS_UNICODE_CONTINUE) {
+ size += njs_utf8_size(NJS_UNICODE_REPLACEMENT);
+ }
+
+ njs_set_number(&value, size);
+
+ array = njs_typed_array_alloc(vm, &value, 1, NJS_OBJ_TYPE_UINT8_ARRAY);
+ if (njs_slow_path(array == NULL)) {
+ return NJS_ERROR;
+ }
+
+ dst = njs_typed_array_buffer(array)->u.u8;
+ njs_utf8_decode_init(&ctx);
+
+ while (start < end) {
+ cp = njs_utf8_decode(&ctx, &start, end);
+
+ if (cp > NJS_UNICODE_MAX_CODEPOINT) {
+ if (cp == NJS_UNICODE_CONTINUE) {
+ continue;
+ }
+
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ dst = njs_utf8_encode(dst, cp);
+ }
+
+ if (cp == NJS_UNICODE_CONTINUE) {
+ (void) njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT);
+ }
+
+ njs_set_typed_array(&vm->retval, array);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_encoder_encode_utf8(njs_vm_t *vm, njs_string_prop_t *prop)
+{
+ njs_value_t value;
+ njs_typed_array_t *array;
+
+ njs_set_number(&value, prop->size);
+
+ array = njs_typed_array_alloc(vm, &value, 1, NJS_OBJ_TYPE_UINT8_ARRAY);
+ if (njs_slow_path(array == NULL)) {
+ return NJS_ERROR;
+ }
+
+ memcpy(njs_typed_array_buffer(array)->u.u8, prop->start, prop->size);
+
+ njs_set_typed_array(&vm->retval, array);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_encoder_encode_into(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *to, *to_end;
+ size_t size;
+ uint32_t cp;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_value_t *this, *input, *dest, retval, read, written;
+ const u_char *start, *end;
+ njs_typed_array_t *array;
+ njs_unicode_decode_t ctx;
+
+ static const njs_value_t read_str = njs_string("read");
+ static const njs_value_t written_str = njs_string("written");
+
+ this = njs_argument(args, 0);
+ input = njs_arg(args, nargs, 1);
+ dest = njs_arg(args, nargs, 2);
+
+ if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_ENCODER))) {
+ njs_type_error(vm, "\"this\" is not a TextEncoder");
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(!njs_is_string(input))) {
+ ret = njs_value_to_string(vm, &retval, input);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ input = &retval;
+ }
+
+ if (njs_slow_path(!njs_is_typed_array_uint8(dest))) {
+ njs_type_error(vm, "The \"destination\" argument must be an instance "
+ "of Uint8Array");
+ return NJS_ERROR;
+ }
+
+ njs_string_get(input, &str);
+
+ start = str.start;
+ end = start + str.length;
+
+ array = njs_typed_array(dest);
+ to = njs_typed_array_buffer(array)->u.u8;
+ to_end = to + array->byte_length;
+
+ cp = 0;
+ njs_set_number(&read, 0);
+ njs_set_number(&written, 0);
+
+ njs_utf8_decode_init(&ctx);
+
+ while (start < end) {
+ cp = njs_utf8_decode(&ctx, &start, end);
+
+ if (cp > NJS_UNICODE_MAX_CODEPOINT) {
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ size = njs_utf8_size(cp);
+
+ if (to + size > to_end) {
+ break;
+ }
+
+ njs_number(&read) += (cp > 0xFFFF) ? 2 : 1;
+ njs_number(&written) += size;
+
+ to = njs_utf8_encode(to, cp);
+ }
+
+ return njs_vm_object_alloc(vm, &vm->retval, &read_str, &read,
+ &written_str, &written, NULL);
+}
+
+
+static const njs_object_prop_t njs_text_encoder_properties[] =
+{
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("constructor"),
+ .value = njs_prop_handler(njs_object_prototype_create_constructor),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("encoding"),
+ .value = njs_string("utf-8"),
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("encode"),
+ .value = njs_native_function(njs_text_encoder_encode, 0),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("encodeInto"),
+ .value = njs_native_function(njs_text_encoder_encode_into, 2),
+ .writable = 1,
+ .configurable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_text_encoder_init = {
+ njs_text_encoder_properties,
+ njs_nitems(njs_text_encoder_properties),
+};
+
+
+static const njs_object_prop_t njs_text_encoder_constructor_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("name"),
+ .value = njs_string("TextEncoder"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("length"),
+ .value = njs_value(NJS_NUMBER, 0, 0.0),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("prototype"),
+ .value = njs_prop_handler(njs_object_prototype_create),
+ },
+};
+
+
+const njs_object_init_t njs_text_encoder_constructor_init = {
+ njs_text_encoder_constructor_properties,
+ njs_nitems(njs_text_encoder_constructor_properties),
+};
+
+
+const njs_object_type_init_t njs_text_encoder_type_init = {
+ .constructor = njs_native_ctor(njs_text_encoder_constructor, 0, 0),
+ .prototype_props = &njs_text_encoder_init,
+ .constructor_props = &njs_text_encoder_constructor_init,
+ .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+static njs_int_t
+njs_text_decoder_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ njs_int_t ret;
+ njs_object_t *proto;
+ njs_object_value_t *ov;
+ njs_encoding_decode_t *data;
+
+ if (!vm->top_frame->ctor) {
+ njs_type_error(vm, "Constructor of TextDecoder requires 'new'");
+ return NJS_ERROR;
+ }
+
+ ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t)
+ + sizeof(njs_encoding_decode_t));
+ if (njs_slow_path(ov == NULL)) {
+ njs_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ proto = &vm->prototypes[NJS_OBJ_TYPE_TEXT_DECODER].object;
+
+ njs_lvlhsh_init(&ov->object.hash);
+ njs_lvlhsh_init(&ov->object.shared_hash);
+ ov->object.type = NJS_OBJECT_VALUE;
+ ov->object.shared = 0;
+ ov->object.extensible = 1;
+ ov->object.error_data = 0;
+ ov->object.fast_array = 0;
+ ov->object.__proto__ = proto;
+ ov->object.slots = NULL;
+
+ data = (njs_encoding_decode_t *) ((uint8_t *) ov
+ + sizeof(njs_object_value_t));
+
+ ret = njs_text_decoder_arg_encoding(vm, args, nargs, data);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_text_decoder_arg_options(vm, args, nargs, data);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ data->codepoint = 0;
+ njs_utf8_decode_init(&data->ctx);
+
+ njs_set_data(&ov->value, data, NJS_DATA_TAG_TEXT_DECODER);
+ njs_set_object_value(&vm->retval, ov);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_decoder_arg_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_encoding_decode_t *data)
+{
+ njs_str_t str;
+ njs_int_t ret;
+ njs_value_t *value;
+ njs_encoding_label_t *label;
+
+ if (nargs < 2) {
+ data->encoding = NJS_ENCODING_UTF8;
+ return NJS_OK;
+ }
+
+ value = njs_argument(args, 1);
+
+ if (njs_slow_path(!njs_is_string(value))) {
+ ret = njs_value_to_string(vm, value, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ njs_string_get(value, &str);
+
+ for (label = &njs_encoding_labels[0]; label->name.length != 0; label++) {
+ if (njs_strstr_eq(&str, &label->name)) {
+ data->encoding = label->encoding;
+ return NJS_OK;
+ }
+ }
+
+ njs_range_error(vm, "The \"%V\" encoding is not supported", &str);
+
+ return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_text_decoder_arg_options(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_encoding_decode_t *data)
+{
+ njs_int_t ret;
+ njs_value_t retval, *value;
+
+ static const njs_value_t fatal_str = njs_string("fatal");
+ static const njs_value_t ignore_bom_str = njs_string("ignoreBOM");
+
+ if (nargs < 3) {
+ data->fatal = 0;
+ data->ignore_bom = 0;
+
+ return NJS_OK;
+ }
+
+ value = njs_argument(args, 2);
+
+ if (njs_slow_path(!njs_is_object(value))) {
+ njs_type_error(vm, "The \"options\" argument must be of type object");
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_property(vm, value, njs_value_arg(&fatal_str), &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ data->fatal = njs_bool(&retval);
+
+ ret = njs_value_property(vm, value, njs_value_arg(&ignore_bom_str),
+ &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ data->ignore_bom = njs_bool(&retval);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_decoder_encoding(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_encoding_decode_t *data;
+
+ static const njs_value_t utf8_str = njs_string("utf-8");
+
+ if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) {
+ njs_set_undefined(retval);
+ return NJS_DECLINED;
+ }
+
+ data = njs_object_data(value);
+
+ switch (data->encoding) {
+ case NJS_ENCODING_UTF8:
+ *retval = utf8_str;
+ break;
+
+ default:
+ njs_type_error(vm, "unknown encoding");
+ return NJS_ERROR;
+ }
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_decoder_fatal(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_encoding_decode_t *data;
+
+ if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) {
+ njs_set_undefined(retval);
+ return NJS_DECLINED;
+ }
+
+ data = njs_object_data(value);
+
+ njs_set_boolean(retval, data->fatal);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_decoder_ignore_bom(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+ njs_encoding_decode_t *data;
+
+ if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) {
+ njs_set_undefined(retval);
+ return NJS_DECLINED;
+ }
+
+ data = njs_object_data(value);
+
+ njs_set_boolean(retval, data->ignore_bom);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_text_decoder_decode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *dst;
+ uint32_t length, cp;
+ uint64_t size;
+ njs_int_t ret;
+ njs_bool_t stream;
+ njs_value_t retval, *this, *typed_array, *options;
+ const u_char *start, *end, *p;
+ njs_unicode_decode_t ctx;
+ njs_encoding_decode_t *data;
+ const njs_typed_array_t *array;
+
+ static const njs_value_t stream_str = njs_string("stream");
+
+ start = NULL;
+ end = NULL;
+
+ stream = 0;
+
+ this = njs_argument(args, 0);
+
+ if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_DECODER))) {
+ njs_type_error(vm, "\"this\" is not a TextDecoder");
+ return NJS_ERROR;
+ }
+
+ if (njs_fast_path(nargs > 1)) {
+ typed_array = njs_argument(args, 1);
+ if (njs_slow_path(!njs_is_typed_array(typed_array))) {
+ njs_type_error(vm, "The \"input\" argument must be an instance "
+ "of TypedArray");
+ return NJS_ERROR;
+ }
+
+ array = njs_typed_array(typed_array);
+
+ start = array->buffer->u.u8;
+ end = start + array->byte_length;
+ }
+
+ if (nargs > 2) {
+ options = njs_argument(args, 2);
+
+ if (njs_slow_path(!njs_is_object(options))) {
+ njs_type_error(vm, "The \"options\" argument must be "
+ "of type object");
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_property(vm, options, njs_value_arg(&stream_str),
+ &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ stream = njs_bool(&retval);
+ }
+
+ data = njs_object_data(this);
+
+ ctx = data->ctx;
+ cp = data->codepoint;
+
+ size = 0;
+ length = 0;
+
+ p = start;
+
+ /* Looking for BOM. */
+
+ if (!data->ignore_bom && p + 3 <= end) {
+ cp = njs_utf8_decode(&ctx, &p, end);
+
+ if (cp == NJS_UNICODE_BOM) {
+ start = p;
+
+ } else {
+ p = start;
+ }
+ }
+
+ while (p < end) {
+ cp = njs_utf8_decode(&ctx, &p, end);
+
+ if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) {
+ if (cp == NJS_UNICODE_CONTINUE) {
+ break;
+ }
+
+ if (data->fatal) {
+ goto fatal;
+ }
+
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ size += njs_utf8_size(cp);
+ length++;
+ }
+
+ if (cp == NJS_UNICODE_CONTINUE && !stream) {
+ if (data->fatal) {
+ goto fatal;
+ }
+
+ size += njs_utf8_size(NJS_UNICODE_REPLACEMENT);
+ length++;
+ }
+
+ dst = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(dst == NULL)) {
+ return NJS_ERROR;
+ }
+
+ while (start < end) {
+ cp = njs_utf8_decode(&data->ctx, &start, end);
+
+ if (cp > NJS_UNICODE_MAX_CODEPOINT) {
+ if (cp == NJS_UNICODE_CONTINUE) {
+ break;
+ }
+
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ dst = njs_utf8_encode(dst, cp);
+ }
+
+ if (stream) {
+ data->codepoint = cp;
+ return NJS_OK;
+ }
+
+ if (cp == NJS_UNICODE_CONTINUE) {
+ (void) njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT);
+ }
+
+ data->codepoint = 0;
+
+ njs_utf8_decode_init(&data->ctx);
+
+ return NJS_OK;
+
+fatal:
+
+ njs_type_error(vm, "The encoded data was not valid");
+
+ return NJS_ERROR;
+}
+
+
+static const njs_object_prop_t njs_text_decoder_properties[] =
+{
+ {
+ .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("encoding"),
+ .value = njs_prop_handler(njs_text_decoder_encoding),
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("fatal"),
+ .value = njs_prop_handler(njs_text_decoder_fatal),
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("ignoreBOM"),
+ .value = njs_prop_handler(njs_text_decoder_ignore_bom),
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("decode"),
+ .value = njs_native_function(njs_text_decoder_decode, 0),
+ .writable = 1,
+ .configurable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_text_decoder_init = {
+ njs_text_decoder_properties,
+ njs_nitems(njs_text_decoder_properties),
+};
+
+
+static const njs_object_prop_t njs_text_decoder_constructor_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("name"),
+ .value = njs_string("TextDecoder"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("length"),
+ .value = njs_value(NJS_NUMBER, 0, 0.0),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("prototype"),
+ .value = njs_prop_handler(njs_object_prototype_create),
+ },
+};
+
+
+const njs_object_init_t njs_text_decoder_constructor_init = {
+ njs_text_decoder_constructor_properties,
+ njs_nitems(njs_text_decoder_constructor_properties),
+};
+
+
+const njs_object_type_init_t njs_text_decoder_type_init = {
+ .constructor = njs_native_ctor(njs_text_decoder_constructor, 0, 0),
+ .prototype_props = &njs_text_decoder_init,
+ .constructor_props = &njs_text_decoder_constructor_init,
+ .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_encoding.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_encoding.h Tue Jul 28 16:58:59 2020 +0300
@@ -0,0 +1,14 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_ENCODING_H_INCLUDED_
+#define _NJS_ENCODING_H_INCLUDED_
+
+extern const njs_object_type_init_t njs_text_encoder_type_init;
+extern const njs_object_type_init_t njs_text_decoder_type_init;
+
+
+#endif /* _NJS_ENCODING_H_INCLUDED_ */
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_main.h
--- a/src/njs_main.h Thu Jul 30 17:47:05 2020 +0000
+++ b/src/njs_main.h Tue Jul 28 16:58:59 2020 +0300
@@ -73,6 +73,7 @@
#include <njs_math.h>
#include <njs_json.h>
+#include <njs_encoding.h>
#include <njs_timer.h>
#include <njs_module.h>
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_object_hash.h
--- a/src/njs_object_hash.h Thu Jul 30 17:47:05 2020 +0000
+++ b/src/njs_object_hash.h Tue Jul 28 16:58:59 2020 +0300
@@ -750,4 +750,34 @@
'd'), 'A'), 'r'), 'r'), 'a'), 'y')
+#define NJS_TEXT_DECODER_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_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add(NJS_DJB_HASH_INIT, \
+ 'T'), 'e'), 'x'), 't'), 'D'), 'e'), 'c'), 'o'), 'd'), 'e'), 'r')
+
+
+#define NJS_TEXT_ENCODER_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_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add( \
+ njs_djb_hash_add(NJS_DJB_HASH_INIT, \
+ 'T'), 'e'), 'x'), 't'), 'E'), 'n'), 'c'), 'o'), 'd'), 'e'), 'r')
+
+
#endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */
diff -r 0fad09ddb37a -r bcd1a41c6a67 src/njs_typed_array.c
--- a/src/njs_typed_array.c Thu Jul 30 17:47:05 2020 +0000
+++ b/src/njs_typed_array.c Tue Jul 28 16:58:59 2020 +0300
@@ -8,9 +8,9 @@
#include <njs_main.h>
-static njs_int_t
-njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t magic)
+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)
{
double num;
int64_t i, length;
@@ -19,7 +19,6 @@ njs_typed_array_constructor(njs_vm_t *vm
njs_int_t ret;
njs_value_t *value, prop;
njs_array_t *src_array;
- njs_object_type_t type;
njs_typed_array_t *array, *src_tarray;
njs_array_buffer_t *buffer;
@@ -31,54 +30,48 @@ njs_typed_array_constructor(njs_vm_t *vm
src_array = NULL;
src_tarray = NULL;
- type = magic;
element_size = njs_typed_array_element_size(type);
- if (!vm->top_frame->ctor) {
- njs_type_error(vm, "Constructor of TypedArray requires 'new'");
- return NJS_ERROR;
- }
-
- value = njs_arg(args, nargs, 1);
+ value = njs_arg(args, nargs, 0);
if (njs_is_array_buffer(value)) {
buffer = njs_array_buffer(value);
- ret = njs_value_to_index(vm, njs_arg(args, nargs, 2), &offset);
+ ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &offset);
if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ return NULL;
}
if (njs_slow_path((offset % element_size) != 0)) {
njs_range_error(vm, "start offset must be multiple of %uD",
element_size);
- return NJS_ERROR;
+ return NULL;
}
- if (!njs_is_undefined(njs_arg(args, nargs, 3))) {
- ret = njs_value_to_index(vm, njs_argument(args, 3), &size);
+ if (!njs_is_undefined(njs_arg(args, nargs, 2))) {
+ ret = njs_value_to_index(vm, njs_argument(args, 2), &size);
if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ return NULL;
}
size *= element_size;
if (njs_slow_path((offset + size) > buffer->size)) {
njs_range_error(vm, "Invalid typed array length: %uL", size);
- return NJS_ERROR;
+ return NULL;
}
} else {
if (njs_slow_path((buffer->size % element_size) != 0)) {
More information about the nginx-devel
mailing list