multi_compress 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/GET_STARTED.md +1 -1
  4. data/README.md +1 -1
  5. data/THIRD_PARTY_NOTICES.md +1 -1
  6. data/ext/multi_compress/multi_compress.c +230 -77
  7. data/ext/multi_compress/vendor/.vendored +1 -1
  8. data/ext/multi_compress/vendor/brotli/c/common/version.h +2 -2
  9. data/ext/multi_compress/vendor/brotli/c/dec/decode.c +59 -30
  10. data/ext/multi_compress/vendor/brotli/c/include/brotli/decode.h +1 -3
  11. data/lib/multi_compress/active_record.rb +11 -5
  12. data/lib/multi_compress/codec.rb +164 -94
  13. data/lib/multi_compress/database/api.rb +42 -0
  14. data/lib/multi_compress/database/decoding.rb +42 -0
  15. data/lib/multi_compress/database/dictionary.rb +72 -0
  16. data/lib/multi_compress/database/encoding.rb +31 -0
  17. data/lib/multi_compress/database/header.rb +71 -0
  18. data/lib/multi_compress/database/operations.rb +73 -0
  19. data/lib/multi_compress/database/validation.rb +95 -0
  20. data/lib/multi_compress/database.rb +10 -282
  21. data/lib/multi_compress/db_deployment/bundle_archiver.rb +70 -0
  22. data/lib/multi_compress/db_deployment/bundle_metadata.rb +56 -0
  23. data/lib/multi_compress/db_deployment/bundle_stager.rb +62 -0
  24. data/lib/multi_compress/db_deployment/commands.rb +309 -0
  25. data/lib/multi_compress/db_deployment/deployment_bundle.rb +80 -0
  26. data/lib/multi_compress/db_deployment/deterministic_tar_writer.rb +76 -0
  27. data/lib/multi_compress/db_deployment/dictionary_registry.rb +244 -0
  28. data/lib/multi_compress/db_deployment/readable_view.rb +112 -0
  29. data/lib/multi_compress/db_deployment/readable_view_options.rb +51 -0
  30. data/lib/multi_compress/db_deployment/sql_identifiers.rb +45 -0
  31. data/lib/multi_compress/db_deployment.rb +10 -844
  32. data/lib/multi_compress/reader/buffer_operations.rb +57 -0
  33. data/lib/multi_compress/reader/read_operations.rb +67 -0
  34. data/lib/multi_compress/reader.rb +97 -0
  35. data/lib/multi_compress/stream_io.rb +13 -0
  36. data/lib/multi_compress/version.rb +1 -1
  37. data/lib/multi_compress/writer.rb +85 -0
  38. data/lib/multi_compress.rb +42 -333
  39. metadata +25 -6
@@ -8,8 +8,9 @@
8
8
  #include <brotli/decode.h>
9
9
  #include <brotli/encode.h>
10
10
  #include <lz4.h>
11
- #include <lz4hc.h>
11
+ #include <limits.h>
12
12
  #include <lz4frame.h>
13
+ #include <lz4hc.h>
13
14
  #include <pthread.h>
14
15
  #include <stdio.h>
15
16
  #include <stdint.h>
@@ -518,30 +519,42 @@ static compress_algo_t detect_algo(const uint8_t *data, size_t len) {
518
519
  return ALGO_ZSTD;
519
520
  }
520
521
 
521
- static inline VALUE rb_binary_str_new(const char *ptr, long len) {
522
- VALUE str = rb_str_new(ptr, len);
522
+ static inline long rb_checked_string_length(size_t len) {
523
+ if ((uintmax_t)len > (uintmax_t)LONG_MAX)
524
+ rb_raise(eDataError, "string size exceeds Ruby's maximum length");
525
+
526
+ return (long)len;
527
+ }
528
+
529
+ static inline VALUE rb_binary_str_new(const char *ptr, size_t len) {
530
+ VALUE str = rb_str_new(ptr, rb_checked_string_length(len));
523
531
  rb_enc_associate(str, binary_encoding);
524
532
  return str;
525
533
  }
526
534
 
527
- static inline VALUE rb_binary_str_buf_new(long capa) {
528
- VALUE str = rb_str_buf_new(capa);
535
+ static inline VALUE rb_binary_str_buf_new(size_t capa) {
536
+ VALUE str = rb_str_buf_new(rb_checked_string_length(capa));
529
537
  rb_enc_associate(str, binary_encoding);
530
538
  return str;
531
539
  }
532
540
 
533
- static inline VALUE rb_binary_str_buf_reserve(long capa) {
534
- VALUE str = rb_str_buf_new(capa);
541
+ static inline VALUE rb_binary_str_buf_reserve(size_t capa) {
542
+ VALUE str = rb_str_buf_new(rb_checked_string_length(capa));
535
543
  rb_enc_associate(str, binary_encoding);
536
544
  return str;
537
545
  }
538
546
 
547
+ static inline void rb_binary_str_set_len(VALUE str, size_t len) {
548
+ rb_str_set_len(str, rb_checked_string_length(len));
549
+ }
550
+
539
551
  static inline void grow_binary_str(VALUE str, size_t cur_len, size_t new_cap) {
540
552
  size_t cur_cap = (size_t)rb_str_capacity(str);
541
553
  if (new_cap <= cur_cap)
542
554
  return;
543
- rb_str_set_len(str, (long)cur_len);
544
- rb_str_modify_expand(str, (long)(new_cap - cur_len));
555
+ (void)rb_checked_string_length(new_cap);
556
+ rb_binary_str_set_len(str, cur_len);
557
+ rb_str_modify_expand(str, rb_checked_string_length(new_cap - cur_len));
545
558
  }
546
559
 
547
560
  typedef struct {
@@ -562,6 +575,8 @@ static void limits_config_apply_parsed(const mc_opts_t *opts, limits_config_t *l
562
575
  size_t max_output_size = NUM2SIZET(val);
563
576
  if (max_output_size == 0)
564
577
  rb_raise(rb_eArgError, "max_output_size must be greater than 0");
578
+ if ((uintmax_t)max_output_size > (uintmax_t)LONG_MAX)
579
+ rb_raise(rb_eArgError, "max_output_size exceeds Ruby's maximum string length");
565
580
  limits->max_output_size = max_output_size;
566
581
  }
567
582
 
@@ -1306,7 +1321,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1306
1321
  rb_raise(eMemError, "zstd: failed to create context");
1307
1322
  if (ZSTD_isError(csize))
1308
1323
  rb_raise(eError, "zstd compress: %s", ZSTD_getErrorName(csize));
1309
- rb_str_set_len(dst, (long)csize);
1324
+ rb_binary_str_set_len(dst, csize);
1310
1325
  RB_GC_GUARD(data);
1311
1326
  RB_GC_GUARD(dict_val);
1312
1327
  return dst;
@@ -1343,7 +1358,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1343
1358
  rb_raise(eError, "zstd compress: %s", ZSTD_getErrorName(fargs.result));
1344
1359
  }
1345
1360
 
1346
- VALUE result = rb_binary_str_new(out_buf, (long)fargs.result);
1361
+ VALUE result = rb_binary_str_new(out_buf, fargs.result);
1347
1362
  free(out_buf);
1348
1363
  RB_GC_GUARD(data);
1349
1364
  RB_GC_GUARD(dict_val);
@@ -1370,7 +1385,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1370
1385
  if (ZSTD_isError(args.result))
1371
1386
  rb_raise(eError, "zstd compress: %s", ZSTD_getErrorName(args.result));
1372
1387
 
1373
- rb_str_set_len(dst, (long)args.result);
1388
+ rb_binary_str_set_len(dst, args.result);
1374
1389
  RB_GC_GUARD(data);
1375
1390
  RB_GC_GUARD(dict_val);
1376
1391
  return dst;
@@ -1383,7 +1398,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1383
1398
  prefs.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled;
1384
1399
  prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
1385
1400
  size_t bound = LZ4F_compressFrameBound(slen, &prefs);
1386
- VALUE dst = rb_binary_str_buf_reserve((long)bound);
1401
+ VALUE dst = rb_binary_str_buf_reserve(bound);
1387
1402
  lz4frame_compress_args_t args = {
1388
1403
  .src = src,
1389
1404
  .src_len = slen,
@@ -1401,7 +1416,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1401
1416
  if (args.error_code)
1402
1417
  rb_raise(eError, "lz4 frame compress failed: %s",
1403
1418
  LZ4F_getErrorName(args.error_code));
1404
- rb_str_set_len(dst, (long)args.result);
1419
+ rb_binary_str_set_len(dst, args.result);
1405
1420
  RB_GC_GUARD(data);
1406
1421
  return dst;
1407
1422
  }
@@ -1438,7 +1453,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1438
1453
  size_t total = 8 + (size_t)csize;
1439
1454
  write_le_u32((uint8_t *)out + total, 0);
1440
1455
 
1441
- rb_str_set_len(dst, (long)(total + 4));
1456
+ rb_binary_str_set_len(dst, total + 4);
1442
1457
  RB_GC_GUARD(data);
1443
1458
  return dst;
1444
1459
  } else {
@@ -1460,7 +1475,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1460
1475
  size_t total = 8 + csize;
1461
1476
  write_le_u32((uint8_t *)out + total, 0);
1462
1477
 
1463
- rb_str_set_len(dst, total + 4);
1478
+ rb_binary_str_set_len(dst, total + 4);
1464
1479
  RB_GC_GUARD(data);
1465
1480
  return dst;
1466
1481
  }
@@ -1516,7 +1531,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1516
1531
  if (!ok)
1517
1532
  rb_raise(eError, "brotli compress with dict failed");
1518
1533
 
1519
- rb_str_set_len(dst, initial_out - available_out);
1534
+ rb_binary_str_set_len(dst, initial_out - available_out);
1520
1535
  RB_GC_GUARD(data);
1521
1536
  RB_GC_GUARD(dict_val);
1522
1537
  return dst;
@@ -1540,7 +1555,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1540
1555
  if (!args.result)
1541
1556
  rb_raise(eError, "brotli compress failed");
1542
1557
 
1543
- rb_str_set_len(dst, (long)actual_out_len);
1558
+ rb_binary_str_set_len(dst, actual_out_len);
1544
1559
  RB_GC_GUARD(data);
1545
1560
  return dst;
1546
1561
  } else {
@@ -1550,7 +1565,7 @@ static VALUE compress_compress(int argc, VALUE *argv, VALUE self) {
1550
1565
  (const uint8_t *)src, &out_len, (uint8_t *)RSTRING_PTR(dst));
1551
1566
  if (!ok)
1552
1567
  rb_raise(eError, "brotli compress failed");
1553
- rb_str_set_len(dst, out_len);
1568
+ rb_binary_str_set_len(dst, out_len);
1554
1569
  RB_GC_GUARD(data);
1555
1570
  return dst;
1556
1571
  }
@@ -1690,7 +1705,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1690
1705
 
1691
1706
  enforce_output_and_ratio_limits(dsize, slen, limits.max_output_size,
1692
1707
  limits.max_ratio_enabled, limits.max_ratio);
1693
- rb_str_set_len(dst, (long)dsize);
1708
+ rb_binary_str_set_len(dst, dsize);
1694
1709
  RB_GC_GUARD(data);
1695
1710
  RB_GC_GUARD(dict_val);
1696
1711
  return dst;
@@ -1714,7 +1729,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1714
1729
  rb_raise(eDataError, "zstd decompress: %s", ZSTD_getErrorName(dsize));
1715
1730
  enforce_output_and_ratio_limits(dsize, slen, limits.max_output_size,
1716
1731
  limits.max_ratio_enabled, limits.max_ratio);
1717
- rb_str_set_len(dst, dsize);
1732
+ rb_binary_str_set_len(dst, dsize);
1718
1733
  RB_GC_GUARD(data);
1719
1734
  RB_GC_GUARD(dict_val);
1720
1735
  return dst;
@@ -1787,7 +1802,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1787
1802
  break;
1788
1803
  }
1789
1804
 
1790
- rb_str_set_len(dst, total_out);
1805
+ rb_binary_str_set_len(dst, total_out);
1791
1806
  RB_GC_GUARD(data);
1792
1807
  RB_GC_GUARD(dict_val);
1793
1808
  return dst;
@@ -1800,7 +1815,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1800
1815
  alloc_size = limits.max_output_size < 4096 ? limits.max_output_size : 4096;
1801
1816
  if (alloc_size == 0)
1802
1817
  alloc_size = limits.max_output_size;
1803
- VALUE dst = rb_binary_str_buf_reserve((long)alloc_size);
1818
+ VALUE dst = rb_binary_str_buf_reserve(alloc_size);
1804
1819
  while (1) {
1805
1820
  lz4frame_decompress_args_t args = {
1806
1821
  .src = src,
@@ -1835,7 +1850,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1835
1850
  LZ4F_getErrorName(args.error_code));
1836
1851
  enforce_output_and_ratio_limits(args.result, slen, limits.max_output_size,
1837
1852
  limits.max_ratio_enabled, limits.max_ratio);
1838
- rb_str_set_len(dst, (long)args.result);
1853
+ rb_binary_str_set_len(dst, args.result);
1839
1854
  RB_GC_GUARD(data);
1840
1855
  return dst;
1841
1856
  }
@@ -1890,7 +1905,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1890
1905
 
1891
1906
  enforce_output_and_ratio_limits(args.out_offset, slen, limits.max_output_size,
1892
1907
  limits.max_ratio_enabled, limits.max_ratio);
1893
- rb_str_set_len(result, args.out_offset);
1908
+ rb_binary_str_set_len(result, args.out_offset);
1894
1909
  RB_GC_GUARD(data);
1895
1910
  return result;
1896
1911
  }
@@ -1976,7 +1991,7 @@ static VALUE compress_decompress(int argc, VALUE *argv, VALUE self) {
1976
1991
  if (res != BROTLI_DECODER_RESULT_SUCCESS) {
1977
1992
  rb_raise(eDataError, "brotli decompress failed");
1978
1993
  }
1979
- rb_str_set_len(dst, total_out);
1994
+ rb_binary_str_set_len(dst, total_out);
1980
1995
  RB_GC_GUARD(data);
1981
1996
  RB_GC_GUARD(dict_val);
1982
1997
  return dst;
@@ -2131,11 +2146,45 @@ static VALUE compress_version(VALUE self, VALUE algo_sym) {
2131
2146
  _Static_assert(LZ4_RING_BUFFER_TOTAL == 2 * LZ4_RING_BUFFER_SIZE,
2132
2147
  "ring buffer total must be exactly twice the window size");
2133
2148
 
2149
+ typedef struct {
2150
+ int *active;
2151
+ VALUE self;
2152
+ VALUE argument;
2153
+ VALUE (*operation)(VALUE, VALUE);
2154
+ } stream_operation_t;
2155
+
2156
+ static VALUE stream_operation_body(VALUE opaque) {
2157
+ stream_operation_t *call = (stream_operation_t *)(uintptr_t)opaque;
2158
+ return call->operation(call->self, call->argument);
2159
+ }
2160
+
2161
+ static VALUE stream_operation_ensure(VALUE opaque) {
2162
+ stream_operation_t *call = (stream_operation_t *)(uintptr_t)opaque;
2163
+ *call->active = 0;
2164
+ return Qnil;
2165
+ }
2166
+
2167
+ static VALUE stream_operation_call(int *active, VALUE self, VALUE argument,
2168
+ VALUE (*operation)(VALUE, VALUE)) {
2169
+ if (*active)
2170
+ rb_raise(eStreamError, "stream operation already in progress");
2171
+
2172
+ stream_operation_t call = {
2173
+ .active = active, .self = self, .argument = argument, .operation = operation};
2174
+ *active = 1;
2175
+ VALUE result = rb_ensure(stream_operation_body, (VALUE)(uintptr_t)&call,
2176
+ stream_operation_ensure, (VALUE)(uintptr_t)&call);
2177
+ RB_GC_GUARD(self);
2178
+ RB_GC_GUARD(argument);
2179
+ return result;
2180
+ }
2181
+
2134
2182
  typedef struct {
2135
2183
  compress_algo_t algo;
2136
2184
  int level;
2137
2185
  int closed;
2138
2186
  int finished;
2187
+ int operation_active;
2139
2188
 
2140
2189
  union {
2141
2190
  ZSTD_CStream *zstd;
@@ -2179,6 +2228,9 @@ static size_t deflater_memsize(const void *ptr) {
2179
2228
  return 0;
2180
2229
 
2181
2230
  size_t s = sizeof(deflater_t);
2231
+ if (d->operation_active)
2232
+ return s;
2233
+
2182
2234
  switch (d->algo) {
2183
2235
  case ALGO_ZSTD:
2184
2236
  if (d->ctx.zstd)
@@ -2325,7 +2377,7 @@ static VALUE lz4_compress_ring_block(deflater_t *d) {
2325
2377
 
2326
2378
  write_le_u32((uint8_t *)out + 4, (uint32_t)csize);
2327
2379
 
2328
- rb_str_set_len(output, 8 + csize);
2380
+ rb_binary_str_set_len(output, 8 + csize);
2329
2381
  d->lz4_ring.pending = 0;
2330
2382
 
2331
2383
  if (d->lz4_ring.ring_offset >= LZ4_RING_BUFFER_SIZE) {
@@ -2335,7 +2387,7 @@ static VALUE lz4_compress_ring_block(deflater_t *d) {
2335
2387
  return output;
2336
2388
  }
2337
2389
 
2338
- static VALUE deflater_write(VALUE self, VALUE chunk) {
2390
+ static VALUE deflater_write_impl(VALUE self, VALUE chunk) {
2339
2391
  deflater_t *d;
2340
2392
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2341
2393
  if (d->closed)
@@ -2405,7 +2457,7 @@ static VALUE deflater_write(VALUE self, VALUE chunk) {
2405
2457
  }
2406
2458
  result_len += output.pos;
2407
2459
  }
2408
- rb_str_set_len(result, result_len);
2460
+ rb_binary_str_set_len(result, result_len);
2409
2461
  RB_GC_GUARD(chunk);
2410
2462
  return result;
2411
2463
  }
@@ -2471,7 +2523,7 @@ static VALUE deflater_write(VALUE self, VALUE chunk) {
2471
2523
  }
2472
2524
  }
2473
2525
  }
2474
- rb_str_set_len(result, result_len);
2526
+ rb_binary_str_set_len(result, result_len);
2475
2527
  RB_GC_GUARD(chunk);
2476
2528
  return result;
2477
2529
  }
@@ -2517,7 +2569,7 @@ static VALUE deflater_write(VALUE self, VALUE chunk) {
2517
2569
  }
2518
2570
  }
2519
2571
  }
2520
- rb_str_set_len(result, result_len);
2572
+ rb_binary_str_set_len(result, result_len);
2521
2573
  RB_GC_GUARD(chunk);
2522
2574
  return result;
2523
2575
  }
@@ -2525,7 +2577,14 @@ static VALUE deflater_write(VALUE self, VALUE chunk) {
2525
2577
  return rb_binary_str_new("", 0);
2526
2578
  }
2527
2579
 
2528
- static VALUE deflater_flush(VALUE self) {
2580
+ static VALUE deflater_write(VALUE self, VALUE chunk) {
2581
+ deflater_t *d;
2582
+ TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2583
+ return stream_operation_call(&d->operation_active, self, chunk, deflater_write_impl);
2584
+ }
2585
+
2586
+ static VALUE deflater_flush_impl(VALUE self, VALUE unused) {
2587
+ (void)unused;
2529
2588
  deflater_t *d;
2530
2589
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2531
2590
  if (d->closed)
@@ -2554,7 +2613,7 @@ static VALUE deflater_flush(VALUE self) {
2554
2613
  result_len += output.pos;
2555
2614
  } while (ret > 0);
2556
2615
 
2557
- rb_str_set_len(result, result_len);
2616
+ rb_binary_str_set_len(result, result_len);
2558
2617
  return result;
2559
2618
  }
2560
2619
  case ALGO_BROTLI: {
@@ -2586,7 +2645,7 @@ static VALUE deflater_flush(VALUE self) {
2586
2645
  }
2587
2646
  } while (BrotliEncoderHasMoreOutput(d->ctx.brotli));
2588
2647
 
2589
- rb_str_set_len(result, result_len);
2648
+ rb_binary_str_set_len(result, result_len);
2590
2649
  return result;
2591
2650
  }
2592
2651
  case ALGO_LZ4:
@@ -2595,7 +2654,14 @@ static VALUE deflater_flush(VALUE self) {
2595
2654
  return rb_binary_str_new("", 0);
2596
2655
  }
2597
2656
 
2598
- static VALUE deflater_finish(VALUE self) {
2657
+ static VALUE deflater_flush(VALUE self) {
2658
+ deflater_t *d;
2659
+ TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2660
+ return stream_operation_call(&d->operation_active, self, Qnil, deflater_flush_impl);
2661
+ }
2662
+
2663
+ static VALUE deflater_finish_impl(VALUE self, VALUE unused) {
2664
+ (void)unused;
2599
2665
  deflater_t *d;
2600
2666
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2601
2667
  if (d->closed)
@@ -2625,7 +2691,7 @@ static VALUE deflater_finish(VALUE self) {
2625
2691
  result_len += output.pos;
2626
2692
  } while (ret > 0);
2627
2693
 
2628
- rb_str_set_len(result, result_len);
2694
+ rb_binary_str_set_len(result, result_len);
2629
2695
  return result;
2630
2696
  }
2631
2697
  case ALGO_BROTLI: {
@@ -2658,7 +2724,7 @@ static VALUE deflater_finish(VALUE self) {
2658
2724
  } while (BrotliEncoderHasMoreOutput(d->ctx.brotli) ||
2659
2725
  !BrotliEncoderIsFinished(d->ctx.brotli));
2660
2726
 
2661
- rb_str_set_len(result, result_len);
2727
+ rb_binary_str_set_len(result, result_len);
2662
2728
  return result;
2663
2729
  }
2664
2730
  case ALGO_LZ4: {
@@ -2689,14 +2755,21 @@ static VALUE deflater_finish(VALUE self) {
2689
2755
  write_le_u32((uint8_t *)out, 0);
2690
2756
  result_len += 4;
2691
2757
 
2692
- rb_str_set_len(result, result_len);
2758
+ rb_binary_str_set_len(result, result_len);
2693
2759
  return result;
2694
2760
  }
2695
2761
  }
2696
2762
  return rb_binary_str_new("", 0);
2697
2763
  }
2698
2764
 
2699
- static VALUE deflater_reset(VALUE self) {
2765
+ static VALUE deflater_finish(VALUE self) {
2766
+ deflater_t *d;
2767
+ TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2768
+ return stream_operation_call(&d->operation_active, self, Qnil, deflater_finish_impl);
2769
+ }
2770
+
2771
+ static VALUE deflater_reset_impl(VALUE self, VALUE unused) {
2772
+ (void)unused;
2700
2773
  deflater_t *d;
2701
2774
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2702
2775
 
@@ -2752,7 +2825,14 @@ static VALUE deflater_reset(VALUE self) {
2752
2825
  return self;
2753
2826
  }
2754
2827
 
2755
- static VALUE deflater_close(VALUE self) {
2828
+ static VALUE deflater_reset(VALUE self) {
2829
+ deflater_t *d;
2830
+ TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2831
+ return stream_operation_call(&d->operation_active, self, Qnil, deflater_reset_impl);
2832
+ }
2833
+
2834
+ static VALUE deflater_close_impl(VALUE self, VALUE unused) {
2835
+ (void)unused;
2756
2836
  deflater_t *d;
2757
2837
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2758
2838
  if (d->closed)
@@ -2782,6 +2862,12 @@ static VALUE deflater_close(VALUE self) {
2782
2862
  return Qnil;
2783
2863
  }
2784
2864
 
2865
+ static VALUE deflater_close(VALUE self) {
2866
+ deflater_t *d;
2867
+ TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
2868
+ return stream_operation_call(&d->operation_active, self, Qnil, deflater_close_impl);
2869
+ }
2870
+
2785
2871
  static VALUE deflater_closed_p(VALUE self) {
2786
2872
  deflater_t *d;
2787
2873
  TypedData_Get_Struct(self, deflater_t, &deflater_type, d);
@@ -2792,6 +2878,7 @@ typedef struct {
2792
2878
  compress_algo_t algo;
2793
2879
  int closed;
2794
2880
  int finished;
2881
+ int operation_active;
2795
2882
  size_t max_output_size;
2796
2883
  size_t total_output;
2797
2884
  size_t total_input;
@@ -2838,6 +2925,9 @@ static size_t inflater_memsize(const void *ptr) {
2838
2925
  return 0;
2839
2926
 
2840
2927
  size_t s = sizeof(inflater_t);
2928
+ if (inf->operation_active)
2929
+ return s;
2930
+
2841
2931
  switch (inf->algo) {
2842
2932
  case ALGO_ZSTD:
2843
2933
  if (inf->ctx.zstd)
@@ -2932,7 +3022,7 @@ static VALUE inflater_initialize(int argc, VALUE *argv, VALUE self) {
2932
3022
  return self;
2933
3023
  }
2934
3024
 
2935
- static VALUE inflater_write(VALUE self, VALUE chunk) {
3025
+ static VALUE inflater_write_impl(VALUE self, VALUE chunk) {
2936
3026
  inflater_t *inf;
2937
3027
  TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
2938
3028
  if (inf->closed)
@@ -3017,7 +3107,7 @@ static VALUE inflater_write(VALUE self, VALUE chunk) {
3017
3107
  "compressed input exceeds representable size");
3018
3108
  inf->total_output = checked_add_size(inf->total_output, result_len,
3019
3109
  "decompressed output exceeds representable size");
3020
- rb_str_set_len(result, result_len);
3110
+ rb_binary_str_set_len(result, result_len);
3021
3111
  RB_GC_GUARD(chunk);
3022
3112
  return result;
3023
3113
  }
@@ -3098,7 +3188,7 @@ static VALUE inflater_write(VALUE self, VALUE chunk) {
3098
3188
  "compressed input exceeds representable size");
3099
3189
  inf->total_output = checked_add_size(inf->total_output, result_len,
3100
3190
  "decompressed output exceeds representable size");
3101
- rb_str_set_len(result, result_len);
3191
+ rb_binary_str_set_len(result, result_len);
3102
3192
  RB_GC_GUARD(chunk);
3103
3193
  return result;
3104
3194
  }
@@ -3201,7 +3291,7 @@ static VALUE inflater_write(VALUE self, VALUE chunk) {
3201
3291
  inf->lz4_buf.offset = pos;
3202
3292
  inf->total_output = checked_add_size(inf->total_output, result_len,
3203
3293
  "decompressed output exceeds representable size");
3204
- rb_str_set_len(result, result_len);
3294
+ rb_binary_str_set_len(result, result_len);
3205
3295
  RB_GC_GUARD(chunk);
3206
3296
  return result;
3207
3297
  }
@@ -3209,7 +3299,14 @@ static VALUE inflater_write(VALUE self, VALUE chunk) {
3209
3299
  return rb_binary_str_new("", 0);
3210
3300
  }
3211
3301
 
3212
- static VALUE inflater_finish(VALUE self) {
3302
+ static VALUE inflater_write(VALUE self, VALUE chunk) {
3303
+ inflater_t *inf;
3304
+ TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3305
+ return stream_operation_call(&inf->operation_active, self, chunk, inflater_write_impl);
3306
+ }
3307
+
3308
+ static VALUE inflater_finish_impl(VALUE self, VALUE unused) {
3309
+ (void)unused;
3213
3310
  inflater_t *inf;
3214
3311
  TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3215
3312
  if (inf->closed)
@@ -3218,7 +3315,14 @@ static VALUE inflater_finish(VALUE self) {
3218
3315
  return rb_binary_str_new("", 0);
3219
3316
  }
3220
3317
 
3221
- static VALUE inflater_reset(VALUE self) {
3318
+ static VALUE inflater_finish(VALUE self) {
3319
+ inflater_t *inf;
3320
+ TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3321
+ return stream_operation_call(&inf->operation_active, self, Qnil, inflater_finish_impl);
3322
+ }
3323
+
3324
+ static VALUE inflater_reset_impl(VALUE self, VALUE unused) {
3325
+ (void)unused;
3222
3326
  inflater_t *inf;
3223
3327
  TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3224
3328
 
@@ -3263,7 +3367,14 @@ static VALUE inflater_reset(VALUE self) {
3263
3367
  return self;
3264
3368
  }
3265
3369
 
3266
- static VALUE inflater_close(VALUE self) {
3370
+ static VALUE inflater_reset(VALUE self) {
3371
+ inflater_t *inf;
3372
+ TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3373
+ return stream_operation_call(&inf->operation_active, self, Qnil, inflater_reset_impl);
3374
+ }
3375
+
3376
+ static VALUE inflater_close_impl(VALUE self, VALUE unused) {
3377
+ (void)unused;
3267
3378
  inflater_t *inf;
3268
3379
  TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3269
3380
  if (inf->closed)
@@ -3289,6 +3400,12 @@ static VALUE inflater_close(VALUE self) {
3289
3400
  return Qnil;
3290
3401
  }
3291
3402
 
3403
+ static VALUE inflater_close(VALUE self) {
3404
+ inflater_t *inf;
3405
+ TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
3406
+ return stream_operation_call(&inf->operation_active, self, Qnil, inflater_close_impl);
3407
+ }
3408
+
3292
3409
  static VALUE inflater_closed_p(VALUE self) {
3293
3410
  inflater_t *inf;
3294
3411
  TypedData_Get_Struct(self, inflater_t, &inflater_type, inf);
@@ -3409,6 +3526,59 @@ static VALUE brotli_train_dictionary(int argc, VALUE *argv, VALUE self) {
3409
3526
  return train_dictionary_internal(samples, parsed_opts.size, ALGO_BROTLI);
3410
3527
  }
3411
3528
 
3529
+ typedef struct {
3530
+ FILE *file;
3531
+ VALUE path_value;
3532
+ const char *path;
3533
+ uint8_t *data;
3534
+ size_t size;
3535
+ int loaded;
3536
+ } dict_file_read_t;
3537
+
3538
+ static VALUE dict_file_read_body(VALUE opaque) {
3539
+ dict_file_read_t *read = (dict_file_read_t *)(uintptr_t)opaque;
3540
+
3541
+ if (fseek(read->file, 0, SEEK_END) != 0)
3542
+ rb_sys_fail(read->path);
3543
+
3544
+ long file_size = ftell(read->file);
3545
+ if (file_size < 0)
3546
+ rb_sys_fail(read->path);
3547
+ if (file_size == 0)
3548
+ rb_raise(eDataError, "dictionary file is empty: %s", read->path);
3549
+ if ((unsigned long long)file_size > DICT_FILE_MAX_SIZE)
3550
+ rb_raise(eDataError, "dictionary file too large (%ld bytes, max=%d)", file_size,
3551
+ (int)DICT_FILE_MAX_SIZE);
3552
+ if (fseek(read->file, 0, SEEK_SET) != 0)
3553
+ rb_sys_fail(read->path);
3554
+
3555
+ read->data = ALLOC_N(uint8_t, file_size);
3556
+ read->path = RSTRING_PTR(read->path_value);
3557
+ read->size = (size_t)file_size;
3558
+ size_t read_bytes = fread(read->data, 1, read->size, read->file);
3559
+ if (read_bytes != read->size) {
3560
+ xfree(read->data);
3561
+ read->data = NULL;
3562
+ rb_raise(eDataError, "failed to read dictionary: %s", read->path);
3563
+ }
3564
+
3565
+ read->loaded = 1;
3566
+ return Qnil;
3567
+ }
3568
+
3569
+ static VALUE dict_file_read_ensure(VALUE opaque) {
3570
+ dict_file_read_t *read = (dict_file_read_t *)(uintptr_t)opaque;
3571
+ if (read->file) {
3572
+ fclose(read->file);
3573
+ read->file = NULL;
3574
+ }
3575
+ if (!read->loaded && read->data) {
3576
+ xfree(read->data);
3577
+ read->data = NULL;
3578
+ }
3579
+ return Qnil;
3580
+ }
3581
+
3412
3582
  static VALUE dict_load(int argc, VALUE *argv, VALUE self) {
3413
3583
  VALUE path, opts;
3414
3584
  scan_one_required_keywords(argc, argv, &path, &opts);
@@ -3425,40 +3595,23 @@ static VALUE dict_load(int argc, VALUE *argv, VALUE self) {
3425
3595
  if (algo == ALGO_LZ4)
3426
3596
  rb_raise(eUnsupportedError, "LZ4 does not support dictionaries");
3427
3597
 
3598
+ VALUE dict_obj = dict_alloc(cDictionary);
3599
+ dictionary_t *d;
3600
+ TypedData_Get_Struct(dict_obj, dictionary_t, &dictionary_type, d);
3601
+
3428
3602
  const char *cpath = RSTRING_PTR(path);
3429
- FILE *f = fopen(cpath, "rb");
3430
- if (!f)
3603
+ FILE *file = fopen(cpath, "rb");
3604
+ if (!file)
3431
3605
  rb_sys_fail(cpath);
3432
3606
 
3433
- fseek(f, 0, SEEK_END);
3434
- long file_size = ftell(f);
3435
- fseek(f, 0, SEEK_SET);
3607
+ dict_file_read_t read = {.file = file, .path_value = path, .path = cpath};
3608
+ rb_ensure(dict_file_read_body, (VALUE)(uintptr_t)&read, dict_file_read_ensure,
3609
+ (VALUE)(uintptr_t)&read);
3436
3610
 
3437
- if (file_size <= 0) {
3438
- fclose(f);
3439
- rb_raise(eDataError, "dictionary file is empty: %s", cpath);
3440
- }
3441
- if ((unsigned long long)file_size > DICT_FILE_MAX_SIZE) {
3442
- fclose(f);
3443
- rb_raise(eDataError, "dictionary file too large (%ld bytes, max=%d)", file_size,
3444
- (int)DICT_FILE_MAX_SIZE);
3445
- }
3446
-
3447
- uint8_t *buf = ALLOC_N(uint8_t, file_size);
3448
- size_t read_bytes = fread(buf, 1, file_size, f);
3449
- fclose(f);
3450
-
3451
- if ((long)read_bytes != file_size) {
3452
- xfree(buf);
3453
- rb_raise(eDataError, "failed to read dictionary: %s", cpath);
3454
- }
3455
-
3456
- VALUE dict_obj = dict_alloc(cDictionary);
3457
- dictionary_t *d;
3458
- TypedData_Get_Struct(dict_obj, dictionary_t, &dictionary_type, d);
3459
3611
  d->algo = algo;
3460
- d->data = buf;
3461
- d->size = (size_t)file_size;
3612
+ d->data = read.data;
3613
+ d->size = read.size;
3614
+ read.data = NULL;
3462
3615
  return dict_obj;
3463
3616
  }
3464
3617
 
@@ -3488,7 +3641,7 @@ static VALUE dict_algo(VALUE self) {
3488
3641
  static VALUE dict_bytes(VALUE self) {
3489
3642
  dictionary_t *d;
3490
3643
  TypedData_Get_Struct(self, dictionary_t, &dictionary_type, d);
3491
- return rb_binary_str_new((const char *)d->data, (long)d->size);
3644
+ return rb_binary_str_new((const char *)d->data, d->size);
3492
3645
  }
3493
3646
 
3494
3647
  static VALUE dict_zstd_id(VALUE self) {
@@ -1,3 +1,3 @@
1
1
  zstd=1.5.7
2
2
  lz4=1.10.0
3
- brotli=1.2.0
3
+ brotli=1.2.1
@@ -19,7 +19,7 @@
19
19
 
20
20
  #define BROTLI_VERSION_MAJOR 1
21
21
  #define BROTLI_VERSION_MINOR 2
22
- #define BROTLI_VERSION_PATCH 0
22
+ #define BROTLI_VERSION_PATCH 1
23
23
 
24
24
  #define BROTLI_VERSION BROTLI_MAKE_HEX_VERSION( \
25
25
  BROTLI_VERSION_MAJOR, BROTLI_VERSION_MINOR, BROTLI_VERSION_PATCH)
@@ -33,7 +33,7 @@
33
33
  */
34
34
 
35
35
  #define BROTLI_ABI_CURRENT 3
36
- #define BROTLI_ABI_REVISION 0
36
+ #define BROTLI_ABI_REVISION 1
37
37
  #define BROTLI_ABI_AGE 2
38
38
 
39
39
  #if BROTLI_VERSION_MAJOR != (BROTLI_ABI_CURRENT - BROTLI_ABI_AGE)