lz4-native-ruby 1.0.0 → 1.0.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +292 -0
  3. data/LICENSE +55 -21
  4. data/README.md +109 -15
  5. data/{vendor/lz4/lib → ext/lz4_native}/Makefile +29 -24
  6. data/{vendor/lz4/lib → ext/lz4_native}/README.md +1 -1
  7. data/ext/lz4_native/extconf.rb +33 -0
  8. data/{vendor/lz4/lib → ext/lz4_native}/liblz4.pc.in +1 -0
  9. data/{vendor/lz4/lib → ext/lz4_native}/lz4.c +26 -23
  10. data/{vendor/lz4/lib → ext/lz4_native}/lz4.h +11 -9
  11. data/ext/lz4_native/lz4_native.c +442 -0
  12. data/ext/lz4_native/lz4file.c +362 -0
  13. data/{vendor/lz4/lib → ext/lz4_native}/lz4file.h +32 -9
  14. data/{vendor/lz4/lib → ext/lz4_native}/lz4frame.c +50 -21
  15. data/{vendor/lz4/lib → ext/lz4_native}/lz4frame.h +48 -28
  16. data/{vendor/lz4/lib → ext/lz4_native}/lz4frame_static.h +1 -1
  17. data/{vendor/lz4/lib → ext/lz4_native}/lz4hc.c +123 -60
  18. data/{vendor/lz4/lib → ext/lz4_native}/lz4hc.h +1 -1
  19. data/lib/lz4_native/lz4_native.so +0 -0
  20. data/lib/lz4_native/version.rb +3 -0
  21. data/lib/lz4_native.rb +47 -0
  22. data/test/test_helper.rb +4 -0
  23. data/test/test_lz4_basic.rb +100 -0
  24. data/test/test_lz4frame.rb +129 -0
  25. data/test/test_lz4hc.rb +75 -0
  26. metadata +50 -43
  27. data/ext/lz4/extconf.rb +0 -12
  28. data/ext/lz4/lz4_ext.c +0 -230
  29. data/lib/lz4/lz4_ext.so +0 -0
  30. data/lib/lz4/version.rb +0 -3
  31. data/lib/lz4.rb +0 -60
  32. data/vendor/lz4/lib/lz4file.c +0 -341
  33. /data/{vendor/lz4/lib → ext/lz4_native}/LICENSE +0 -0
  34. /data/{vendor/lz4/lib → ext/lz4_native}/dll/example/Makefile +0 -0
  35. /data/{vendor/lz4/lib → ext/lz4_native}/dll/example/README.md +0 -0
  36. /data/{vendor/lz4/lib → ext/lz4_native}/dll/example/fullbench-dll.sln +0 -0
  37. /data/{vendor/lz4/lib → ext/lz4_native}/dll/example/fullbench-dll.vcxproj +0 -0
  38. /data/{vendor/lz4/lib → ext/lz4_native}/liblz4-dll.rc.in +0 -0
  39. /data/{vendor/lz4/lib → ext/lz4_native}/xxhash.c +0 -0
  40. /data/{vendor/lz4/lib → ext/lz4_native}/xxhash.h +0 -0
@@ -0,0 +1,129 @@
1
+ require "test_helper"
2
+
3
+ class TestLZ4Frame < Minitest::Test
4
+ def setup
5
+ @test_data = "Hello, World! " * 100
6
+ @large_data = "A" * 100000
7
+ end
8
+
9
+ def test_version
10
+ version = LZ4Native::LZ4Frame.version
11
+ assert version.is_a?(Integer)
12
+ assert version > 0
13
+ end
14
+
15
+ def test_compression_level_max
16
+ max_level = LZ4Native::LZ4Frame.compression_level_max
17
+ assert max_level.is_a?(Integer)
18
+ assert max_level >= 12
19
+ end
20
+
21
+ def test_compress_decompress_frame_default
22
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data)
23
+ assert compressed.is_a?(String)
24
+ assert compressed.bytesize > 0
25
+
26
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
27
+ assert_equal @test_data, decompressed
28
+ end
29
+
30
+ def test_compress_decompress_frame_large_data
31
+ compressed = LZ4Native::LZ4Frame.compress_frame(@large_data)
32
+ assert compressed.bytesize < @large_data.bytesize
33
+
34
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
35
+ assert_equal @large_data, decompressed
36
+ end
37
+
38
+ def test_frame_with_block_size_max64kb
39
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_size: :max64KB)
40
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
41
+ assert_equal @test_data, decompressed
42
+ end
43
+
44
+ def test_frame_with_block_size_max256kb
45
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_size: :max256KB)
46
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
47
+ assert_equal @test_data, decompressed
48
+ end
49
+
50
+ def test_frame_with_block_size_max1mb
51
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_size: :max1MB)
52
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
53
+ assert_equal @test_data, decompressed
54
+ end
55
+
56
+ def test_frame_with_block_size_max4mb
57
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_size: :max4MB)
58
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
59
+ assert_equal @test_data, decompressed
60
+ end
61
+
62
+ def test_frame_with_block_mode_linked
63
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_mode: :linked)
64
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
65
+ assert_equal @test_data, decompressed
66
+ end
67
+
68
+ def test_frame_with_block_mode_independent
69
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, block_mode: :independent)
70
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
71
+ assert_equal @test_data, decompressed
72
+ end
73
+
74
+ def test_frame_with_checksum
75
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, checksum: true)
76
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
77
+ assert_equal @test_data, decompressed
78
+ end
79
+
80
+ def test_frame_with_compression_levels
81
+ [0, 1, 5, 9, 12].each do |level|
82
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, compression_level: level)
83
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
84
+ assert_equal @test_data, decompressed
85
+ end
86
+ end
87
+
88
+ def test_frame_with_multiple_options
89
+ options = {
90
+ block_size: :max256KB,
91
+ block_mode: :independent,
92
+ checksum: true,
93
+ compression_level: 9
94
+ }
95
+ compressed = LZ4Native::LZ4Frame.compress_frame(@test_data, options)
96
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
97
+ assert_equal @test_data, decompressed
98
+ end
99
+
100
+ def test_high_level_compress_frame
101
+ compressed = LZ4Native.compress_frame(@test_data)
102
+ assert compressed.is_a?(String)
103
+ end
104
+
105
+ def test_high_level_decompress_frame
106
+ compressed = LZ4Native.compress_frame(@test_data)
107
+ decompressed = LZ4Native.decompress_frame(compressed)
108
+ assert_equal @test_data, decompressed
109
+ end
110
+
111
+ def test_high_level_compress_frame_with_options
112
+ compressed = LZ4Native.compress_frame(@test_data, checksum: true, compression_level: 9)
113
+ decompressed = LZ4Native.decompress_frame(compressed)
114
+ assert_equal @test_data, decompressed
115
+ end
116
+
117
+ def test_decompress_corrupt_frame
118
+ corrupt_data = "this is not a valid lz4 frame"
119
+ assert_raises(LZ4Native::FrameError) do
120
+ LZ4Native::LZ4Frame.decompress_frame(corrupt_data)
121
+ end
122
+ end
123
+
124
+ def test_empty_data
125
+ compressed = LZ4Native::LZ4Frame.compress_frame("")
126
+ decompressed = LZ4Native::LZ4Frame.decompress_frame(compressed)
127
+ assert_equal "", decompressed
128
+ end
129
+ end
@@ -0,0 +1,75 @@
1
+ require "test_helper"
2
+
3
+ class TestLZ4HC < Minitest::Test
4
+ def setup
5
+ @test_data = "Hello, World! " * 100
6
+ @large_data = "The quick brown fox jumps over the lazy dog. " * 1000
7
+ end
8
+
9
+ def test_compress_default_level
10
+ compressed = LZ4Native::LZ4HC.compress(@test_data)
11
+ assert compressed.is_a?(String)
12
+ assert compressed.bytesize < @test_data.bytesize
13
+ assert compressed.bytesize > 0
14
+
15
+ # Should be decompressible with standard LZ4
16
+ decompressed = LZ4Native::LZ4.decompress_safe(compressed, @test_data.bytesize * 2)
17
+ assert_equal @test_data, decompressed
18
+ end
19
+
20
+ def test_compress_specific_levels
21
+ [1, 5, 9, 12].each do |level|
22
+ compressed = LZ4Native::LZ4HC.compress(@test_data, level)
23
+ assert compressed.is_a?(String)
24
+ assert compressed.bytesize > 0
25
+
26
+ decompressed = LZ4Native::LZ4.decompress_safe(compressed, @test_data.bytesize * 2)
27
+ assert_equal @test_data, decompressed
28
+ end
29
+ end
30
+
31
+ def test_higher_compression_levels_produce_smaller_output
32
+ level_1 = LZ4Native::LZ4HC.compress(@large_data, 1)
33
+ level_12 = LZ4Native::LZ4HC.compress(@large_data, 12)
34
+
35
+ # Higher compression levels should generally produce smaller output
36
+ # Though this isn't guaranteed for all data, it's true for repetitive data
37
+ assert level_12.bytesize <= level_1.bytesize
38
+ end
39
+
40
+ def test_hc_vs_default_compression_ratio
41
+ default_compressed = LZ4Native::LZ4.compress_default(@large_data)
42
+ hc_compressed = LZ4Native::LZ4HC.compress(@large_data, 9)
43
+
44
+ # HC should generally compress better than default
45
+ assert hc_compressed.bytesize <= default_compressed.bytesize
46
+ end
47
+
48
+ def test_sizeof_state
49
+ size = LZ4Native::LZ4HC.sizeof_state
50
+ assert size.is_a?(Integer)
51
+ assert size > 0
52
+ end
53
+
54
+ def test_high_level_compress_hc
55
+ compressed = LZ4Native.compress_hc(@test_data)
56
+ assert compressed.is_a?(String)
57
+ decompressed = LZ4Native.decompress(compressed, @test_data.bytesize * 2)
58
+ assert_equal @test_data, decompressed
59
+ end
60
+
61
+ def test_high_level_compress_hc_with_level
62
+ compressed = LZ4Native.compress_hc(@test_data, 12)
63
+ assert compressed.is_a?(String)
64
+ decompressed = LZ4Native.decompress(compressed, @test_data.bytesize * 2)
65
+ assert_equal @test_data, decompressed
66
+ end
67
+
68
+ def test_constants
69
+ assert LZ4Native::LZ4HC::CLEVEL_MIN.is_a?(Integer)
70
+ assert LZ4Native::LZ4HC::CLEVEL_DEFAULT.is_a?(Integer)
71
+ assert LZ4Native::LZ4HC::CLEVEL_MAX.is_a?(Integer)
72
+ assert LZ4Native::LZ4HC::CLEVEL_MIN < LZ4Native::LZ4HC::CLEVEL_DEFAULT
73
+ assert LZ4Native::LZ4HC::CLEVEL_DEFAULT < LZ4Native::LZ4HC::CLEVEL_MAX
74
+ end
75
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lz4-native-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Greninger
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-02 00:00:00.000000000 Z
11
+ date: 2025-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -25,74 +25,81 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '13.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake-compiler
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.2'
33
+ version: '5.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.2'
40
+ version: '5.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: rake-compiler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '5.0'
55
- description: Fast LZ4 compression and decompression with file support
54
+ version: '1.0'
55
+ description: Complete Ruby bindings for LZ4 compression library, including lz4.h,
56
+ lz4hc.h, and lz4frame.h interfaces. Bundles the official LZ4 source code.
56
57
  email:
57
- - v-greningerj@microsoft.com
58
+ - jgreninger@hotmail.com
58
59
  executables: []
59
60
  extensions:
60
- - ext/lz4/extconf.rb
61
+ - ext/lz4_native/extconf.rb
61
62
  extra_rdoc_files: []
62
63
  files:
64
+ - CLAUDE.md
63
65
  - LICENSE
64
66
  - README.md
65
- - ext/lz4/extconf.rb
66
- - ext/lz4/lz4_ext.c
67
- - lib/lz4.rb
68
- - lib/lz4/lz4_ext.so
69
- - lib/lz4/version.rb
70
- - vendor/lz4/lib/LICENSE
71
- - vendor/lz4/lib/Makefile
72
- - vendor/lz4/lib/README.md
73
- - vendor/lz4/lib/dll/example/Makefile
74
- - vendor/lz4/lib/dll/example/README.md
75
- - vendor/lz4/lib/dll/example/fullbench-dll.sln
76
- - vendor/lz4/lib/dll/example/fullbench-dll.vcxproj
77
- - vendor/lz4/lib/liblz4-dll.rc.in
78
- - vendor/lz4/lib/liblz4.pc.in
79
- - vendor/lz4/lib/lz4.c
80
- - vendor/lz4/lib/lz4.h
81
- - vendor/lz4/lib/lz4file.c
82
- - vendor/lz4/lib/lz4file.h
83
- - vendor/lz4/lib/lz4frame.c
84
- - vendor/lz4/lib/lz4frame.h
85
- - vendor/lz4/lib/lz4frame_static.h
86
- - vendor/lz4/lib/lz4hc.c
87
- - vendor/lz4/lib/lz4hc.h
88
- - vendor/lz4/lib/xxhash.c
89
- - vendor/lz4/lib/xxhash.h
90
- homepage: https://github.com/jpgreninger/lz4-ruby
67
+ - ext/lz4_native/LICENSE
68
+ - ext/lz4_native/Makefile
69
+ - ext/lz4_native/README.md
70
+ - ext/lz4_native/dll/example/Makefile
71
+ - ext/lz4_native/dll/example/README.md
72
+ - ext/lz4_native/dll/example/fullbench-dll.sln
73
+ - ext/lz4_native/dll/example/fullbench-dll.vcxproj
74
+ - ext/lz4_native/extconf.rb
75
+ - ext/lz4_native/liblz4-dll.rc.in
76
+ - ext/lz4_native/liblz4.pc.in
77
+ - ext/lz4_native/lz4.c
78
+ - ext/lz4_native/lz4.h
79
+ - ext/lz4_native/lz4_native.c
80
+ - ext/lz4_native/lz4file.c
81
+ - ext/lz4_native/lz4file.h
82
+ - ext/lz4_native/lz4frame.c
83
+ - ext/lz4_native/lz4frame.h
84
+ - ext/lz4_native/lz4frame_static.h
85
+ - ext/lz4_native/lz4hc.c
86
+ - ext/lz4_native/lz4hc.h
87
+ - ext/lz4_native/xxhash.c
88
+ - ext/lz4_native/xxhash.h
89
+ - lib/lz4_native.rb
90
+ - lib/lz4_native/lz4_native.so
91
+ - lib/lz4_native/version.rb
92
+ - test/test_helper.rb
93
+ - test/test_lz4_basic.rb
94
+ - test/test_lz4frame.rb
95
+ - test/test_lz4hc.rb
96
+ homepage: https://github.com/jgreninger/lz4-native-ruby
91
97
  licenses:
92
- - MIT
98
+ - BSD-2-Clause
93
99
  metadata:
94
- homepage_uri: https://github.com/jpgreninger/lz4-ruby
95
- source_code_uri: https://github.com/jpgreninger/lz4-ruby
100
+ homepage_uri: https://github.com/jgreninger/lz4-native-ruby
101
+ source_code_uri: https://github.com/jgreninger/lz4-native-ruby
102
+ changelog_uri: https://github.com/jgreninger/lz4-native-ruby/blob/master/CHANGELOG.md
96
103
  post_install_message:
97
104
  rdoc_options: []
98
105
  require_paths:
@@ -101,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
108
  requirements:
102
109
  - - ">="
103
110
  - !ruby/object:Gem::Version
104
- version: 2.6.0
111
+ version: 2.5.0
105
112
  required_rubygems_version: !ruby/object:Gem::Requirement
106
113
  requirements:
107
114
  - - ">="
@@ -111,5 +118,5 @@ requirements: []
111
118
  rubygems_version: 3.4.20
112
119
  signing_key:
113
120
  specification_version: 4
114
- summary: Ruby bindings for LZ4 compression library
121
+ summary: Native LZ4 compression bindings for Ruby
115
122
  test_files: []
data/ext/lz4/extconf.rb DELETED
@@ -1,12 +0,0 @@
1
- require "mkmf"
2
-
3
- # Add LZ4 library source directory to include path
4
- $INCFLAGS << " -I$(srcdir)/../../vendor/lz4/lib"
5
-
6
- # Add LZ4 source directory to VPATH so make can find the source files
7
- $VPATH << "$(srcdir)/../../vendor/lz4/lib"
8
-
9
- # Add LZ4 source files to be compiled with just the base filenames
10
- $srcs = ["lz4_ext.c", "lz4.c", "lz4hc.c"]
11
-
12
- create_makefile("lz4/lz4_ext")
data/ext/lz4/lz4_ext.c DELETED
@@ -1,230 +0,0 @@
1
- #include <ruby.h>
2
- #include "lz4.h"
3
- #include "lz4hc.h"
4
- #include <stdio.h>
5
- #include <stdlib.h>
6
- #include <string.h>
7
-
8
- static VALUE mLZ4;
9
-
10
- #define CHUNK_SIZE (16 * 1024) // 16KB chunks
11
-
12
- /*
13
- * Compress a file using LZ4
14
- *
15
- * @param input_path [String] Path to input file
16
- * @param output_path [String] Path to output file
17
- * @param compression_level [Integer] Compression level (1-12)
18
- * @return [Boolean] true on success
19
- */
20
- static VALUE lz4_compress_file(VALUE self, VALUE input_path, VALUE output_path, VALUE compression_level) {
21
- Check_Type(input_path, T_STRING);
22
- Check_Type(output_path, T_STRING);
23
- Check_Type(compression_level, T_FIXNUM);
24
-
25
- const char* input_filename = StringValueCStr(input_path);
26
- const char* output_filename = StringValueCStr(output_path);
27
- int level = FIX2INT(compression_level);
28
-
29
- // Validate compression level
30
- if (level < 1 || level > LZ4HC_CLEVEL_MAX) {
31
- rb_raise(rb_eArgError, "Compression level must be between 1 and %d", LZ4HC_CLEVEL_MAX);
32
- }
33
-
34
- FILE* input_file = fopen(input_filename, "rb");
35
- if (!input_file) {
36
- rb_raise(rb_eIOError, "Cannot open input file: %s", input_filename);
37
- }
38
-
39
- FILE* output_file = fopen(output_filename, "wb");
40
- if (!output_file) {
41
- fclose(input_file);
42
- rb_raise(rb_eIOError, "Cannot open output file: %s", output_filename);
43
- }
44
-
45
- // Get file size
46
- fseek(input_file, 0, SEEK_END);
47
- size_t input_size = ftell(input_file);
48
- fseek(input_file, 0, SEEK_SET);
49
-
50
- // Write uncompressed size as header (8 bytes for size_t)
51
- fwrite(&input_size, sizeof(size_t), 1, output_file);
52
-
53
- // Allocate buffers
54
- char* input_buffer = (char*)malloc(CHUNK_SIZE);
55
- int max_compressed_size = LZ4_compressBound(CHUNK_SIZE);
56
- char* output_buffer = (char*)malloc(max_compressed_size);
57
-
58
- if (!input_buffer || !output_buffer) {
59
- free(input_buffer);
60
- free(output_buffer);
61
- fclose(input_file);
62
- fclose(output_file);
63
- rb_raise(rb_eNoMemError, "Failed to allocate memory");
64
- }
65
-
66
- // Compress file in chunks
67
- size_t bytes_read;
68
- while ((bytes_read = fread(input_buffer, 1, CHUNK_SIZE, input_file)) > 0) {
69
- int compressed_size;
70
-
71
- if (level >= LZ4HC_CLEVEL_MIN) {
72
- compressed_size = LZ4_compress_HC(input_buffer, output_buffer, bytes_read, max_compressed_size, level);
73
- } else {
74
- compressed_size = LZ4_compress_default(input_buffer, output_buffer, bytes_read, max_compressed_size);
75
- }
76
-
77
- if (compressed_size <= 0) {
78
- free(input_buffer);
79
- free(output_buffer);
80
- fclose(input_file);
81
- fclose(output_file);
82
- rb_raise(rb_eRuntimeError, "Compression failed");
83
- }
84
-
85
- // Write chunk header: original size and compressed size
86
- uint32_t chunk_original = (uint32_t)bytes_read;
87
- uint32_t chunk_compressed = (uint32_t)compressed_size;
88
- fwrite(&chunk_original, sizeof(uint32_t), 1, output_file);
89
- fwrite(&chunk_compressed, sizeof(uint32_t), 1, output_file);
90
- fwrite(output_buffer, 1, compressed_size, output_file);
91
- }
92
-
93
- free(input_buffer);
94
- free(output_buffer);
95
- fclose(input_file);
96
- fclose(output_file);
97
-
98
- return Qtrue;
99
- }
100
-
101
- /*
102
- * Decompress a file using LZ4
103
- *
104
- * @param input_path [String] Path to compressed input file
105
- * @param output_path [String] Path to output file
106
- * @return [Boolean] true on success
107
- */
108
- static VALUE lz4_decompress_file(VALUE self, VALUE input_path, VALUE output_path) {
109
- Check_Type(input_path, T_STRING);
110
- Check_Type(output_path, T_STRING);
111
-
112
- const char* input_filename = StringValueCStr(input_path);
113
- const char* output_filename = StringValueCStr(output_path);
114
-
115
- FILE* input_file = fopen(input_filename, "rb");
116
- if (!input_file) {
117
- rb_raise(rb_eIOError, "Cannot open input file: %s", input_filename);
118
- }
119
-
120
- FILE* output_file = fopen(output_filename, "wb");
121
- if (!output_file) {
122
- fclose(input_file);
123
- rb_raise(rb_eIOError, "Cannot open output file: %s", output_filename);
124
- }
125
-
126
- // Read uncompressed size header
127
- size_t total_size;
128
- if (fread(&total_size, sizeof(size_t), 1, input_file) != 1) {
129
- fclose(input_file);
130
- fclose(output_file);
131
- rb_raise(rb_eRuntimeError, "Failed to read file header");
132
- }
133
-
134
- // Allocate buffers
135
- char* input_buffer = (char*)malloc(LZ4_compressBound(CHUNK_SIZE));
136
- char* output_buffer = (char*)malloc(CHUNK_SIZE);
137
-
138
- if (!input_buffer || !output_buffer) {
139
- free(input_buffer);
140
- free(output_buffer);
141
- fclose(input_file);
142
- fclose(output_file);
143
- rb_raise(rb_eNoMemError, "Failed to allocate memory");
144
- }
145
-
146
- // Decompress file in chunks
147
- while (!feof(input_file)) {
148
- uint32_t chunk_original, chunk_compressed;
149
-
150
- // Read chunk header
151
- if (fread(&chunk_original, sizeof(uint32_t), 1, input_file) != 1) {
152
- break; // End of file
153
- }
154
- if (fread(&chunk_compressed, sizeof(uint32_t), 1, input_file) != 1) {
155
- free(input_buffer);
156
- free(output_buffer);
157
- fclose(input_file);
158
- fclose(output_file);
159
- rb_raise(rb_eRuntimeError, "Failed to read chunk header");
160
- }
161
-
162
- // Read compressed data
163
- if (fread(input_buffer, 1, chunk_compressed, input_file) != chunk_compressed) {
164
- free(input_buffer);
165
- free(output_buffer);
166
- fclose(input_file);
167
- fclose(output_file);
168
- rb_raise(rb_eRuntimeError, "Failed to read compressed data");
169
- }
170
-
171
- // Decompress
172
- int decompressed_size = LZ4_decompress_safe(input_buffer, output_buffer, chunk_compressed, chunk_original);
173
-
174
- if (decompressed_size < 0) {
175
- free(input_buffer);
176
- free(output_buffer);
177
- fclose(input_file);
178
- fclose(output_file);
179
- rb_raise(rb_eRuntimeError, "Decompression failed");
180
- }
181
-
182
- if (decompressed_size != chunk_original) {
183
- free(input_buffer);
184
- free(output_buffer);
185
- fclose(input_file);
186
- fclose(output_file);
187
- rb_raise(rb_eRuntimeError, "Decompressed size mismatch");
188
- }
189
-
190
- // Write decompressed data
191
- fwrite(output_buffer, 1, decompressed_size, output_file);
192
- }
193
-
194
- free(input_buffer);
195
- free(output_buffer);
196
- fclose(input_file);
197
- fclose(output_file);
198
-
199
- return Qtrue;
200
- }
201
-
202
- /*
203
- * Get LZ4 version string
204
- *
205
- * @return [String] LZ4 version
206
- */
207
- static VALUE lz4_version(VALUE self) {
208
- int version = LZ4_versionNumber();
209
- int major = version / 10000;
210
- int minor = (version % 10000) / 100;
211
- int patch = version % 100;
212
-
213
- char version_str[32];
214
- snprintf(version_str, sizeof(version_str), "%d.%d.%d", major, minor, patch);
215
-
216
- return rb_str_new_cstr(version_str);
217
- }
218
-
219
- void Init_lz4_ext(void) {
220
- mLZ4 = rb_define_module("LZ4");
221
-
222
- rb_define_singleton_method(mLZ4, "compress_file", lz4_compress_file, 3);
223
- rb_define_singleton_method(mLZ4, "decompress_file", lz4_decompress_file, 2);
224
- rb_define_singleton_method(mLZ4, "version", lz4_version, 0);
225
-
226
- // Define constants
227
- rb_define_const(mLZ4, "MIN_COMPRESSION_LEVEL", INT2FIX(1));
228
- rb_define_const(mLZ4, "MAX_COMPRESSION_LEVEL", INT2FIX(LZ4HC_CLEVEL_MAX));
229
- rb_define_const(mLZ4, "DEFAULT_COMPRESSION_LEVEL", INT2FIX(9));
230
- }
data/lib/lz4/lz4_ext.so DELETED
Binary file
data/lib/lz4/version.rb DELETED
@@ -1,3 +0,0 @@
1
- module LZ4
2
- VERSION = "1.0.0"
3
- end
data/lib/lz4.rb DELETED
@@ -1,60 +0,0 @@
1
- require "lz4/lz4_ext"
2
- require "lz4/version"
3
-
4
- module LZ4
5
- class Error < StandardError; end
6
- class CompressionError < Error; end
7
- class DecompressionError < Error; end
8
-
9
- class << self
10
- # Save references to the C extension methods
11
- alias_method :_compress_file_ext, :compress_file
12
- alias_method :_decompress_file_ext, :decompress_file
13
-
14
- # Compress a file using LZ4
15
- #
16
- # @param input_path [String] Path to the input file
17
- # @param output_path [String] Path to the output file
18
- # @param compression_level [Integer] Compression level (1-12), default: 9
19
- # @return [Boolean] true on success
20
- # @raise [ArgumentError] if compression level is invalid
21
- # @raise [IOError] if file operations fail
22
- # @raise [CompressionError] if compression fails
23
- def compress_file(input_path, output_path, compression_level: DEFAULT_COMPRESSION_LEVEL)
24
- raise ArgumentError, "input_path must be a String" unless input_path.is_a?(String)
25
- raise ArgumentError, "output_path must be a String" unless output_path.is_a?(String)
26
- raise ArgumentError, "Input file does not exist: #{input_path}" unless File.exist?(input_path)
27
-
28
- compression_level = compression_level.to_i
29
-
30
- begin
31
- _compress_file_ext(input_path, output_path, compression_level)
32
- rescue ArgumentError
33
- # Let ArgumentError pass through (e.g., invalid compression level)
34
- raise
35
- rescue => e
36
- raise CompressionError, "Failed to compress file: #{e.message}"
37
- end
38
- end
39
-
40
- # Decompress a file using LZ4
41
- #
42
- # @param input_path [String] Path to the compressed input file
43
- # @param output_path [String] Path to the output file
44
- # @return [Boolean] true on success
45
- # @raise [ArgumentError] if arguments are invalid
46
- # @raise [IOError] if file operations fail
47
- # @raise [DecompressionError] if decompression fails
48
- def decompress_file(input_path, output_path)
49
- raise ArgumentError, "input_path must be a String" unless input_path.is_a?(String)
50
- raise ArgumentError, "output_path must be a String" unless output_path.is_a?(String)
51
- raise ArgumentError, "Input file does not exist: #{input_path}" unless File.exist?(input_path)
52
-
53
- begin
54
- _decompress_file_ext(input_path, output_path)
55
- rescue => e
56
- raise DecompressionError, "Failed to decompress file: #{e.message}"
57
- end
58
- end
59
- end
60
- end