1 /* 2 * Copyright (C) 2024 Thibaut VARÈNE <hacks@slashdirt.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * # Zlib bindings 19 * 20 * The `zlib` module provides single-call and stream-oriented functions for interacting with zlib data. 21 * 22 * @module zlib 23 */ 24 25 #include <assert.h> 26 #include <errno.h> 27 #include <zlib.h> 28 29 #include "ucode/module.h" 30 #include "ucode/internal/util.h" 31 32 // https://zlib.net/zlib_how.html 33 34 /* 35 * CHUNK is simply the buffer size for feeding data to and pulling data from 36 * the zlib routines. Larger buffer sizes would be more efficient, especially 37 * for inflate(). If the memory is available, buffers sizes on the order of 38 * 128K or 256K bytes should be used. 39 */ 40 #ifndef UC_ZLIB_CHUNK 41 #define UC_ZLIB_CHUNK 16384 42 #endif 43 44 #ifdef CHUNK 45 #undef CHUNK 46 #endif 47 #define CHUNK (UC_ZLIB_CHUNK) 48 49 static unused const unsigned int _chunk_check = CHUNK; 50 51 static uc_resource_type_t *zstrmd_type, *zstrmi_type; 52 53 static int last_error = 0; 54 #define err_return(err) do { last_error = err; return NULL; } while(0) 55 56 typedef struct { 57 z_stream strm; 58 uc_stringbuf_t *outbuf; 59 int flush; 60 } zstrm_t; 61 62 /* zlib init error message */ 63 static const char * ziniterr(int ret) 64 { 65 const char * msg; 66 67 switch (ret) { 68 case Z_ERRNO: 69 msg = strerror(errno); 70 break; 71 case Z_STREAM_ERROR: // can only happen for deflateInit2() by construction 72 msg = "invalid compression level"; 73 break; 74 case Z_MEM_ERROR: 75 msg = "out of memory"; 76 break; 77 case Z_VERSION_ERROR: 78 msg = "zlib version mismatch!"; 79 break; 80 default: 81 msg = "unknown error"; 82 break; 83 } 84 85 return msg; 86 } 87 88 static int 89 def_chunks(zstrm_t * const zstrm) 90 { 91 int ret; 92 93 /* run deflate() on input until output buffer not full */ 94 do { 95 printbuf_memset(zstrm->outbuf, -1, 0, CHUNK); 96 zstrm->outbuf->bpos -= CHUNK; 97 98 zstrm->strm.avail_out = CHUNK; 99 zstrm->strm.next_out = (unsigned char *)(zstrm->outbuf->buf + zstrm->outbuf->bpos); 100 101 ret = deflate(&zstrm->strm, zstrm->flush); 102 assert(ret != Z_STREAM_ERROR); 103 104 zstrm->outbuf->bpos += CHUNK - zstrm->strm.avail_out; 105 } while (zstrm->strm.avail_out == 0); 106 assert(zstrm->strm.avail_in == 0); // all input will be used 107 108 return ret; 109 } 110 111 static bool 112 uc_zlib_def_object(uc_vm_t *const vm, uc_value_t * const obj, zstrm_t * const zstrm) 113 { 114 int ret; 115 bool eof = false, rv = false; 116 uc_value_t *rfn, *rbuf = NULL; 117 118 rfn = ucv_get(ucv_property_get(obj, "read")); 119 120 if (!ucv_is_callable(rfn)) { 121 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 122 "Input object does not implement read() method"); 123 goto out; 124 } 125 126 do { 127 rbuf = NULL; 128 uc_vm_stack_push(vm, ucv_get(obj)); 129 uc_vm_stack_push(vm, ucv_get(rfn)); 130 uc_vm_stack_push(vm, ucv_int64_new(CHUNK)); 131 132 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) 133 goto out; 134 135 rbuf = uc_vm_stack_pop(vm); // read output chunk 136 137 /* we only accept strings */ 138 if (rbuf != NULL && ucv_type(rbuf) != UC_STRING) { 139 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 140 "Input object read() method returned non-string value"); 141 goto out; 142 } 143 144 /* check EOF */ 145 eof = (rbuf == NULL || ucv_string_length(rbuf) == 0); 146 147 zstrm->strm.next_in = (unsigned char *)ucv_string_get(rbuf); 148 zstrm->strm.avail_in = ucv_string_length(rbuf); 149 150 zstrm->flush = eof ? Z_FINISH : Z_NO_FLUSH; 151 ret = def_chunks(zstrm); 152 (void)ret; // XXX make annoying compiler that ignores assert() happy 153 154 ucv_put(rbuf); // release rbuf 155 rbuf = NULL; 156 } while (!eof); // finish compression if all of source has been read in 157 assert(ret == Z_STREAM_END); // stream will be complete 158 159 rv = true; 160 161 out: 162 ucv_put(rbuf); 163 ucv_put(rfn); 164 return rv; 165 } 166 167 static bool 168 uc_zlib_def_string(uc_vm_t * const vm, uc_value_t * const str, zstrm_t * const zstrm) 169 { 170 zstrm->strm.next_in = (unsigned char *)ucv_string_get(str); 171 zstrm->strm.avail_in = ucv_string_length(str); 172 173 last_error = def_chunks(zstrm); 174 175 return true; 176 } 177 178 /** 179 * Compresses data in Zlib or gzip format. 180 * 181 * If the input argument is a plain string, it is directly compressed. 182 * 183 * If an array, object or resource value is given, this function will attempt to 184 * invoke a `read()` method on it to read chunks of input text to incrementally 185 * compress. Reading will stop if the object's `read()` method returns 186 * either `null` or an empty string. 187 * 188 * Throws an exception on errors. 189 * 190 * Returns the compressed data. 191 * 192 * @function module:zlib#deflate 193 * 194 * @param {string} str_or_resource 195 * The string or resource object to be compressed. 196 * 197 * @param {?boolean} [gzip=false] 198 * Add a gzip header if true (creates a gzip-compliant output, otherwise defaults to Zlib) 199 * 200 * @param {?number} [level=Z_DEFAULT_COMPRESSION] 201 * The compression level (0-9). 202 * 203 * @returns {?string} 204 * 205 * @example 206 * // deflate content using default compression 207 * const deflated = deflate(content); 208 * 209 * // deflate content using fastest compression 210 * const deflated = deflate(content, Z_BEST_SPEED); 211 */ 212 static uc_value_t * 213 uc_zlib_deflate(uc_vm_t * const vm, const size_t nargs) 214 { 215 uc_value_t *rv = NULL; 216 uc_value_t *src = uc_fn_arg(0); 217 uc_value_t *gzip = uc_fn_arg(1); 218 uc_value_t *level = uc_fn_arg(2); 219 int ret, lvl = Z_DEFAULT_COMPRESSION; 220 bool success, gz = false; 221 zstrm_t zstrm = { 222 .strm = { 223 .zalloc = Z_NULL, 224 .zfree = Z_NULL, 225 .opaque = Z_NULL, 226 }, 227 .outbuf = NULL, 228 .flush = Z_FINISH, 229 }; 230 231 if (gzip) { 232 if (ucv_type(gzip) != UC_BOOLEAN) { 233 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed gzip flag is not a boolean"); 234 goto out; 235 } 236 237 gz = (int)ucv_boolean_get(gzip); 238 } 239 240 if (level) { 241 if (ucv_type(level) != UC_INTEGER) { 242 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed level is not a number"); 243 goto out; 244 } 245 246 lvl = (int)ucv_int64_get(level); 247 } 248 249 ret = deflateInit2(&zstrm.strm, lvl, 250 Z_DEFLATED, // only allowed method 251 gz ? 15+16 : 15, // 15 Zlib default, +16 for gzip 252 8, // default value 253 Z_DEFAULT_STRATEGY); // default value 254 if (ret != Z_OK) { 255 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", ziniterr(ret)); 256 goto out; 257 } 258 259 zstrm.outbuf = ucv_stringbuf_new(); 260 261 switch (ucv_type(src)) { 262 case UC_STRING: 263 success = uc_zlib_def_string(vm, src, &zstrm); 264 break; 265 266 case UC_RESOURCE: 267 case UC_OBJECT: 268 case UC_ARRAY: 269 success = uc_zlib_def_object(vm, src, &zstrm); 270 break; 271 272 default: 273 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 274 "Passed value is neither a string nor an object"); 275 printbuf_free(zstrm.outbuf); 276 goto out; 277 } 278 279 if (!success) { 280 if (vm->exception.type == EXCEPTION_NONE) // do not clobber previous exception 281 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", zstrm.strm.msg); 282 printbuf_free(zstrm.outbuf); 283 goto out; 284 } 285 286 rv = ucv_stringbuf_finish(zstrm.outbuf); 287 288 out: 289 (void)deflateEnd(&zstrm.strm); 290 return rv; 291 } 292 293 static int 294 inf_chunks(zstrm_t * const zstrm) 295 { 296 int ret; 297 298 /* run inflate() on input until output buffer not full */ 299 do { 300 printbuf_memset(zstrm->outbuf, -1, 0, CHUNK); 301 zstrm->outbuf->bpos -= CHUNK; 302 303 zstrm->strm.avail_out = CHUNK; 304 zstrm->strm.next_out = (unsigned char *)(zstrm->outbuf->buf + zstrm->outbuf->bpos); 305 306 ret = inflate(&zstrm->strm, zstrm->flush); 307 assert(ret != Z_STREAM_ERROR); 308 switch (ret) { 309 case Z_NEED_DICT: 310 case Z_DATA_ERROR: 311 case Z_MEM_ERROR: 312 return ret; 313 } 314 315 zstrm->outbuf->bpos += CHUNK - zstrm->strm.avail_out; 316 } while (zstrm->strm.avail_out == 0); 317 318 return ret; 319 } 320 321 static bool 322 uc_zlib_inf_object(uc_vm_t *const vm, uc_value_t * const obj, zstrm_t * const zstrm) 323 { 324 int ret = Z_STREAM_ERROR; // error out if EOF on first loop 325 bool eof = false, rv = false; 326 uc_value_t *rfn, *rbuf = NULL; 327 328 rfn = ucv_get(ucv_property_get(obj, "read")); 329 330 if (!ucv_is_callable(rfn)) { 331 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 332 "Input object does not implement read() method"); 333 goto out; 334 } 335 336 do { 337 rbuf = NULL; 338 uc_vm_stack_push(vm, ucv_get(obj)); 339 uc_vm_stack_push(vm, ucv_get(rfn)); 340 uc_vm_stack_push(vm, ucv_int64_new(CHUNK)); 341 342 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) 343 goto out; 344 345 rbuf = uc_vm_stack_pop(vm); // read output chunk 346 347 /* we only accept strings */ 348 if (rbuf != NULL && ucv_type(rbuf) != UC_STRING) { 349 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 350 "Input object read() method returned non-string value"); 351 goto out; 352 } 353 354 /* check EOF */ 355 eof = (rbuf == NULL || ucv_string_length(rbuf) == 0); 356 if (eof) 357 break; 358 359 zstrm->strm.next_in = (unsigned char *)ucv_string_get(rbuf); 360 zstrm->strm.avail_in = ucv_string_length(rbuf); 361 362 ret = inf_chunks(zstrm); 363 switch (ret) { 364 case Z_NEED_DICT: 365 case Z_DATA_ERROR: 366 case Z_MEM_ERROR: 367 goto out; 368 } 369 370 ucv_put(rbuf); // release rbuf 371 rbuf = NULL; 372 } while (ret != Z_STREAM_END); // done when inflate() says it's done 373 374 rv = (ret == Z_STREAM_END); // data error otherwise 375 376 out: 377 ucv_put(rbuf); 378 ucv_put(rfn); 379 return rv; 380 } 381 382 static bool 383 uc_zlib_inf_string(uc_vm_t * const vm, uc_value_t * const str, zstrm_t * const zstrm) 384 { 385 int ret; 386 387 zstrm->strm.next_in = (unsigned char *)ucv_string_get(str); 388 zstrm->strm.avail_in = ucv_string_length(str); 389 390 ret = inf_chunks(zstrm); 391 assert(zstrm->strm.avail_in == 0); 392 last_error = ret; 393 394 return Z_STREAM_END == ret; 395 } 396 397 /** 398 * Decompresses data in Zlib or gzip format. 399 * 400 * If the input argument is a plain string, it is directly decompressed. 401 * 402 * If an array, object or resource value is given, this function will attempt to 403 * invoke a `read()` method on it to read chunks of input text to incrementally 404 * decompress. Reading will stop if the object's `read()` method returns 405 * either `null` or an empty string. 406 * 407 * Throws an exception on errors. 408 * 409 * Returns the decompressed data. 410 * 411 * @function module:zlib#inflate 412 * 413 * @param {string} str_or_resource 414 * The string or resource object to be parsed as JSON. 415 * 416 * @returns {?string} 417 */ 418 static uc_value_t * 419 uc_zlib_inflate(uc_vm_t * const vm, const size_t nargs) 420 { 421 uc_value_t *rv = NULL; 422 uc_value_t *src = uc_fn_arg(0); 423 bool success; 424 int ret; 425 zstrm_t zstrm = { 426 .strm = { 427 .zalloc = Z_NULL, 428 .zfree = Z_NULL, 429 .opaque = Z_NULL, 430 .avail_in = 0, // must be initialized before call to inflateInit 431 .next_in = Z_NULL, // must be initialized before call to inflateInit 432 }, 433 .outbuf = NULL, 434 }; 435 436 /* tell inflateInit2 to perform either zlib or gzip decompression: 15+32 */ 437 ret = inflateInit2(&zstrm.strm, 15+32); 438 if (ret != Z_OK) { 439 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", ziniterr(ret)); 440 goto out; 441 } 442 443 zstrm.outbuf = ucv_stringbuf_new(); 444 445 switch (ucv_type(src)) { 446 case UC_STRING: 447 zstrm.flush = Z_FINISH; 448 success = uc_zlib_inf_string(vm, src, &zstrm); 449 break; 450 451 case UC_RESOURCE: 452 case UC_OBJECT: 453 case UC_ARRAY: 454 zstrm.flush = Z_NO_FLUSH; 455 success = uc_zlib_inf_object(vm, src, &zstrm); 456 break; 457 458 default: 459 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 460 "Passed value is neither a string nor an object"); 461 printbuf_free(zstrm.outbuf); 462 goto out; 463 } 464 465 if (!success) { 466 if (vm->exception.type == EXCEPTION_NONE) // do not clobber previous exception 467 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", zstrm.strm.msg); 468 printbuf_free(zstrm.outbuf); 469 goto out; 470 } 471 472 rv = ucv_stringbuf_finish(zstrm.outbuf); 473 474 out: 475 (void)inflateEnd(&zstrm.strm); 476 return rv; 477 } 478 479 /** 480 * Represents a handle for interacting with a deflate stream initiated by deflater(). 481 * 482 * @class module:zlib.deflate 483 * @hideconstructor 484 * 485 * @see {@link module:zlib#deflater()} 486 * 487 * @example 488 * 489 * const zstrmd = deflater(…); 490 * 491 * for (let data = ...; data; data = ...) { 492 * zstrmd.write(data, Z_PARTIAL_FLUSH); // write uncompressed data to stream 493 * if (foo) 494 * let defl = zstrmd.read(); // read back compressed stream content 495 * } 496 * 497 * // terminate the stream at the end of data input to complete a valid archive 498 * zstrmd.write(last_data, Z_FINISH); 499 * defl = ztrmd.read(); 500 * 501 * zstrmd.error(); 502 */ 503 504 /** 505 * Initializes a deflate stream. 506 * 507 * Returns a stream handle on success. 508 * 509 * Returns `null` if an error occurred. 510 * 511 * @function module:zlib#deflater 512 * 513 * @param {?boolean} [gzip=false] 514 * Add a gzip header if true (creates a gzip-compliant output, otherwise defaults to Zlib) 515 * 516 * @param {?number} [level=Z_DEFAULT_COMPRESSION] 517 * The compression level (0-9). 518 * 519 * @returns {?module:zlib.deflate} 520 * 521 * @example 522 * // initialize a Zlib deflate stream using default compression 523 * const zstrmd = deflater(); 524 * 525 * // initialize a gzip deflate stream using fastest compression 526 * const zstrmd = deflater(true, Z_BEST_SPEED); 527 */ 528 static uc_value_t * 529 uc_zlib_deflater(uc_vm_t *vm, size_t nargs) 530 { 531 uc_value_t *gzip = uc_fn_arg(0); 532 uc_value_t *level = uc_fn_arg(1); 533 int ret, lvl = Z_DEFAULT_COMPRESSION; 534 bool gz = false; 535 zstrm_t *zstrm; 536 537 zstrm = calloc(1, sizeof(*zstrm)); 538 if (!zstrm) 539 err_return(ENOMEM); 540 541 zstrm->strm.zalloc = Z_NULL; 542 zstrm->strm.zfree = Z_NULL; 543 zstrm->strm.opaque = Z_NULL; 544 545 if (gzip) { 546 if (ucv_type(gzip) != UC_BOOLEAN) { 547 last_error = EINVAL; 548 goto fail; 549 } 550 551 gz = (int)ucv_boolean_get(gzip); 552 } 553 554 if (level) { 555 if (ucv_type(level) != UC_INTEGER) { 556 last_error = EINVAL; 557 goto fail; 558 } 559 560 lvl = (int)ucv_int64_get(level); 561 } 562 563 ret = deflateInit2(&zstrm->strm, lvl, 564 Z_DEFLATED, // only allowed method 565 gz ? 15+16 : 15, // 15 Zlib default, +16 for gzip 566 8, // default value 567 Z_DEFAULT_STRATEGY); // default value 568 if (ret != Z_OK) { 569 last_error = ret; 570 goto fail; 571 } 572 573 return uc_resource_new(zstrmd_type, zstrm); 574 575 fail: 576 free(zstrm); 577 return NULL; 578 } 579 580 /** 581 * Writes a chunk of data to the deflate stream. 582 * 583 * Input data must be a string, it is internally compressed by the zlib `deflate()` routine, 584 * the end result is buffered according to the requested `flush` mode until read via 585 * {@link module:zlib.zstrmd#read}. 586 * Valid `flush`values are `Z_NO_FLUSH` (the default), 587 * `Z_SYNC_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH`. 588 * If `flush` is `Z_FINISH` then no more data can be written to the stream. 589 * Refer to the {@link https://zlib.net/manual.html Zlib manual} for details 590 * on each flush mode. 591 * 592 * Returns `true` on success. 593 * 594 * Returns `null` if an error occurred. 595 * 596 * @function module:zlib.deflate#write 597 * 598 * @param {string} src 599 * The string of data to deflate. 600 * 601 * @param {?number} [flush=Z_NO_FLUSH] 602 * The zlib flush mode. 603 * 604 * @returns {?boolean} 605 */ 606 static uc_value_t * 607 uc_zlib_defwrite(uc_vm_t *vm, size_t nargs) 608 { 609 uc_value_t *src = uc_fn_arg(0); 610 uc_value_t *flush = uc_fn_arg(1); 611 zstrm_t **z = uc_fn_this("zlib.deflate"); 612 zstrm_t *zstrm; 613 614 if (!z || !*z) 615 err_return(EBADF); 616 617 zstrm = *z; 618 619 if (Z_FINISH == zstrm->flush) 620 err_return(EPIPE); // can't reuse a finished stream 621 622 if (flush) { 623 if (ucv_type(flush) != UC_INTEGER) 624 err_return(EINVAL); 625 626 zstrm->flush = (int)ucv_int64_get(flush); 627 switch (zstrm->flush) { 628 case Z_NO_FLUSH: 629 case Z_SYNC_FLUSH: 630 case Z_PARTIAL_FLUSH: 631 case Z_FULL_FLUSH: 632 case Z_FINISH: 633 break; 634 default: 635 err_return(EINVAL); 636 } 637 } 638 else 639 zstrm->flush = Z_NO_FLUSH; 640 641 /* we only accept strings */ 642 if (!src || ucv_type(src) != UC_STRING) 643 err_return(EINVAL); 644 645 if (!zstrm->outbuf) 646 zstrm->outbuf = ucv_stringbuf_new(); 647 648 return ucv_boolean_new(uc_zlib_def_string(vm, src, zstrm)); 649 } 650 651 /** 652 * Reads a chunk of compressed data from the deflate stream. 653 * 654 * Returns the current content of the deflate buffer, fed through 655 * {@link module:zlib.deflate#write}. 656 * 657 * Returns compressed chunk on success. 658 * 659 * Returns `null` if an error occurred. 660 * 661 * @function module:zlib.deflate#read 662 * 663 * @returns {?string} 664 */ 665 static uc_value_t * 666 uc_zlib_defread(uc_vm_t *vm, size_t nargs) 667 { 668 zstrm_t **z = uc_fn_this("zlib.deflate"); 669 zstrm_t *zstrm; 670 uc_value_t *rv; 671 672 if (!z || !*z) 673 err_return(EBADF); 674 675 zstrm = *z; 676 677 if (!zstrm->outbuf) 678 err_return(ENODATA); 679 680 if (Z_FINISH == zstrm->flush) 681 (void)deflateEnd(&zstrm->strm); 682 683 rv = ucv_stringbuf_finish(zstrm->outbuf); 684 zstrm->outbuf = NULL; // outbuf is now unuseable 685 return rv; 686 } 687 688 /** 689 * Represents a handle for interacting with an inflate stream initiated by inflater(). 690 * 691 * @class module:zlib.inflate 692 * @hideconstructor 693 * 694 * @borrows module:zlib.deflate#error as module:fs.inflate#error 695 * 696 * @see {@link module:zlib#inflater()} 697 * 698 * @example 699 * 700 * const zstrmi = inflater(); 701 * 702 * for (let data = ...; data; data = ...) { 703 * zstrmi.write(data, Z_SYNC_FLUSH); // write compressed data to stream 704 * if (foo) 705 * let defl = zstrmi.read(); // read back decompressed stream content 706 * } 707 * 708 * zstrmi.error(); 709 */ 710 711 /** 712 * Initializes an inflate stream. Can process either Zlib or gzip data. 713 * 714 * Returns a stream handle on success. 715 * 716 * Returns `null` if an error occurred. 717 * 718 * @function module:zlib#inflater 719 * 720 * @returns {?module:zlib.inflate} 721 * 722 * @example 723 * // initialize an inflate stream 724 * const zstrmi = inflater(); 725 */ 726 static uc_value_t * 727 uc_zlib_inflater(uc_vm_t *vm, size_t nargs) 728 { 729 int ret; 730 zstrm_t *zstrm; 731 732 zstrm = calloc(1, sizeof(*zstrm)); 733 if (!zstrm) 734 err_return(ENOMEM); 735 736 zstrm->strm.zalloc = Z_NULL; 737 zstrm->strm.zfree = Z_NULL; 738 zstrm->strm.opaque = Z_NULL; 739 zstrm->strm.avail_in = 0; 740 zstrm->strm.next_in = Z_NULL; 741 742 /* tell inflateInit2 to perform either zlib or gzip decompression: 15+32 */ 743 ret = inflateInit2(&zstrm->strm, 15+32); 744 if (ret != Z_OK) { 745 last_error = ret; 746 goto fail; 747 } 748 749 return uc_resource_new(zstrmi_type, zstrm); 750 751 fail: 752 free(zstrm); 753 return NULL; 754 } 755 756 /** 757 * Writes a chunk of data to the inflate stream. 758 * 759 * Input data must be a string, it is internally decompressed by the zlib `inflate()` routine, 760 * the end result is buffered according to the requested `flush` mode until read via 761 * {@link module:zlib.inflate#read}. 762 * Valid `flush` values are `Z_NO_FLUSH` (the default), `Z_SYNC_FLUSH, Z_FINISH`. 763 * If `flush` is `Z_FINISH` then no more data can be written to the stream. 764 * Refer to the {@link https://zlib.net/manual.html Zlib manual} for details 765 * on each flush mode. 766 * 767 * Returns `true` on success. 768 * 769 * Returns `null` if an error occurred. 770 * 771 * @function module:zlib.inflate#write 772 * 773 * @param {string} src 774 * The string of data to inflate. 775 * 776 * @param {?number} [flush=Z_NO_FLUSH] 777 * The zlib flush mode. 778 * 779 * @returns {?boolean} 780 */ 781 static uc_value_t * 782 uc_zlib_infwrite(uc_vm_t *vm, size_t nargs) 783 { 784 uc_value_t *src = uc_fn_arg(0); 785 uc_value_t *flush = uc_fn_arg(1); 786 zstrm_t **z = uc_fn_this("zlib.inflate"); 787 zstrm_t *zstrm; 788 789 if (!z || !*z) 790 err_return(EBADF); 791 792 zstrm = *z; 793 794 if (Z_FINISH == zstrm->flush) 795 err_return(EPIPE); // can't reuse a finished stream 796 797 if (flush) { 798 if (ucv_type(flush) != UC_INTEGER) 799 err_return(EINVAL); 800 801 zstrm->flush = (int)ucv_int64_get(flush); 802 switch (zstrm->flush) { 803 case Z_NO_FLUSH: 804 case Z_SYNC_FLUSH: 805 case Z_FINISH: 806 break; 807 default: 808 err_return(EINVAL); 809 } 810 } 811 else 812 zstrm->flush = Z_NO_FLUSH; 813 814 /* we only accept strings */ 815 if (!src || ucv_type(src) != UC_STRING) 816 err_return(EINVAL); 817 818 if (!zstrm->outbuf) 819 zstrm->outbuf = ucv_stringbuf_new(); 820 821 return ucv_boolean_new(uc_zlib_inf_string(vm, src, zstrm)); 822 } 823 824 /** 825 * Reads a chunk of decompressed data from the inflate stream. 826 * 827 * Returns the current content of the inflate buffer, fed through 828 * {@link module:zlib.inflate#write}. 829 * 830 * Returns decompressed chunk on success. 831 * 832 * Returns `null` if an error occurred. 833 * 834 * @function module:zlib.inflate#read 835 * 836 * @returns {?string} 837 */ 838 static uc_value_t * 839 uc_zlib_infread(uc_vm_t *vm, size_t nargs) 840 { 841 zstrm_t **z = uc_fn_this("zlib.inflate"); 842 zstrm_t *zstrm; 843 uc_value_t *rv; 844 845 if (!z || !*z) 846 err_return(EBADF); 847 848 zstrm = *z; 849 850 if (!zstrm->outbuf) 851 err_return(ENODATA); 852 853 if (Z_FINISH == zstrm->flush) 854 (void)inflateEnd(&zstrm->strm); 855 856 rv = ucv_stringbuf_finish(zstrm->outbuf); 857 zstrm->outbuf = NULL; // outbuf is now unuseable 858 return rv; 859 } 860 861 /** 862 * Queries error information. 863 * 864 * Returns a string containing a description of the last occurred error or 865 * `null` if there is no error information. 866 * 867 * @function module:zlib.deflate#error 868 * 869 * 870 * @returns {?string} 871 */ 872 static uc_value_t * 873 uc_zlib_error(uc_vm_t *vm, size_t nargs) 874 { 875 uc_value_t *errmsg; 876 877 if (!last_error) 878 return NULL; 879 880 // negative last_error only happens for zlib init returns 881 errmsg = ucv_string_new(last_error < 0 ? ziniterr(last_error) : strerror(last_error)); 882 last_error = 0; 883 return errmsg; 884 } 885 886 static const uc_function_list_t strmd_fns[] = { 887 { "write", uc_zlib_defwrite }, 888 { "read", uc_zlib_defread }, 889 { "error", uc_zlib_error }, 890 }; 891 892 static const uc_function_list_t strmi_fns[] = { 893 { "write", uc_zlib_infwrite }, 894 { "read", uc_zlib_infread }, 895 { "error", uc_zlib_error }, 896 }; 897 898 static const uc_function_list_t global_fns[] = { 899 { "deflate", uc_zlib_deflate }, 900 { "inflate", uc_zlib_inflate }, 901 { "deflater", uc_zlib_deflater }, 902 { "inflater", uc_zlib_inflater }, 903 }; 904 905 static void destroy_zstrmd(void *z) 906 { 907 zstrm_t *zstrm = z; 908 909 if (zstrm) { 910 (void)deflateEnd(&zstrm->strm); 911 printbuf_free(zstrm->outbuf); 912 free(zstrm); 913 } 914 } 915 916 static void destroy_zstrmi(void *z) 917 { 918 zstrm_t *zstrm = z; 919 920 if (zstrm) { 921 (void)inflateEnd(&zstrm->strm); 922 printbuf_free(zstrm->outbuf); 923 free(zstrm); 924 } 925 } 926 927 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 928 { 929 uc_function_list_register(scope, global_fns); 930 931 zstrmd_type = uc_type_declare(vm, "zlib.deflate", strmd_fns, destroy_zstrmd); 932 zstrmi_type = uc_type_declare(vm, "zlib.inflate", strmi_fns, destroy_zstrmi); 933 934 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) 935 936 /** 937 * @typedef 938 * @name Compression levels 939 * @description Constants representing predefined compression levels. 940 * @property {number} Z_NO_COMPRESSION. 941 * @property {number} Z_BEST_SPEED. 942 * @property {number} Z_BEST_COMPRESSION. 943 * @property {number} Z_DEFAULT_COMPRESSION - default compromise between speed and compression (currently equivalent to level 6). 944 */ 945 ADD_CONST(Z_NO_COMPRESSION); 946 ADD_CONST(Z_BEST_SPEED); 947 ADD_CONST(Z_BEST_COMPRESSION); 948 ADD_CONST(Z_DEFAULT_COMPRESSION); 949 950 /** 951 * @typedef 952 * @name flush options 953 * @description Constants representing flush options. 954 * @property {number} Z_NO_FLUSH. 955 * @property {number} Z_PARTIAL_FLUSH. 956 * @property {number} Z_SYNC_FLUSH. 957 * @property {number} Z_FULL_FLUSH. 958 * @property {number} Z_FINISH. 959 */ 960 ADD_CONST(Z_NO_FLUSH); 961 ADD_CONST(Z_PARTIAL_FLUSH); 962 ADD_CONST(Z_SYNC_FLUSH); 963 ADD_CONST(Z_FULL_FLUSH); 964 ADD_CONST(Z_FINISH); 965 } 966
This page was automatically generated by LXR 0.3.1. • OpenWrt