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