deflate-ruby 0.1.0

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/CLAUDE.md +138 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +117 -0
  5. data/ext/deflate_ruby/deflate_ruby.c +301 -0
  6. data/ext/deflate_ruby/extconf.rb +34 -0
  7. data/ext/deflate_ruby/libdeflate/CMakeLists.txt +270 -0
  8. data/ext/deflate_ruby/libdeflate/COPYING +22 -0
  9. data/ext/deflate_ruby/libdeflate/NEWS.md +494 -0
  10. data/ext/deflate_ruby/libdeflate/README.md +228 -0
  11. data/ext/deflate_ruby/libdeflate/common_defs.h +747 -0
  12. data/ext/deflate_ruby/libdeflate/lib/adler32.c +162 -0
  13. data/ext/deflate_ruby/libdeflate/lib/arm/adler32_impl.h +358 -0
  14. data/ext/deflate_ruby/libdeflate/lib/arm/cpu_features.c +230 -0
  15. data/ext/deflate_ruby/libdeflate/lib/arm/cpu_features.h +214 -0
  16. data/ext/deflate_ruby/libdeflate/lib/arm/crc32_impl.h +600 -0
  17. data/ext/deflate_ruby/libdeflate/lib/arm/crc32_pmull_helpers.h +156 -0
  18. data/ext/deflate_ruby/libdeflate/lib/arm/crc32_pmull_wide.h +226 -0
  19. data/ext/deflate_ruby/libdeflate/lib/arm/matchfinder_impl.h +78 -0
  20. data/ext/deflate_ruby/libdeflate/lib/bt_matchfinder.h +342 -0
  21. data/ext/deflate_ruby/libdeflate/lib/cpu_features_common.h +93 -0
  22. data/ext/deflate_ruby/libdeflate/lib/crc32.c +262 -0
  23. data/ext/deflate_ruby/libdeflate/lib/crc32_multipliers.h +377 -0
  24. data/ext/deflate_ruby/libdeflate/lib/crc32_tables.h +587 -0
  25. data/ext/deflate_ruby/libdeflate/lib/decompress_template.h +777 -0
  26. data/ext/deflate_ruby/libdeflate/lib/deflate_compress.c +4129 -0
  27. data/ext/deflate_ruby/libdeflate/lib/deflate_compress.h +15 -0
  28. data/ext/deflate_ruby/libdeflate/lib/deflate_constants.h +56 -0
  29. data/ext/deflate_ruby/libdeflate/lib/deflate_decompress.c +1208 -0
  30. data/ext/deflate_ruby/libdeflate/lib/gzip_compress.c +90 -0
  31. data/ext/deflate_ruby/libdeflate/lib/gzip_constants.h +45 -0
  32. data/ext/deflate_ruby/libdeflate/lib/gzip_decompress.c +144 -0
  33. data/ext/deflate_ruby/libdeflate/lib/hc_matchfinder.h +401 -0
  34. data/ext/deflate_ruby/libdeflate/lib/ht_matchfinder.h +234 -0
  35. data/ext/deflate_ruby/libdeflate/lib/lib_common.h +106 -0
  36. data/ext/deflate_ruby/libdeflate/lib/matchfinder_common.h +224 -0
  37. data/ext/deflate_ruby/libdeflate/lib/riscv/matchfinder_impl.h +97 -0
  38. data/ext/deflate_ruby/libdeflate/lib/utils.c +141 -0
  39. data/ext/deflate_ruby/libdeflate/lib/x86/adler32_impl.h +134 -0
  40. data/ext/deflate_ruby/libdeflate/lib/x86/adler32_template.h +518 -0
  41. data/ext/deflate_ruby/libdeflate/lib/x86/cpu_features.c +183 -0
  42. data/ext/deflate_ruby/libdeflate/lib/x86/cpu_features.h +169 -0
  43. data/ext/deflate_ruby/libdeflate/lib/x86/crc32_impl.h +160 -0
  44. data/ext/deflate_ruby/libdeflate/lib/x86/crc32_pclmul_template.h +495 -0
  45. data/ext/deflate_ruby/libdeflate/lib/x86/decompress_impl.h +57 -0
  46. data/ext/deflate_ruby/libdeflate/lib/x86/matchfinder_impl.h +122 -0
  47. data/ext/deflate_ruby/libdeflate/lib/zlib_compress.c +82 -0
  48. data/ext/deflate_ruby/libdeflate/lib/zlib_constants.h +21 -0
  49. data/ext/deflate_ruby/libdeflate/lib/zlib_decompress.c +104 -0
  50. data/ext/deflate_ruby/libdeflate/libdeflate-config.cmake.in +3 -0
  51. data/ext/deflate_ruby/libdeflate/libdeflate.h +411 -0
  52. data/ext/deflate_ruby/libdeflate/libdeflate.pc.in +18 -0
  53. data/ext/deflate_ruby/libdeflate/programs/CMakeLists.txt +105 -0
  54. data/ext/deflate_ruby/libdeflate/programs/benchmark.c +696 -0
  55. data/ext/deflate_ruby/libdeflate/programs/checksum.c +218 -0
  56. data/ext/deflate_ruby/libdeflate/programs/config.h.in +19 -0
  57. data/ext/deflate_ruby/libdeflate/programs/gzip.c +688 -0
  58. data/ext/deflate_ruby/libdeflate/programs/prog_util.c +521 -0
  59. data/ext/deflate_ruby/libdeflate/programs/prog_util.h +225 -0
  60. data/ext/deflate_ruby/libdeflate/programs/test_checksums.c +200 -0
  61. data/ext/deflate_ruby/libdeflate/programs/test_custom_malloc.c +155 -0
  62. data/ext/deflate_ruby/libdeflate/programs/test_incomplete_codes.c +385 -0
  63. data/ext/deflate_ruby/libdeflate/programs/test_invalid_streams.c +130 -0
  64. data/ext/deflate_ruby/libdeflate/programs/test_litrunlen_overflow.c +72 -0
  65. data/ext/deflate_ruby/libdeflate/programs/test_overread.c +95 -0
  66. data/ext/deflate_ruby/libdeflate/programs/test_slow_decompression.c +472 -0
  67. data/ext/deflate_ruby/libdeflate/programs/test_trailing_bytes.c +151 -0
  68. data/ext/deflate_ruby/libdeflate/programs/test_util.c +237 -0
  69. data/ext/deflate_ruby/libdeflate/programs/test_util.h +61 -0
  70. data/ext/deflate_ruby/libdeflate/programs/tgetopt.c +118 -0
  71. data/ext/deflate_ruby/libdeflate/scripts/android_build.sh +118 -0
  72. data/ext/deflate_ruby/libdeflate/scripts/android_tests.sh +69 -0
  73. data/ext/deflate_ruby/libdeflate/scripts/benchmark.sh +10 -0
  74. data/ext/deflate_ruby/libdeflate/scripts/checksum.sh +10 -0
  75. data/ext/deflate_ruby/libdeflate/scripts/checksum_benchmarks.sh +253 -0
  76. data/ext/deflate_ruby/libdeflate/scripts/cmake-helper.sh +17 -0
  77. data/ext/deflate_ruby/libdeflate/scripts/deflate_benchmarks.sh +119 -0
  78. data/ext/deflate_ruby/libdeflate/scripts/exec_tests.sh +38 -0
  79. data/ext/deflate_ruby/libdeflate/scripts/gen-release-archives.sh +37 -0
  80. data/ext/deflate_ruby/libdeflate/scripts/gen_bitreverse_tab.py +19 -0
  81. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_multipliers.c +199 -0
  82. data/ext/deflate_ruby/libdeflate/scripts/gen_crc32_tables.c +105 -0
  83. data/ext/deflate_ruby/libdeflate/scripts/gen_default_litlen_costs.py +44 -0
  84. data/ext/deflate_ruby/libdeflate/scripts/gen_offset_slot_map.py +29 -0
  85. data/ext/deflate_ruby/libdeflate/scripts/gzip_tests.sh +523 -0
  86. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/corpus/0 +0 -0
  87. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_compress/fuzz.c +95 -0
  88. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/corpus/0 +3 -0
  89. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/deflate_decompress/fuzz.c +62 -0
  90. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/fuzz.sh +108 -0
  91. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/corpus/0 +0 -0
  92. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/gzip_decompress/fuzz.c +19 -0
  93. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/corpus/0 +3 -0
  94. data/ext/deflate_ruby/libdeflate/scripts/libFuzzer/zlib_decompress/fuzz.c +19 -0
  95. data/ext/deflate_ruby/libdeflate/scripts/run_tests.sh +416 -0
  96. data/ext/deflate_ruby/libdeflate/scripts/toolchain-i686-w64-mingw32.cmake +8 -0
  97. data/ext/deflate_ruby/libdeflate/scripts/toolchain-x86_64-w64-mingw32.cmake +8 -0
  98. data/lib/deflate_ruby/version.rb +5 -0
  99. data/lib/deflate_ruby.rb +71 -0
  100. metadata +191 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 68bc8b3a391756a9e043136b9b124086964806fcbb5683810181f4fcd19442ab
4
+ data.tar.gz: 17442db0cf381e96a52c0c198efdbf762b360c7b5decb552e82d1e8b72f0adec
5
+ SHA512:
6
+ metadata.gz: 46dbf853b6d7a0632253f167a2f792a69cc85c6623f8eaa77f4f96f6153c7f42af13e4f8c7efb3675245f2303899c80825e12aa3d94cdfbd03ace89d646bc9f8
7
+ data.tar.gz: 00fffef0a4c970467a3ffd9501f843af740d280f181af56255defb0189c82f5f0552133a3b3f27f54e322bfff0cbe860ab31771028c469f9d4ed58f23ba34a08
data/CLAUDE.md ADDED
@@ -0,0 +1,138 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ DeflateRuby is a Ruby gem that wraps the libdeflate C library to provide fast compression and decompression for DEFLATE, zlib, and gzip formats. The gem is built as a C extension that directly integrates libdeflate source files.
8
+
9
+ ## Architecture
10
+
11
+ ### Core Components
12
+
13
+ 1. **C Extension** (`ext/deflate_ruby/`)
14
+ - `deflate_ruby.c`: Main C extension wrapper implementing Ruby bindings for libdeflate
15
+ - `extconf.rb`: Extension configuration that compiles libdeflate source files along with the wrapper
16
+ - `libdeflate/`: Complete libdeflate source tree (v1.21) including all platform-specific optimizations
17
+
18
+ 2. **Ruby Library** (`lib/deflate_ruby/`)
19
+ - `deflate_ruby.rb`: High-level Ruby API providing file-based compression/decompression methods
20
+ - `version.rb`: Gem version constant
21
+ - `deflate_ruby.so`: Compiled C extension (generated during build)
22
+
23
+ 3. **Test Suite** (`test/`)
24
+ - `deflate_test.rb`: Tests for raw DEFLATE format
25
+ - `zlib_test.rb`: Tests for zlib format including stdlib interoperability
26
+ - `gzip_test.rb`: Tests for gzip format including stdlib interoperability
27
+
28
+ ### Key Design Decisions
29
+
30
+ - **Embedded libdeflate**: The libdeflate source is included directly in `ext/deflate_ruby/libdeflate/` and compiled as part of the extension. This ensures consistent behavior across platforms and avoids external dependencies.
31
+
32
+ - **Buffer Management**: Decompression uses an adaptive buffer strategy starting at 100x the compressed size and doubling up to 10 attempts. This handles highly compressible data (like repetitive text) that can expand significantly.
33
+
34
+ - **Dual API**: The gem provides both string-based methods (e.g., `deflate_compress(data)`) and file-based methods (e.g., `deflate_compress_file(input, output)`) for flexibility.
35
+
36
+ ## Common Development Commands
37
+
38
+ ### Building the Extension
39
+
40
+ ```bash
41
+ # Initial setup
42
+ cd ext/deflate_ruby
43
+ ruby extconf.rb
44
+ make
45
+
46
+ # Copy compiled extension to lib directory
47
+ cp deflate_ruby.so ../../lib/deflate_ruby/
48
+
49
+ # Or use rake
50
+ cd ../..
51
+ rake compile
52
+ ```
53
+
54
+ ### Running Tests
55
+
56
+ ```bash
57
+ # Run all tests
58
+ rake test
59
+
60
+ # Run specific test file
61
+ ruby -Ilib:test test/deflate_test.rb
62
+ ruby -Ilib:test test/zlib_test.rb
63
+ ruby -Ilib:test test/gzip_test.rb
64
+
65
+ # Run single test method
66
+ ruby -Ilib:test test/deflate_test.rb -n test_deflate_compress_decompress_roundtrip
67
+ ```
68
+
69
+ ### Cleaning Build Artifacts
70
+
71
+ ```bash
72
+ cd ext/deflate_ruby
73
+ make clean
74
+
75
+ # Or use rake
76
+ cd ../..
77
+ rake clean
78
+ ```
79
+
80
+ ## Important Implementation Details
81
+
82
+ ### C Extension (ext/deflate_ruby/deflate_ruby.c)
83
+
84
+ - **Error Handling**: Uses custom `DeflateRuby::Error` exception class for all compression/decompression errors
85
+ - **Memory Management**: All libdeflate compressor/decompressor objects are properly freed after use
86
+ - **Compression Levels**: Supports levels 1-12 (libdeflate extends beyond standard 1-9)
87
+ - **Buffer Allocation**: Uses Ruby's string buffer API (`rb_str_buf_new`, `rb_str_resize`) for efficient memory handling
88
+
89
+ ### Extension Configuration (ext/deflate_ruby/extconf.rb)
90
+
91
+ - Compiles all libdeflate source files from `lib/*.c` and architecture-specific subdirectories (`lib/x86/*.c`, `lib/arm/*.c`)
92
+ - Uses `-O2` optimization and `-std=c99` for C99 compatibility
93
+ - Sets up include paths to find `libdeflate.h`
94
+
95
+ ### Test Data Characteristics
96
+
97
+ - Tests use moderately large data (1000x repetitions) to see meaningful compression level differences
98
+ - For gzip tests, binary data is prepended to text to make compression level differences more apparent
99
+ - Compression level tests compare level 1 vs level 6 (not higher levels) as very high levels can sometimes produce larger output on small, highly compressible data
100
+
101
+ ## libdeflate Integration
102
+
103
+ The libdeflate library (https://github.com/ebiggers/libdeflate) is a heavily optimized compression library that:
104
+
105
+ - Provides faster compression/decompression than zlib for whole-buffer operations
106
+ - Includes CPU-specific optimizations (x86 SSE/AVX, ARM NEON)
107
+ - Supports DEFLATE, zlib, and gzip formats
108
+ - Is designed for non-streaming (whole-buffer) use cases
109
+
110
+ **Version**: Currently using libdeflate v1.21
111
+
112
+ **Updating libdeflate**: To update to a new version:
113
+ 1. Download new release from https://github.com/ebiggers/libdeflate/releases
114
+ 2. Extract to `ext/deflate_ruby/libdeflate/`
115
+ 3. Test thoroughly as API may have changed
116
+ 4. Update version reference in this file
117
+
118
+ ## Testing Philosophy
119
+
120
+ This gem follows test-driven development (TDD):
121
+
122
+ 1. Tests were written first, defining expected behavior
123
+ 2. Implementation follows the test specifications
124
+ 3. All three formats (deflate, zlib, gzip) have comprehensive test coverage including:
125
+ - Round-trip compression/decompression
126
+ - Multiple compression levels
127
+ - Empty data handling
128
+ - Invalid data error handling
129
+ - Binary data support
130
+ - Interoperability with Ruby's stdlib Zlib
131
+
132
+ ## Compression Format Notes
133
+
134
+ - **DEFLATE**: Raw compressed stream with no wrapper or checksum
135
+ - **zlib**: DEFLATE stream with zlib header and Adler-32 checksum trailer
136
+ - **gzip**: DEFLATE stream with gzip header (includes magic bytes 0x1f 0x8b) and CRC32 checksum trailer
137
+
138
+ The gem's zlib and gzip implementations are fully interoperable with Ruby's standard library and command-line tools.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # DeflateRuby
2
+
3
+ Fast compression and decompression for Ruby using [libdeflate](https://github.com/ebiggers/libdeflate).
4
+
5
+ DeflateRuby provides Ruby bindings for libdeflate, a heavily optimized library for DEFLATE, zlib, and gzip compression and decompression. It's significantly faster than Ruby's built-in Zlib for whole-buffer compression/decompression operations.
6
+
7
+ ## Installation
8
+
9
+ ### From RubyGems.org
10
+
11
+ Install the gem directly:
12
+
13
+ ```bash
14
+ gem install deflate-ruby
15
+ ```
16
+
17
+ Or add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'deflate-ruby'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ ### From Source
30
+
31
+ To build and install from source:
32
+
33
+ ```bash
34
+ git clone https://github.com/deflate-ruby/deflate-ruby.git
35
+ cd deflate-ruby
36
+ gem build deflate-ruby.gemspec
37
+ gem install deflate-ruby-0.1.0.gem
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ DeflateRuby supports three compression formats: DEFLATE (raw), zlib, and gzip.
43
+
44
+ ### String Compression/Decompression
45
+
46
+ ```ruby
47
+ require 'deflate_ruby'
48
+
49
+ data = "Hello, World!" * 100
50
+
51
+ # DEFLATE
52
+ compressed = DeflateRuby.deflate_compress(data, 6) # level 1-12
53
+ decompressed = DeflateRuby.deflate_decompress(compressed)
54
+
55
+ # Zlib
56
+ compressed = DeflateRuby.zlib_compress(data, 6)
57
+ decompressed = DeflateRuby.zlib_decompress(compressed)
58
+
59
+ # Gzip
60
+ compressed = DeflateRuby.gzip_compress(data, 6)
61
+ decompressed = DeflateRuby.gzip_decompress(compressed)
62
+ ```
63
+
64
+ ### File Compression/Decompression
65
+
66
+ ```ruby
67
+ # DEFLATE
68
+ DeflateRuby.deflate_compress_file("input.txt", "output.deflate", level: 9)
69
+ DeflateRuby.deflate_decompress_file("output.deflate", "decompressed.txt")
70
+
71
+ # Zlib
72
+ DeflateRuby.zlib_compress_file("input.txt", "output.zlib", level: 6)
73
+ DeflateRuby.zlib_decompress_file("output.zlib", "decompressed.txt")
74
+
75
+ # Gzip
76
+ DeflateRuby.gzip_compress_file("input.txt", "output.gz", level: 6)
77
+ DeflateRuby.gzip_decompress_file("output.gz", "decompressed.txt")
78
+ ```
79
+
80
+ ### Compression Levels
81
+
82
+ DeflateRuby supports compression levels from 1 to 12:
83
+ - Levels 1-9: Standard compression levels (1 = fastest, 9 = best compression)
84
+ - Levels 10-12: Enhanced compression using minimum-cost-path algorithms
85
+
86
+ Default level is 6, which provides a good balance between speed and compression ratio.
87
+
88
+ ## Compatibility
89
+
90
+ DeflateRuby's zlib and gzip formats are fully compatible with Ruby's built-in Zlib library and standard command-line tools like `gzip`.
91
+
92
+ ## Performance
93
+
94
+ libdeflate is optimized for modern CPUs and can be significantly faster than zlib, especially for compression. It's designed for whole-buffer operations (not streaming), making it ideal for:
95
+
96
+ - Compressing/decompressing entire files
97
+ - Working with in-memory data
98
+ - Batch compression operations
99
+ - High-throughput scenarios
100
+
101
+ ## Development
102
+
103
+ After checking out the repo, run:
104
+
105
+ ```bash
106
+ bundle install
107
+ rake compile
108
+ rake test
109
+ ```
110
+
111
+ ## License
112
+
113
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
114
+
115
+ ## Credits
116
+
117
+ This gem wraps [libdeflate](https://github.com/ebiggers/libdeflate) by Eric Biggers.
@@ -0,0 +1,301 @@
1
+ #include <ruby.h>
2
+ #include "libdeflate.h"
3
+ #include <string.h>
4
+
5
+ static VALUE rb_mDeflateRuby;
6
+ static VALUE rb_eDeflateError;
7
+
8
+ // Helper function to raise errors
9
+ static void raise_deflate_error(const char *message) {
10
+ rb_raise(rb_eDeflateError, "%s", message);
11
+ }
12
+
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);
17
+
18
+ Check_Type(data, T_STRING);
19
+
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");
23
+ }
24
+
25
+ const char *in_data = RSTRING_PTR(data);
26
+ size_t in_size = RSTRING_LEN(data);
27
+
28
+ struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
29
+ if (!compressor) {
30
+ raise_deflate_error("Failed to allocate compressor");
31
+ }
32
+
33
+ size_t max_out_size = libdeflate_deflate_compress_bound(compressor, in_size);
34
+ VALUE out_str = rb_str_buf_new(max_out_size);
35
+
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
+ );
43
+
44
+ libdeflate_free_compressor(compressor);
45
+
46
+ if (actual_size == 0) {
47
+ raise_deflate_error("Compression failed");
48
+ }
49
+
50
+ rb_str_set_len(out_str, actual_size);
51
+ return out_str;
52
+ }
53
+
54
+ // DEFLATE decompression
55
+ static VALUE rb_deflate_decompress(VALUE self, VALUE data) {
56
+ Check_Type(data, T_STRING);
57
+
58
+ const char *in_data = RSTRING_PTR(data);
59
+ size_t in_size = RSTRING_LEN(data);
60
+
61
+ struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
62
+ if (!decompressor) {
63
+ raise_deflate_error("Failed to allocate decompressor");
64
+ }
65
+
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
+ }
94
+
95
+ libdeflate_free_decompressor(decompressor);
96
+
97
+ if (result != LIBDEFLATE_SUCCESS) {
98
+ raise_deflate_error("Decompression failed: output buffer too small");
99
+ }
100
+
101
+ rb_str_set_len(out_str, actual_size);
102
+ return out_str;
103
+ }
104
+
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);
109
+
110
+ Check_Type(data, T_STRING);
111
+
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");
115
+ }
116
+
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
+ }
124
+
125
+ size_t max_out_size = libdeflate_zlib_compress_bound(compressor, in_size);
126
+ VALUE out_str = rb_str_buf_new(max_out_size);
127
+
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
134
+ );
135
+
136
+ libdeflate_free_compressor(compressor);
137
+
138
+ if (actual_size == 0) {
139
+ raise_deflate_error("Compression failed");
140
+ }
141
+
142
+ rb_str_set_len(out_str, actual_size);
143
+ return out_str;
144
+ }
145
+
146
+ // ZLIB decompression
147
+ static VALUE rb_zlib_decompress(VALUE self, VALUE data) {
148
+ Check_Type(data, T_STRING);
149
+
150
+ const char *in_data = RSTRING_PTR(data);
151
+ size_t in_size = RSTRING_LEN(data);
152
+
153
+ struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
154
+ if (!decompressor) {
155
+ raise_deflate_error("Failed to allocate decompressor");
156
+ }
157
+
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
+ }
183
+ }
184
+
185
+ libdeflate_free_decompressor(decompressor);
186
+
187
+ if (result != LIBDEFLATE_SUCCESS) {
188
+ raise_deflate_error("Decompression failed: output buffer too small");
189
+ }
190
+
191
+ rb_str_set_len(out_str, actual_size);
192
+ return out_str;
193
+ }
194
+
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);
199
+
200
+ Check_Type(data, T_STRING);
201
+
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");
205
+ }
206
+
207
+ const char *in_data = RSTRING_PTR(data);
208
+ size_t in_size = RSTRING_LEN(data);
209
+
210
+ struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
211
+ if (!compressor) {
212
+ raise_deflate_error("Failed to allocate compressor");
213
+ }
214
+
215
+ size_t max_out_size = libdeflate_gzip_compress_bound(compressor, in_size);
216
+ VALUE out_str = rb_str_buf_new(max_out_size);
217
+
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
+ );
225
+
226
+ libdeflate_free_compressor(compressor);
227
+
228
+ if (actual_size == 0) {
229
+ raise_deflate_error("Compression failed");
230
+ }
231
+
232
+ rb_str_set_len(out_str, actual_size);
233
+ return out_str;
234
+ }
235
+
236
+ // GZIP decompression
237
+ static VALUE rb_gzip_decompress(VALUE self, VALUE data) {
238
+ Check_Type(data, T_STRING);
239
+
240
+ const char *in_data = RSTRING_PTR(data);
241
+ size_t in_size = RSTRING_LEN(data);
242
+
243
+ struct libdeflate_decompressor *decompressor = libdeflate_alloc_decompressor();
244
+ if (!decompressor) {
245
+ raise_deflate_error("Failed to allocate decompressor");
246
+ }
247
+
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");
272
+ }
273
+ }
274
+
275
+ libdeflate_free_decompressor(decompressor);
276
+
277
+ if (result != LIBDEFLATE_SUCCESS) {
278
+ raise_deflate_error("Decompression failed: output buffer too small");
279
+ }
280
+
281
+ rb_str_set_len(out_str, actual_size);
282
+ return out_str;
283
+ }
284
+
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);
289
+
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);
293
+
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);
297
+
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);
301
+ }
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ # Add libdeflate source directory to the include path
6
+ $INCFLAGS << " -I$(srcdir)/libdeflate"
7
+ $CFLAGS << " -O2 -std=c99"
8
+
9
+ # Define source files to compile
10
+ libdeflate_sources = %w[
11
+ libdeflate/lib/deflate_compress.c
12
+ libdeflate/lib/deflate_decompress.c
13
+ libdeflate/lib/zlib_compress.c
14
+ libdeflate/lib/zlib_decompress.c
15
+ libdeflate/lib/gzip_compress.c
16
+ libdeflate/lib/gzip_decompress.c
17
+ libdeflate/lib/adler32.c
18
+ libdeflate/lib/crc32.c
19
+ libdeflate/lib/utils.c
20
+ ]
21
+
22
+ # Add CPU architecture-specific files
23
+ arch_dirs = Dir.glob("libdeflate/lib/*/").select { |d| File.directory?(d) }
24
+ arch_dirs.each do |dir|
25
+ Dir.glob("#{dir}*.c").each do |source|
26
+ libdeflate_sources << source
27
+ end
28
+ end
29
+
30
+ # Set object files for libdeflate
31
+ $objs = libdeflate_sources.map { |src| src.sub(/\.c$/, ".o") }
32
+ $objs << "deflate_ruby.o"
33
+
34
+ create_makefile("deflate_ruby/deflate_ruby")