[njs] Added support for typed-arrays in String.bytesFrom().

Dmitry Volyntsev xeioex at nginx.com
Thu Jan 16 12:15:42 UTC 2020


details:   https://hg.nginx.org/njs/rev/cb4f1052954f
branches:  
changeset: 1300:cb4f1052954f
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Jan 16 15:14:38 2020 +0300
description:
Added support for typed-arrays in String.bytesFrom().

diffstat:

 src/njs_string.c         |  146 +++++++++++++++++++++++++++++++---------------
 src/test/njs_unit_test.c |   20 +++++-
 2 files changed, 117 insertions(+), 49 deletions(-)

diffs (239 lines):

diff -r b840b7af946e -r cb4f1052954f src/njs_string.c
--- a/src/njs_string.c	Tue Jan 14 16:52:42 2020 +0300
+++ b/src/njs_string.c	Thu Jan 16 15:14:38 2020 +0300
@@ -64,10 +64,10 @@ static njs_int_t njs_string_from_code_po
     njs_uint_t nargs, njs_index_t unused);
 static njs_int_t njs_string_bytes_from(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_string_bytes_from_array(njs_vm_t *vm,
-    const njs_value_t *value);
+static njs_int_t njs_string_bytes_from_array_like(njs_vm_t *vm,
+    njs_value_t *value);
 static njs_int_t njs_string_bytes_from_string(njs_vm_t *vm,
-    const njs_value_t *args, njs_uint_t nargs);
+    const njs_value_t *string, const njs_value_t *encoding);
 static njs_int_t njs_string_starts_or_ends_with(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_bool_t starts);
 static njs_int_t njs_string_trim(njs_vm_t *vm, njs_value_t *value,
@@ -1603,8 +1603,8 @@ done:
 
 
 /*
- * String.bytesFrom(array).
- * Converts an array containing octets into a byte string.
+ * String.bytesFrom(array-like).
+ * Converts an array-like object containing octets into a byte string.
  *
  * String.bytesFrom(string[, encoding]).
  * Converts a string using provided encoding: hex, base64, base64url to
@@ -1619,38 +1619,63 @@ njs_string_bytes_from(njs_vm_t *vm, njs_
 
     value = njs_arg(args, nargs, 1);
 
-    if (njs_is_string(value)) {
-        return njs_string_bytes_from_string(vm, args, nargs);
-    }
-
-    if (njs_is_array(value)) {
-        return njs_string_bytes_from_array(vm, njs_arg(args, nargs, 1));
-    }
-
-    njs_type_error(vm, "value must be a string or array");
+    switch (value->type) {
+    case NJS_OBJECT_STRING:
+        value = njs_object_value(value);
+
+        /* Fall through. */
+
+    case NJS_STRING:
+        return njs_string_bytes_from_string(vm, value, njs_arg(args, nargs, 2));
+
+    default:
+        if (njs_is_object(value)) {
+            return njs_string_bytes_from_array_like(vm, value);
+        }
+    }
+
+    njs_type_error(vm, "value must be a string or array-like object");
 
     return NJS_ERROR;
 }
 
 
 static njs_int_t
-njs_string_bytes_from_array(njs_vm_t *vm, const njs_value_t *value)
+njs_string_bytes_from_array_like(njs_vm_t *vm, njs_value_t *value)
 {
-    u_char       *p;
-    uint32_t     i, length;
-    njs_int_t    ret;
-    njs_array_t  *array;
-    njs_value_t  *octet;
-
-    array = njs_array(value);
-    length = array->length;
-
-    for (i = 0; i < length; i++) {
-        if (!njs_is_numeric(&array->start[i])) {
-            ret = njs_value_to_numeric(vm, &array->start[i], &array->start[i]);
-            if (ret != NJS_OK) {
-                return ret;
-            }
+    u_char              *p;
+    uint32_t            u32, length;
+    njs_int_t           ret;
+    njs_array_t         *array;
+    njs_value_t         *octet, index, prop;
+    njs_array_buffer_t  *buffer;
+
+    array = NULL;
+    buffer = NULL;
+
+    switch (value->type) {
+    case NJS_ARRAY:
+        array = njs_array(value);
+        length = array->length;
+        break;
+
+    case NJS_ARRAY_BUFFER:
+    case NJS_TYPED_ARRAY:
+
+        if (njs_is_typed_array(value)) {
+            buffer = njs_typed_array(value)->buffer;
+
+        } else {
+            buffer = njs_array_buffer(value);
+        }
+
+        length = buffer->size;
+        break;
+
+    default:
+        ret = njs_object_length(vm, value, &length);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
         }
     }
 
@@ -1659,12 +1684,42 @@ njs_string_bytes_from_array(njs_vm_t *vm
         return NJS_ERROR;
     }
 
-    octet = array->start;
-
-    while (length != 0) {
-        *p++ = (u_char) njs_number_to_uint32(njs_number(octet));
-        octet++;
-        length--;
+    if (array != NULL) {
+        octet = array->start;
+
+        while (length != 0) {
+            ret = njs_value_to_uint32(vm, octet, &u32);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            *p++ = (u_char) u32;
+            octet++;
+            length--;
+        }
+
+    } else if (buffer != NULL) {
+        memcpy(p, buffer->u.u8, length);
+
+    } else {
+        p += length - 1;
+
+        while (length != 0) {
+            njs_uint32_to_string(&index, length - 1);
+
+            ret = njs_value_property(vm, value, &index, &prop);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
+            }
+
+            ret = njs_value_to_uint32(vm, &prop, &u32);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            *p-- = (u_char) u32;
+            length--;
+        }
     }
 
     return NJS_OK;
@@ -1672,21 +1727,18 @@ njs_string_bytes_from_array(njs_vm_t *vm
 
 
 static njs_int_t
-njs_string_bytes_from_string(njs_vm_t *vm, const njs_value_t *args,
-    njs_uint_t nargs)
+njs_string_bytes_from_string(njs_vm_t *vm, const njs_value_t *string,
+    const njs_value_t *encoding)
 {
-    njs_str_t    enc, str;
-    njs_value_t  *enc_val;
-
-    enc_val = njs_arg(args, nargs, 2);
-
-    if (njs_slow_path(nargs > 1 && !njs_is_string(enc_val))) {
-        njs_type_error(vm, "encoding must be a string");
+    njs_str_t  enc, str;
+
+    if (!njs_is_string(encoding)) {
+        njs_type_error(vm, "\"encoding\" must be a string");
         return NJS_ERROR;
     }
 
-    njs_string_get(enc_val, &enc);
-    njs_string_get(&args[1], &str);
+    njs_string_get(encoding, &enc);
+    njs_string_get(string, &str);
 
     if (enc.length == 3 && memcmp(enc.start, "hex", 3) == 0) {
         return njs_string_decode_hex(vm, &vm->retval, &str);
diff -r b840b7af946e -r cb4f1052954f src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Tue Jan 14 16:52:42 2020 +0300
+++ b/src/test/njs_unit_test.c	Thu Jan 16 15:14:38 2020 +0300
@@ -7635,8 +7635,15 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("'abc'.padEnd(10, Symbol())"),
       njs_str("TypeError: Cannot convert a Symbol value to a string") },
 
-    { njs_str("String.bytesFrom({})"),
-      njs_str("TypeError: value must be a string or array") },
+    { njs_str("[undefined, null, Symbol()]"
+              ".every(v=> { try {String.bytesFrom(v);} catch(e) {return e.name == 'TypeError'} })"),
+      njs_str("true") },
+
+    { njs_str("String.bytesFrom({}).length"),
+      njs_str("0") },
+
+    { njs_str("String.bytesFrom({length:5, 0:'A'.charCodeAt(0), 2:'X', 3:NaN,4:0xfd}).toString('hex')"),
+      njs_str("41000000fd") },
 
     { njs_str("String.bytesFrom([1, 2, 0.23, '5', 'A']).toString('hex')"),
       njs_str("0102000500") },
@@ -7644,12 +7651,21 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("String.bytesFrom([NaN, Infinity]).toString('hex')"),
       njs_str("0000") },
 
+    { njs_str("String.bytesFrom(new Uint8Array([0xff,0xde,0xba])).toString('hex')"),
+      njs_str("ffdeba") },
+
+    { njs_str("String.bytesFrom((new Uint8Array([0xff,0xde,0xba])).buffer).toString('hex')"),
+      njs_str("ffdeba") },
+
     { njs_str("String.bytesFrom('', 'hex')"),
       njs_str("") },
 
     { njs_str("String.bytesFrom('00aabbcc', 'hex').toString('hex')"),
       njs_str("00aabbcc") },
 
+    { njs_str("String.bytesFrom(new String('00aabbcc'), 'hex').toString('hex')"),
+      njs_str("00aabbcc") },
+
     { njs_str("String.bytesFrom('deadBEEF##', 'hex').toString('hex')"),
       njs_str("deadbeef") },
 


More information about the nginx-devel mailing list