From fa2c9857d90f827b8d524b85b1e95b4ef1416d5e Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Thu, 7 Sep 2023 16:12:40 -0700 Subject: [PATCH] Modules: implemented items() method of a shared dictionary. --- nginx/ngx_js_shared_dict.c | 120 +++++++++++++++++++++++++++++++++++++ nginx/t/js_shared_dict.t | 27 ++++++++- 2 files changed, 144 insertions(+), 3 deletions(-) diff --git a/nginx/ngx_js_shared_dict.c b/nginx/ngx_js_shared_dict.c index 7a5c5c8c..c36a585b 100644 --- a/nginx/ngx_js_shared_dict.c +++ b/nginx/ngx_js_shared_dict.c @@ -64,6 +64,8 @@ static njs_int_t njs_js_ext_shared_dict_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); +static njs_int_t njs_js_ext_shared_dict_items(njs_vm_t *vm, njs_value_t *args, + njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); @@ -184,6 +186,17 @@ static njs_external_t ngx_js_ext_shared_dict[] = { } }, + { + .flags = NJS_EXTERN_METHOD, + .name.string = njs_str("items"), + .writable = 1, + .configurable = 1, + .enumerable = 1, + .u.method = { + .native = njs_js_ext_shared_dict_items, + } + }, + { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("get"), @@ -738,6 +751,113 @@ njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } +static njs_int_t +njs_js_ext_shared_dict_items(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused, njs_value_t *retval) +{ + njs_int_t rc; + ngx_int_t max_count; + ngx_msec_t now; + ngx_time_t *tp; + njs_value_t *value, *kv; + ngx_rbtree_t *rbtree; + ngx_js_dict_t *dict; + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *rn; + ngx_js_dict_node_t *node; + + shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, + njs_argument(args, 0)); + if (shm_zone == NULL) { + njs_vm_type_error(vm, "\"this\" is not a shared dict"); + return NJS_ERROR; + } + + dict = shm_zone->data; + + max_count = 1024; + + if (nargs > 1) { + if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &max_count) != NGX_OK) { + return NJS_ERROR; + } + } + + rc = njs_vm_array_alloc(vm, retval, 8); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + ngx_rwlock_rlock(&dict->sh->rwlock); + + if (dict->timeout) { + tp = ngx_timeofday(); + now = tp->sec * 1000 + tp->msec; + ngx_js_dict_expire(dict, now); + } + + rbtree = &dict->sh->rbtree; + + if (rbtree->root == rbtree->sentinel) { + goto done; + } + + for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); + rn != NULL; + rn = ngx_rbtree_next(rbtree, rn)) + { + if (max_count-- == 0) { + break; + } + + node = (ngx_js_dict_node_t *) rn; + + kv = njs_vm_array_push(vm, retval); + if (kv == NULL) { + goto fail; + } + + rc = njs_vm_array_alloc(vm, kv, 2); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + value = njs_vm_array_push(vm, kv); + if (value == NULL) { + goto fail; + } + + rc = njs_vm_value_string_set(vm, value, node->sn.str.data, + node->sn.str.len); + if (rc != NJS_OK) { + goto fail; + } + + value = njs_vm_array_push(vm, kv); + if (value == NULL) { + goto fail; + } + + rc = ngx_js_dict_copy_value_locked(vm, dict, node, value); + if (rc != NJS_OK) { + goto fail; + } + } + +done: + + ngx_rwlock_unlock(&dict->sh->rwlock); + + return NJS_OK; + +fail: + + ngx_rwlock_unlock(&dict->sh->rwlock); + + return NJS_ERROR; +} + + static njs_int_t njs_js_ext_shared_dict_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) diff --git a/nginx/t/js_shared_dict.t b/nginx/t/js_shared_dict.t index d43a13be..1cf794ce 100644 --- a/nginx/t/js_shared_dict.t +++ b/nginx/t/js_shared_dict.t @@ -86,6 +86,10 @@ http { js_content test.incr; } + location /items { + js_content test.items; + } + location /keys { js_content test.keys; } @@ -208,6 +212,19 @@ $t->write_file('test.js', <<'EOF'); r.return(200, `[${ks.toSorted()}]`); } + function items(r) { + var kvs; + + if (r.args.max) { + kvs = ngx.shared[r.args.dict].items(parseInt(r.args.max)); + + } else { + kvs = ngx.shared[r.args.dict].items(); + } + + r.return(200, njs.dump(kvs.toSorted())); + } + function name(r) { r.return(200, ngx.shared[r.args.dict].name); } @@ -247,11 +264,11 @@ $t->write_file('test.js', <<'EOF'); } export default { add, capacity, chain, clear, del, free_space, get, has, - incr, keys, name, njs: test_njs, pop, replace, set, size, - zones }; + incr, items, keys, name, njs: test_njs, pop, replace, set, + size, zones }; EOF -$t->try_run('no js_shared_dict_zone')->plan(41); +$t->try_run('no js_shared_dict_zone')->plan(43); ############################################################################### @@ -311,6 +328,10 @@ like(http_get('/keys?dict=foo'), qr/\[]/, 'foo keys after expire'); like(http_get('/keys?dict=bar'), qr/\[FOO\,FOO2]/, 'bar keys after a delay'); like(http_get('/size?dict=foo'), qr/size: 0/, 'no of items in foo after expire'); +like(http_get('/items?dict=bar'), qr/\[\['FOO','zzz'],\['FOO2','aaa']]/, + 'bar items'); +like(http_get('/items?dict=waka'), + qr/\[\['FOO',47],\['FOO2',7779],\['FOO3',3338]]/, 'waka items'); } -- 2.47.3