[njs] Added %TypedArray%.prototype.sort().

Dmitry Volyntsev xeioex at nginx.com
Fri Jun 5 11:59:56 UTC 2020


details:   https://hg.nginx.org/njs/rev/23df5a31bafd
branches:  
changeset: 1424:23df5a31bafd
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Jun 05 11:42:40 2020 +0000
description:
Added %TypedArray%.prototype.sort().

diffstat:

 src/njs_typed_array.c    |  334 +++++++++++++++++++++++++++++++++++++++++++++++
 src/test/njs_unit_test.c |   31 ++++
 2 files changed, 365 insertions(+), 0 deletions(-)

diffs (392 lines):

diff -r 43de01740782 -r 23df5a31bafd src/njs_typed_array.c
--- a/src/njs_typed_array.c	Fri Jun 05 11:40:42 2020 +0000
+++ b/src/njs_typed_array.c	Fri Jun 05 11:42:40 2020 +0000
@@ -813,6 +813,332 @@ njs_typed_array_prototype_copy_within(nj
 }
 
 
+static int
+njs_typed_array_compare_i8(const void *a, const void *b, void *c)
+{
+    return *((const int8_t *) a) - *((const int8_t *) b);
+}
+
+
+static int
+njs_typed_array_compare_u8(const void *a, const void *b, void *c)
+{
+    return *((const uint8_t *) a) - *((const uint8_t *) b);
+}
+
+
+static int
+njs_typed_array_compare_i16(const void *a, const void *b, void *c)
+{
+    return *((const int16_t *) a) - *((const int16_t *) b);
+}
+
+
+static int
+njs_typed_array_compare_u16(const void *a, const void *b, void *c)
+{
+    return *((const uint16_t *) a) - *((const uint16_t *) b);
+}
+
+
+static int
+njs_typed_array_compare_i32(const void *a, const void *b, void *c)
+{
+    int32_t  ai, bi;
+
+    ai = *(const int32_t *) a;
+    bi = *(const int32_t *) b;
+
+    return (ai > bi) - (ai < bi);
+}
+
+
+static int
+njs_typed_array_compare_u32(const void *a, const void *b, void *c)
+{
+    uint32_t  au, bu;
+
+    au = *(const uint32_t *) a;
+    bu = *(const uint32_t *) b;
+
+    return (au > bu) - (au < bu);
+}
+
+
+njs_inline int
+njs_typed_array_compare(double a, double b)
+{
+    if (njs_slow_path(isnan(a))) {
+        if (isnan(b)) {
+            return 0;
+        }
+
+        return 1;
+    }
+
+    if (njs_slow_path(isnan(b))) {
+        return -1;
+    }
+
+    if (a < b) {
+        return -1;
+    }
+
+    if (a > b) {
+        return 1;
+    }
+
+    return signbit(b) - signbit(a);
+}
+
+
+static int
+njs_typed_array_compare_f32(const void *a, const void *b, void *c)
+{
+    return njs_typed_array_compare(*(const float *) a, *(const float *) b);
+}
+
+
+static int
+njs_typed_array_compare_f64(const void *a, const void *b, void *c)
+{
+    return njs_typed_array_compare(*(const double *) a, *(const double *) b);
+}
+
+
+static double
+njs_typed_array_get_u8(const void *a)
+{
+    return *(const uint8_t *) a;
+}
+
+
+static double
+njs_typed_array_get_i8(const void *a)
+{
+    return *(const int8_t *) a;
+}
+
+
+static double
+njs_typed_array_get_u16(const void *a)
+{
+    return *(const uint16_t *) a;
+}
+
+
+static double
+njs_typed_array_get_i16(const void *a)
+{
+    return *(const int16_t *) a;
+}
+
+
+static double
+njs_typed_array_get_u32(const void *a)
+{
+    return *(const uint32_t *) a;
+}
+
+
+static double
+njs_typed_array_get_i32(const void *a)
+{
+    return *(const int32_t *) a;
+}
+
+
+static double
+njs_typed_array_get_f32(const void *a)
+{
+    return *(const float *) a;
+}
+
+
+static double
+njs_typed_array_get_f64(const void *a)
+{
+    return *(const double *) a;
+}
+
+
+typedef struct {
+    njs_vm_t               *vm;
+    njs_function_t         *function;
+    njs_bool_t             exception;
+    double                 (*get)(const void *v);
+} njs_typed_array_sort_ctx_t;
+
+typedef int (*njs_typed_array_cmp_t)(const void *, const void *, void *ctx);
+
+
+static int
+njs_typed_array_generic_compare(const void *a, const void *b, void *c)
+{
+    double                      num;
+    njs_int_t                   ret;
+    njs_value_t                 arguments[3], retval;
+    njs_typed_array_sort_ctx_t  *ctx;
+
+    ctx = c;
+
+    if (njs_slow_path(ctx->exception)) {
+        return 0;
+    }
+
+    njs_set_undefined(&arguments[0]);
+    njs_set_number(&arguments[1], ctx->get(a));
+    njs_set_number(&arguments[2], ctx->get(b));
+
+    ret = njs_function_apply(ctx->vm, ctx->function, arguments, 3, &retval);
+    if (njs_slow_path(ret != NJS_OK)) {
+        goto exception;
+    }
+
+    ret = njs_value_to_number(ctx->vm, &retval, &num);
+    if (njs_slow_path(ret != NJS_OK)) {
+        goto exception;
+    }
+
+    if (njs_slow_path(isnan(num))) {
+        return 0;
+    }
+
+    if (num != 0) {
+        return (num > 0) - (num < 0);
+    }
+
+    return 0;
+
+exception:
+
+    ctx->exception = 1;
+
+    return 0;
+}
+
+
+static njs_int_t
+njs_typed_array_prototype_sort(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    u_char                      *base, *orig;
+    int64_t                     length;
+    uint32_t                    element_size;
+    njs_value_t                 *this, *comparefn;
+    njs_typed_array_t           *array;
+    njs_array_buffer_t          *buffer;
+    njs_typed_array_cmp_t       cmp;
+    njs_typed_array_sort_ctx_t  ctx;
+
+    this = njs_argument(args, 0);
+    if (njs_slow_path(!njs_is_typed_array(this))) {
+        njs_type_error(vm, "this is not a typed array");
+        return NJS_ERROR;
+    }
+
+    ctx.vm = vm;
+    ctx.exception = 0;
+
+    comparefn = njs_arg(args, nargs, 1);
+
+    if (njs_is_defined(comparefn)) {
+        if (njs_slow_path(!njs_is_function(comparefn))) {
+            njs_type_error(vm, "comparefn must be callable or undefined");
+            return NJS_ERROR;
+        }
+
+        ctx.function = njs_function(comparefn);
+
+    } else {
+        ctx.function = NULL;
+    }
+
+    array = njs_typed_array(this);
+
+    switch (array->type) {
+    case NJS_OBJ_TYPE_UINT8_ARRAY:
+    case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY:
+        cmp = njs_typed_array_compare_u8;
+        ctx.get = njs_typed_array_get_u8;
+        break;
+
+    case NJS_OBJ_TYPE_INT8_ARRAY:
+        cmp = njs_typed_array_compare_i8;
+        ctx.get = njs_typed_array_get_i8;
+        break;
+
+    case NJS_OBJ_TYPE_UINT16_ARRAY:
+        cmp = njs_typed_array_compare_u16;
+        ctx.get = njs_typed_array_get_u16;
+        break;
+
+    case NJS_OBJ_TYPE_INT16_ARRAY:
+        cmp = njs_typed_array_compare_i16;
+        ctx.get = njs_typed_array_get_i16;
+        break;
+
+    case NJS_OBJ_TYPE_UINT32_ARRAY:
+        cmp = njs_typed_array_compare_u32;
+        ctx.get = njs_typed_array_get_u32;
+        break;
+
+    case NJS_OBJ_TYPE_INT32_ARRAY:
+        cmp = njs_typed_array_compare_i32;
+        ctx.get = njs_typed_array_get_i32;
+        break;
+
+    case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+        cmp = njs_typed_array_compare_f32;
+        ctx.get = njs_typed_array_get_f32;
+        break;
+
+    default:
+
+        /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+        cmp = njs_typed_array_compare_f64;
+        ctx.get = njs_typed_array_get_f64;
+        break;
+    }
+
+    length = njs_typed_array_length(array);
+    buffer = njs_typed_array_buffer(array);
+    element_size = njs_typed_array_element_size(array->type);
+    base = &buffer->u.u8[array->offset * element_size];
+    orig = base;
+
+    if (ctx.function != NULL) {
+        cmp = njs_typed_array_generic_compare;
+        base = njs_mp_alloc(vm->mem_pool, length * element_size);
+        if (njs_slow_path(base == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        memcpy(base, &buffer->u.u8[array->offset * element_size],
+               length * element_size);
+    }
+
+    njs_qsort(base, length, element_size, cmp, &ctx);
+    if (ctx.function != NULL) {
+        if (&buffer->u.u8[array->offset * element_size] == orig) {
+            memcpy(orig, base, length * element_size);
+        }
+
+        njs_mp_free(vm->mem_pool, base);
+    }
+
+    if (njs_slow_path(ctx.exception)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_typed_array(&vm->retval, array);
+
+    return NJS_OK;
+}
+
+
 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)
@@ -1052,6 +1378,14 @@ static const njs_object_prop_t  njs_type
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("sort"),
+        .value = njs_native_function(njs_typed_array_prototype_sort, 1),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("copyWithin"),
         .value = njs_native_function(njs_typed_array_prototype_copy_within, 2),
         .writable = 1,
diff -r 43de01740782 -r 23df5a31bafd src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Fri Jun 05 11:40:42 2020 +0000
+++ b/src/test/njs_unit_test.c	Fri Jun 05 11:42:40 2020 +0000
@@ -5719,6 +5719,37 @@ static njs_unit_test_t  njs_test[] =
               "           return a.toString() === '1,2,3,3'})"),
       njs_str("true") },
 
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([]); a.sort(); "
+              "           return a.toString() === ''})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([5]); a.sort(); "
+              "           return a.toString() === '5'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([3,3,2,1]); a.sort(); "
+              "           return a.toString() === '1,2,3,3'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = new v([3,3,2,1]); a.sort((x,y)=>x-y); "
+              "           return a.toString() === '1,2,3,3'})"),
+      njs_str("true") },
+
+    { njs_str(NJS_TYPED_ARRAY_LIST
+              ".every(v=>{var a = (new v([255,255,3,3,2,1])).slice(2); a.sort(); "
+              "           return a.toString() === '1,2,3,3'})"),
+      njs_str("true") },
+
+    { njs_str("(new Float32Array([255,255,NaN,3,NaN,Infinity,3,-Infinity,0,-0,2,1,-5])).slice(2).sort()"),
+      njs_str("-Infinity,-5,0,0,1,2,3,3,Infinity,NaN,NaN") },
+
+    { njs_str("(new Float64Array([255,255,NaN,3,NaN,Infinity,3,-Infinity,0,-0,2,1,-5])).slice(2).sort()"),
+      njs_str("-Infinity,-5,0,0,1,2,3,3,Infinity,NaN,NaN") },
+
 #if NJS_HAVE_LARGE_STACK
     { njs_str("var o = Object({length: 3});"
                  "Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});"


More information about the nginx-devel mailing list