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 <stdio.h> 26 #include <string.h> 27 #include <assert.h> 28 #include <errno.h> 29 #include <zlib.h> 30 31 #include "ucode/module.h" 32 #include "ucode/platform.h" 33 34 // https://zlib.net/zlib_how.html 35 36 /* 37 * CHUNK is simply the buffer size for feeding data to and pulling data from 38 * the zlib routines. Larger buffer sizes would be more efficient, especially 39 * for inflate(). If the memory is available, buffers sizes on the order of 40 * 128K or 256K bytes should be used. 41 */ 42 #ifndef UC_ZLIB_CHUNK 43 #define UC_ZLIB_CHUNK 16384 44 #endif 45 46 #ifdef CHUNK 47 #undef CHUNK 48 #endif 49 #define CHUNK (UC_ZLIB_CHUNK) 50 51 static const __attribute__((unused)) unsigned int _chunk_check = CHUNK; 52 53 static uc_resource_type_t *zstrmd_type, *zstrmi_type; 54 55 static int last_error = 0; 56 #define err_return(err) do { last_error = err; return NULL; } while(0) 57 58 typedef struct { 59 z_stream strm; 60 uc_stringbuf_t *outbuf; 61 int flush; 62 } zstrm_t; 63 64 /* zlib init error message */ 65 static const char * ziniterr(int ret) 66 { 67 const char * msg; 68 69 switch (ret) { 70 case Z_ERRNO: 71 msg = strerror(errno); 72 break; 73 case Z_STREAM_ERROR: // can only happen for deflateInit2() by construction 74 msg = "invalid compression level"; 75 break; 76 case Z_MEM_ERROR: 77 msg = "out of memory"; 78 break; 79 case Z_VERSION_ERROR: 80 msg = "zlib version mismatch!"; 81 break; 82 default: 83 msg = "unknown error"; 84 break; 85 } 86 87 return msg; 88 } 89 90 static int 91 def_chunks(zstrm_t * const zstrm) 92 { 93 int ret; 94 95 /* run deflate() on input until output buffer not full */ 96 do { 97 printbuf_memset(zstrm->outbuf, printbuf_length(zstrm->outbuf) + CHUNK - 1, 0, 1); 98 zstrm->outbuf->bpos -= CHUNK; 99 100 zstrm->strm.avail_out = CHUNK; 101 zstrm->strm.next_out = (unsigned char *)(zstrm->outbuf->buf + zstrm->outbuf->bpos); 102 103 ret = deflate(&zstrm->strm, zstrm->flush); 104 assert(ret != Z_STREAM_ERROR); 105 106 zstrm->outbuf->bpos += CHUNK - zstrm->strm.avail_out; 107 } while (zstrm->strm.avail_out == 0); 108 assert(zstrm->strm.avail_in == 0); // all input will be used 109 110 return ret; 111 } 112 113 static bool 114 uc_zlib_def_object(uc_vm_t *const vm, uc_value_t * const obj, zstrm_t * const zstrm) 115 { 116 int ret; 117 bool eof = false; 118 uc_value_t *rfn, *rbuf; 119 120 rfn = ucv_property_get(obj, "read"); 121 122 if (!ucv_is_callable(rfn)) { 123 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 124 "Input object does not implement read() method"); 125 return false; 126 } 127 128 do { 129 rbuf = NULL; 130 uc_vm_stack_push(vm, ucv_get(obj)); 131 uc_vm_stack_push(vm, ucv_get(rfn)); 132 uc_vm_stack_push(vm, ucv_int64_new(CHUNK)); 133 134 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) 135 goto fail; 136 137 rbuf = uc_vm_stack_pop(vm); // read output chunk 138 139 /* we only accept strings */ 140 if (rbuf != NULL && ucv_type(rbuf) != UC_STRING) { 141 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 142 "Input object read() method returned non-string value"); 143 goto fail; 144 } 145 146 /* check EOF */ 147 eof = (rbuf == NULL || ucv_string_length(rbuf) == 0); 148 149 zstrm->strm.next_in = (unsigned char *)ucv_string_get(rbuf); 150 zstrm->strm.avail_in = ucv_string_length(rbuf); 151 152 zstrm->flush = eof ? Z_FINISH : Z_NO_FLUSH; 153 ret = def_chunks(zstrm); 154 (void)ret; // XXX make annoying compiler that ignores assert() happy 155 156 ucv_put(rbuf); // release rbuf 157 } while (!eof); // finish compression if all of source has been read in 158 assert(ret == Z_STREAM_END); // stream will be complete 159 160 return true; 161 162 fail: 163 ucv_put(rbuf); 164 return false; 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, printbuf_length(zstrm->outbuf) + CHUNK - 1, 0, 1); 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; 326 uc_value_t *rfn, *rbuf; 327 328 rfn = 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 return false; 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 fail; 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 fail; 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 fail; 368 } 369 370 ucv_put(rbuf); // release rbuf 371 } while (ret != Z_STREAM_END); // done when inflate() says it's done 372 373 if (ret != Z_STREAM_END) // data error 374 return false; 375 376 return true; 377 378 fail: 379 ucv_put(rbuf); 380 return false; 381 } 382 383 static bool 384 uc_zlib_inf_string(uc_vm_t * const vm, uc_value_t * const str, zstrm_t * const zstrm) 385 { 386 int ret; 387 388 zstrm->strm.next_in = (unsigned char *)ucv_string_get(str); 389 zstrm->strm.avail_in = ucv_string_length(str); 390 391 ret = inf_chunks(zstrm); 392 assert(zstrm->strm.avail_in == 0); 393 last_error = ret; 394 395 return Z_STREAM_END == ret; 396 } 397 398 /** 399 * Decompresses data in Zlib or gzip format. 400 * 401 * If the input argument is a plain string, it is directly decompressed. 402 * 403 * If an array, object or resource value is given, this function will attempt to 404 * invoke a `read()` method on it to read chunks of input text to incrementally 405 * decompress. Reading will stop if the object's `read()` method returns 406 * either `null` or an empty string. 407 * 408 * Throws an exception on errors. 409 * 410 * Returns the decompressed data. 411 * 412 * @function module:zlib#inflate 413 * 414 * @param {string} str_or_resource 415 * The string or resource object to be parsed as JSON. 416 * 417 * @returns {?string} 418 */ 419 static uc_value_t * 420 uc_zlib_inflate(uc_vm_t * const vm, const size_t nargs) 421 { 422 uc_value_t *rv = NULL; 423 uc_value_t *src = uc_fn_arg(0); 424 bool success; 425 int ret; 426 zstrm_t zstrm = { 427 .strm = { 428 .zalloc = Z_NULL, 429 .zfree = Z_NULL, 430 .opaque = Z_NULL, 431 .avail_in = 0, // must be initialized before call to inflateInit 432 .next_in = Z_NULL, // must be initialized before call to inflateInit 433 }, 434 .outbuf = NULL, 435 }; 436 437 /* tell inflateInit2 to perform either zlib or gzip decompression: 15+32 */ 438 ret = inflateInit2(&zstrm.strm, 15+32); 439 if (ret != Z_OK) { 440 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", ziniterr(ret)); 441 goto out; 442 } 443 444 zstrm.outbuf = ucv_stringbuf_new(); 445 446 switch (ucv_type(src)) { 447 case UC_STRING: 448 zstrm.flush = Z_FINISH; 449 success = uc_zlib_inf_string(vm, src, &zstrm); 450 break; 451 452 case UC_RESOURCE: 453 case UC_OBJECT: 454 case UC_ARRAY: 455 zstrm.flush = Z_NO_FLUSH; 456 success = uc_zlib_inf_object(vm, src, &zstrm); 457 break; 458 459 default: 460 uc_vm_raise_exception(vm, EXCEPTION_TYPE, 461 "Passed value is neither a string nor an object"); 462 printbuf_free(zstrm.outbuf); 463 goto out; 464 } 465 466 if (!success) { 467 if (vm->exception.type == EXCEPTION_NONE) // do not clobber previous exception 468 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Zlib error: %s", zstrm.strm.msg); 469 printbuf_free(zstrm.outbuf); 470 goto out; 471 } 472 473 rv = ucv_stringbuf_finish(zstrm.outbuf); 474 475 out: 476 (void)inflateEnd(&zstrm.strm); 477 return rv; 478 } 479 480 /** 481 * Represents a handle for interacting with a deflate stream initiated by deflater(). 482 * 483 * @class module:zlib.deflate 484 * @hideconstructor 485 * 486 * @see {@link module:zlib#deflater()} 487 * 488 * @example 489 * 490 * const zstrmd = deflater(…); 491 * 492 * for (let data = ...; data; data = ...) { 493 * zstrmd.write(data, Z_PARTIAL_FLUSH); // write uncompressed data to stream 494 * if (foo) 495 * let defl = zstrmd.read(); // read back compressed stream content 496 * } 497 * 498 * // terminate the stream at the end of data input to complete a valid archive 499 * zstrmd.write(last_data, Z_FINISH); 500 * defl = ztrmd.read(); 501 * 502 * zstrmd.error(); 503 */ 504 505 /** 506 * Initializes a deflate stream. 507 * 508 * Returns a stream handle on success. 509 * 510 * Returns `null` if an error occurred. 511 * 512 * @function module:zlib#deflater 513 * 514 * @param {?boolean} [gzip=false] 515 * Add a gzip header if true (creates a gzip-compliant output, otherwise defaults to Zlib) 516 * 517 * @param {?number} [level=Z_DEFAULT_COMPRESSION] 518 * The compression level (0-9). 519 * 520 * @returns {?module:zlib.deflate} 521 * 522 * @example 523 * // initialize a Zlib deflate stream using default compression 524 * const zstrmd = deflater(); 525 * 526 * // initialize a gzip deflate stream using fastest compression 527 * const zstrmd = deflater(true, Z_BEST_SPEED); 528 */ 529 static uc_value_t * 530 uc_zlib_deflater(uc_vm_t *vm, size_t nargs) 531 { 532 uc_value_t *gzip = uc_fn_arg(0); 533 uc_value_t *level = uc_fn_arg(1); 534 int ret, lvl = Z_DEFAULT_COMPRESSION; 535 bool gz = false; 536 zstrm_t *zstrm; 537 538 zstrm = calloc(1, sizeof(*zstrm)); 539 if (!zstrm) 540 err_return(ENOMEM); 541 542 zstrm->strm.zalloc = Z_NULL; 543 zstrm->strm.zfree = Z_NULL; 544 zstrm->strm.opaque = Z_NULL; 545 546 if (gzip) { 547 if (ucv_type(gzip) != UC_BOOLEAN) { 548 last_error = EINVAL; 549 goto fail; 550 } 551 552 gz = (int)ucv_boolean_get(gzip); 553 } 554 555 if (level) { 556 if (ucv_type(level) != UC_INTEGER) { 557 last_error = EINVAL; 558 goto fail; 559 } 560 561 lvl = (int)ucv_int64_get(level); 562 } 563 564 ret = deflateInit2(&zstrm->strm, lvl, 565 Z_DEFLATED, // only allowed method 566 gz ? 15+16 : 15, // 15 Zlib default, +16 for gzip 567 8, // default value 568 Z_DEFAULT_STRATEGY); // default value 569 if (ret != Z_OK) { 570 last_error = ret; 571 goto fail; 572 } 573 574 return uc_resource_new(zstrmd_type, zstrm); 575 576 fail: 577 free(zstrm); 578 return NULL; 579 } 580 581 /** 582 * Writes a chunk of data to the deflate stream. 583 * 584 * Input data must be a string, it is internally compressed by the zlib `deflate()` routine, 585 * the end result is buffered according to the requested `flush` mode until read via 586 * {@link module:zlib.zstrmd#read}. 587 * Valid `flush`values are `Z_NO_FLUSH` (the default), 588 * `Z_SYNC_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH`. 589 * If `flush` is `Z_FINISH` then no more data can be written to the stream. 590 * Refer to the {@link https://zlib.net/manual.html Zlib manual} for details 591 * on each flush mode. 592 * 593 * Returns `true` on success. 594 * 595 * Returns `null` if an error occurred. 596 * 597 * @function module:zlib.deflate#write 598 * 599 * @param {string} src 600 * The string of data to deflate. 601 * 602 * @param {?number} [flush=Z_NO_FLUSH] 603 * The zlib flush mode. 604 * 605 * @returns {?boolean} 606 */ 607 static uc_value_t * 608 uc_zlib_defwrite(uc_vm_t *vm, size_t nargs) 609 { 610 uc_value_t *src = uc_fn_arg(0); 611 uc_value_t *flush = uc_fn_arg(1); 612 zstrm_t **z = uc_fn_this("zlib.deflate"); 613 zstrm_t *zstrm; 614 615 if (!z || !*z) 616 err_return(EBADF); 617 618 zstrm = *z; 619 620 if (Z_FINISH == zstrm->flush) 621 err_return(EPIPE); // can't reuse a finished stream 622 623 if (flush) { 624 if (ucv_type(flush) != UC_INTEGER) 625 err_return(EINVAL); 626 627 zstrm->flush = (int)ucv_int64_get(flush); 628 switch (zstrm->flush) { 629 case Z_NO_FLUSH: 630 case Z_SYNC_FLUSH: 631 case Z_PARTIAL_FLUSH: 632 case Z_FULL_FLUSH: 633 case Z_FINISH: 634 break; 635 default: 636 err_return(EINVAL); 637 } 638 } 639 else 640 zstrm->flush = Z_NO_FLUSH; 641 642 /* we only accept strings */ 643 if (!src || ucv_type(src) != UC_STRING) 644 err_return(EINVAL); 645 646 if (!zstrm->outbuf) 647 zstrm->outbuf = ucv_stringbuf_new(); 648 649 return ucv_boolean_new(uc_zlib_def_string(vm, src, zstrm)); 650 } 651 652 /** 653 * Reads a chunk of compressed data from the deflate stream. 654 * 655 * Returns the current content of the deflate buffer, fed through 656 * {@link module:zlib.deflate#write}. 657 * 658 * Returns compressed chunk on success. 659 * 660 * Returns `null` if an error occurred. 661 * 662 * @function module:zlib.deflate#read 663 * 664 * @returns {?string} 665 */ 666 static uc_value_t * 667 uc_zlib_defread(uc_vm_t *vm, size_t nargs) 668 { 669 zstrm_t **z = uc_fn_this("zlib.deflate"); 670 zstrm_t *zstrm; 671 uc_value_t *rv; 672 673 if (!z || !*z) 674 err_return(EBADF); 675 676 zstrm = *z; 677 678 if (!zstrm->outbuf) 679 err_return(ENODATA); 680 681 if (Z_FINISH == zstrm->flush) 682 (void)deflateEnd(&zstrm->strm); 683 684 rv = ucv_stringbuf_finish(zstrm->outbuf); 685 zstrm->outbuf = NULL; // outbuf is now unuseable 686 return rv; 687 } 688 689 /** 690 * Represents a handle for interacting with an inflate stream initiated by inflater(). 691 * 692 * @class module:zlib.inflate 693 * @hideconstructor 694 * 695 * @borrows module:zlib.deflate#error as module:fs.inflate#error 696 * 697 * @see {@link module:zlib#inflater()} 698 * 699 * @example 700 * 701 * const zstrmi = inflater(); 702 * 703 * for (let data = ...; data; data = ...) { 704 * zstrmi.write(data, Z_SYNC_FLUSH); // write compressed data to stream 705 * if (foo) 706 * let defl = zstrmi.read(); // read back decompressed stream content 707 * } 708 * 709 * zstrmi.error(); 710 */ 711 712 /** 713 * Initializes an inflate stream. Can process either Zlib or gzip data. 714 * 715 * Returns a stream handle on success. 716 * 717 * Returns `null` if an error occurred. 718 * 719 * @function module:zlib#inflater 720 * 721 * @returns {?module:zlib.inflate} 722 * 723 * @example 724 * // initialize an inflate stream 725 * const zstrmi = inflater(); 726 */ 727 static uc_value_t * 728 uc_zlib_inflater(uc_vm_t *vm, size_t nargs) 729 { 730 int ret; 731 zstrm_t *zstrm; 732 733 zstrm = calloc(1, sizeof(*zstrm)); 734 if (!zstrm) 735 err_return(ENOMEM); 736 737 zstrm->strm.zalloc = Z_NULL; 738 zstrm->strm.zfree = Z_NULL; 739 zstrm->strm.opaque = Z_NULL; 740 zstrm->strm.avail_in = 0; 741 zstrm->strm.next_in = Z_NULL; 742 743 /* tell inflateInit2 to perform either zlib or gzip decompression: 15+32 */ 744 ret = inflateInit2(&zstrm->strm, 15+32); 745 if (ret != Z_OK) { 746 last_error = ret; 747 goto fail; 748 } 749 750 return uc_resource_new(zstrmi_type, zstrm); 751 752 fail: 753 free(zstrm); 754 return NULL; 755 } 756 757 /** 758 * Writes a chunk of data to the inflate stream. 759 * 760 * Input data must be a string, it is internally decompressed by the zlib `inflate()` routine, 761 * the end result is buffered according to the requested `flush` mode until read via 762 * {@link module:zlib.inflate#read}. 763 * Valid `flush` values are `Z_NO_FLUSH` (the default), `Z_SYNC_FLUSH, Z_FINISH`. 764 * If `flush` is `Z_FINISH` then no more data can be written to the stream. 765 * Refer to the {@link https://zlib.net/manual.html Zlib manual} for details 766 * on each flush mode. 767 * 768 * Returns `true` on success. 769 * 770 * Returns `null` if an error occurred. 771 * 772 * @function module:zlib.inflate#write 773 * 774 * @param {string} src 775 * The string of data to inflate. 776 * 777 * @param {?number} [flush=Z_NO_FLUSH] 778 * The zlib flush mode. 779 * 780 * @returns {?boolean} 781 */ 782 static uc_value_t * 783 uc_zlib_infwrite(uc_vm_t *vm, size_t nargs) 784 { 785 uc_value_t *src = uc_fn_arg(0); 786 uc_value_t *flush = uc_fn_arg(1); 787 zstrm_t **z = uc_fn_this("zlib.inflate"); 788 zstrm_t *zstrm; 789 790 if (!z || !*z) 791 err_return(EBADF); 792 793 zstrm = *z; 794 795 if (Z_FINISH == zstrm->flush) 796 err_return(EPIPE); // can't reuse a finished stream 797 798 if (flush) { 799 if (ucv_type(flush) != UC_INTEGER) 800 err_return(EINVAL); 801 802 zstrm->flush = (int)ucv_int64_get(flush); 803 switch (zstrm->flush) { 804 case Z_NO_FLUSH: 805 case Z_SYNC_FLUSH: 806 case Z_FINISH: 807 break; 808 default: 809 err_return(EINVAL); 810 } 811 } 812 else 813 zstrm->flush = Z_NO_FLUSH; 814 815 /* we only accept strings */ 816 if (!src || ucv_type(src) != UC_STRING) 817 err_return(EINVAL); 818 819 if (!zstrm->outbuf) 820 zstrm->outbuf = ucv_stringbuf_new(); 821 822 return ucv_boolean_new(uc_zlib_inf_string(vm, src, zstrm)); 823 } 824 825 /** 826 * Reads a chunk of decompressed data from the inflate stream. 827 * 828 * Returns the current content of the inflate buffer, fed through 829 * {@link module:zlib.inflate#write}. 830 * 831 * Returns decompressed chunk on success. 832 * 833 * Returns `null` if an error occurred. 834 * 835 * @function module:zlib.inflate#read 836 * 837 * @returns {?string} 838 */ 839 static uc_value_t * 840 uc_zlib_infread(uc_vm_t *vm, size_t nargs) 841 { 842 zstrm_t **z = uc_fn_this("zlib.inflate"); 843 zstrm_t *zstrm; 844 uc_value_t *rv; 845 846 if (!z || !*z) 847 err_return(EBADF); 848 849 zstrm = *z; 850 851 if (!zstrm->outbuf) 852 err_return(ENODATA); 853 854 if (Z_FINISH == zstrm->flush) 855 (void)inflateEnd(&zstrm->strm); 856 857 rv = ucv_stringbuf_finish(zstrm->outbuf); 858 zstrm->outbuf = NULL; // outbuf is now unuseable 859 return rv; 860 } 861 862 /** 863 * Queries error information. 864 * 865 * Returns a string containing a description of the last occurred error or 866 * `null` if there is no error information. 867 * 868 * @function module:zlib.deflate#error 869 * 870 * 871 * @returns {?string} 872 */ 873 static uc_value_t * 874 uc_zlib_error(uc_vm_t *vm, size_t nargs) 875 { 876 uc_value_t *errmsg; 877 878 if (!last_error) 879 return NULL; 880 881 // negative last_error only happens for zlib init returns 882 errmsg = ucv_string_new(last_error < 0 ? ziniterr(last_error) : strerror(last_error)); 883 last_error = 0; 884 return errmsg; 885 } 886 887 static const uc_function_list_t strmd_fns[] = { 888 { "write", uc_zlib_defwrite }, 889 { "read", uc_zlib_defread }, 890 { "error", uc_zlib_error }, 891 }; 892 893 static const uc_function_list_t strmi_fns[] = { 894 { "write", uc_zlib_infwrite }, 895 { "read", uc_zlib_infread }, 896 { "error", uc_zlib_error }, 897 }; 898 899 static const uc_function_list_t global_fns[] = { 900 { "deflate", uc_zlib_deflate }, 901 { "inflate", uc_zlib_inflate }, 902 { "deflater", uc_zlib_deflater }, 903 { "inflater", uc_zlib_inflater }, 904 }; 905 906 static void destroy_zstrmd(void *z) 907 { 908 zstrm_t *zstrm = z; 909 910 if (zstrm) { 911 (void)deflateEnd(&zstrm->strm); 912 printbuf_free(zstrm->outbuf); 913 free(zstrm); 914 } 915 } 916 917 static void destroy_zstrmi(void *z) 918 { 919 zstrm_t *zstrm = z; 920 921 if (zstrm) { 922 (void)inflateEnd(&zstrm->strm); 923 printbuf_free(zstrm->outbuf); 924 free(zstrm); 925 } 926 } 927 928 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 929 { 930 uc_function_list_register(scope, global_fns); 931 932 zstrmd_type = uc_type_declare(vm, "zlib.deflate", strmd_fns, destroy_zstrmd); 933 zstrmi_type = uc_type_declare(vm, "zlib.inflate", strmi_fns, destroy_zstrmi); 934 935 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x)) 936 937 /** 938 * @typedef 939 * @name Compression levels 940 * @description Constants representing predefined compression levels. 941 * @property {number} Z_NO_COMPRESSION. 942 * @property {number} Z_BEST_SPEED. 943 * @property {number} Z_BEST_COMPRESSION. 944 * @property {number} Z_DEFAULT_COMPRESSION - default compromise between speed and compression (currently equivalent to level 6). 945 */ 946 ADD_CONST(Z_NO_COMPRESSION); 947 ADD_CONST(Z_BEST_SPEED); 948 ADD_CONST(Z_BEST_COMPRESSION); 949 ADD_CONST(Z_DEFAULT_COMPRESSION); 950 951 /** 952 * @typedef 953 * @name flush options 954 * @description Constants representing flush options. 955 * @property {number} Z_NO_FLUSH. 956 * @property {number} Z_PARTIAL_FLUSH. 957 * @property {number} Z_SYNC_FLUSH. 958 * @property {number} Z_FULL_FLUSH. 959 * @property {number} Z_FINISH. 960 */ 961 ADD_CONST(Z_NO_FLUSH); 962 ADD_CONST(Z_PARTIAL_FLUSH); 963 ADD_CONST(Z_SYNC_FLUSH); 964 ADD_CONST(Z_FULL_FLUSH); 965 ADD_CONST(Z_FINISH); 966 } 967
This page was automatically generated by LXR 0.3.1. • OpenWrt