summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorThibaut VARÈNE <hacks@slashdirt.org>2024-07-17 18:01:09 +0200
committerJo-Philipp Wich <jo@mein.io>2025-02-20 09:09:56 +0100
commit0255af462be3c4b2aeaa77014376e22e9300ffc3 (patch)
tree0bef56c93bb38f8db117905486dfd65b0ccdfdf4
parentd20f7bb87d478a9bba1a9ee7d4074dfc1297bebb (diff)
zlib: add stream-oriented inflate operations
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
-rw-r--r--lib/zlib.c195
1 files changed, 194 insertions, 1 deletions
diff --git a/lib/zlib.c b/lib/zlib.c
index c1a1e03..038a352 100644
--- a/lib/zlib.c
+++ b/lib/zlib.c
@@ -41,7 +41,7 @@
*/
#define CHUNK 16384
-static uc_resource_type_t *zstrmd_type;
+static uc_resource_type_t *zstrmd_type, *zstrmi_type;
static int last_error = 0;
#define err_return(err) do { last_error = err; return NULL; } while(0)
@@ -380,6 +380,7 @@ uc_zlib_inf_string(uc_vm_t * const vm, uc_value_t * const str, zstrm_t * const z
ret = inf_chunks(zstrm);
assert(zstrm->strm.avail_in == 0);
+ last_error = ret;
return Z_STREAM_END == ret;
}
@@ -672,6 +673,179 @@ uc_zlib_defread(uc_vm_t *vm, size_t nargs)
}
/**
+ * Represents a handle for interacting with an inflate stream initiated by infnew().
+ *
+ * @class module:zlib.zstrmi
+ * @hideconstructor
+ *
+ * @see {@link module:zlib#infnew()}
+ *
+ * @example
+ *
+ * const zstrmi = infnew();
+ *
+ * for (let data = ...; data; data = ...) {
+ * zstrmi.write(data, Z_SYNC_FLUSH); // write compressed data to stream
+ * if (foo)
+ * let defl = zstrmi.read(); // read back decompressed stream content
+ * }
+ *
+ * // terminate the stream if needed (for e.g. file output)
+ * zstrmi.write('', Z_FINISH);
+ * defl = ztrmi.read();
+ *
+ * zstrmi.error();
+ */
+
+/**
+ * Initializes an inflate stream. Can process either Zlib or gzip data.
+ *
+ * Returns a stream handle on success.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:zlib#infnew
+ *
+ * @returns {?module:zlib.zstrmi}
+ *
+ * @example
+ * // initialize an inflate stream
+ * const zstrmi = infnew();
+ */
+ static uc_value_t *
+uc_zlib_infnew(uc_vm_t *vm, size_t nargs)
+{
+ int ret;
+ zstrm_t *zstrm;
+
+ zstrm = calloc(1, sizeof(*zstrm));
+ if (!zstrm)
+ err_return(ENOMEM);
+
+ zstrm->strm.zalloc = Z_NULL;
+ zstrm->strm.zfree = Z_NULL;
+ zstrm->strm.opaque = Z_NULL;
+ zstrm->strm.avail_in = 0;
+ zstrm->strm.next_in = Z_NULL;
+
+ /* tell inflateInit2 to perform either zlib or gzip decompression: 15+32 */
+ ret = inflateInit2(&zstrm->strm, 15+32);
+ if (ret != Z_OK) {
+ last_error = ret;
+ goto fail;
+ }
+
+ return uc_resource_new(zstrmi_type, zstrm);
+
+fail:
+ free(zstrm);
+ return NULL;
+}
+
+/**
+ * Writes a chunk of data to the inflate stream.
+ *
+ * Input data must be a string, it is internally decompressed by the zlib deflate() routine,
+ * the end is buffered according to the requested `flush` mode until read via
+ * @see {@link module:zlib.zstrmd#read}.
+ * If `flush` is `Z_FINISH` (the default) then no more data can be written to the stream.
+ * Valid `flush` values are `Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH`
+ *
+ * Returns `true` on success.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:zlib.zstrmi#write
+ *
+ * @param {string} src
+ * The string of data to deflate.
+ *
+ * @param {?number} [flush=Z_FINISH]
+ * The zlib flush mode.
+ *
+ * @returns {?boolean}
+ */
+static uc_value_t *
+uc_zlib_infwrite(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *src = uc_fn_arg(0);
+ uc_value_t *flush = uc_fn_arg(1);
+ zstrm_t **z = uc_fn_this("zlib.strmi");
+ zstrm_t *zstrm;
+
+ if (!z || !*z)
+ err_return(EBADF);
+
+ zstrm = *z;
+
+ if (Z_FINISH == zstrm->flush)
+ err_return(EPIPE); // can't reuse a finished stream
+
+ if (flush) {
+ if (ucv_type(flush) != UC_INTEGER)
+ err_return(EINVAL);
+
+ zstrm->flush = (int)ucv_int64_get(flush);
+ switch (zstrm->flush) {
+ case Z_NO_FLUSH:
+ case Z_SYNC_FLUSH:
+ case Z_FINISH:
+ break;
+ default:
+ err_return(EINVAL);
+ }
+ }
+ else
+ zstrm->flush = Z_FINISH;
+
+ /* we only accept strings */
+ if (!src || ucv_type(src) != UC_STRING)
+ err_return(EINVAL);
+
+ if (!zstrm->outbuf)
+ zstrm->outbuf = ucv_stringbuf_new();
+
+ return ucv_boolean_new(uc_zlib_inf_string(vm, src, zstrm));
+}
+
+/**
+ * Reads a chunk of decompressed data from the inflate stream.
+ *
+ * Returns the current content of the inflate buffer, fed through
+ * @see {@link module:zlib.zstrmi#write}.
+ *
+ * Returns compressed chunk on success.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:zlib.zstrmd#read
+ *
+ * @returns {?string}
+ */
+static uc_value_t *
+uc_zlib_infread(uc_vm_t *vm, size_t nargs)
+{
+ zstrm_t **z = uc_fn_this("zlib.strmi");
+ zstrm_t *zstrm;
+ uc_value_t *rv;
+
+ if (!z || !*z)
+ err_return(EBADF);
+
+ zstrm = *z;
+
+ if (!zstrm->outbuf)
+ err_return(ENODATA);
+
+ if (Z_FINISH == zstrm->flush)
+ (void)inflateEnd(&zstrm->strm);
+
+ rv = ucv_stringbuf_finish(zstrm->outbuf);
+ zstrm->outbuf = NULL; // outbuf is now unuseable
+ return rv;
+}
+
+/**
* Query error information.
*
* Returns a string containing a description of the last occurred error or
@@ -702,10 +876,17 @@ static const uc_function_list_t strmd_fns[] = {
{ "error", uc_zlib_error },
};
+static const uc_function_list_t strmi_fns[] = {
+ { "write", uc_zlib_infwrite },
+ { "read", uc_zlib_infread },
+ { "error", uc_zlib_error },
+};
+
static const uc_function_list_t global_fns[] = {
{ "deflate", uc_zlib_deflate },
{ "inflate", uc_zlib_inflate },
{ "defnew", uc_zlib_defnew },
+ { "infnew", uc_zlib_infnew },
};
static void destroy_zstrmd(void *z)
@@ -719,11 +900,23 @@ static void destroy_zstrmd(void *z)
}
}
+static void destroy_zstrmi(void *z)
+{
+ zstrm_t *zstrm = z;
+
+ if (zstrm) {
+ (void)inflateEnd(&zstrm->strm);
+ printbuf_free(zstrm->outbuf);
+ free(zstrm);
+ }
+}
+
void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
{
uc_function_list_register(scope, global_fns);
zstrmd_type = uc_type_declare(vm, "zlib.strmd", strmd_fns, destroy_zstrmd);
+ zstrmi_type = uc_type_declare(vm, "zlib.strmi", strmi_fns, destroy_zstrmi);
#define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))