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