lz4-native-ruby 0.1.1 → 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
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  LZ4 - Fast LZ compression algorithm
3
- Copyright (C) 2011-2023, Yann Collet.
3
+ Copyright (c) Yann Collet. All rights reserved.
4
4
 
5
5
  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
6
6
 
@@ -77,7 +77,8 @@
77
77
  #ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */
78
78
  # if defined(__GNUC__) && \
79
79
  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
80
- || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
80
+ || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
81
+ || (defined(__riscv) && defined(__riscv_zicclsm)) )
81
82
  # define LZ4_FORCE_MEMORY_ACCESS 2
82
83
  # elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) || defined(_MSC_VER)
83
84
  # define LZ4_FORCE_MEMORY_ACCESS 1
@@ -301,12 +302,12 @@ static int LZ4_isAligned(const void* ptr, size_t alignment)
301
302
  #include <limits.h>
302
303
  #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
303
304
  # include <stdint.h>
304
- typedef uint8_t BYTE;
305
- typedef uint16_t U16;
306
- typedef uint32_t U32;
307
- typedef int32_t S32;
308
- typedef uint64_t U64;
309
- typedef uintptr_t uptrval;
305
+ typedef unsigned char BYTE; /*uint8_t not necessarily blessed to alias arbitrary type*/
306
+ typedef uint16_t U16;
307
+ typedef uint32_t U32;
308
+ typedef int32_t S32;
309
+ typedef uint64_t U64;
310
+ typedef uintptr_t uptrval;
310
311
  #else
311
312
  # if UINT_MAX != 4294967295UL
312
313
  # error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"
@@ -478,13 +479,15 @@ static const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3};
478
479
  #ifndef LZ4_FAST_DEC_LOOP
479
480
  # if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64
480
481
  # define LZ4_FAST_DEC_LOOP 1
481
- # elif defined(__aarch64__) && defined(__APPLE__)
482
- # define LZ4_FAST_DEC_LOOP 1
483
- # elif defined(__aarch64__) && !defined(__clang__)
484
- /* On non-Apple aarch64, we disable this optimization for clang because
482
+ # elif defined(__aarch64__)
483
+ # if defined(__clang__) && defined(__ANDROID__)
484
+ /* On Android aarch64, we disable this optimization for clang because
485
485
  * on certain mobile chipsets, performance is reduced with clang. For
486
486
  * more information refer to https://github.com/lz4/lz4/pull/707 */
487
- # define LZ4_FAST_DEC_LOOP 1
487
+ # define LZ4_FAST_DEC_LOOP 0
488
+ # else
489
+ # define LZ4_FAST_DEC_LOOP 1
490
+ # endif
488
491
  # else
489
492
  # define LZ4_FAST_DEC_LOOP 0
490
493
  # endif
@@ -896,7 +899,7 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
896
899
  || tableType == byPtr
897
900
  || inputSize >= 4 KB)
898
901
  {
899
- DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
902
+ DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", (void*)cctx);
900
903
  MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
901
904
  cctx->currentOffset = 0;
902
905
  cctx->tableType = (U32)clearedTable;
@@ -1532,7 +1535,7 @@ LZ4_stream_t* LZ4_createStream(void)
1532
1535
  {
1533
1536
  LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
1534
1537
  LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));
1535
- DEBUGLOG(4, "LZ4_createStream %p", lz4s);
1538
+ DEBUGLOG(4, "LZ4_createStream %p", (void*)lz4s);
1536
1539
  if (lz4s == NULL) return NULL;
1537
1540
  LZ4_initStream(lz4s, sizeof(*lz4s));
1538
1541
  return lz4s;
@@ -1563,7 +1566,7 @@ LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)
1563
1566
  * prefer initStream() which is more general */
1564
1567
  void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
1565
1568
  {
1566
- DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
1569
+ DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", (void*)LZ4_stream);
1567
1570
  MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));
1568
1571
  }
1569
1572
 
@@ -1575,7 +1578,7 @@ void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
1575
1578
  int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
1576
1579
  {
1577
1580
  if (!LZ4_stream) return 0; /* support free on NULL */
1578
- DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
1581
+ DEBUGLOG(5, "LZ4_freeStream %p", (void*)LZ4_stream);
1579
1582
  FREEMEM(LZ4_stream);
1580
1583
  return (0);
1581
1584
  }
@@ -1594,7 +1597,7 @@ int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict,
1594
1597
  const BYTE* const dictEnd = p + dictSize;
1595
1598
  U32 idx32;
1596
1599
 
1597
- DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
1600
+ DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, (void*)dictionary, (void*)LZ4_dict);
1598
1601
 
1599
1602
  /* It's necessary to reset the context,
1600
1603
  * and not just continue it with prepareTable()
@@ -1661,7 +1664,7 @@ void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dict
1661
1664
  &(dictionaryStream->internal_donotuse);
1662
1665
 
1663
1666
  DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",
1664
- workingStream, dictionaryStream,
1667
+ (void*)workingStream, (void*)dictionaryStream,
1665
1668
  dictCtx != NULL ? dictCtx->dictSize : 0);
1666
1669
 
1667
1670
  if (dictCtx != NULL) {
@@ -1725,7 +1728,7 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
1725
1728
  && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */
1726
1729
  && (streamPtr->dictCtx == NULL) /* usingDictCtx */
1727
1730
  ) {
1728
- DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);
1731
+ DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, (void*)streamPtr->dictionary);
1729
1732
  /* remove dictionary existence from history, to employ faster prefix mode */
1730
1733
  streamPtr->dictSize = 0;
1731
1734
  streamPtr->dictionary = (const BYTE*)source;
@@ -1815,7 +1818,7 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
1815
1818
  {
1816
1819
  LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
1817
1820
 
1818
- DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer);
1821
+ DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, (void*)safeBuffer);
1819
1822
 
1820
1823
  if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */
1821
1824
  if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }
@@ -2311,8 +2314,8 @@ LZ4_decompress_generic(
2311
2314
  */
2312
2315
  if ((ip+length != iend) || (cpy > oend)) {
2313
2316
  DEBUGLOG(5, "should have been last run of literals")
2314
- DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);
2315
- DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT);
2317
+ DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", (void*)ip, (int)length, (void*)(ip+length), (void*)iend);
2318
+ DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", (void*)cpy, (void*)(oend-MFLIMIT));
2316
2319
  DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize);
2317
2320
  goto _output_error;
2318
2321
  }
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * LZ4 - Fast LZ compression algorithm
3
3
  * Header File
4
- * Copyright (C) 2011-2023, Yann Collet.
4
+ * Copyright (c) Yann Collet. All rights reserved.
5
5
 
6
6
  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
7
7
 
@@ -258,10 +258,12 @@ LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* d
258
258
  * @return : Nb bytes written into 'dst' (necessarily <= dstCapacity)
259
259
  * or 0 if compression fails.
260
260
  *
261
- * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
262
- * the produced compressed content could, in specific circumstances,
263
- * require to be decompressed into a destination buffer larger
264
- * by at least 1 byte than the content to decompress.
261
+ * Note : 'targetDstSize' must be >= 1, because it's the smallest valid lz4 payload.
262
+ *
263
+ * Note 2:from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
264
+ * the produced compressed content could, in rare circumstances,
265
+ * require to be decompressed into a destination buffer
266
+ * larger by at least 1 byte than decompressesSize.
265
267
  * If an application uses `LZ4_compress_destSize()`,
266
268
  * it's highly recommended to update liblz4 to v1.9.2 or better.
267
269
  * If this can't be done or ensured,
@@ -698,10 +700,10 @@ int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int*
698
700
 
699
701
  #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
700
702
  # include <stdint.h>
701
- typedef int8_t LZ4_i8;
702
- typedef uint8_t LZ4_byte;
703
- typedef uint16_t LZ4_u16;
704
- typedef uint32_t LZ4_u32;
703
+ typedef int8_t LZ4_i8;
704
+ typedef unsigned char LZ4_byte;
705
+ typedef uint16_t LZ4_u16;
706
+ typedef uint32_t LZ4_u32;
705
707
  #else
706
708
  typedef signed char LZ4_i8;
707
709
  typedef unsigned char LZ4_byte;
@@ -0,0 +1,442 @@
1
+ /*
2
+ * LZ4 Native Ruby Extension
3
+ * Provides complete bindings for lz4.h, lz4hc.h, and lz4frame.h
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #include "lz4.h"
8
+ #include "lz4hc.h"
9
+ #include "lz4frame.h"
10
+ #include <string.h>
11
+
12
+ /* Module and class definitions */
13
+ static VALUE mLZ4Native;
14
+ static VALUE mLZ4;
15
+ static VALUE mLZ4HC;
16
+ static VALUE mLZ4Frame;
17
+ static VALUE eLZ4Error;
18
+ static VALUE eLZ4CompressionError;
19
+ static VALUE eLZ4DecompressionError;
20
+ static VALUE eLZ4FrameError;
21
+
22
+ /*
23
+ * ============================================================================
24
+ * LZ4 Basic Functions (lz4.h)
25
+ * ============================================================================
26
+ */
27
+
28
+ /*
29
+ * call-seq:
30
+ * LZ4.compress_default(source) -> compressed_string
31
+ *
32
+ * Compresses source data using default LZ4 compression.
33
+ * Returns compressed data as a binary string.
34
+ */
35
+ static VALUE
36
+ lz4_compress_default_wrapper(VALUE self, VALUE source)
37
+ {
38
+ Check_Type(source, T_STRING);
39
+
40
+ const char* src = RSTRING_PTR(source);
41
+ int src_size = (int)RSTRING_LEN(source);
42
+
43
+ if (src_size > LZ4_MAX_INPUT_SIZE) {
44
+ rb_raise(eLZ4CompressionError, "Input size exceeds LZ4_MAX_INPUT_SIZE");
45
+ }
46
+
47
+ int max_dst_size = LZ4_compressBound(src_size);
48
+ if (max_dst_size == 0) {
49
+ rb_raise(eLZ4CompressionError, "Invalid input size");
50
+ }
51
+
52
+ VALUE result = rb_str_buf_new(max_dst_size);
53
+ char* dst = RSTRING_PTR(result);
54
+
55
+ int compressed_size = LZ4_compress_default(src, dst, src_size, max_dst_size);
56
+
57
+ if (compressed_size <= 0) {
58
+ rb_raise(eLZ4CompressionError, "Compression failed");
59
+ }
60
+
61
+ rb_str_set_len(result, compressed_size);
62
+ return result;
63
+ }
64
+
65
+ /*
66
+ * call-seq:
67
+ * LZ4.decompress_safe(source, max_size) -> decompressed_string
68
+ *
69
+ * Decompresses LZ4 compressed data safely.
70
+ * max_size must be >= actual decompressed size.
71
+ */
72
+ static VALUE
73
+ lz4_decompress_safe_wrapper(VALUE self, VALUE source, VALUE max_size)
74
+ {
75
+ Check_Type(source, T_STRING);
76
+ Check_Type(max_size, T_FIXNUM);
77
+
78
+ const char* src = RSTRING_PTR(source);
79
+ int src_size = (int)RSTRING_LEN(source);
80
+ int dst_capacity = NUM2INT(max_size);
81
+
82
+ if (dst_capacity <= 0) {
83
+ rb_raise(eLZ4DecompressionError, "Invalid max_size");
84
+ }
85
+
86
+ VALUE result = rb_str_buf_new(dst_capacity);
87
+ char* dst = RSTRING_PTR(result);
88
+
89
+ int decompressed_size = LZ4_decompress_safe(src, dst, src_size, dst_capacity);
90
+
91
+ if (decompressed_size < 0) {
92
+ rb_raise(eLZ4DecompressionError, "Decompression failed - possibly corrupt data");
93
+ }
94
+
95
+ rb_str_set_len(result, decompressed_size);
96
+ return result;
97
+ }
98
+
99
+ /*
100
+ * call-seq:
101
+ * LZ4.compress_bound(input_size) -> max_compressed_size
102
+ *
103
+ * Returns maximum compressed size for given input size.
104
+ */
105
+ static VALUE
106
+ lz4_compress_bound_wrapper(VALUE self, VALUE input_size)
107
+ {
108
+ Check_Type(input_size, T_FIXNUM);
109
+ int size = NUM2INT(input_size);
110
+ int bound = LZ4_compressBound(size);
111
+ return INT2NUM(bound);
112
+ }
113
+
114
+ /*
115
+ * call-seq:
116
+ * LZ4.compress_fast(source, acceleration) -> compressed_string
117
+ *
118
+ * Compresses with acceleration factor (higher = faster, less compression).
119
+ */
120
+ static VALUE
121
+ lz4_compress_fast_wrapper(VALUE self, VALUE source, VALUE acceleration)
122
+ {
123
+ Check_Type(source, T_STRING);
124
+ Check_Type(acceleration, T_FIXNUM);
125
+
126
+ const char* src = RSTRING_PTR(source);
127
+ int src_size = (int)RSTRING_LEN(source);
128
+ int accel = NUM2INT(acceleration);
129
+
130
+ int max_dst_size = LZ4_compressBound(src_size);
131
+ VALUE result = rb_str_buf_new(max_dst_size);
132
+ char* dst = RSTRING_PTR(result);
133
+
134
+ int compressed_size = LZ4_compress_fast(src, dst, src_size, max_dst_size, accel);
135
+
136
+ if (compressed_size <= 0) {
137
+ rb_raise(eLZ4CompressionError, "Compression failed");
138
+ }
139
+
140
+ rb_str_set_len(result, compressed_size);
141
+ return result;
142
+ }
143
+
144
+ /*
145
+ * call-seq:
146
+ * LZ4.version_number -> integer
147
+ *
148
+ * Returns the LZ4 library version number.
149
+ */
150
+ static VALUE
151
+ lz4_version_number_wrapper(VALUE self)
152
+ {
153
+ return INT2NUM(LZ4_versionNumber());
154
+ }
155
+
156
+ /*
157
+ * call-seq:
158
+ * LZ4.version_string -> string
159
+ *
160
+ * Returns the LZ4 library version string.
161
+ */
162
+ static VALUE
163
+ lz4_version_string_wrapper(VALUE self)
164
+ {
165
+ return rb_str_new_cstr(LZ4_versionString());
166
+ }
167
+
168
+ /*
169
+ * ============================================================================
170
+ * LZ4 HC Functions (lz4hc.h)
171
+ * ============================================================================
172
+ */
173
+
174
+ /*
175
+ * call-seq:
176
+ * LZ4HC.compress(source, compression_level = 9) -> compressed_string
177
+ *
178
+ * Compresses using high-compression mode.
179
+ * compression_level: 1-12 (default 9), higher = better compression, slower.
180
+ */
181
+ static VALUE
182
+ lz4hc_compress_wrapper(int argc, VALUE *argv, VALUE self)
183
+ {
184
+ VALUE source, level;
185
+ rb_scan_args(argc, argv, "11", &source, &level);
186
+
187
+ Check_Type(source, T_STRING);
188
+ int compression_level = NIL_P(level) ? LZ4HC_CLEVEL_DEFAULT : NUM2INT(level);
189
+
190
+ if (compression_level < LZ4HC_CLEVEL_MIN) {
191
+ compression_level = LZ4HC_CLEVEL_MIN;
192
+ } else if (compression_level > LZ4HC_CLEVEL_MAX) {
193
+ compression_level = LZ4HC_CLEVEL_MAX;
194
+ }
195
+
196
+ const char* src = RSTRING_PTR(source);
197
+ int src_size = (int)RSTRING_LEN(source);
198
+
199
+ int max_dst_size = LZ4_compressBound(src_size);
200
+ VALUE result = rb_str_buf_new(max_dst_size);
201
+ char* dst = RSTRING_PTR(result);
202
+
203
+ int compressed_size = LZ4_compress_HC(src, dst, src_size, max_dst_size, compression_level);
204
+
205
+ if (compressed_size <= 0) {
206
+ rb_raise(eLZ4CompressionError, "HC compression failed");
207
+ }
208
+
209
+ rb_str_set_len(result, compressed_size);
210
+ return result;
211
+ }
212
+
213
+ /*
214
+ * call-seq:
215
+ * LZ4HC.sizeof_state -> integer
216
+ *
217
+ * Returns the size needed for HC compression state.
218
+ */
219
+ static VALUE
220
+ lz4hc_sizeof_state_wrapper(VALUE self)
221
+ {
222
+ return INT2NUM(LZ4_sizeofStateHC());
223
+ }
224
+
225
+ /*
226
+ * ============================================================================
227
+ * LZ4 Frame Functions (lz4frame.h)
228
+ * ============================================================================
229
+ */
230
+
231
+ /*
232
+ * call-seq:
233
+ * LZ4Frame.compress_frame(source, options = {}) -> compressed_frame
234
+ *
235
+ * Compresses data into a complete LZ4 frame (self-contained format).
236
+ * Options hash can include:
237
+ * - block_size: :max64KB, :max256KB, :max1MB, :max4MB (default :max64KB)
238
+ * - block_mode: :linked, :independent (default :linked)
239
+ * - checksum: true/false (default false)
240
+ * - compression_level: 0-12 (default 0 = fast mode)
241
+ */
242
+ static VALUE
243
+ lz4frame_compress_frame_wrapper(int argc, VALUE *argv, VALUE self)
244
+ {
245
+ VALUE source, options;
246
+ rb_scan_args(argc, argv, "11", &source, &options);
247
+
248
+ Check_Type(source, T_STRING);
249
+
250
+ const char* src = RSTRING_PTR(source);
251
+ size_t src_size = RSTRING_LEN(source);
252
+
253
+ /* Set up preferences */
254
+ LZ4F_preferences_t prefs;
255
+ memset(&prefs, 0, sizeof(prefs));
256
+ prefs.frameInfo.blockSizeID = LZ4F_max64KB;
257
+ prefs.frameInfo.blockMode = LZ4F_blockLinked;
258
+ prefs.frameInfo.contentChecksumFlag = LZ4F_noContentChecksum;
259
+ prefs.compressionLevel = 0;
260
+
261
+ /* Parse options if provided */
262
+ if (!NIL_P(options)) {
263
+ Check_Type(options, T_HASH);
264
+
265
+ VALUE block_size = rb_hash_aref(options, ID2SYM(rb_intern("block_size")));
266
+ if (!NIL_P(block_size)) {
267
+ ID bs_id = SYM2ID(block_size);
268
+ if (bs_id == rb_intern("max64KB")) prefs.frameInfo.blockSizeID = LZ4F_max64KB;
269
+ else if (bs_id == rb_intern("max256KB")) prefs.frameInfo.blockSizeID = LZ4F_max256KB;
270
+ else if (bs_id == rb_intern("max1MB")) prefs.frameInfo.blockSizeID = LZ4F_max1MB;
271
+ else if (bs_id == rb_intern("max4MB")) prefs.frameInfo.blockSizeID = LZ4F_max4MB;
272
+ }
273
+
274
+ VALUE block_mode = rb_hash_aref(options, ID2SYM(rb_intern("block_mode")));
275
+ if (!NIL_P(block_mode)) {
276
+ ID bm_id = SYM2ID(block_mode);
277
+ if (bm_id == rb_intern("linked")) prefs.frameInfo.blockMode = LZ4F_blockLinked;
278
+ else if (bm_id == rb_intern("independent")) prefs.frameInfo.blockMode = LZ4F_blockIndependent;
279
+ }
280
+
281
+ VALUE checksum = rb_hash_aref(options, ID2SYM(rb_intern("checksum")));
282
+ if (RTEST(checksum)) {
283
+ prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
284
+ }
285
+
286
+ VALUE comp_level = rb_hash_aref(options, ID2SYM(rb_intern("compression_level")));
287
+ if (!NIL_P(comp_level)) {
288
+ prefs.compressionLevel = NUM2INT(comp_level);
289
+ }
290
+ }
291
+
292
+ size_t dst_capacity = LZ4F_compressFrameBound(src_size, &prefs);
293
+ VALUE result = rb_str_buf_new(dst_capacity);
294
+ char* dst = RSTRING_PTR(result);
295
+
296
+ size_t compressed_size = LZ4F_compressFrame(dst, dst_capacity, src, src_size, &prefs);
297
+
298
+ if (LZ4F_isError(compressed_size)) {
299
+ rb_raise(eLZ4FrameError, "Frame compression failed: %s",
300
+ LZ4F_getErrorName(compressed_size));
301
+ }
302
+
303
+ rb_str_set_len(result, compressed_size);
304
+ return result;
305
+ }
306
+
307
+ /*
308
+ * call-seq:
309
+ * LZ4Frame.decompress_frame(source) -> decompressed_string
310
+ *
311
+ * Decompresses a complete LZ4 frame.
312
+ */
313
+ static VALUE
314
+ lz4frame_decompress_frame_wrapper(VALUE self, VALUE source)
315
+ {
316
+ Check_Type(source, T_STRING);
317
+
318
+ const char* src = RSTRING_PTR(source);
319
+ size_t src_size = RSTRING_LEN(source);
320
+
321
+ /* Create decompression context */
322
+ LZ4F_dctx* dctx;
323
+ LZ4F_errorCode_t err = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
324
+ if (LZ4F_isError(err)) {
325
+ rb_raise(eLZ4FrameError, "Failed to create decompression context: %s",
326
+ LZ4F_getErrorName(err));
327
+ }
328
+
329
+ /* Allocate output buffer - start with 4x input size, may need to grow */
330
+ size_t dst_capacity = src_size * 4;
331
+ if (dst_capacity < 1024) dst_capacity = 1024;
332
+
333
+ VALUE result = rb_str_buf_new(dst_capacity);
334
+ char* dst = RSTRING_PTR(result);
335
+ size_t total_decompressed = 0;
336
+
337
+ size_t src_pos = 0;
338
+
339
+ while (src_pos < src_size) {
340
+ size_t src_remaining = src_size - src_pos;
341
+ size_t dst_remaining = dst_capacity - total_decompressed;
342
+
343
+ /* Ensure we have space, grow if needed */
344
+ if (dst_remaining < 64 * 1024) {
345
+ dst_capacity *= 2;
346
+ rb_str_resize(result, dst_capacity);
347
+ dst = RSTRING_PTR(result);
348
+ dst_remaining = dst_capacity - total_decompressed;
349
+ }
350
+
351
+ size_t ret = LZ4F_decompress(dctx,
352
+ dst + total_decompressed, &dst_remaining,
353
+ src + src_pos, &src_remaining,
354
+ NULL);
355
+
356
+ if (LZ4F_isError(ret)) {
357
+ LZ4F_freeDecompressionContext(dctx);
358
+ rb_raise(eLZ4FrameError, "Frame decompression failed: %s",
359
+ LZ4F_getErrorName(ret));
360
+ }
361
+
362
+ src_pos += src_remaining;
363
+ total_decompressed += dst_remaining;
364
+
365
+ /* ret == 0 means frame is complete */
366
+ if (ret == 0) {
367
+ break;
368
+ }
369
+ }
370
+
371
+ LZ4F_freeDecompressionContext(dctx);
372
+
373
+ rb_str_set_len(result, total_decompressed);
374
+ return result;
375
+ }
376
+
377
+ /*
378
+ * call-seq:
379
+ * LZ4Frame.version -> integer
380
+ *
381
+ * Returns the LZ4F API version number.
382
+ */
383
+ static VALUE
384
+ lz4frame_version_wrapper(VALUE self)
385
+ {
386
+ return INT2NUM(LZ4F_getVersion());
387
+ }
388
+
389
+ /*
390
+ * call-seq:
391
+ * LZ4Frame.compression_level_max -> integer
392
+ *
393
+ * Returns the maximum compression level supported.
394
+ */
395
+ static VALUE
396
+ lz4frame_compression_level_max_wrapper(VALUE self)
397
+ {
398
+ return INT2NUM(LZ4F_compressionLevel_max());
399
+ }
400
+
401
+ /*
402
+ * Extension initialization
403
+ */
404
+ void
405
+ Init_lz4_native(void)
406
+ {
407
+ /* Main module */
408
+ mLZ4Native = rb_define_module("LZ4Native");
409
+
410
+ /* LZ4 basic module */
411
+ mLZ4 = rb_define_module_under(mLZ4Native, "LZ4");
412
+ rb_define_singleton_method(mLZ4, "compress_default", lz4_compress_default_wrapper, 1);
413
+ rb_define_singleton_method(mLZ4, "decompress_safe", lz4_decompress_safe_wrapper, 2);
414
+ rb_define_singleton_method(mLZ4, "compress_bound", lz4_compress_bound_wrapper, 1);
415
+ rb_define_singleton_method(mLZ4, "compress_fast", lz4_compress_fast_wrapper, 2);
416
+ rb_define_singleton_method(mLZ4, "version_number", lz4_version_number_wrapper, 0);
417
+ rb_define_singleton_method(mLZ4, "version_string", lz4_version_string_wrapper, 0);
418
+
419
+ /* LZ4HC module */
420
+ mLZ4HC = rb_define_module_under(mLZ4Native, "LZ4HC");
421
+ rb_define_singleton_method(mLZ4HC, "compress", lz4hc_compress_wrapper, -1);
422
+ rb_define_singleton_method(mLZ4HC, "sizeof_state", lz4hc_sizeof_state_wrapper, 0);
423
+
424
+ /* LZ4Frame module */
425
+ mLZ4Frame = rb_define_module_under(mLZ4Native, "LZ4Frame");
426
+ rb_define_singleton_method(mLZ4Frame, "compress_frame", lz4frame_compress_frame_wrapper, -1);
427
+ rb_define_singleton_method(mLZ4Frame, "decompress_frame", lz4frame_decompress_frame_wrapper, 1);
428
+ rb_define_singleton_method(mLZ4Frame, "version", lz4frame_version_wrapper, 0);
429
+ rb_define_singleton_method(mLZ4Frame, "compression_level_max", lz4frame_compression_level_max_wrapper, 0);
430
+
431
+ /* Exception classes */
432
+ eLZ4Error = rb_define_class_under(mLZ4Native, "Error", rb_eStandardError);
433
+ eLZ4CompressionError = rb_define_class_under(mLZ4Native, "CompressionError", eLZ4Error);
434
+ eLZ4DecompressionError = rb_define_class_under(mLZ4Native, "DecompressionError", eLZ4Error);
435
+ eLZ4FrameError = rb_define_class_under(mLZ4Native, "FrameError", eLZ4Error);
436
+
437
+ /* Constants */
438
+ rb_define_const(mLZ4, "MAX_INPUT_SIZE", INT2NUM(LZ4_MAX_INPUT_SIZE));
439
+ rb_define_const(mLZ4HC, "CLEVEL_MIN", INT2NUM(LZ4HC_CLEVEL_MIN));
440
+ rb_define_const(mLZ4HC, "CLEVEL_DEFAULT", INT2NUM(LZ4HC_CLEVEL_DEFAULT));
441
+ rb_define_const(mLZ4HC, "CLEVEL_MAX", INT2NUM(LZ4HC_CLEVEL_MAX));
442
+ }