[njs] Moving generic iterator code to a proper location.

Alexander Borisov alexander.borisov at nginx.com
Wed Aug 11 18:49:48 UTC 2021


details:   https://hg.nginx.org/njs/rev/eb6c1c9823f1
branches:  
changeset: 1683:eb6c1c9823f1
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Wed Aug 11 21:48:50 2021 +0300
description:
Moving generic iterator code to a proper location.

diffstat:

 src/njs_array.c    |  441 ++--------------------------------------------------
 src/njs_array.h    |    3 +
 src/njs_iterator.c |  379 +++++++++++++++++++++++++++++++++++++++++++++
 src/njs_iterator.h |   22 ++
 4 files changed, 428 insertions(+), 417 deletions(-)

diffs (truncated from 1011 to 1000 lines):

diff -r 424dd99ada9a -r eb6c1c9823f1 src/njs_array.c
--- a/src/njs_array.c	Thu Jul 15 20:32:44 2021 +0300
+++ b/src/njs_array.c	Wed Aug 11 21:48:50 2021 +0300
@@ -8,9 +8,6 @@
 #include <njs_main.h>
 
 
-#define njs_fast_object(_sz)        ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH)
-
-
 #define njs_array_func(type)                                                  \
     ((type << 1) | NJS_ARRAY_FUNC)
 
@@ -49,22 +46,6 @@ typedef enum {
 } njs_array_iterator_arg_t;
 
 
-typedef struct {
-    njs_function_t  *function;
-    njs_value_t     *argument;
-    njs_value_t     *value;
-
-    njs_array_t     *array;
-
-    int64_t        from;
-    int64_t        to;
-} njs_array_iterator_args_t;
-
-
-typedef njs_int_t (*njs_array_iterator_handler_t)(njs_vm_t *vm,
-    njs_array_iterator_args_t *args, njs_value_t *entry, int64_t n);
-
-
 static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm,
     njs_value_t *this, int64_t start, int64_t length);
 
@@ -2065,7 +2046,7 @@ njs_array_prototype_fill(njs_vm_t *vm, n
 
 
 njs_inline njs_int_t
-njs_array_iterator_call(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_iterator_call(njs_vm_t *vm, njs_iterator_args_t *args,
     const njs_value_t *entry, uint32_t n)
 {
     njs_value_t  arguments[3];
@@ -2082,204 +2063,7 @@ njs_array_iterator_call(njs_vm_t *vm, nj
 
 
 static njs_int_t
-njs_array_object_handler(njs_vm_t *vm, njs_array_iterator_handler_t handler,
-    njs_array_iterator_args_t *args, njs_value_t *key, int64_t i)
-{
-    njs_int_t    ret;
-    njs_value_t  prop, *entry;
-
-    if (key != NULL) {
-        ret = njs_value_property(vm, args->value, key, &prop);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
-        }
-
-    } else {
-        ret = njs_value_property_i64(vm, args->value, i, &prop);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
-        }
-    }
-
-    entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid);
-
-    ret = handler(vm, args, entry, i);
-    if (njs_slow_path(ret != NJS_OK)) {
-        if (ret == NJS_DONE) {
-            return NJS_DONE;
-        }
-
-        return NJS_ERROR;
-    }
-
-    return ret;
-}
-
-
-njs_inline njs_int_t
-njs_array_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args,
-    njs_array_iterator_handler_t handler)
-{
-    double             idx;
-    int64_t            length, i, from, to;
-    njs_int_t          ret;
-    njs_array_t        *array, *keys;
-    njs_value_t        *value, *entry, prop, character, string_obj;
-    njs_object_t       *object;
-    const u_char       *p, *end, *pos;
-    njs_string_prop_t  string_prop;
-
-    value = args->value;
-    from = args->from;
-    to = args->to;
-
-    if (njs_is_array(value)) {
-        array = njs_array(value);
-
-        for (; from < to; from++) {
-            if (njs_slow_path(!array->object.fast_array)) {
-                goto process_object;
-            }
-
-            if (njs_fast_path(from < array->length
-                              && njs_is_valid(&array->start[from])))
-            {
-                ret = handler(vm, args, &array->start[from], from);
-
-            } else {
-                entry = njs_value_arg(&njs_value_invalid);
-                ret = njs_value_property_i64(vm, value, from, &prop);
-                if (njs_slow_path(ret != NJS_DECLINED)) {
-                    if (ret == NJS_ERROR) {
-                        return NJS_ERROR;
-                    }
-
-                    entry = ∝
-                }
-
-                ret = handler(vm, args, entry, from);
-            }
-
-            if (njs_slow_path(ret != NJS_OK)) {
-                if (ret == NJS_DONE) {
-                    return NJS_DONE;
-                }
-
-                return NJS_ERROR;
-            }
-        }
-
-        return NJS_OK;
-    }
-
-    if (njs_is_string(value) || njs_is_object_string(value)) {
-
-        if (njs_is_string(value)) {
-            object = njs_object_value_alloc(vm, value, NJS_STRING);
-            if (njs_slow_path(object == NULL)) {
-                return NJS_ERROR;
-            }
-
-            njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING);
-
-            args->value = &string_obj;
-        }
-        else {
-            value = njs_object_value(value);
-        }
-
-        length = njs_string_prop(&string_prop, value);
-
-        p = string_prop.start;
-        end = p + string_prop.size;
-
-        if ((size_t) length == string_prop.size) {
-            /* Byte or ASCII string. */
-
-            for (i = from; i < to; i++) {
-                /* This cannot fail. */
-                (void) njs_string_new(vm, &character, p + i, 1, 1);
-
-                ret = handler(vm, args, &character, i);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret == NJS_DONE) {
-                        return NJS_DONE;
-                    }
-
-                    return NJS_ERROR;
-                }
-            }
-
-        } else {
-            /* UTF-8 string. */
-
-            for (i = from; i < to; i++) {
-                pos = njs_utf8_next(p, end);
-
-                /* This cannot fail. */
-                (void) njs_string_new(vm, &character, p, pos - p, 1);
-
-                ret = handler(vm, args, &character, i);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret == NJS_DONE) {
-                        return NJS_DONE;
-                    }
-
-                    return NJS_ERROR;
-                }
-
-                p = pos;
-            }
-        }
-
-        return NJS_OK;
-    }
-
-    if (!njs_is_object(value)) {
-        return NJS_OK;
-    }
-
-process_object:
-
-    if (!njs_fast_object(to - from)) {
-        keys = njs_array_indices(vm, value);
-        if (njs_slow_path(keys == NULL)) {
-            return NJS_ERROR;
-        }
-
-        for (i = 0; i < keys->length; i++) {
-            idx = njs_string_to_index(&keys->start[i]);
-
-            if (idx < from || idx >= to) {
-                continue;
-            }
-
-            ret = njs_array_object_handler(vm, handler, args, &keys->start[i],
-                                           idx);
-            if (njs_slow_path(ret != NJS_OK)) {
-                njs_array_destroy(vm, keys);
-                return ret;
-            }
-        }
-
-        njs_array_destroy(vm, keys);
-
-        return NJS_OK;
-    }
-
-    for (i = from; i < to; i++) {
-        ret = njs_array_object_handler(vm, handler, args, NULL, i);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-    }
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_array_handler_every(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_every(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t  ret;
@@ -2301,7 +2085,7 @@ njs_array_handler_every(njs_vm_t *vm, nj
 
 
 static njs_int_t
-njs_array_handler_some(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_some(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t  ret;
@@ -2323,7 +2107,7 @@ njs_array_handler_some(njs_vm_t *vm, njs
 
 
 static njs_int_t
-njs_array_handler_includes(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_includes(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     if (!njs_is_valid(entry)) {
@@ -2341,7 +2125,7 @@ njs_array_handler_includes(njs_vm_t *vm,
 
 
 static njs_int_t
-njs_array_handler_index_of(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_index_of(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     if (njs_values_strict_equal(args->argument, entry)) {
@@ -2355,7 +2139,7 @@ njs_array_handler_index_of(njs_vm_t *vm,
 
 
 static njs_int_t
-njs_array_handler_for_each(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_for_each(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     if (njs_is_valid(entry)) {
@@ -2367,7 +2151,7 @@ njs_array_handler_for_each(njs_vm_t *vm,
 
 
 static njs_int_t
-njs_array_handler_find(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_find(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t    ret;
@@ -2396,7 +2180,7 @@ njs_array_handler_find(njs_vm_t *vm, njs
 
 
 static njs_int_t
-njs_array_handler_find_index(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_find_index(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t    ret;
@@ -2425,7 +2209,7 @@ njs_array_handler_find_index(njs_vm_t *v
 
 
 static njs_int_t
-njs_array_handler_reduce(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_reduce(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t    ret;
@@ -2457,7 +2241,7 @@ njs_array_handler_reduce(njs_vm_t *vm, n
 
 
 static njs_int_t
-njs_array_handler_filter(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_filter(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t    ret;
@@ -2484,7 +2268,7 @@ njs_array_handler_filter(njs_vm_t *vm, n
 
 
 static njs_int_t
-njs_array_handler_map(njs_vm_t *vm, njs_array_iterator_args_t *args,
+njs_array_handler_map(njs_vm_t *vm, njs_iterator_args_t *args,
     njs_value_t *entry, int64_t n)
 {
     njs_int_t    ret;
@@ -2526,12 +2310,12 @@ static njs_int_t
 njs_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t magic)
 {
-    int64_t                       i, length;
-    njs_int_t                     ret;
-    njs_array_t                   *array;
-    njs_value_t                   accumulator;
-    njs_array_iterator_args_t     iargs;
-    njs_array_iterator_handler_t  handler;
+    int64_t                 i, length;
+    njs_int_t               ret;
+    njs_array_t             *array;
+    njs_value_t             accumulator;
+    njs_iterator_args_t     iargs;
+    njs_iterator_handler_t  handler;
 
     iargs.value = njs_argument(args, 0);
 
@@ -2652,7 +2436,7 @@ njs_array_prototype_iterator(njs_vm_t *v
         break;
     }
 
-    ret = njs_array_iterator(vm, &iargs, handler);
+    ret = njs_object_iterate(vm, &iargs, handler);
     if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
@@ -2704,192 +2488,15 @@ done:
 }
 
 
-njs_inline njs_int_t
-njs_array_reverse_iterator(njs_vm_t *vm, njs_array_iterator_args_t *args,
-    njs_array_iterator_handler_t handler)
-{
-    double             idx;
-    int64_t            i, from, to, length;
-    njs_int_t          ret;
-    njs_array_t        *array, *keys;
-    njs_value_t        *entry, *value, prop, character, string_obj;
-    njs_object_t       *object;
-    const u_char       *p, *end, *pos;
-    njs_string_prop_t  string_prop;
-
-    value = args->value;
-    from = args->from;
-    to = args->to;
-
-    if (njs_is_array(value)) {
-        array = njs_array(value);
-
-        from += 1;
-
-        while (from-- > to) {
-            if (njs_slow_path(!array->object.fast_array)) {
-                goto process_object;
-            }
-
-            if (njs_fast_path(from < array->length
-                              && njs_is_valid(&array->start[from])))
-            {
-                ret = handler(vm, args, &array->start[from], from);
-
-            } else {
-                entry = njs_value_arg(&njs_value_invalid);
-                ret = njs_value_property_i64(vm, value, from, &prop);
-                if (njs_slow_path(ret != NJS_DECLINED)) {
-                    if (ret == NJS_ERROR) {
-                        return NJS_ERROR;
-                    }
-
-                    entry = ∝
-                }
-
-                ret = handler(vm, args, entry, from);
-            }
-
-            if (njs_slow_path(ret != NJS_OK)) {
-                if (ret == NJS_DONE) {
-                    return NJS_DONE;
-                }
-
-                return NJS_ERROR;
-            }
-        }
-
-        return NJS_OK;
-    }
-
-    if (njs_is_string(value) || njs_is_object_string(value)) {
-
-        if (njs_is_string(value)) {
-            object = njs_object_value_alloc(vm, value, NJS_STRING);
-            if (njs_slow_path(object == NULL)) {
-                return NJS_ERROR;
-            }
-
-            njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING);
-
-            args->value = &string_obj;
-        }
-        else {
-            value = njs_object_value(value);
-        }
-
-        length = njs_string_prop(&string_prop, value);
-        end = string_prop.start + string_prop.size;
-
-        if ((size_t) length == string_prop.size) {
-            /* Byte or ASCII string. */
-
-            p = string_prop.start + from;
-
-            i = from + 1;
-
-            while (i-- > to) {
-                /* This cannot fail. */
-                (void) njs_string_new(vm, &character, p, 1, 1);
-
-                ret = handler(vm, args, &character, i);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret == NJS_DONE) {
-                        return NJS_DONE;
-                    }
-
-                    return NJS_ERROR;
-                }
-
-                p--;
-            }
-
-        } else {
-            /* UTF-8 string. */
-
-            p = njs_string_offset(string_prop.start, end, from);
-            p = njs_utf8_next(p, end);
-
-            i = from + 1;
-
-            while (i-- > to) {
-                pos = njs_utf8_prev(p);
-
-                /* This cannot fail. */
-                (void) njs_string_new(vm, &character, pos, p - pos , 1);
-
-                ret = handler(vm, args, &character, i);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    if (ret == NJS_DONE) {
-                        return NJS_DONE;
-                    }
-
-                    return NJS_ERROR;
-                }
-
-                p = pos;
-            }
-        }
-
-        return NJS_OK;
-    }
-
-    if (!njs_is_object(value)) {
-        return NJS_OK;
-    }
-
-process_object:
-
-    if (!njs_fast_object(from - to)) {
-        keys = njs_array_indices(vm, value);
-        if (njs_slow_path(keys == NULL)) {
-            return NJS_ERROR;
-        }
-
-        i = keys->length;
-
-        while (i > 0) {
-            idx = njs_string_to_index(&keys->start[--i]);
-
-            if (idx < to || idx > from) {
-                continue;
-            }
-
-            ret = njs_array_object_handler(vm, handler, args, &keys->start[i],
-                                           idx);
-            if (njs_slow_path(ret != NJS_OK)) {
-                njs_array_destroy(vm, keys);
-                return ret;
-            }
-        }
-
-        njs_array_destroy(vm, keys);
-
-        return NJS_OK;
-    }
-
-    i = from + 1;
-
-    while (i-- > to) {
-        ret = njs_array_object_handler(vm, handler, args, NULL, i);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-    }
-
-    return NJS_OK;
-}
-
-
 static njs_int_t
 njs_array_prototype_reverse_iterator(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t type)
 {
-    int64_t                       from, length;
-    njs_int_t                     ret;
-    njs_value_t                   accumulator;
-    njs_array_iterator_args_t     iargs;
-    njs_array_iterator_handler_t  handler;
+    int64_t                 from, length;
+    njs_int_t               ret;
+    njs_value_t             accumulator;
+    njs_iterator_args_t     iargs;
+    njs_iterator_handler_t  handler;
 
     iargs.value = njs_argument(args, 0);
 
@@ -2960,7 +2567,7 @@ njs_array_prototype_reverse_iterator(njs
     iargs.from = from;
     iargs.to = 0;
 
-    ret = njs_array_reverse_iterator(vm, &iargs, handler);
+    ret = njs_object_iterate_reverse(vm, &iargs, handler);
     if (njs_fast_path(ret == NJS_ERROR)) {
         return NJS_ERROR;
     }
diff -r 424dd99ada9a -r eb6c1c9823f1 src/njs_array.h
--- a/src/njs_array.h	Thu Jul 15 20:32:44 2021 +0300
+++ b/src/njs_array.h	Wed Aug 11 21:48:50 2021 +0300
@@ -16,6 +16,9 @@
 #define NJS_ARRAY_LARGE_OBJECT_LENGTH  (32768)
 #define NJS_ARRAY_FLAT_MAX_LENGTH      (1048576)
 
+#define njs_fast_object(_sz)           ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH)
+
+
 njs_array_t *njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length,
     uint32_t spare);
 void njs_array_destroy(njs_vm_t *vm, njs_array_t *array);
diff -r 424dd99ada9a -r eb6c1c9823f1 src/njs_iterator.c
--- a/src/njs_iterator.c	Thu Jul 15 20:32:44 2021 +0300
+++ b/src/njs_iterator.c	Wed Aug 11 21:48:50 2021 +0300
@@ -22,6 +22,11 @@ static const njs_value_t  string_done = 
 static const njs_value_t  string_value = njs_string("value");
 
 
+static njs_int_t njs_iterator_object_handler(njs_vm_t *vm,
+    njs_iterator_handler_t handler, njs_iterator_args_t *args,
+    njs_value_t *key, int64_t i);
+
+
 njs_int_t
 njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *target,
     njs_value_t *retval, njs_object_enum_t kind)
@@ -297,3 +302,377 @@ const njs_object_type_init_t  njs_array_
     .prototype_props = &njs_array_iterator_prototype_init,
     .prototype_value = { .object = { .type = NJS_OBJECT } },
 };
+
+
+njs_int_t
+njs_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args,
+    njs_iterator_handler_t handler)
+{
+    double             idx;
+    int64_t            length, i, from, to;
+    njs_int_t          ret;
+    njs_array_t        *array, *keys;
+    njs_value_t        *value, *entry, prop, character, string_obj;
+    njs_object_t       *object;
+    const u_char       *p, *end, *pos;
+    njs_string_prop_t  string_prop;
+
+    value = args->value;
+    from = args->from;
+    to = args->to;
+
+    if (njs_is_array(value)) {
+        array = njs_array(value);
+
+        for (; from < to; from++) {
+            if (njs_slow_path(!array->object.fast_array)) {
+                goto process_object;
+            }
+
+            if (njs_fast_path(from < array->length
+                              && njs_is_valid(&array->start[from])))
+            {
+                ret = handler(vm, args, &array->start[from], from);
+
+            } else {
+                entry = njs_value_arg(&njs_value_invalid);
+                ret = njs_value_property_i64(vm, value, from, &prop);
+                if (njs_slow_path(ret != NJS_DECLINED)) {
+                    if (ret == NJS_ERROR) {
+                        return NJS_ERROR;
+                    }
+
+                    entry = ∝
+                }
+
+                ret = handler(vm, args, entry, from);
+            }
+
+            if (njs_slow_path(ret != NJS_OK)) {
+                if (ret == NJS_DONE) {
+                    return NJS_DONE;
+                }
+
+                return NJS_ERROR;
+            }
+        }
+
+        return NJS_OK;
+    }
+
+    if (njs_is_string(value) || njs_is_object_string(value)) {
+
+        if (njs_is_string(value)) {
+            object = njs_object_value_alloc(vm, value, NJS_STRING);
+            if (njs_slow_path(object == NULL)) {
+                return NJS_ERROR;
+            }
+
+            njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING);
+
+            args->value = &string_obj;
+        }
+        else {
+            value = njs_object_value(value);
+        }
+
+        length = njs_string_prop(&string_prop, value);
+
+        p = string_prop.start;
+        end = p + string_prop.size;
+
+        if ((size_t) length == string_prop.size) {
+            /* Byte or ASCII string. */
+
+            for (i = from; i < to; i++) {
+                /* This cannot fail. */
+                (void) njs_string_new(vm, &character, p + i, 1, 1);
+
+                ret = handler(vm, args, &character, i);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    if (ret == NJS_DONE) {
+                        return NJS_DONE;
+                    }
+
+                    return NJS_ERROR;
+                }
+            }
+
+        } else {
+            /* UTF-8 string. */
+
+            for (i = from; i < to; i++) {
+                pos = njs_utf8_next(p, end);
+
+                /* This cannot fail. */
+                (void) njs_string_new(vm, &character, p, pos - p, 1);
+
+                ret = handler(vm, args, &character, i);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    if (ret == NJS_DONE) {
+                        return NJS_DONE;
+                    }
+
+                    return NJS_ERROR;
+                }
+
+                p = pos;
+            }
+        }
+
+        return NJS_OK;
+    }
+
+    if (!njs_is_object(value)) {
+        return NJS_OK;
+    }
+
+process_object:
+
+    if (!njs_fast_object(to - from)) {
+        keys = njs_array_indices(vm, value);
+        if (njs_slow_path(keys == NULL)) {
+            return NJS_ERROR;
+        }
+
+        for (i = 0; i < keys->length; i++) {
+            idx = njs_string_to_index(&keys->start[i]);
+
+            if (idx < from || idx >= to) {
+                continue;
+            }
+
+            ret = njs_iterator_object_handler(vm, handler, args, &keys->start[i],
+                                           idx);
+            if (njs_slow_path(ret != NJS_OK)) {
+                njs_array_destroy(vm, keys);
+                return ret;
+            }
+        }
+
+        njs_array_destroy(vm, keys);
+
+        return NJS_OK;
+    }
+
+    for (i = from; i < to; i++) {
+        ret = njs_iterator_object_handler(vm, handler, args, NULL, i);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_object_iterate_reverse(njs_vm_t *vm, njs_iterator_args_t *args,
+    njs_iterator_handler_t handler)
+{
+    double             idx;
+    int64_t            i, from, to, length;
+    njs_int_t          ret;
+    njs_array_t        *array, *keys;
+    njs_value_t        *entry, *value, prop, character, string_obj;
+    njs_object_t       *object;
+    const u_char       *p, *end, *pos;
+    njs_string_prop_t  string_prop;
+
+    value = args->value;
+    from = args->from;
+    to = args->to;
+
+    if (njs_is_array(value)) {
+        array = njs_array(value);
+
+        from += 1;
+
+        while (from-- > to) {
+            if (njs_slow_path(!array->object.fast_array)) {
+                goto process_object;
+            }
+
+            if (njs_fast_path(from < array->length
+                              && njs_is_valid(&array->start[from])))
+            {
+                ret = handler(vm, args, &array->start[from], from);
+
+            } else {
+                entry = njs_value_arg(&njs_value_invalid);
+                ret = njs_value_property_i64(vm, value, from, &prop);
+                if (njs_slow_path(ret != NJS_DECLINED)) {
+                    if (ret == NJS_ERROR) {
+                        return NJS_ERROR;
+                    }
+
+                    entry = ∝
+                }
+
+                ret = handler(vm, args, entry, from);
+            }
+
+            if (njs_slow_path(ret != NJS_OK)) {
+                if (ret == NJS_DONE) {
+                    return NJS_DONE;
+                }
+
+                return NJS_ERROR;
+            }
+        }
+
+        return NJS_OK;
+    }
+
+    if (njs_is_string(value) || njs_is_object_string(value)) {
+
+        if (njs_is_string(value)) {
+            object = njs_object_value_alloc(vm, value, NJS_STRING);
+            if (njs_slow_path(object == NULL)) {
+                return NJS_ERROR;
+            }
+
+            njs_set_type_object(&string_obj, object, NJS_OBJECT_STRING);
+
+            args->value = &string_obj;
+        }
+        else {
+            value = njs_object_value(value);
+        }
+
+        length = njs_string_prop(&string_prop, value);
+        end = string_prop.start + string_prop.size;
+
+        if ((size_t) length == string_prop.size) {
+            /* Byte or ASCII string. */
+
+            p = string_prop.start + from;
+
+            i = from + 1;
+
+            while (i-- > to) {
+                /* This cannot fail. */
+                (void) njs_string_new(vm, &character, p, 1, 1);
+
+                ret = handler(vm, args, &character, i);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    if (ret == NJS_DONE) {
+                        return NJS_DONE;
+                    }
+
+                    return NJS_ERROR;
+                }
+
+                p--;
+            }
+
+        } else {
+            /* UTF-8 string. */
+
+            p = njs_string_offset(string_prop.start, end, from);
+            p = njs_utf8_next(p, end);
+
+            i = from + 1;
+
+            while (i-- > to) {
+                pos = njs_utf8_prev(p);
+
+                /* This cannot fail. */
+                (void) njs_string_new(vm, &character, pos, p - pos , 1);
+
+                ret = handler(vm, args, &character, i);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    if (ret == NJS_DONE) {
+                        return NJS_DONE;
+                    }
+
+                    return NJS_ERROR;
+                }
+
+                p = pos;
+            }
+        }
+
+        return NJS_OK;
+    }
+
+    if (!njs_is_object(value)) {
+        return NJS_OK;
+    }
+
+process_object:
+
+    if (!njs_fast_object(from - to)) {
+        keys = njs_array_indices(vm, value);
+        if (njs_slow_path(keys == NULL)) {
+            return NJS_ERROR;
+        }
+
+        i = keys->length;
+
+        while (i > 0) {
+            idx = njs_string_to_index(&keys->start[--i]);
+
+            if (idx < to || idx > from) {
+                continue;
+            }
+
+            ret = njs_iterator_object_handler(vm, handler, args,
+                                              &keys->start[i], idx);
+            if (njs_slow_path(ret != NJS_OK)) {
+                njs_array_destroy(vm, keys);
+                return ret;
+            }
+        }
+
+        njs_array_destroy(vm, keys);
+
+        return NJS_OK;
+    }
+
+    i = from + 1;
+
+    while (i-- > to) {
+        ret = njs_iterator_object_handler(vm, handler, args, NULL, i);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_iterator_object_handler(njs_vm_t *vm, njs_iterator_handler_t handler,
+    njs_iterator_args_t *args, njs_value_t *key, int64_t i)
+{
+    njs_int_t    ret;
+    njs_value_t  prop, *entry;
+
+    if (key != NULL) {
+        ret = njs_value_property(vm, args->value, key, &prop);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+    } else {
+        ret = njs_value_property_i64(vm, args->value, i, &prop);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+    }
+
+    entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid);
+
+    ret = handler(vm, args, entry, i);
+    if (njs_slow_path(ret != NJS_OK)) {
+        if (ret == NJS_DONE) {
+            return NJS_DONE;
+        }
+
+        return NJS_ERROR;
+    }
+
+    return ret;
+}
diff -r 424dd99ada9a -r eb6c1c9823f1 src/njs_iterator.h
--- a/src/njs_iterator.h	Thu Jul 15 20:32:44 2021 +0300
+++ b/src/njs_iterator.h	Wed Aug 11 21:48:50 2021 +0300
@@ -8,12 +8,34 @@
 #define _NJS_ITERATOR_H_INCLUDED_
 
 
+typedef struct {
+    njs_function_t  *function;
+    njs_value_t     *argument;
+    njs_value_t     *value;
+
+    njs_array_t     *array;
+
+    int64_t         from;
+    int64_t         to;
+} njs_iterator_args_t;
+
+
+typedef njs_int_t (*njs_iterator_handler_t)(njs_vm_t *vm,
+    njs_iterator_args_t *args, njs_value_t *entry, int64_t n);
+
+
 njs_int_t njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *src,
     njs_value_t *dst, njs_object_enum_t kind);
 
 njs_int_t njs_array_iterator_next(njs_vm_t *vm, njs_value_t *iterator,


More information about the nginx-devel mailing list