deflate-ruby 1.0.1 → 1.0.2

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +95 -92
  3. data/LICENSE.txt +6 -6
  4. data/README.md +87 -65
  5. data/Rakefile +23 -0
  6. data/ext/deflate_ruby/{libdeflate/lib/x86/adler32_impl.h → adler32_impl.h} +8 -7
  7. data/ext/deflate_ruby/common_defs.h +748 -0
  8. data/ext/deflate_ruby/{libdeflate/lib/x86/cpu_features.c → cpu_features.c} +46 -16
  9. data/ext/deflate_ruby/{libdeflate/lib/x86/cpu_features.h → cpu_features.h} +2 -1
  10. data/ext/deflate_ruby/{libdeflate/lib/x86/crc32_impl.h → crc32_impl.h} +22 -23
  11. data/ext/deflate_ruby/{libdeflate/lib/crc32_multipliers.h → crc32_multipliers.h} +2 -4
  12. data/ext/deflate_ruby/{libdeflate/lib/x86/crc32_pclmul_template.h → crc32_pclmul_template.h} +23 -94
  13. data/ext/deflate_ruby/{libdeflate/lib/crc32_tables.h → crc32_tables.h} +1 -1
  14. data/ext/deflate_ruby/{libdeflate/lib/deflate_compress.c → deflate_compress.c} +59 -60
  15. data/ext/deflate_ruby/deflate_ruby.c +392 -218
  16. data/ext/deflate_ruby/deflate_ruby.h +6 -0
  17. data/ext/deflate_ruby/extconf.rb +35 -25
  18. data/ext/deflate_ruby/libdeflate/adler32.c +162 -0
  19. data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/adler32_impl.h +14 -7
  20. data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/crc32_impl.h +25 -31
  21. data/ext/deflate_ruby/libdeflate/arm/crc32_pmull_helpers.h +156 -0
  22. data/ext/deflate_ruby/libdeflate/arm/crc32_pmull_wide.h +226 -0
  23. data/ext/deflate_ruby/libdeflate/bt_matchfinder.h +342 -0
  24. data/ext/deflate_ruby/libdeflate/common_defs.h +2 -1
  25. data/ext/deflate_ruby/libdeflate/cpu_features_common.h +93 -0
  26. data/ext/deflate_ruby/libdeflate/crc32.c +262 -0
  27. data/ext/deflate_ruby/libdeflate/crc32_multipliers.h +375 -0
  28. data/ext/deflate_ruby/libdeflate/crc32_tables.h +587 -0
  29. data/ext/deflate_ruby/libdeflate/decompress_template.h +777 -0
  30. data/ext/deflate_ruby/libdeflate/deflate_compress.c +4128 -0
  31. data/ext/deflate_ruby/libdeflate/deflate_compress.h +15 -0
  32. data/ext/deflate_ruby/libdeflate/deflate_constants.h +56 -0
  33. data/ext/deflate_ruby/libdeflate/deflate_decompress.c +1208 -0
  34. data/ext/deflate_ruby/libdeflate/gzip_compress.c +90 -0
  35. data/ext/deflate_ruby/libdeflate/gzip_constants.h +45 -0
  36. data/ext/deflate_ruby/libdeflate/gzip_decompress.c +144 -0
  37. data/ext/deflate_ruby/libdeflate/hc_matchfinder.h +401 -0
  38. data/ext/deflate_ruby/libdeflate/ht_matchfinder.h +234 -0
  39. data/ext/deflate_ruby/libdeflate/lib_common.h +106 -0
  40. data/ext/deflate_ruby/libdeflate/libdeflate.h +2 -2
  41. data/ext/deflate_ruby/libdeflate/{lib/matchfinder_common.h → matchfinder_common.h} +3 -3
  42. data/ext/deflate_ruby/libdeflate/x86/adler32_impl.h +135 -0
  43. data/ext/deflate_ruby/libdeflate/x86/adler32_template.h +518 -0
  44. data/ext/deflate_ruby/libdeflate/x86/cpu_features.c +213 -0
  45. data/ext/deflate_ruby/libdeflate/x86/cpu_features.h +170 -0
  46. data/ext/deflate_ruby/libdeflate/x86/crc32_impl.h +159 -0
  47. data/ext/deflate_ruby/libdeflate/x86/crc32_pclmul_template.h +424 -0
  48. data/ext/deflate_ruby/libdeflate/x86/decompress_impl.h +57 -0
  49. data/ext/deflate_ruby/libdeflate.h +411 -0
  50. data/ext/deflate_ruby/matchfinder_common.h +224 -0
  51. data/ext/deflate_ruby/matchfinder_impl.h +122 -0
  52. data/ext/deflate_ruby/utils.c +141 -0
  53. data/ext/deflate_ruby/zlib_compress.c +82 -0
  54. data/ext/deflate_ruby/zlib_constants.h +21 -0
  55. data/ext/deflate_ruby/zlib_decompress.c +104 -0
  56. data/lib/deflate_ruby/version.rb +1 -1
  57. data/lib/deflate_ruby.rb +1 -63
  58. data/sig/deflate_ruby.rbs +4 -0
  59. data/test/test_deflate_ruby.rb +220 -0
  60. data/test/test_helper.rb +6 -0
  61. metadata +89 -144
  62. data/ext/deflate_ruby/libdeflate/CMakeLists.txt +0 -270
  63. data/ext/deflate_ruby/libdeflate/NEWS.md +0 -494
  64. data/ext/deflate_ruby/libdeflate/README.md +0 -228
  65. data/ext/deflate_ruby/libdeflate/libdeflate-config.cmake.in +0 -3
  66. data/ext/deflate_ruby/libdeflate/libdeflate.pc.in +0 -18
  67. data/ext/deflate_ruby/libdeflate/programs/CMakeLists.txt +0 -105
  68. data/ext/deflate_ruby/libdeflate/programs/benchmark.c +0 -696
  69. data/ext/deflate_ruby/libdeflate/programs/checksum.c +0 -218
  70. data/ext/deflate_ruby/libdeflate/programs/config.h.in +0 -19
  71. data/ext/deflate_ruby/libdeflate/programs/gzip.c +0 -688
  72. data/ext/deflate_ruby/libdeflate/programs/prog_util.c +0 -521
  73. data/ext/deflate_ruby/libdeflate/programs/prog_util.h +0 -225
  74. data/ext/deflate_ruby/libdeflate/programs/test_checksums.c +0 -200
  75. data/ext/deflate_ruby/libdeflate/programs/test_custom_malloc.c +0 -155
  76. data/ext/deflate_ruby/libdeflate/programs/test_incomplete_codes.c +0 -385
  77. data/ext/deflate_ruby/libdeflate/programs/test_invalid_streams.c +0 -130
  78. data/ext/deflate_ruby/libdeflate/programs/test_litrunlen_overflow.c +0 -72
  79. data/ext/deflate_ruby/libdeflate/programs/test_overread.c +0 -95
  80. data/ext/deflate_ruby/libdeflate/programs/test_slow_decompression.c +0 -472
  81. data/ext/deflate_ruby/libdeflate/programs/test_trailing_bytes.c +0 -151
  82. data/ext/deflate_ruby/libdeflate/programs/test_util.c +0 -237
  83. data/ext/deflate_ruby/libdeflate/programs/test_util.h +0 -61
  84. data/ext/deflate_ruby/libdeflate/programs/tgetopt.c +0 -118
  85. data/ext/deflate_ruby/libdeflate/scripts/android_build.sh +0 -118
  86. data/ext/deflate_ruby/libdeflate/scripts/android_tests.sh +0 -69
  87. data/ext/deflate_ruby/libdeflate/scripts/benchmark.sh +0 -10
  88. data/ext/deflate_ruby/libdeflate/scripts/checksum.sh +0 -10
  89. data/ext/deflate_ruby/libdeflate/scripts/checksum_benchmarks.sh +0 -253
  90. data/ext/deflate_ruby/libdeflate/scripts/cmake-helper.sh +0 -17
  91. data/ext/deflate_ruby/libdeflate/scripts/deflate_benchmarks.sh +0 -119
  92. data/ext/deflate_ruby/libdeflate/scripts/exec_tests.sh +0 -38
  93. data/ext/deflate_ruby/libdeflate/scripts/gen-release-archives.sh +0 -37
  94. data/ext/deflate_ruby/libdeflate/scripts/gen_bitreverse_tab.py +0 -19
  95. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_multipliers.c +0 -199
  96. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_tables.c +0 -105
  97. data/ext/deflate_ruby/libdeflate/scripts/gen_default_litlen_costs.py +0 -44
  98. data/ext/deflate_ruby/libdeflate/scripts/gen_offset_slot_map.py +0 -29
  99. data/ext/deflate_ruby/libdeflate/scripts/gzip_tests.sh +0 -523
  100. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/corpus/0 +0 -0
  101. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/fuzz.c +0 -95
  102. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/corpus/0 +0 -3
  103. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/fuzz.c +0 -62
  104. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/fuzz.sh +0 -108
  105. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/corpus/0 +0 -0
  106. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/fuzz.c +0 -19
  107. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/corpus/0 +0 -3
  108. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/fuzz.c +0 -19
  109. data/ext/deflate_ruby/libdeflate/scripts/run_tests.sh +0 -416
  110. data/ext/deflate_ruby/libdeflate/scripts/toolchain-i686-w64-mingw32.cmake +0 -8
  111. data/ext/deflate_ruby/libdeflate/scripts/toolchain-x86_64-w64-mingw32.cmake +0 -8
  112. /data/ext/deflate_ruby/{libdeflate/lib/adler32.c → adler32.c} +0 -0
  113. /data/ext/deflate_ruby/{libdeflate/lib/x86/adler32_template.h → adler32_template.h} +0 -0
  114. /data/ext/deflate_ruby/{libdeflate/lib/bt_matchfinder.h → bt_matchfinder.h} +0 -0
  115. /data/ext/deflate_ruby/{libdeflate/lib/cpu_features_common.h → cpu_features_common.h} +0 -0
  116. /data/ext/deflate_ruby/{libdeflate/lib/crc32.c → crc32.c} +0 -0
  117. /data/ext/deflate_ruby/{libdeflate/lib/arm/crc32_pmull_helpers.h → crc32_pmull_helpers.h} +0 -0
  118. /data/ext/deflate_ruby/{libdeflate/lib/arm/crc32_pmull_wide.h → crc32_pmull_wide.h} +0 -0
  119. /data/ext/deflate_ruby/{libdeflate/lib/x86/decompress_impl.h → decompress_impl.h} +0 -0
  120. /data/ext/deflate_ruby/{libdeflate/lib/decompress_template.h → decompress_template.h} +0 -0
  121. /data/ext/deflate_ruby/{libdeflate/lib/deflate_compress.h → deflate_compress.h} +0 -0
  122. /data/ext/deflate_ruby/{libdeflate/lib/deflate_constants.h → deflate_constants.h} +0 -0
  123. /data/ext/deflate_ruby/{libdeflate/lib/deflate_decompress.c → deflate_decompress.c} +0 -0
  124. /data/ext/deflate_ruby/{libdeflate/lib/gzip_compress.c → gzip_compress.c} +0 -0
  125. /data/ext/deflate_ruby/{libdeflate/lib/gzip_constants.h → gzip_constants.h} +0 -0
  126. /data/ext/deflate_ruby/{libdeflate/lib/gzip_decompress.c → gzip_decompress.c} +0 -0
  127. /data/ext/deflate_ruby/{libdeflate/lib/hc_matchfinder.h → hc_matchfinder.h} +0 -0
  128. /data/ext/deflate_ruby/{libdeflate/lib/ht_matchfinder.h → ht_matchfinder.h} +0 -0
  129. /data/ext/deflate_ruby/{libdeflate/lib/lib_common.h → lib_common.h} +0 -0
  130. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/cpu_features.c +0 -0
  131. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/cpu_features.h +0 -0
  132. /data/ext/deflate_ruby/libdeflate/{lib/arm → arm}/matchfinder_impl.h +0 -0
  133. /data/ext/deflate_ruby/libdeflate/{lib/riscv → riscv}/matchfinder_impl.h +0 -0
  134. /data/ext/deflate_ruby/libdeflate/{lib/utils.c → utils.c} +0 -0
  135. /data/ext/deflate_ruby/libdeflate/{lib/x86 → x86}/matchfinder_impl.h +0 -0
  136. /data/ext/deflate_ruby/libdeflate/{lib/zlib_compress.c → zlib_compress.c} +0 -0
  137. /data/ext/deflate_ruby/libdeflate/{lib/zlib_constants.h → zlib_constants.h} +0 -0
  138. /data/ext/deflate_ruby/libdeflate/{lib/zlib_decompress.c → zlib_decompress.c} +0 -0
@@ -1,301 +1,475 @@
1
- #include <ruby.h>
2
- #include "libdeflate.h"
1
+ #include "deflate_ruby.h"
2
+ #include "libdeflate/libdeflate.h"
3
3
  #include <string.h>
4
4
 
5
- static VALUE rb_mDeflateRuby;
6
- static VALUE rb_eDeflateError;
5
+ VALUE rb_mDeflateRuby;
6
+ VALUE rb_cCompressor;
7
+ VALUE rb_cDecompressor;
8
+ VALUE rb_eDeflateError;
9
+ VALUE rb_eCompressionError;
10
+ VALUE rb_eDecompressionError;
11
+
12
+ // Compressor wrapper structure
13
+ typedef struct {
14
+ struct libdeflate_compressor *compressor;
15
+ } CompressorData;
16
+
17
+ // Decompressor wrapper structure
18
+ typedef struct {
19
+ struct libdeflate_decompressor *decompressor;
20
+ } DecompressorData;
21
+
22
+ // Free compressor
23
+ static void compressor_free(void *ptr) {
24
+ CompressorData *data = (CompressorData *)ptr;
25
+ if (data->compressor) {
26
+ libdeflate_free_compressor(data->compressor);
27
+ data->compressor = NULL;
28
+ }
29
+ xfree(data);
30
+ }
7
31
 
8
- // Helper function to raise errors
9
- static void raise_deflate_error(const char *message) {
10
- rb_raise(rb_eDeflateError, "%s", message);
32
+ // Compressor allocation
33
+ static const rb_data_type_t compressor_type = {
34
+ "DeflateRuby::Compressor",
35
+ {NULL, compressor_free, NULL},
36
+ 0, 0,
37
+ RUBY_TYPED_FREE_IMMEDIATELY
38
+ };
39
+
40
+ // Free decompressor
41
+ static void decompressor_free(void *ptr) {
42
+ DecompressorData *data = (DecompressorData *)ptr;
43
+ if (data->decompressor) {
44
+ libdeflate_free_decompressor(data->decompressor);
45
+ data->decompressor = NULL;
46
+ }
47
+ xfree(data);
11
48
  }
12
49
 
13
- // DEFLATE compression
14
- static VALUE rb_deflate_compress(int argc, VALUE *argv, VALUE self) {
15
- VALUE data, level_val;
16
- rb_scan_args(argc, argv, "11", &data, &level_val);
50
+ // Decompressor allocation
51
+ static const rb_data_type_t decompressor_type = {
52
+ "DeflateRuby::Decompressor",
53
+ {NULL, decompressor_free, NULL},
54
+ 0, 0,
55
+ RUBY_TYPED_FREE_IMMEDIATELY
56
+ };
57
+
58
+ // Compressor.new(level: 6)
59
+ static VALUE compressor_initialize(int argc, VALUE *argv, VALUE self) {
60
+ VALUE opts;
61
+ rb_scan_args(argc, argv, "0:", &opts);
62
+
63
+ int level = 6; // default compression level
64
+
65
+ if (!NIL_P(opts)) {
66
+ VALUE level_val = rb_hash_aref(opts, ID2SYM(rb_intern("level")));
67
+ if (!NIL_P(level_val)) {
68
+ level = NUM2INT(level_val);
69
+ if (level < 0 || level > 12) {
70
+ rb_raise(rb_eArgError, "compression level must be between 0 and 12");
71
+ }
72
+ }
73
+ }
17
74
 
18
- Check_Type(data, T_STRING);
75
+ CompressorData *data;
76
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
19
77
 
20
- int level = NIL_P(level_val) ? 6 : NUM2INT(level_val);
21
- if (level < 1 || level > 12) {
22
- rb_raise(rb_eArgError, "compression level must be between 1 and 12");
78
+ data->compressor = libdeflate_alloc_compressor(level);
79
+ if (!data->compressor) {
80
+ rb_raise(rb_eCompressionError, "failed to allocate compressor");
23
81
  }
24
82
 
25
- const char *in_data = RSTRING_PTR(data);
26
- size_t in_size = RSTRING_LEN(data);
83
+ return self;
84
+ }
85
+
86
+ // Allocate compressor
87
+ static VALUE compressor_alloc(VALUE klass) {
88
+ CompressorData *data = ALLOC(CompressorData);
89
+ data->compressor = NULL;
90
+ return TypedData_Wrap_Struct(klass, &compressor_type, data);
91
+ }
92
+
93
+ // compressor.deflate_compress(data) -> String
94
+ static VALUE compressor_deflate_compress(VALUE self, VALUE input) {
95
+ StringValue(input);
27
96
 
28
- struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
29
- if (!compressor) {
30
- raise_deflate_error("Failed to allocate compressor");
97
+ CompressorData *data;
98
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
99
+
100
+ if (!data->compressor) {
101
+ rb_raise(rb_eCompressionError, "compressor is closed");
31
102
  }
32
103
 
33
- size_t max_out_size = libdeflate_deflate_compress_bound(compressor, in_size);
34
- VALUE out_str = rb_str_buf_new(max_out_size);
104
+ const char *in_data = RSTRING_PTR(input);
105
+ size_t in_size = RSTRING_LEN(input);
35
106
 
36
- size_t actual_size = libdeflate_deflate_compress(
37
- compressor,
38
- in_data,
39
- in_size,
40
- RSTRING_PTR(out_str),
41
- max_out_size
42
- );
107
+ size_t out_bound = libdeflate_deflate_compress_bound(data->compressor, in_size);
108
+ VALUE output = rb_str_buf_new(out_bound);
43
109
 
44
- libdeflate_free_compressor(compressor);
110
+ size_t out_size = libdeflate_deflate_compress(
111
+ data->compressor,
112
+ in_data, in_size,
113
+ RSTRING_PTR(output), out_bound
114
+ );
45
115
 
46
- if (actual_size == 0) {
47
- raise_deflate_error("Compression failed");
116
+ if (out_size == 0) {
117
+ rb_raise(rb_eCompressionError, "compression failed");
48
118
  }
49
119
 
50
- rb_str_set_len(out_str, actual_size);
51
- return out_str;
120
+ rb_str_set_len(output, out_size);
121
+ return output;
52
122
  }
53
123
 
54
- // DEFLATE decompression
55
- static VALUE rb_deflate_decompress(VALUE self, VALUE data) {
56
- Check_Type(data, T_STRING);
124
+ // compressor.zlib_compress(data) -> String
125
+ static VALUE compressor_zlib_compress(VALUE self, VALUE input) {
126
+ StringValue(input);
57
127
 
58
- const char *in_data = RSTRING_PTR(data);
59
- size_t in_size = RSTRING_LEN(data);
128
+ CompressorData *data;
129
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
60
130
 
61
- struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
62
- if (!decompressor) {
63
- raise_deflate_error("Failed to allocate decompressor");
131
+ if (!data->compressor) {
132
+ rb_raise(rb_eCompressionError, "compressor is closed");
64
133
  }
65
134
 
66
- // Start with a reasonable buffer size and grow if needed
67
- size_t out_size = in_size * 100; // Start larger for highly compressible data
68
- VALUE out_str = rb_str_buf_new(out_size);
69
- size_t actual_size;
70
-
71
- enum libdeflate_result result;
72
-
73
- // Try decompression, increase buffer if needed
74
- for (int attempts = 0; attempts < 10; attempts++) {
75
- result = libdeflate_deflate_decompress(
76
- decompressor,
77
- in_data,
78
- in_size,
79
- RSTRING_PTR(out_str),
80
- out_size,
81
- &actual_size
82
- );
83
-
84
- if (result == LIBDEFLATE_SUCCESS) {
85
- break;
86
- } else if (result == LIBDEFLATE_INSUFFICIENT_SPACE) {
87
- out_size *= 2;
88
- rb_str_resize(out_str, out_size);
89
- } else {
90
- libdeflate_free_decompressor(decompressor);
91
- raise_deflate_error("Decompression failed: invalid or corrupted data");
92
- }
93
- }
135
+ const char *in_data = RSTRING_PTR(input);
136
+ size_t in_size = RSTRING_LEN(input);
94
137
 
95
- libdeflate_free_decompressor(decompressor);
138
+ size_t out_bound = libdeflate_zlib_compress_bound(data->compressor, in_size);
139
+ VALUE output = rb_str_buf_new(out_bound);
96
140
 
97
- if (result != LIBDEFLATE_SUCCESS) {
98
- raise_deflate_error("Decompression failed: output buffer too small");
141
+ size_t out_size = libdeflate_zlib_compress(
142
+ data->compressor,
143
+ in_data, in_size,
144
+ RSTRING_PTR(output), out_bound
145
+ );
146
+
147
+ if (out_size == 0) {
148
+ rb_raise(rb_eCompressionError, "compression failed");
99
149
  }
100
150
 
101
- rb_str_set_len(out_str, actual_size);
102
- return out_str;
151
+ rb_str_set_len(output, out_size);
152
+ return output;
103
153
  }
104
154
 
105
- // ZLIB compression
106
- static VALUE rb_zlib_compress(int argc, VALUE *argv, VALUE self) {
107
- VALUE data, level_val;
108
- rb_scan_args(argc, argv, "11", &data, &level_val);
155
+ // compressor.gzip_compress(data) -> String
156
+ static VALUE compressor_gzip_compress(VALUE self, VALUE input) {
157
+ StringValue(input);
109
158
 
110
- Check_Type(data, T_STRING);
159
+ CompressorData *data;
160
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
111
161
 
112
- int level = NIL_P(level_val) ? 6 : NUM2INT(level_val);
113
- if (level < 1 || level > 12) {
114
- rb_raise(rb_eArgError, "compression level must be between 1 and 12");
162
+ if (!data->compressor) {
163
+ rb_raise(rb_eCompressionError, "compressor is closed");
115
164
  }
116
165
 
117
- const char *in_data = RSTRING_PTR(data);
118
- size_t in_size = RSTRING_LEN(data);
119
-
120
- struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
121
- if (!compressor) {
122
- raise_deflate_error("Failed to allocate compressor");
123
- }
166
+ const char *in_data = RSTRING_PTR(input);
167
+ size_t in_size = RSTRING_LEN(input);
124
168
 
125
- size_t max_out_size = libdeflate_zlib_compress_bound(compressor, in_size);
126
- VALUE out_str = rb_str_buf_new(max_out_size);
169
+ size_t out_bound = libdeflate_gzip_compress_bound(data->compressor, in_size);
170
+ VALUE output = rb_str_buf_new(out_bound);
127
171
 
128
- size_t actual_size = libdeflate_zlib_compress(
129
- compressor,
130
- in_data,
131
- in_size,
132
- RSTRING_PTR(out_str),
133
- max_out_size
172
+ size_t out_size = libdeflate_gzip_compress(
173
+ data->compressor,
174
+ in_data, in_size,
175
+ RSTRING_PTR(output), out_bound
134
176
  );
135
177
 
136
- libdeflate_free_compressor(compressor);
178
+ if (out_size == 0) {
179
+ rb_raise(rb_eCompressionError, "compression failed");
180
+ }
181
+
182
+ rb_str_set_len(output, out_size);
183
+ return output;
184
+ }
185
+
186
+ // compressor.deflate_compress_bound(size) -> Integer
187
+ static VALUE compressor_deflate_compress_bound(VALUE self, VALUE size) {
188
+ CompressorData *data;
189
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
137
190
 
138
- if (actual_size == 0) {
139
- raise_deflate_error("Compression failed");
191
+ if (!data->compressor) {
192
+ rb_raise(rb_eCompressionError, "compressor is closed");
140
193
  }
141
194
 
142
- rb_str_set_len(out_str, actual_size);
143
- return out_str;
195
+ size_t bound = libdeflate_deflate_compress_bound(data->compressor, NUM2SIZET(size));
196
+ return SIZET2NUM(bound);
144
197
  }
145
198
 
146
- // ZLIB decompression
147
- static VALUE rb_zlib_decompress(VALUE self, VALUE data) {
148
- Check_Type(data, T_STRING);
199
+ // compressor.zlib_compress_bound(size) -> Integer
200
+ static VALUE compressor_zlib_compress_bound(VALUE self, VALUE size) {
201
+ CompressorData *data;
202
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
203
+
204
+ if (!data->compressor) {
205
+ rb_raise(rb_eCompressionError, "compressor is closed");
206
+ }
207
+
208
+ size_t bound = libdeflate_zlib_compress_bound(data->compressor, NUM2SIZET(size));
209
+ return SIZET2NUM(bound);
210
+ }
149
211
 
150
- const char *in_data = RSTRING_PTR(data);
151
- size_t in_size = RSTRING_LEN(data);
212
+ // compressor.gzip_compress_bound(size) -> Integer
213
+ static VALUE compressor_gzip_compress_bound(VALUE self, VALUE size) {
214
+ CompressorData *data;
215
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
152
216
 
153
- struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
154
- if (!decompressor) {
155
- raise_deflate_error("Failed to allocate decompressor");
217
+ if (!data->compressor) {
218
+ rb_raise(rb_eCompressionError, "compressor is closed");
156
219
  }
157
220
 
158
- size_t out_size = in_size * 100; // Start larger for highly compressible data
159
- VALUE out_str = rb_str_buf_new(out_size);
160
- size_t actual_size;
161
-
162
- enum libdeflate_result result;
163
-
164
- for (int attempts = 0; attempts < 10; attempts++) {
165
- result = libdeflate_zlib_decompress(
166
- decompressor,
167
- in_data,
168
- in_size,
169
- RSTRING_PTR(out_str),
170
- out_size,
171
- &actual_size
172
- );
173
-
174
- if (result == LIBDEFLATE_SUCCESS) {
175
- break;
176
- } else if (result == LIBDEFLATE_INSUFFICIENT_SPACE) {
177
- out_size *= 2;
178
- rb_str_resize(out_str, out_size);
179
- } else {
180
- libdeflate_free_decompressor(decompressor);
181
- raise_deflate_error("Decompression failed: invalid or corrupted zlib data");
182
- }
221
+ size_t bound = libdeflate_gzip_compress_bound(data->compressor, NUM2SIZET(size));
222
+ return SIZET2NUM(bound);
223
+ }
224
+
225
+ // compressor.close
226
+ static VALUE compressor_close(VALUE self) {
227
+ CompressorData *data;
228
+ TypedData_Get_Struct(self, CompressorData, &compressor_type, data);
229
+
230
+ if (data->compressor) {
231
+ libdeflate_free_compressor(data->compressor);
232
+ data->compressor = NULL;
183
233
  }
184
234
 
185
- libdeflate_free_decompressor(decompressor);
235
+ return Qnil;
236
+ }
186
237
 
187
- if (result != LIBDEFLATE_SUCCESS) {
188
- raise_deflate_error("Decompression failed: output buffer too small");
238
+ // Decompressor.new
239
+ static VALUE decompressor_initialize(VALUE self) {
240
+ DecompressorData *data;
241
+ TypedData_Get_Struct(self, DecompressorData, &decompressor_type, data);
242
+
243
+ data->decompressor = libdeflate_alloc_decompressor();
244
+ if (!data->decompressor) {
245
+ rb_raise(rb_eDecompressionError, "failed to allocate decompressor");
189
246
  }
190
247
 
191
- rb_str_set_len(out_str, actual_size);
192
- return out_str;
248
+ return self;
249
+ }
250
+
251
+ // Allocate decompressor
252
+ static VALUE decompressor_alloc(VALUE klass) {
253
+ DecompressorData *data = ALLOC(DecompressorData);
254
+ data->decompressor = NULL;
255
+ return TypedData_Wrap_Struct(klass, &decompressor_type, data);
193
256
  }
194
257
 
195
- // GZIP compression
196
- static VALUE rb_gzip_compress(int argc, VALUE *argv, VALUE self) {
197
- VALUE data, level_val;
198
- rb_scan_args(argc, argv, "11", &data, &level_val);
258
+ // decompressor.deflate_decompress(data, output_size) -> String
259
+ static VALUE decompressor_deflate_decompress(VALUE self, VALUE input, VALUE out_size_val) {
260
+ StringValue(input);
199
261
 
200
- Check_Type(data, T_STRING);
262
+ DecompressorData *data;
263
+ TypedData_Get_Struct(self, DecompressorData, &decompressor_type, data);
201
264
 
202
- int level = NIL_P(level_val) ? 6 : NUM2INT(level_val);
203
- if (level < 1 || level > 12) {
204
- rb_raise(rb_eArgError, "compression level must be between 1 and 12");
265
+ if (!data->decompressor) {
266
+ rb_raise(rb_eDecompressionError, "decompressor is closed");
205
267
  }
206
268
 
207
- const char *in_data = RSTRING_PTR(data);
208
- size_t in_size = RSTRING_LEN(data);
269
+ const char *in_data = RSTRING_PTR(input);
270
+ size_t in_size = RSTRING_LEN(input);
271
+ size_t out_size = NUM2SIZET(out_size_val);
209
272
 
210
- struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
211
- if (!compressor) {
212
- raise_deflate_error("Failed to allocate compressor");
273
+ VALUE output = rb_str_buf_new(out_size);
274
+ size_t actual_out_size;
275
+
276
+ enum libdeflate_result result = libdeflate_deflate_decompress(
277
+ data->decompressor,
278
+ in_data, in_size,
279
+ RSTRING_PTR(output), out_size,
280
+ &actual_out_size
281
+ );
282
+
283
+ if (result != LIBDEFLATE_SUCCESS) {
284
+ const char *msg;
285
+ switch (result) {
286
+ case LIBDEFLATE_BAD_DATA:
287
+ msg = "bad compressed data";
288
+ break;
289
+ case LIBDEFLATE_SHORT_OUTPUT:
290
+ msg = "output buffer too small";
291
+ break;
292
+ case LIBDEFLATE_INSUFFICIENT_SPACE:
293
+ msg = "insufficient space";
294
+ break;
295
+ default:
296
+ msg = "decompression failed";
297
+ }
298
+ rb_raise(rb_eDecompressionError, "%s", msg);
213
299
  }
214
300
 
215
- size_t max_out_size = libdeflate_gzip_compress_bound(compressor, in_size);
216
- VALUE out_str = rb_str_buf_new(max_out_size);
301
+ rb_str_set_len(output, actual_out_size);
302
+ return output;
303
+ }
217
304
 
218
- size_t actual_size = libdeflate_gzip_compress(
219
- compressor,
220
- in_data,
221
- in_size,
222
- RSTRING_PTR(out_str),
223
- max_out_size
224
- );
305
+ // decompressor.zlib_decompress(data, output_size) -> String
306
+ static VALUE decompressor_zlib_decompress(VALUE self, VALUE input, VALUE out_size_val) {
307
+ StringValue(input);
308
+
309
+ DecompressorData *data;
310
+ TypedData_Get_Struct(self, DecompressorData, &decompressor_type, data);
311
+
312
+ if (!data->decompressor) {
313
+ rb_raise(rb_eDecompressionError, "decompressor is closed");
314
+ }
315
+
316
+ const char *in_data = RSTRING_PTR(input);
317
+ size_t in_size = RSTRING_LEN(input);
318
+ size_t out_size = NUM2SIZET(out_size_val);
225
319
 
226
- libdeflate_free_compressor(compressor);
320
+ VALUE output = rb_str_buf_new(out_size);
321
+ size_t actual_out_size;
227
322
 
228
- if (actual_size == 0) {
229
- raise_deflate_error("Compression failed");
323
+ enum libdeflate_result result = libdeflate_zlib_decompress(
324
+ data->decompressor,
325
+ in_data, in_size,
326
+ RSTRING_PTR(output), out_size,
327
+ &actual_out_size
328
+ );
329
+
330
+ if (result != LIBDEFLATE_SUCCESS) {
331
+ const char *msg;
332
+ switch (result) {
333
+ case LIBDEFLATE_BAD_DATA:
334
+ msg = "bad compressed data";
335
+ break;
336
+ case LIBDEFLATE_SHORT_OUTPUT:
337
+ msg = "output buffer too small";
338
+ break;
339
+ case LIBDEFLATE_INSUFFICIENT_SPACE:
340
+ msg = "insufficient space";
341
+ break;
342
+ default:
343
+ msg = "decompression failed";
344
+ }
345
+ rb_raise(rb_eDecompressionError, "%s", msg);
230
346
  }
231
347
 
232
- rb_str_set_len(out_str, actual_size);
233
- return out_str;
348
+ rb_str_set_len(output, actual_out_size);
349
+ return output;
234
350
  }
235
351
 
236
- // GZIP decompression
237
- static VALUE rb_gzip_decompress(VALUE self, VALUE data) {
238
- Check_Type(data, T_STRING);
352
+ // decompressor.gzip_decompress(data, output_size) -> String
353
+ static VALUE decompressor_gzip_decompress(VALUE self, VALUE input, VALUE out_size_val) {
354
+ StringValue(input);
239
355
 
240
- const char *in_data = RSTRING_PTR(data);
241
- size_t in_size = RSTRING_LEN(data);
356
+ DecompressorData *data;
357
+ TypedData_Get_Struct(self, DecompressorData, &decompressor_type, data);
242
358
 
243
- struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
244
- if (!decompressor) {
245
- raise_deflate_error("Failed to allocate decompressor");
359
+ if (!data->decompressor) {
360
+ rb_raise(rb_eDecompressionError, "decompressor is closed");
246
361
  }
247
362
 
248
- size_t out_size = in_size * 100; // Start larger for highly compressible data
249
- VALUE out_str = rb_str_buf_new(out_size);
250
- size_t actual_size;
251
-
252
- enum libdeflate_result result;
253
-
254
- for (int attempts = 0; attempts < 10; attempts++) {
255
- result = libdeflate_gzip_decompress(
256
- decompressor,
257
- in_data,
258
- in_size,
259
- RSTRING_PTR(out_str),
260
- out_size,
261
- &actual_size
262
- );
263
-
264
- if (result == LIBDEFLATE_SUCCESS) {
265
- break;
266
- } else if (result == LIBDEFLATE_INSUFFICIENT_SPACE) {
267
- out_size *= 2;
268
- rb_str_resize(out_str, out_size);
269
- } else {
270
- libdeflate_free_decompressor(decompressor);
271
- raise_deflate_error("Decompression failed: invalid or corrupted gzip data");
363
+ const char *in_data = RSTRING_PTR(input);
364
+ size_t in_size = RSTRING_LEN(input);
365
+ size_t out_size = NUM2SIZET(out_size_val);
366
+
367
+ VALUE output = rb_str_buf_new(out_size);
368
+ size_t actual_out_size;
369
+
370
+ enum libdeflate_result result = libdeflate_gzip_decompress(
371
+ data->decompressor,
372
+ in_data, in_size,
373
+ RSTRING_PTR(output), out_size,
374
+ &actual_out_size
375
+ );
376
+
377
+ if (result != LIBDEFLATE_SUCCESS) {
378
+ const char *msg;
379
+ switch (result) {
380
+ case LIBDEFLATE_BAD_DATA:
381
+ msg = "bad compressed data";
382
+ break;
383
+ case LIBDEFLATE_SHORT_OUTPUT:
384
+ msg = "output buffer too small";
385
+ break;
386
+ case LIBDEFLATE_INSUFFICIENT_SPACE:
387
+ msg = "insufficient space";
388
+ break;
389
+ default:
390
+ msg = "decompression failed";
272
391
  }
392
+ rb_raise(rb_eDecompressionError, "%s", msg);
273
393
  }
274
394
 
275
- libdeflate_free_decompressor(decompressor);
395
+ rb_str_set_len(output, actual_out_size);
396
+ return output;
397
+ }
276
398
 
277
- if (result != LIBDEFLATE_SUCCESS) {
278
- raise_deflate_error("Decompression failed: output buffer too small");
399
+ // decompressor.close
400
+ static VALUE decompressor_close(VALUE self) {
401
+ DecompressorData *data;
402
+ TypedData_Get_Struct(self, DecompressorData, &decompressor_type, data);
403
+
404
+ if (data->decompressor) {
405
+ libdeflate_free_decompressor(data->decompressor);
406
+ data->decompressor = NULL;
279
407
  }
280
408
 
281
- rb_str_set_len(out_str, actual_size);
282
- return out_str;
409
+ return Qnil;
283
410
  }
284
411
 
285
- // Initialize the extension
286
- void Init_deflate_ruby(void) {
287
- rb_mDeflateRuby = rb_define_module("DeflateRuby");
288
- rb_eDeflateError = rb_define_class_under(rb_mDeflateRuby, "Error", rb_eStandardError);
412
+ // DeflateRuby.crc32(data, initial = 0) -> Integer
413
+ static VALUE module_crc32(int argc, VALUE *argv, VALUE self) {
414
+ VALUE input, initial;
415
+ rb_scan_args(argc, argv, "11", &input, &initial);
416
+
417
+ StringValue(input);
289
418
 
290
- // DEFLATE methods
291
- rb_define_module_function(rb_mDeflateRuby, "deflate_compress", rb_deflate_compress, -1);
292
- rb_define_module_function(rb_mDeflateRuby, "deflate_decompress", rb_deflate_decompress, 1);
419
+ uint32_t crc = NIL_P(initial) ? 0 : NUM2UINT(initial);
293
420
 
294
- // ZLIB methods
295
- rb_define_module_function(rb_mDeflateRuby, "zlib_compress", rb_zlib_compress, -1);
296
- rb_define_module_function(rb_mDeflateRuby, "zlib_decompress", rb_zlib_decompress, 1);
421
+ crc = libdeflate_crc32(crc, RSTRING_PTR(input), RSTRING_LEN(input));
422
+
423
+ return UINT2NUM(crc);
424
+ }
425
+
426
+ // DeflateRuby.adler32(data, initial = 1) -> Integer
427
+ static VALUE module_adler32(int argc, VALUE *argv, VALUE self) {
428
+ VALUE input, initial;
429
+ rb_scan_args(argc, argv, "11", &input, &initial);
430
+
431
+ StringValue(input);
432
+
433
+ uint32_t adler = NIL_P(initial) ? 1 : NUM2UINT(initial);
434
+
435
+ adler = libdeflate_adler32(adler, RSTRING_PTR(input), RSTRING_LEN(input));
436
+
437
+ return UINT2NUM(adler);
438
+ }
439
+
440
+ RUBY_FUNC_EXPORTED void
441
+ Init_deflate_ruby(void)
442
+ {
443
+ // Define module
444
+ rb_mDeflateRuby = rb_define_module("DeflateRuby");
297
445
 
298
- // GZIP methods
299
- rb_define_module_function(rb_mDeflateRuby, "gzip_compress", rb_gzip_compress, -1);
300
- rb_define_module_function(rb_mDeflateRuby, "gzip_decompress", rb_gzip_decompress, 1);
446
+ // Define exception classes
447
+ rb_eDeflateError = rb_define_class_under(rb_mDeflateRuby, "DeflateError", rb_eStandardError);
448
+ rb_eCompressionError = rb_define_class_under(rb_mDeflateRuby, "CompressionError", rb_eDeflateError);
449
+ rb_eDecompressionError = rb_define_class_under(rb_mDeflateRuby, "DecompressionError", rb_eDeflateError);
450
+
451
+ // Define Compressor class
452
+ rb_cCompressor = rb_define_class_under(rb_mDeflateRuby, "Compressor", rb_cObject);
453
+ rb_define_alloc_func(rb_cCompressor, compressor_alloc);
454
+ rb_define_method(rb_cCompressor, "initialize", compressor_initialize, -1);
455
+ rb_define_method(rb_cCompressor, "deflate_compress", compressor_deflate_compress, 1);
456
+ rb_define_method(rb_cCompressor, "zlib_compress", compressor_zlib_compress, 1);
457
+ rb_define_method(rb_cCompressor, "gzip_compress", compressor_gzip_compress, 1);
458
+ rb_define_method(rb_cCompressor, "deflate_compress_bound", compressor_deflate_compress_bound, 1);
459
+ rb_define_method(rb_cCompressor, "zlib_compress_bound", compressor_zlib_compress_bound, 1);
460
+ rb_define_method(rb_cCompressor, "gzip_compress_bound", compressor_gzip_compress_bound, 1);
461
+ rb_define_method(rb_cCompressor, "close", compressor_close, 0);
462
+
463
+ // Define Decompressor class
464
+ rb_cDecompressor = rb_define_class_under(rb_mDeflateRuby, "Decompressor", rb_cObject);
465
+ rb_define_alloc_func(rb_cDecompressor, decompressor_alloc);
466
+ rb_define_method(rb_cDecompressor, "initialize", decompressor_initialize, 0);
467
+ rb_define_method(rb_cDecompressor, "deflate_decompress", decompressor_deflate_decompress, 2);
468
+ rb_define_method(rb_cDecompressor, "zlib_decompress", decompressor_zlib_decompress, 2);
469
+ rb_define_method(rb_cDecompressor, "gzip_decompress", decompressor_gzip_decompress, 2);
470
+ rb_define_method(rb_cDecompressor, "close", decompressor_close, 0);
471
+
472
+ // Define module methods
473
+ rb_define_module_function(rb_mDeflateRuby, "crc32", module_crc32, -1);
474
+ rb_define_module_function(rb_mDeflateRuby, "adler32", module_adler32, -1);
301
475
  }