[njs] QuickJS: added zlib module.

Dmitry Volyntsev xeioex at nginx.com
Thu May 2 00:31:39 UTC 2024


details:   https://hg.nginx.org/njs/rev/f146b5dc21cc
branches:  
changeset: 2325:f146b5dc21cc
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Wed May 01 17:31:01 2024 -0700
description:
QuickJS: added zlib module.

diffstat:

 auto/init                  |     1 +
 auto/make                  |    42 +-
 auto/qjs_module            |     6 +
 auto/qjs_modules           |    20 +
 auto/quickjs               |    21 +
 auto/sources               |     4 +
 configure                  |     3 +-
 external/njs_shell.c       |    15 +-
 external/qjs_zlib_module.c |   512 +++++++++++++++++++
 src/qjs.c                  |   109 ++++
 src/qjs.h                  |    79 ++
 src/qjs_buffer.c           |  1174 ++++++++++++++++++++++++++++++++++++++++++++
 src/test/njs_unit_test.c   |   301 -----------
 test/buffer.t.js           |   236 ++++++++
 test/harness/runTsuite.js  |    10 +-
 test/setup                 |     2 +-
 test/zlib.t.mjs            |   109 ++++
 17 files changed, 2322 insertions(+), 322 deletions(-)

diffs (truncated from 2828 to 1000 lines):

diff -r 18fc657411ca -r f146b5dc21cc auto/init
--- a/auto/init	Fri Apr 26 16:48:19 2024 -0700
+++ b/auto/init	Wed May 01 17:31:01 2024 -0700
@@ -16,6 +16,7 @@ NJS_CFLAGS=${NJS_CFLAGS=}
 NJS_BUILD_DIR=${NJS_BUILD_DIR:-build}
 
 NJS_LIB_MODULES=
+QJS_LIB_MODULES=
 
 NJS_LIBRT=
 
diff -r 18fc657411ca -r f146b5dc21cc auto/make
--- a/auto/make	Fri Apr 26 16:48:19 2024 -0700
+++ b/auto/make	Wed May 01 17:31:01 2024 -0700
@@ -15,11 +15,6 @@ njs_modules_c=$NJS_BUILD_DIR/njs_modules
 
 NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_modules_c"
 
-njs_incs=`echo $NJS_LIB_INCS \
-        | sed -e "s# *\([^ ]*\)#$njs_regex_cont-I\1#g"`
-njs_objs=`echo $NJS_LIB_SRCS \
-        | sed -e "s# *\([^ ]*\.\)c#$NJS_BUILD_DIR/\1o$njs_regex_cont#g"`
-
 cat << END                                    > $njs_modules_c
 
 #include <njs_main.h>
@@ -45,6 +40,43 @@ cat << END                              
 
 END
 
+if [ $NJS_HAVE_QUICKJS = YES ]; then
+
+qjs_modules_c=$NJS_BUILD_DIR/qjs_modules.c
+
+NJS_LIB_SRCS="$NJS_LIB_SRCS $qjs_modules_c"
+
+cat << END                                    > $qjs_modules_c
+
+#include <qjs.h>
+
+END
+
+for mod in $QJS_LIB_MODULES
+do
+    echo "extern qjs_module_t  $mod;"         >> $qjs_modules_c
+done
+
+echo                                          >> $qjs_modules_c
+echo 'qjs_module_t *qjs_modules[] = {'        >> $qjs_modules_c
+
+for mod in $QJS_LIB_MODULES
+do
+    echo "    &$mod,"                         >> $qjs_modules_c
+done
+
+cat << END                                    >> $qjs_modules_c
+    NULL
+};
+
+END
+fi
+
+njs_incs=`echo $NJS_LIB_INCS \
+        | sed -e "s# *\([^ ]*\)#$njs_regex_cont-I\1#g"`
+njs_objs=`echo $NJS_LIB_SRCS \
+        | sed -e "s# *\([^ ]*\.\)c#$NJS_BUILD_DIR/\1o$njs_regex_cont#g"`
+
 cat << END > $NJS_MAKEFILE
 
 # This file is auto-generated by configure
diff -r 18fc657411ca -r f146b5dc21cc auto/qjs_module
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/qjs_module	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,6 @@
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) F5, Inc
+
+QJS_LIB_MODULES="$QJS_LIB_MODULES $njs_module_name"
+NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_module_srcs"
+NJS_LIB_INCS="$NJS_LIB_INCS $njs_module_incs"
diff -r 18fc657411ca -r f146b5dc21cc auto/qjs_modules
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/qjs_modules	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,20 @@
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) F5, Inc
+
+if [ $NJS_HAVE_QUICKJS = YES ]; then
+
+    njs_module_name=qjs_buffer_module
+    njs_module_incs=
+    njs_module_srcs=src/qjs_buffer.c
+
+    . auto/qjs_module
+
+    if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then
+        njs_module_name=qjs_zlib_module
+        njs_module_incs=
+        njs_module_srcs=external/qjs_zlib_module.c
+
+        . auto/qjs_module
+    fi
+
+fi
diff -r 18fc657411ca -r f146b5dc21cc auto/quickjs
--- a/auto/quickjs	Fri Apr 26 16:48:19 2024 -0700
+++ b/auto/quickjs	Wed May 01 17:31:01 2024 -0700
@@ -25,6 +25,7 @@ if [ $NJS_TRY_QUICKJS = YES ]; then
                           JSRuntime *rt;
 
                           rt = JS_NewRuntime();
+                          (void) JS_GetClassID;
                           JS_FreeRuntime(rt);
                           return 0;
                      }"
@@ -54,6 +55,26 @@ if [ $NJS_TRY_QUICKJS = YES ]; then
     fi
 
     if [ $njs_found = yes ]; then
+
+        njs_feature="QuickJS JS_NewTypedArray()"
+        njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8)
+                          #pragma GCC diagnostic push
+                          #pragma GCC diagnostic ignored \"-Wcast-function-type\"
+                          #endif
+
+                          #include <quickjs.h>
+
+                          int main() {
+                              (void) JS_NewTypedArray;
+                              return 0;
+                         }"
+
+        . auto/feature
+
+        if [ $njs_found = yes ]; then
+            njs_define=NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY . auto/define
+        fi
+
         NJS_HAVE_QUICKJS=YES
         NJS_QUICKJS_LIB="$njs_feature_libs"
         NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs"
diff -r 18fc657411ca -r f146b5dc21cc auto/sources
--- a/auto/sources	Fri Apr 26 16:48:19 2024 -0700
+++ b/auto/sources	Wed May 01 17:31:01 2024 -0700
@@ -72,6 +72,10 @@ if [ "$NJS_HAVE_LIBBFD" = "YES" -a "$NJS
 	NJS_LIB_SRCS="$NJS_LIB_SRCS src/njs_addr2line.c"
 fi
 
+if [ "$NJS_HAVE_QUICKJS" = "YES" ]; then
+	NJS_LIB_SRCS="$NJS_LIB_SRCS src/qjs.c"
+fi
+
 NJS_TS_SRCS=$(find ts/ -name "*.d.ts" -o -name "*.json")
 
 NJS_TEST_TS_SRCS=$(find test/ts/ -name "*.ts" -o -name "*.json")
diff -r 18fc657411ca -r f146b5dc21cc configure
--- a/configure	Fri Apr 26 16:48:19 2024 -0700
+++ b/configure	Wed May 01 17:31:01 2024 -0700
@@ -21,7 +21,7 @@ NJS_AUTOCONF_ERR=$NJS_BUILD_DIR/autoconf
 NJS_AUTO_CONFIG_H=$NJS_BUILD_DIR/njs_auto_config.h
 NJS_MAKEFILE=$NJS_BUILD_DIR/Makefile
 
-NJS_LIB_INCS="src $NJS_BUILD_DIR"
+NJS_LIB_INCS="src external $NJS_BUILD_DIR"
 
 test -d $NJS_BUILD_DIR || mkdir $NJS_BUILD_DIR
 
@@ -59,6 +59,7 @@ NJS_LIB_AUX_LIBS=
 
 . auto/sources
 . auto/modules
+. auto/qjs_modules
 . auto/make
 . auto/expect
 . auto/summary
diff -r 18fc657411ca -r f146b5dc21cc external/njs_shell.c
--- a/external/njs_shell.c	Fri Apr 26 16:48:19 2024 -0700
+++ b/external/njs_shell.c	Wed May 01 17:31:01 2024 -0700
@@ -12,18 +12,7 @@
 #include <njs_rbtree.h>
 
 #if (NJS_HAVE_QUICKJS)
-#if defined(__GNUC__) && (__GNUC__ >= 8)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-function-type"
-#endif
-
-#include <quickjs.h>
-
-#if defined(__GNUC__) && (__GNUC__ >= 8)
-#pragma GCC diagnostic pop
-#endif
-#define NJS_QUICKJS_VERSION  "Unknown version"
-#include <pthread.h>
+#include <qjs.h>
 #endif
 
 #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE)
@@ -2822,7 +2811,7 @@ njs_engine_qjs_init(njs_engine_t *engine
         return NJS_ERROR;
     }
 
-    engine->u.qjs.ctx = JS_NewContext(engine->u.qjs.rt);
+    engine->u.qjs.ctx = qjs_new_context(engine->u.qjs.rt);
     if (engine->u.qjs.ctx == NULL) {
         njs_stderror("JS_NewContext() failed\n");
         return NJS_ERROR;
diff -r 18fc657411ca -r f146b5dc21cc external/qjs_zlib_module.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/external/qjs_zlib_module.c	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,512 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) F5, Inc.
+ */
+
+#include <qjs.h>
+#include <zlib.h>
+
+#define NJS_ZLIB_CHUNK_SIZE  1024
+
+static JSValue qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val,
+    int argc, JSValueConst *argv, int raw);
+static JSValue qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val,
+    int argc, JSValueConst *argv, int raw);
+
+static JSModuleDef *qjs_zlib_init(JSContext *ctx, const char *name);
+static void *qjs_zlib_alloc(void *opaque, u_int items, u_int size);
+static void qjs_zlib_free(void *opaque, void *address);
+
+
+static const JSCFunctionListEntry qjs_zlib_constants[] = {
+    JS_PROP_INT32_DEF("Z_NO_COMPRESSION",
+                      Z_NO_COMPRESSION,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_BEST_SPEED",
+                      Z_BEST_SPEED,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_BEST_COMPRESSION",
+                      Z_BEST_COMPRESSION,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_FILTERED",
+                      Z_FILTERED,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_HUFFMAN_ONLY",
+                      Z_HUFFMAN_ONLY,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_RLE",
+                      Z_RLE,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_FIXED",
+                      Z_FIXED,
+                      JS_PROP_ENUMERABLE),
+    JS_PROP_INT32_DEF("Z_DEFAULT_STRATEGY",
+                      Z_DEFAULT_STRATEGY,
+                      JS_PROP_ENUMERABLE),
+};
+
+static const JSCFunctionListEntry qjs_zlib_export[] = {
+    JS_CFUNC_MAGIC_DEF("deflateRawSync", 2, qjs_zlib_ext_deflate, 1),
+    JS_CFUNC_MAGIC_DEF("deflateSync", 2, qjs_zlib_ext_deflate, 0),
+    JS_CFUNC_MAGIC_DEF("inflateRawSync", 2, qjs_zlib_ext_inflate, 1),
+    JS_CFUNC_MAGIC_DEF("inflateSync", 2, qjs_zlib_ext_inflate, 0),
+    JS_OBJECT_DEF("constants",
+                  qjs_zlib_constants,
+                  njs_nitems(qjs_zlib_constants),
+                  JS_PROP_CONFIGURABLE),
+};
+
+
+qjs_module_t  qjs_zlib_module = {
+    .name = "zlib",
+    .init = qjs_zlib_init,
+};
+
+
+static JSValue
+qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv, int raw)
+{
+    int          rc, chunk_size, level, mem_level, strategy, window_bits;
+    JSValue      ret, options;
+    z_stream     stream;
+    njs_chb_t    chain;
+    qjs_bytes_t  bytes, dictionary;
+
+    chunk_size = NJS_ZLIB_CHUNK_SIZE;
+    mem_level = 8;
+    level = Z_DEFAULT_COMPRESSION;
+    strategy = Z_DEFAULT_STRATEGY;
+    window_bits = raw ? -MAX_WBITS : MAX_WBITS;
+
+    NJS_CHB_CTX_INIT(&chain, ctx);
+    dictionary.start = NULL;
+    dictionary.length = 0;
+    stream.opaque = NULL;
+
+    options = argv[1];
+
+    if (JS_IsObject(options)) {
+        ret = JS_GetPropertyStr(ctx, options, "chunkSize");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &chunk_size, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (chunk_size < 64) {
+                JS_ThrowRangeError(ctx, "chunkSize must be >= 64");
+                return JS_EXCEPTION;
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "level");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &level, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (level < Z_DEFAULT_COMPRESSION || level > Z_BEST_COMPRESSION) {
+                JS_ThrowRangeError(ctx, "level must be in the range %d..%d",
+                                   Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
+                return JS_EXCEPTION;
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "windowBits");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &window_bits, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (raw) {
+                if (window_bits < -15 || window_bits > -9) {
+                    JS_ThrowRangeError(ctx, "windowBits must be in the range "
+                                       "-15..-9");
+                    return JS_EXCEPTION;
+                }
+
+            } else {
+                if (window_bits < 9 || window_bits > 15) {
+                    JS_ThrowRangeError(ctx, "windowBits must be in the range "
+                                       "9..15");
+                    return JS_EXCEPTION;
+                }
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "memLevel");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &mem_level, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (mem_level < 1 || mem_level > 9) {
+                JS_ThrowRangeError(ctx, "memLevel must be in the range 1..9");
+                return JS_EXCEPTION;
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "strategy");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &strategy, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            switch (strategy) {
+            case Z_FILTERED:
+            case Z_HUFFMAN_ONLY:
+            case Z_RLE:
+            case Z_FIXED:
+            case Z_DEFAULT_STRATEGY:
+                break;
+
+            default:
+                JS_ThrowRangeError(ctx, "unknown strategy: %d", strategy);
+                return JS_EXCEPTION;
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "dictionary");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = qjs_to_bytes(ctx, &dictionary, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+        }
+    }
+
+    rc = qjs_to_bytes(ctx, &bytes, argv[0]);
+    if (rc != 0) {
+        return JS_EXCEPTION;
+    }
+
+    stream.next_in = bytes.start;
+    stream.avail_in = bytes.length;
+
+    stream.zalloc = qjs_zlib_alloc;
+    stream.zfree = qjs_zlib_free;
+    stream.opaque = ctx;
+
+    rc = deflateInit2(&stream, level, Z_DEFLATED, window_bits, mem_level,
+                      strategy);
+    if (njs_slow_path(rc != Z_OK)) {
+        JS_ThrowInternalError(ctx, "deflateInit2() failed");
+        goto fail;
+    }
+
+    if (dictionary.start != NULL) {
+        rc = deflateSetDictionary(&stream, dictionary.start, dictionary.length);
+        if (rc != Z_OK) {
+            JS_ThrowInternalError(ctx, "deflateSetDictionary() failed");
+            goto fail;
+        }
+    }
+
+    do {
+        stream.next_out = njs_chb_reserve(&chain, chunk_size);
+        if (njs_slow_path(stream.next_out == NULL)) {
+            JS_ThrowOutOfMemory(ctx);
+            goto fail;
+        }
+
+        stream.avail_out = chunk_size;
+
+        rc = deflate(&stream, Z_FINISH);
+        if (njs_slow_path(rc < 0)) {
+            JS_ThrowInternalError(ctx, "failed to deflate the data: %s",
+                                  stream.msg);
+            goto fail;
+        }
+
+        njs_chb_written(&chain, chunk_size - stream.avail_out);
+
+    } while (stream.avail_out == 0);
+
+    deflateEnd(&stream);
+
+    qjs_bytes_free(ctx, &bytes);
+
+    if (dictionary.start != NULL) {
+        qjs_bytes_free(ctx, &dictionary);
+    }
+
+    ret = qjs_buffer_chb_alloc(ctx, &chain);
+
+    njs_chb_destroy(&chain);
+
+    return ret;
+
+fail:
+
+    qjs_bytes_free(ctx, &bytes);
+
+    if (dictionary.start != NULL) {
+        qjs_bytes_free(ctx, &dictionary);
+    }
+
+    if (stream.opaque != NULL) {
+        deflateEnd(&stream);
+    }
+
+    if (chain.pool != NULL) {
+        njs_chb_destroy(&chain);
+    }
+
+    return JS_EXCEPTION;
+}
+
+
+static JSValue
+qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv, int raw)
+{
+    int          rc, chunk_size, window_bits;
+    JSValue      ret, options;
+    z_stream     stream;
+    njs_chb_t    chain;
+    qjs_bytes_t  bytes, dictionary;
+
+    chunk_size = NJS_ZLIB_CHUNK_SIZE;
+    window_bits = raw ? -MAX_WBITS : MAX_WBITS;
+
+    NJS_CHB_CTX_INIT(&chain, ctx);
+    dictionary.start = NULL;
+    dictionary.length = 0;
+    stream.opaque = NULL;
+
+    options = argv[1];
+
+    if (JS_IsObject(options)) {
+        ret = JS_GetPropertyStr(ctx, options, "chunkSize");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &chunk_size, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (chunk_size < 64) {
+                JS_ThrowRangeError(ctx, "chunkSize must be >= 64");
+                return JS_EXCEPTION;
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "windowBits");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = JS_ToInt32(ctx, &window_bits, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+
+            if (raw) {
+                if (window_bits < -15 || window_bits > -8) {
+                    JS_ThrowRangeError(ctx, "windowBits must be in the range "
+                                       "-15..-8");
+                    return JS_EXCEPTION;
+                }
+
+            } else {
+                if (window_bits < 8 || window_bits > 15) {
+                    JS_ThrowRangeError(ctx, "windowBits must be in the range "
+                                       "8..15");
+                    return JS_EXCEPTION;
+                }
+            }
+        }
+
+        ret = JS_GetPropertyStr(ctx, options, "dictionary");
+        if (JS_IsException(ret)) {
+            return JS_EXCEPTION;
+        }
+
+        if (!JS_IsUndefined(ret)) {
+            rc = qjs_to_bytes(ctx, &dictionary, ret);
+            JS_FreeValue(ctx, ret);
+            if (rc != 0) {
+                return JS_EXCEPTION;
+            }
+        }
+    }
+
+    rc = qjs_to_bytes(ctx, &bytes, argv[0]);
+    if (rc != 0) {
+        return JS_EXCEPTION;
+    }
+
+    stream.next_in = bytes.start;
+    stream.avail_in = bytes.length;
+
+    stream.zalloc = qjs_zlib_alloc;
+    stream.zfree = qjs_zlib_free;
+    stream.opaque = ctx;
+
+    rc = inflateInit2(&stream, window_bits);
+    if (njs_slow_path(rc != Z_OK)) {
+        JS_ThrowInternalError(ctx, "inflateInit2() failed");
+        goto fail;
+    }
+
+    if (dictionary.start != NULL) {
+        rc = inflateSetDictionary(&stream, dictionary.start, dictionary.length);
+        if (rc != Z_OK) {
+            JS_ThrowInternalError(ctx, "inflateSetDictionary() failed");
+            goto fail;
+        }
+    }
+
+    while (rc != Z_STREAM_END) {
+        stream.next_out = njs_chb_reserve(&chain, chunk_size);
+        if (njs_slow_path(stream.next_out == NULL)) {
+            JS_ThrowOutOfMemory(ctx);
+            goto fail;
+        }
+
+        stream.avail_out = chunk_size;
+
+        rc = inflate(&stream, Z_NO_FLUSH);
+        if (njs_slow_path(rc < 0)) {
+            JS_ThrowInternalError(ctx, "failed to inflate the data: %s",
+                                  stream.msg);
+            goto fail;
+        }
+
+        njs_chb_written(&chain, chunk_size - stream.avail_out);
+    }
+
+    rc = inflateEnd(&stream);
+    if (njs_slow_path(rc != Z_OK)) {
+        JS_ThrowInternalError(ctx, "inflateEnd() failed");
+        goto fail;
+    }
+
+    qjs_bytes_free(ctx, &bytes);
+
+    if (dictionary.start != NULL) {
+        qjs_bytes_free(ctx, &dictionary);
+    }
+
+    ret = qjs_buffer_chb_alloc(ctx, &chain);
+
+    njs_chb_destroy(&chain);
+
+    return ret;
+
+fail:
+
+    qjs_bytes_free(ctx, &bytes);
+
+    if (dictionary.start != NULL) {
+        qjs_bytes_free(ctx, &dictionary);
+    }
+
+    if (stream.opaque != NULL) {
+        inflateEnd(&stream);
+    }
+
+    if (chain.pool != NULL) {
+        njs_chb_destroy(&chain);
+    }
+
+    return JS_EXCEPTION;
+}
+
+
+static int
+qjs_zlib_module_init(JSContext *ctx, JSModuleDef *m)
+{
+    int      rc;
+    JSValue  proto;
+
+    proto = JS_NewObject(ctx);
+    JS_SetPropertyFunctionList(ctx, proto, qjs_zlib_export,
+                               njs_nitems(qjs_zlib_export));
+
+    rc = JS_SetModuleExport(ctx, m, "default", proto);
+    if (rc != 0) {
+        return -1;
+    }
+
+    return JS_SetModuleExportList(ctx, m, qjs_zlib_export,
+                                  njs_nitems(qjs_zlib_export));
+}
+
+
+static JSModuleDef *
+qjs_zlib_init(JSContext *ctx, const char *name)
+{
+    int          rc;
+    JSModuleDef  *m;
+
+    m = JS_NewCModule(ctx, name, qjs_zlib_module_init);
+    if (m == NULL) {
+        return NULL;
+    }
+
+    JS_AddModuleExport(ctx, m, "default");
+    rc = JS_AddModuleExportList(ctx, m, qjs_zlib_export,
+                                njs_nitems(qjs_zlib_export));
+    if (rc != 0) {
+        return NULL;
+    }
+
+    return m;
+}
+
+
+static void *
+qjs_zlib_alloc(void *opaque, u_int items, u_int size)
+{
+    return js_malloc(opaque, items * size);
+}
+
+
+static void
+qjs_zlib_free(void *opaque, void *address)
+{
+    js_free(opaque, address);
+}
diff -r 18fc657411ca -r f146b5dc21cc src/qjs.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qjs.c	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) F5, Inc.
+ */
+
+#include <qjs.h>
+
+
+JSContext *
+qjs_new_context(JSRuntime *rt)
+{
+    JSContext     *ctx;
+    qjs_module_t  **module;
+
+    ctx = JS_NewContext(rt);
+    if (ctx == NULL) {
+        return NULL;
+    }
+
+    for (module = qjs_modules; *module != NULL; module++) {
+        if ((*module)->init(ctx, (*module)->name) == NULL) {
+            return NULL;
+        }
+    }
+
+    return ctx;
+}
+
+
+int
+qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value)
+{
+    size_t   byte_offset, byte_length;
+    JSValue  val;
+
+    val = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL);
+    if (!JS_IsException(val)) {
+        bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, val);
+
+        JS_FreeValue(ctx, val);
+
+        if (bytes->start != NULL) {
+            bytes->tag = JS_TAG_OBJECT;
+            bytes->start += byte_offset;
+            bytes->length = byte_length;
+            return 0;
+        }
+    }
+
+    bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, value);
+    if (bytes->start != NULL) {
+        bytes->tag = JS_TAG_OBJECT;
+        return 0;
+    }
+
+    bytes->tag = JS_TAG_STRING;
+
+    if (!JS_IsString(value)) {
+        val = JS_ToString(ctx, value);
+
+        bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, val);
+
+        JS_FreeValue(ctx, val);
+
+        if (bytes->start == NULL) {
+            return -1;
+        }
+    }
+
+    bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, value);
+
+    return (bytes->start != NULL) ? 0 : -1;
+}
+
+
+void
+qjs_bytes_free(JSContext *ctx, qjs_bytes_t *bytes)
+{
+    if (bytes->tag == JS_TAG_STRING) {
+        JS_FreeCString(ctx, (char *) bytes->start);
+    }
+}
+
+
+JSValue
+qjs_typed_array_data(JSContext *ctx, JSValueConst value, njs_str_t *data)
+{
+    size_t  byte_offset, byte_length;
+
+    value = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length,
+                                   NULL);
+    if (JS_IsException(value)) {
+        return value;
+    }
+
+    data->start = JS_GetArrayBuffer(ctx, &data->length, value);
+
+    JS_FreeValue(ctx, value);
+
+    if (data->start == NULL) {
+        return JS_EXCEPTION;
+    }
+
+    data->start += byte_offset;
+    data->length = byte_length;
+
+    return JS_UNDEFINED;
+}
diff -r 18fc657411ca -r f146b5dc21cc src/qjs.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qjs.h	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) F5, Inc.
+ */
+
+#ifndef _QJS_H_INCLUDED_
+#define _QJS_H_INCLUDED_
+
+#include <njs_auto_config.h>
+
+#include <njs_types.h>
+#include <njs_clang.h>
+#include <string.h>
+#include <njs_str.h>
+#include <njs_unicode.h>
+#include <njs_utf8.h>
+#include <njs_chb.h>
+
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+
+#include <quickjs.h>
+
+#if defined(__GNUC__) && (__GNUC__ >= 8)
+#pragma GCC diagnostic pop
+#endif
+#define NJS_QUICKJS_VERSION  "Unknown version"
+#include <pthread.h>
+
+
+typedef JSModuleDef *(*qjs_addon_init_pt)(JSContext *ctx, const char *name);
+
+typedef struct {
+    const char                     *name;
+    qjs_addon_init_pt               init;
+} qjs_module_t;
+
+
+JSContext *qjs_new_context(JSRuntime *rt);
+
+
+JSValue qjs_buffer_alloc(JSContext *ctx, size_t size);
+JSValue qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain);
+
+typedef int (*qjs_buffer_encode_t)(JSContext *ctx, const njs_str_t *src,
+    njs_str_t *dst);
+typedef size_t (*qjs_buffer_encode_length_t)(JSContext *ctx,
+    const njs_str_t *src);
+
+typedef struct {
+    njs_str_t                   name;
+    qjs_buffer_encode_t         encode;
+    qjs_buffer_encode_length_t  encode_length;
+    qjs_buffer_encode_t         decode;
+    qjs_buffer_encode_length_t  decode_length;
+} qjs_buffer_encoding_t;
+
+const qjs_buffer_encoding_t *qjs_buffer_encoding(JSContext *ctx,
+    JSValueConst value, JS_BOOL thrw);
+
+
+typedef struct {
+    int                         tag;
+    size_t                      length;
+    u_char                      *start;
+} qjs_bytes_t;
+
+int qjs_to_bytes(JSContext *ctx, qjs_bytes_t *data, JSValueConst value);
+void qjs_bytes_free(JSContext *ctx, qjs_bytes_t *data);
+JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value,
+    njs_str_t *data);
+
+
+extern qjs_module_t              *qjs_modules[];
+
+#endif /* _QJS_H_INCLUDED_ */
diff -r 18fc657411ca -r f146b5dc21cc src/qjs_buffer.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qjs_buffer.c	Wed May 01 17:31:01 2024 -0700
@@ -0,0 +1,1174 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) F5, Inc.
+ */
+
+#include <qjs.h>
+
+static JSValue qjs_buffer(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv);
+static JSValue qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc,
+    JSValueConst *argv);
+static JSValue qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val,
+    int argc, JSValueConst *argv);
+static JSValue qjs_buffer_prototype_to_json(JSContext *ctx,
+    JSValueConst this_val, int argc, JSValueConst *argv);
+static JSValue qjs_buffer_prototype_to_string(JSContext *ctx,
+    JSValueConst this_val, int argc, JSValueConst *argv);
+static JSValue qjs_buffer_from_string(JSContext *ctx, JSValueConst str,
+    JSValueConst encoding);
+static JSValue qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst obj,
+    size_t offset, size_t size, size_t bytes, int float32);
+static JSValue qjs_buffer_from_array_buffer(JSContext *ctx, u_char *buf,
+    size_t size, JSValueConst offset, JSValueConst length);
+static JSValue qjs_buffer_from_object(JSContext *ctx, JSValueConst obj);
+static int qjs_base64_encode(JSContext *ctx, const njs_str_t *src,
+    njs_str_t *dst);
+static size_t qjs_base64_encode_length(JSContext *ctx, const njs_str_t *src);
+static int qjs_base64_decode(JSContext *ctx, const njs_str_t *src,
+    njs_str_t *dst);
+static size_t qjs_base64_decode_length(JSContext *ctx, const njs_str_t *src);
+static int qjs_base64url_encode(JSContext *ctx, const njs_str_t *src,
+    njs_str_t *dst);
+static int qjs_base64url_decode(JSContext *ctx, const njs_str_t *src,
+    njs_str_t *dst);
+static size_t qjs_base64url_decode_length(JSContext *ctx, const njs_str_t *src);
+static int qjs_hex_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst);
+static size_t qjs_hex_encode_length(JSContext *ctx, const njs_str_t *src);
+static int qjs_hex_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst);
+static size_t qjs_hex_decode_length(JSContext *ctx, const njs_str_t *src);
+static JSValue qjs_new_uint8_array(JSContext *ctx, size_t size);
+static JSModuleDef *qjs_buffer_init(JSContext *ctx, const char *name);
+
+
+static qjs_buffer_encoding_t  qjs_buffer_encodings[] =
+{
+    {
+        njs_str("utf-8"),
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+    },
+
+    {
+        njs_str("utf8"),
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+    },
+
+    {
+        njs_str("base64"),
+        qjs_base64_encode,
+        qjs_base64_encode_length,
+        qjs_base64_decode,
+        qjs_base64_decode_length,
+    },
+
+    {
+        njs_str("base64url"),
+        qjs_base64url_encode,
+        qjs_base64_encode_length,
+        qjs_base64url_decode,
+        qjs_base64url_decode_length,


More information about the nginx-devel mailing list