compress-bsc 1.0.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 (58) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +2 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +192 -0
  6. data/README.md +279 -0
  7. data/Rakefile +96 -0
  8. data/bin/rbsc +306 -0
  9. data/certs/djberg96_pub.pem +26 -0
  10. data/compress-bsc.gemspec +45 -0
  11. data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc.png +0 -0
  12. data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  13. data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_both.png +0 -0
  14. data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc.png +0 -0
  15. data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  16. data/coverage/assets/0.13.2/application.css +1 -0
  17. data/coverage/assets/0.13.2/application.js +7 -0
  18. data/coverage/assets/0.13.2/colorbox/border.png +0 -0
  19. data/coverage/assets/0.13.2/colorbox/controls.png +0 -0
  20. data/coverage/assets/0.13.2/colorbox/loading.gif +0 -0
  21. data/coverage/assets/0.13.2/colorbox/loading_background.png +0 -0
  22. data/coverage/assets/0.13.2/favicon_green.png +0 -0
  23. data/coverage/assets/0.13.2/favicon_red.png +0 -0
  24. data/coverage/assets/0.13.2/favicon_yellow.png +0 -0
  25. data/coverage/assets/0.13.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  26. data/coverage/assets/0.13.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  27. data/coverage/assets/0.13.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  28. data/coverage/assets/0.13.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  29. data/coverage/assets/0.13.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  30. data/coverage/assets/0.13.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  31. data/coverage/assets/0.13.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  32. data/coverage/assets/0.13.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  33. data/coverage/assets/0.13.2/images/ui-icons_222222_256x240.png +0 -0
  34. data/coverage/assets/0.13.2/images/ui-icons_2e83ff_256x240.png +0 -0
  35. data/coverage/assets/0.13.2/images/ui-icons_454545_256x240.png +0 -0
  36. data/coverage/assets/0.13.2/images/ui-icons_888888_256x240.png +0 -0
  37. data/coverage/assets/0.13.2/images/ui-icons_cd0a0a_256x240.png +0 -0
  38. data/coverage/assets/0.13.2/loading.gif +0 -0
  39. data/coverage/assets/0.13.2/magnify.png +0 -0
  40. data/coverage/index.html +4779 -0
  41. data/examples/usage_example.rb +215 -0
  42. data/lib/compress/bsc/compressor.rb +81 -0
  43. data/lib/compress/bsc/decompressor.rb +159 -0
  44. data/lib/compress/bsc/error.rb +18 -0
  45. data/lib/compress/bsc/library.rb +100 -0
  46. data/lib/compress/bsc/version.rb +5 -0
  47. data/lib/compress/bsc.rb +26 -0
  48. data/lib/compress-bsc.rb +5 -0
  49. data/spec/compressor_spec.rb +124 -0
  50. data/spec/decompressor_spec.rb +135 -0
  51. data/spec/error_spec.rb +63 -0
  52. data/spec/examples.txt +60 -0
  53. data/spec/ffi_bsc_spec.rb +101 -0
  54. data/spec/library_spec.rb +97 -0
  55. data/spec/spec_helper.rb +53 -0
  56. data.tar.gz.sig +0 -0
  57. metadata +232 -0
  58. metadata.gz.sig +0 -0
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/compress/bsc'
4
+
5
+ # Example usage of compress-bsc
6
+ def main
7
+ puts "compress-bsc Example Usage"
8
+ puts "=" * 40
9
+
10
+ begin
11
+ # Initialize the BSC library
12
+ puts "Initializing BSC library..."
13
+ bsc = Compress::BSC.new
14
+ puts "✓ BSC library initialized successfully"
15
+
16
+ # Initialize with custom features
17
+ puts "\nInitializing BSC library with custom features..."
18
+ bsc_custom = Compress::BSC.new(features: Compress::BSC::Library::LIBBSC_FEATURE_FASTMODE)
19
+ puts "✓ BSC library with custom features initialized successfully"
20
+
21
+ # Initialize with multiple features
22
+ puts "\nInitializing BSC library with multiple features..."
23
+ bsc_multi = Compress::BSC.new(
24
+ features: Compress::BSC::Library::LIBBSC_FEATURE_FASTMODE |
25
+ Compress::BSC::Library::LIBBSC_FEATURE_MULTITHREADING |
26
+ Compress::BSC::Library::LIBBSC_FEATURE_LARGEPAGES
27
+ )
28
+ puts "✓ BSC library with multiple features initialized successfully"
29
+
30
+ # Test data
31
+ original_data = generate_test_data
32
+ puts "\nOriginal data size: #{original_data.bytesize} bytes"
33
+
34
+ # Basic compression/decompression
35
+ puts "\n1. Basic Compression Test"
36
+ puts "-" * 30
37
+
38
+ compressed = bsc.compress(original_data)
39
+ puts "Compressed size: #{compressed.bytesize} bytes"
40
+
41
+ ratio = original_data.bytesize.to_f / compressed.bytesize
42
+ puts "Compression ratio: #{ratio.round(2)}:1"
43
+
44
+ decompressed = bsc.decompress(compressed)
45
+ puts "Decompression successful: #{original_data == decompressed}"
46
+
47
+ # Advanced compression with custom settings
48
+ puts "\n2. Advanced Compression Test"
49
+ puts "-" * 30
50
+
51
+ compressor = Compress::BSC::Compressor.new(
52
+ block_sorter: Compress::BSC::Library::LIBBSC_BLOCKSORTER_BWT,
53
+ coder: Compress::BSC::Library::LIBBSC_CODER_QLFC_ADAPTIVE,
54
+ features: Compress::BSC::Library::LIBBSC_FEATURE_FASTMODE |
55
+ Compress::BSC::Library::LIBBSC_FEATURE_MULTITHREADING
56
+ )
57
+
58
+ compressed_advanced = compressor.compress(original_data)
59
+ puts "Advanced compressed size: #{compressed_advanced.bytesize} bytes"
60
+
61
+ ratio_advanced = original_data.bytesize.to_f / compressed_advanced.bytesize
62
+ puts "Advanced compression ratio: #{ratio_advanced.round(2)}:1"
63
+
64
+ decompressor = Compress::BSC::Decompressor.new
65
+ decompressed_advanced = decompressor.decompress(compressed_advanced)
66
+ puts "Advanced decompression successful: #{original_data == decompressed_advanced}"
67
+
68
+ # LZP preprocessing test
69
+ puts "\n3. LZP Preprocessing Test"
70
+ puts "-" * 30
71
+
72
+ lzp_compressor = Compress::BSC::Compressor.new(
73
+ lzp_hash_size: Compress::BSC::Library::LIBBSC_DEFAULT_LZPHASHSIZE,
74
+ lzp_min_len: Compress::BSC::Library::LIBBSC_DEFAULT_LZPMINLEN,
75
+ block_sorter: Compress::BSC::Library::LIBBSC_BLOCKSORTER_BWT,
76
+ coder: Compress::BSC::Library::LIBBSC_CODER_QLFC_STATIC
77
+ )
78
+
79
+ compressed_lzp = lzp_compressor.compress(original_data)
80
+ puts "LZP compressed size: #{compressed_lzp.bytesize} bytes"
81
+
82
+ ratio_lzp = original_data.bytesize.to_f / compressed_lzp.bytesize
83
+ puts "LZP compression ratio: #{ratio_lzp.round(2)}:1"
84
+
85
+ decompressed_lzp = decompressor.decompress(compressed_lzp)
86
+ puts "LZP decompression successful: #{original_data == decompressed_lzp}"
87
+
88
+ # Block info test
89
+ puts "\n4. Block Information Test"
90
+ puts "-" * 30
91
+
92
+ info = Compress::BSC::Decompressor.block_info(compressed)
93
+ puts "Block size: #{info[:block_size]} bytes"
94
+ puts "Data size: #{info[:data_size]} bytes"
95
+ puts "Header overhead: #{info[:block_size] - info[:data_size]} bytes"
96
+
97
+ # File compression test
98
+ puts "\n5. File Compression Test"
99
+ puts "-" * 30
100
+
101
+ test_file_compression
102
+
103
+ # Performance comparison
104
+ puts "\n6. Performance Comparison"
105
+ puts "-" * 30
106
+
107
+ performance_test(original_data)
108
+
109
+ puts "\n✓ All tests completed successfully!"
110
+
111
+ rescue Compress::BSC::Error => e
112
+ puts "❌ BSC Error: #{e.error_name} (#{e.code})"
113
+ puts e.message
114
+ rescue LoadError => e
115
+ puts "❌ Library not found: #{e.message}"
116
+ puts "Please install libbsc library"
117
+ rescue => e
118
+ puts "❌ Unexpected error: #{e.message}"
119
+ puts e.backtrace.first(3)
120
+ end
121
+ end
122
+
123
+ def generate_test_data
124
+ # Generate mixed test data for better compression testing
125
+ text_data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " * 200
126
+ repetitive_data = "ABCDEFGH" * 500
127
+ random_data = Random.bytes(1000)
128
+ binary_data = (0..255).to_a.pack('C*') * 10
129
+
130
+ text_data + repetitive_data + random_data + binary_data
131
+ end
132
+
133
+ def test_file_compression
134
+ test_content = generate_test_data
135
+ input_file = 'example_input.txt'
136
+ compressed_file = 'example_output.bsc'
137
+ decompressed_file = 'example_restored.txt'
138
+
139
+ begin
140
+ # Write test file
141
+ File.binwrite(input_file, test_content)
142
+
143
+ # Compress file
144
+ compressor = Compress::BSC::Compressor.new
145
+ compressed_size = compressor.compress_file(input_file, compressed_file)
146
+ puts "File compressed: #{File.size(input_file)} → #{compressed_size} bytes"
147
+
148
+ # Decompress file
149
+ decompressor = Compress::BSC::Decompressor.new
150
+ decompressed_size = decompressor.decompress_file(compressed_file, decompressed_file)
151
+ puts "File decompressed: #{compressed_size} → #{decompressed_size} bytes"
152
+
153
+ # Verify integrity
154
+ original_content = File.binread(input_file)
155
+ restored_content = File.binread(decompressed_file)
156
+ puts "File integrity verified: #{original_content == restored_content}"
157
+
158
+ ensure
159
+ # Clean up
160
+ [input_file, compressed_file, decompressed_file].each do |file|
161
+ File.delete(file) if File.exist?(file)
162
+ end
163
+ end
164
+ end
165
+
166
+ def performance_test(data)
167
+ require 'benchmark'
168
+
169
+ puts "Testing with #{data.bytesize} bytes of data..."
170
+
171
+ results = Benchmark.bm(20) do |x|
172
+ compressed_basic = nil
173
+ compressed_adaptive = nil
174
+ compressed_fast = nil
175
+ compressed_lzp = nil
176
+
177
+ x.report("Basic compression:") do
178
+ compressor = Compress::BSC::Compressor.new
179
+ compressed_basic = compressor.compress(data)
180
+ end
181
+
182
+ x.report("Adaptive coder:") do
183
+ compressor = Compress::BSC::Compressor.new(
184
+ coder: Compress::BSC::Library::LIBBSC_CODER_QLFC_ADAPTIVE
185
+ )
186
+ compressed_adaptive = compressor.compress(data)
187
+ end
188
+
189
+ x.report("Fast coder:") do
190
+ compressor = Compress::BSC::Compressor.new(
191
+ coder: Compress::BSC::Library::LIBBSC_CODER_QLFC_FAST
192
+ )
193
+ compressed_fast = compressor.compress(data)
194
+ end
195
+
196
+ x.report("With LZP:") do
197
+ compressor = Compress::BSC::Compressor.new(
198
+ lzp_hash_size: 15,
199
+ lzp_min_len: 128
200
+ )
201
+ compressed_lzp = compressor.compress(data)
202
+ end
203
+
204
+ # Show compression ratios
205
+ puts "\nCompression Ratios:"
206
+ puts " Basic: #{(data.bytesize.to_f / compressed_basic.bytesize).round(2)}:1" if compressed_basic
207
+ puts " Adaptive: #{(data.bytesize.to_f / compressed_adaptive.bytesize).round(2)}:1" if compressed_adaptive
208
+ puts " Fast: #{(data.bytesize.to_f / compressed_fast.bytesize).round(2)}:1" if compressed_fast
209
+ puts " LZP: #{(data.bytesize.to_f / compressed_lzp.bytesize).round(2)}:1" if compressed_lzp
210
+ end
211
+ end
212
+
213
+ if __FILE__ == $0
214
+ main
215
+ end
@@ -0,0 +1,81 @@
1
+ module Compress
2
+ class BSC
3
+ class Compressor
4
+ attr_reader :lzp_hash_size, :lzp_min_len, :block_sorter, :coder, :features
5
+
6
+ def initialize(options = {})
7
+ @lzp_hash_size = options[:lzp_hash_size] || 0 # Disable LZP by default
8
+ @lzp_min_len = options[:lzp_min_len] || 0 # Disable LZP by default
9
+ @block_sorter = options[:block_sorter] || Library::LIBBSC_DEFAULT_BLOCKSORTER
10
+ @coder = options[:coder] || Library::LIBBSC_DEFAULT_CODER
11
+ @features = options[:features] || Library::LIBBSC_DEFAULT_FEATURES
12
+ end
13
+
14
+ def compress(input_data)
15
+ raise ArgumentError, "Input data cannot be nil" if input_data.nil?
16
+ raise ArgumentError, "Input data must be a string" unless input_data.is_a?(String)
17
+
18
+ return input_data if input_data.empty?
19
+
20
+ # Store original encoding for later restoration
21
+ original_encoding = input_data.encoding
22
+
23
+ # Convert to binary for compression (make a copy first!)
24
+ binary_data = input_data.dup.force_encoding(Encoding::BINARY)
25
+ input_size = binary_data.bytesize
26
+
27
+ # Calculate maximum possible output size (worst case)
28
+ # BSC adds a header, so we need at least input_size + HEADER_SIZE
29
+ max_output_size = input_size + Library::LIBBSC_HEADER_SIZE + 1024 # Add some buffer
30
+
31
+ # Allocate input and output buffers
32
+ input_ptr = FFI::MemoryPointer.new(:char, input_size)
33
+ input_ptr.put_bytes(0, binary_data)
34
+
35
+ output_ptr = FFI::MemoryPointer.new(:char, max_output_size)
36
+
37
+ begin
38
+ # Perform compression
39
+ result = Library.bsc_compress(
40
+ input_ptr,
41
+ output_ptr,
42
+ input_size,
43
+ @lzp_hash_size,
44
+ @lzp_min_len,
45
+ @block_sorter,
46
+ @coder,
47
+ @features
48
+ )
49
+
50
+ if result == Library::LIBBSC_NOT_COMPRESSIBLE
51
+ # Return original data if not compressible
52
+ return input_data
53
+ end
54
+
55
+ Error.check_result(result)
56
+
57
+ # Extract compressed data and add encoding marker
58
+ compressed_data = output_ptr.get_bytes(0, result)
59
+
60
+ # Store original encoding in the compressed data metadata
61
+ # We'll use a simple approach: prepend encoding name as a header
62
+ encoding_name = original_encoding.name
63
+ encoding_header = [encoding_name.bytesize, encoding_name].pack("Ca*")
64
+
65
+ # Return with encoding header
66
+ (encoding_header + compressed_data).force_encoding(Encoding::BINARY)
67
+ ensure
68
+ input_ptr.free if input_ptr
69
+ output_ptr.free if output_ptr
70
+ end
71
+ end
72
+
73
+ def compress_file(input_path, output_path)
74
+ input_data = File.binread(input_path)
75
+ compressed_data = compress(input_data)
76
+ File.binwrite(output_path, compressed_data)
77
+ compressed_data.bytesize
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,159 @@
1
+ module Compress
2
+ class BSC
3
+ class Decompressor
4
+ attr_reader :features
5
+
6
+ def initialize(options = {})
7
+ @features = options[:features] || Library::LIBBSC_DEFAULT_FEATURES
8
+ end
9
+
10
+ def decompress(compressed_data)
11
+ raise ArgumentError, "Compressed data cannot be nil" if compressed_data.nil?
12
+ raise ArgumentError, "Compressed data must be a string" unless compressed_data.is_a?(String)
13
+
14
+ return compressed_data if compressed_data.empty?
15
+
16
+ # Extract encoding information from header
17
+ encoding_name = nil
18
+ actual_compressed_data = compressed_data
19
+
20
+ if compressed_data.bytesize > 1
21
+ # Try to extract encoding header
22
+ encoding_name_length = compressed_data.bytes[0]
23
+ if encoding_name_length > 0 && encoding_name_length < 50 && compressed_data.bytesize > encoding_name_length + 1
24
+ encoding_name = compressed_data[1, encoding_name_length]
25
+ actual_compressed_data = compressed_data[(encoding_name_length + 1)..-1]
26
+ end
27
+ end
28
+
29
+ input_size = actual_compressed_data.bytesize
30
+
31
+ # Need at least header size for the BSC data
32
+ if input_size < Library::LIBBSC_HEADER_SIZE
33
+ raise Error.new(Library::LIBBSC_DATA_CORRUPT)
34
+ end
35
+
36
+ # Allocate input buffer
37
+ input_ptr = FFI::MemoryPointer.new(:char, input_size)
38
+ input_ptr.put_bytes(0, actual_compressed_data)
39
+
40
+ # Get block info to determine output size
41
+ block_size_ptr = FFI::MemoryPointer.new(:int)
42
+ data_size_ptr = FFI::MemoryPointer.new(:int)
43
+
44
+ result = Library.bsc_block_info(
45
+ input_ptr,
46
+ input_size,
47
+ block_size_ptr,
48
+ data_size_ptr,
49
+ @features
50
+ )
51
+
52
+ Error.check_result(result)
53
+
54
+ block_size = block_size_ptr.read_int
55
+ data_size = data_size_ptr.read_int
56
+
57
+ # Validate sizes
58
+ if input_size < block_size || data_size <= 0
59
+ raise Error.new(Library::LIBBSC_DATA_CORRUPT)
60
+ end
61
+
62
+ # Allocate output buffer
63
+ output_ptr = FFI::MemoryPointer.new(:char, data_size)
64
+
65
+ begin
66
+ # Perform decompression
67
+ result = Library.bsc_decompress(
68
+ input_ptr,
69
+ input_size,
70
+ output_ptr,
71
+ data_size,
72
+ @features
73
+ )
74
+
75
+ Error.check_result(result)
76
+
77
+ # Extract decompressed data
78
+ decompressed_data = output_ptr.get_bytes(0, data_size)
79
+
80
+ # Restore original encoding if available
81
+ if encoding_name && !encoding_name.empty?
82
+ begin
83
+ target_encoding = Encoding.find(encoding_name)
84
+ decompressed_data.force_encoding(target_encoding)
85
+ rescue ArgumentError
86
+ # If encoding is not found, keep as binary
87
+ decompressed_data.force_encoding(Encoding::BINARY)
88
+ end
89
+ else
90
+ decompressed_data.force_encoding(Encoding::BINARY)
91
+ end
92
+
93
+ decompressed_data
94
+ ensure
95
+ input_ptr.free if input_ptr
96
+ output_ptr.free if output_ptr
97
+ block_size_ptr.free if block_size_ptr
98
+ data_size_ptr.free if data_size_ptr
99
+ end
100
+ end
101
+
102
+ def decompress_file(input_path, output_path)
103
+ compressed_data = File.binread(input_path)
104
+ decompressed_data = decompress(compressed_data)
105
+ File.binwrite(output_path, decompressed_data)
106
+ decompressed_data.bytesize
107
+ end
108
+
109
+ def self.block_info(compressed_data, features = Library::LIBBSC_DEFAULT_FEATURES)
110
+ raise ArgumentError, "Compressed data cannot be nil" if compressed_data.nil?
111
+ raise ArgumentError, "Compressed data must be a string" unless compressed_data.is_a?(String)
112
+
113
+ # Extract actual compressed data, skipping encoding header if present
114
+ actual_compressed_data = compressed_data
115
+
116
+ if compressed_data.bytesize > 1
117
+ # Try to extract encoding header
118
+ encoding_name_length = compressed_data.bytes[0]
119
+ if encoding_name_length > 0 && encoding_name_length < 50 && compressed_data.bytesize > encoding_name_length + 1
120
+ actual_compressed_data = compressed_data[(encoding_name_length + 1)..-1]
121
+ end
122
+ end
123
+
124
+ input_size = actual_compressed_data.bytesize
125
+
126
+ if input_size < Library::LIBBSC_HEADER_SIZE
127
+ raise Error.new(Library::LIBBSC_DATA_CORRUPT)
128
+ end
129
+
130
+ input_ptr = FFI::MemoryPointer.new(:char, input_size)
131
+ input_ptr.put_bytes(0, actual_compressed_data)
132
+
133
+ block_size_ptr = FFI::MemoryPointer.new(:int)
134
+ data_size_ptr = FFI::MemoryPointer.new(:int)
135
+
136
+ begin
137
+ result = Library.bsc_block_info(
138
+ input_ptr,
139
+ input_size,
140
+ block_size_ptr,
141
+ data_size_ptr,
142
+ features
143
+ )
144
+
145
+ Error.check_result(result)
146
+
147
+ {
148
+ block_size: block_size_ptr.read_int,
149
+ data_size: data_size_ptr.read_int
150
+ }
151
+ ensure
152
+ input_ptr.free if input_ptr
153
+ block_size_ptr.free if block_size_ptr
154
+ data_size_ptr.free if data_size_ptr
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,18 @@
1
+ module Compress
2
+ class BSC
3
+ class Error < StandardError
4
+ attr_reader :code, :error_name
5
+
6
+ def initialize(code)
7
+ @code = code
8
+ @error_name = Library.error_name(code)
9
+ super("BSC Error: #{@error_name} (#{@code})")
10
+ end
11
+
12
+ def self.check_result(result)
13
+ raise Error.new(result) if result < Library::LIBBSC_NO_ERROR
14
+ result
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,100 @@
1
+ require 'ffi'
2
+
3
+ module Compress
4
+ class BSC
5
+ module Library
6
+ extend FFI::Library
7
+
8
+ begin
9
+ ffi_lib 'bsc'
10
+ rescue LoadError
11
+ ffi_lib '/usr/local/lib/libbsc.so'
12
+ rescue LoadError
13
+ ffi_lib 'libbsc'
14
+ end
15
+
16
+ # Version constants
17
+ LIBBSC_VERSION_MAJOR = 3
18
+ LIBBSC_VERSION_MINOR = 3
19
+ LIBBSC_VERSION_PATCH = 9
20
+ LIBBSC_VERSION_STRING = "3.3.9"
21
+
22
+ # Error codes
23
+ LIBBSC_NO_ERROR = 0
24
+ LIBBSC_BAD_PARAMETER = -1
25
+ LIBBSC_NOT_ENOUGH_MEMORY = -2
26
+ LIBBSC_NOT_COMPRESSIBLE = -3
27
+ LIBBSC_NOT_SUPPORTED = -4
28
+ LIBBSC_UNEXPECTED_EOB = -5
29
+ LIBBSC_DATA_CORRUPT = -6
30
+ LIBBSC_GPU_ERROR = -7
31
+ LIBBSC_GPU_NOT_SUPPORTED = -8
32
+ LIBBSC_GPU_NOT_ENOUGH_MEMORY = -9
33
+
34
+ # Block sorter constants
35
+ LIBBSC_BLOCKSORTER_NONE = 0
36
+ LIBBSC_BLOCKSORTER_BWT = 1
37
+ LIBBSC_BLOCKSORTER_ST3 = 3
38
+ LIBBSC_BLOCKSORTER_ST4 = 4
39
+ LIBBSC_BLOCKSORTER_ST5 = 5
40
+ LIBBSC_BLOCKSORTER_ST6 = 6
41
+ LIBBSC_BLOCKSORTER_ST7 = 7
42
+ LIBBSC_BLOCKSORTER_ST8 = 8
43
+
44
+ # Coder constants
45
+ LIBBSC_CODER_NONE = 0
46
+ LIBBSC_CODER_QLFC_STATIC = 1
47
+ LIBBSC_CODER_QLFC_ADAPTIVE = 2
48
+ LIBBSC_CODER_QLFC_FAST = 3
49
+
50
+ # Feature constants
51
+ LIBBSC_FEATURE_NONE = 0
52
+ LIBBSC_FEATURE_FASTMODE = 1
53
+ LIBBSC_FEATURE_MULTITHREADING = 2
54
+ LIBBSC_FEATURE_LARGEPAGES = 4
55
+ LIBBSC_FEATURE_CUDA = 8
56
+
57
+ # Default values
58
+ LIBBSC_DEFAULT_LZPHASHSIZE = 15
59
+ LIBBSC_DEFAULT_LZPMINLEN = 128
60
+ LIBBSC_DEFAULT_BLOCKSORTER = LIBBSC_BLOCKSORTER_BWT
61
+ LIBBSC_DEFAULT_CODER = LIBBSC_CODER_QLFC_STATIC
62
+ LIBBSC_DEFAULT_FEATURES = LIBBSC_FEATURE_FASTMODE | LIBBSC_FEATURE_MULTITHREADING
63
+
64
+ # Header size
65
+ LIBBSC_HEADER_SIZE = 28
66
+
67
+ # Function bindings
68
+ attach_function :bsc_init, [:int], :int
69
+ attach_function :bsc_init_full, [:int, :pointer, :pointer, :pointer], :int
70
+
71
+ attach_function :bsc_compress, [:pointer, :pointer, :int, :int, :int, :int, :int, :int], :int
72
+ attach_function :bsc_decompress, [:pointer, :int, :pointer, :int, :int], :int
73
+
74
+ attach_function :bsc_block_info, [:pointer, :int, :pointer, :pointer, :int], :int
75
+
76
+ # Platform functions
77
+ attach_function :bsc_malloc, [:size_t], :pointer
78
+ attach_function :bsc_zero_malloc, [:size_t], :pointer
79
+ attach_function :bsc_free, [:pointer], :void
80
+
81
+ # Error code names for debugging
82
+ ERROR_NAMES = {
83
+ LIBBSC_NO_ERROR => 'LIBBSC_NO_ERROR',
84
+ LIBBSC_BAD_PARAMETER => 'LIBBSC_BAD_PARAMETER',
85
+ LIBBSC_NOT_ENOUGH_MEMORY => 'LIBBSC_NOT_ENOUGH_MEMORY',
86
+ LIBBSC_NOT_COMPRESSIBLE => 'LIBBSC_NOT_COMPRESSIBLE',
87
+ LIBBSC_NOT_SUPPORTED => 'LIBBSC_NOT_SUPPORTED',
88
+ LIBBSC_UNEXPECTED_EOB => 'LIBBSC_UNEXPECTED_EOB',
89
+ LIBBSC_DATA_CORRUPT => 'LIBBSC_DATA_CORRUPT',
90
+ LIBBSC_GPU_ERROR => 'LIBBSC_GPU_ERROR',
91
+ LIBBSC_GPU_NOT_SUPPORTED => 'LIBBSC_GPU_NOT_SUPPORTED',
92
+ LIBBSC_GPU_NOT_ENOUGH_MEMORY => 'LIBBSC_GPU_NOT_ENOUGH_MEMORY'
93
+ }.freeze
94
+
95
+ def self.error_name(code)
96
+ ERROR_NAMES[code] || "UNKNOWN_ERROR(#{code})"
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,5 @@
1
+ module Compress
2
+ class BSC
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ require 'ffi'
2
+ require_relative 'bsc/version'
3
+ require_relative 'bsc/library'
4
+ require_relative 'bsc/compressor'
5
+ require_relative 'bsc/decompressor'
6
+ require_relative 'bsc/error'
7
+
8
+ module Compress
9
+ class BSC
10
+ # Initialize the BSC library with configurable features
11
+ def initialize(features: Library::LIBBSC_DEFAULT_FEATURES)
12
+ result = Library.bsc_init(features)
13
+ raise Error.new(result) unless result == Library::LIBBSC_NO_ERROR
14
+ end
15
+
16
+ # Simple compression interface
17
+ def compress(data, options = {})
18
+ Compressor.new(options).compress(data)
19
+ end
20
+
21
+ # Simple decompression interface
22
+ def decompress(data, options = {})
23
+ Decompressor.new(options).decompress(data)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ # Convenience file for requiring compress-bsc
2
+ # This allows users to do: require 'compress-bsc'
3
+ # which is equivalent to: require 'compress/bsc'
4
+
5
+ require_relative 'compress/bsc'