zstd-ruby 1.5.7.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50ffda13ae5c1470be924476eb600af7695a2e76f0764666f8f4a90b07b35428
4
- data.tar.gz: e28d50be732039fc58762c02870e3ecf227b5843d647fce6a5cebac91dbf8487
3
+ metadata.gz: 91a2e54ff93b0c602d6b2d064f6f8c615a59f0ef963c20b24e4db6d17643163e
4
+ data.tar.gz: cbeb4c7c21187336a284cb714765fb80b02b982c6b4fafa60784d8f7950fe528
5
5
  SHA512:
6
- metadata.gz: 71433e488936220118f3b4611594f8eaac68c3544816e890c412fa76f531e450f44d62d4b03d900e95b47e25d37f35b73c41baeb8275917adc1013172763fc11
7
- data.tar.gz: 6e1f7a19a7fb75633b790d3d3a5c2c20bc520a6f2ed03f17941f186f7c9e5e2a00dfa7398fc428cfd665a935f5ed516115d81a826439bfa3d304119ae1f5cc79
6
+ metadata.gz: 70190b1a1233782dc440e5ef913f4a83b6bc936ea96d744f5a44b3dbceb41f8a1ef9c3264746eb251356b95c13c5503b2b2567076bc45c28d628ee278b9c2522
7
+ data.tar.gz: 600bc166bef41126b93a0e78190bbf6b5698259797e166ee4d787a113d6c4e27e7c6c927135e54b33a7e4cc25a74b2df4cb19413f74618892191863cc4c9b4fb
data/README.md CHANGED
@@ -12,6 +12,20 @@ Fork from https://github.com/jarredholman/ruby-zstd.
12
12
  ## Zstd version
13
13
  [v1.5.7](https://github.com/facebook/zstd/tree/v1.5.7)
14
14
 
15
+ ## Versioning Policy
16
+
17
+ Starting from v2.0.0, this gem follows Semantic Versioning.
18
+
19
+ - **Major version** (X.0.0): Breaking changes to the API
20
+ - **Minor version** (X.Y.0): New features, including Zstd library version updates
21
+ - **Patch version** (X.Y.Z): Bug fixes and other backward-compatible changes
22
+
23
+ ### Zstd Library Updates
24
+
25
+ Updates to the underlying Zstd library version will be released as **minor version** updates, as they may introduce new features or performance improvements while maintaining backward compatibility.
26
+
27
+ **Note**: Versions prior to v2.0.0 followed the Zstd library versioning scheme with an additional patch number (e.g., 1.5.6.2). This approach has been replaced with semantic versioning to provide clearer expectations for API stability.
28
+
15
29
  ## Installation
16
30
 
17
31
  Add this line to your application's Gemfile:
@@ -155,6 +169,20 @@ result << stream.decompress(cstr[10..-1])
155
169
 
156
170
  DDict can also be specified to `dict:`.
157
171
 
172
+ #### Streaming Decompression with Position Tracking
173
+
174
+ If you need to know how much of the input data was consumed during decompression, you can use the `decompress_with_pos` method:
175
+
176
+ ```ruby
177
+ cstr = "" # Compressed data
178
+ stream = Zstd::StreamingDecompress.new
179
+ result, consumed_bytes = stream.decompress_with_pos(cstr[0, 10])
180
+ # result contains the decompressed data
181
+ # consumed_bytes contains the number of bytes from input that were processed
182
+ ```
183
+
184
+ This is particularly useful when processing streaming data where you need to track the exact position in the input stream.
185
+
158
186
  ### Skippable frame
159
187
 
160
188
  ```ruby
@@ -18,7 +18,7 @@ static int convert_compression_level(VALUE compression_level_value)
18
18
  return NUM2INT(compression_level_value);
19
19
  }
20
20
 
21
- static void set_compress_params(ZSTD_CCtx* const ctx, VALUE level_from_args, VALUE kwargs)
21
+ static void set_compress_params(ZSTD_CCtx* const ctx, VALUE kwargs)
22
22
  {
23
23
  ID kwargs_keys[2];
24
24
  kwargs_keys[0] = rb_intern("level");
@@ -29,9 +29,6 @@ static void set_compress_params(ZSTD_CCtx* const ctx, VALUE level_from_args, VAL
29
29
  int compression_level = ZSTD_CLEVEL_DEFAULT;
30
30
  if (kwargs_values[0] != Qundef && kwargs_values[0] != Qnil) {
31
31
  compression_level = convert_compression_level(kwargs_values[0]);
32
- } else if (!NIL_P(level_from_args)) {
33
- rb_warn("`level` in args is deprecated; use keyword args `level:` instead.");
34
- compression_level = convert_compression_level(level_from_args);
35
32
  }
36
33
  ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
37
34
 
@@ -0,0 +1 @@
1
+ _Init_zstdruby
@@ -2,9 +2,14 @@ require "mkmf"
2
2
 
3
3
  have_func('rb_gc_mark_movable')
4
4
 
5
- $CFLAGS = '-I. -O3 -std=c99 -DZSTD_STATIC_LINKING_ONLY -DZSTD_MULTITHREAD -pthread -DDEBUGLEVEL=0'
5
+ $CFLAGS = '-I. -O3 -std=c99 -DZSTD_STATIC_LINKING_ONLY -DZSTD_MULTITHREAD -pthread -DDEBUGLEVEL=0 -fvisibility=hidden -DZSTDLIB_VISIBLE=\'__attribute__((visibility("hidden")))\' -DZSTDLIB_HIDDEN=\'__attribute__((visibility("hidden")))\''
6
6
  $CPPFLAGS += " -fdeclspec" if CONFIG['CXX'] =~ /clang/
7
7
 
8
+ # macOS specific: Use exported_symbols_list to control symbol visibility
9
+ if RUBY_PLATFORM =~ /darwin/
10
+ $LDFLAGS += " -exported_symbols_list #{File.expand_path('exports.txt', __dir__)}"
11
+ end
12
+
8
13
  Dir.chdir File.expand_path('..', __FILE__) do
9
14
  $srcs = Dir['**/*.c', '**/*.S']
10
15
 
data/ext/zstdruby/main.c CHANGED
@@ -8,7 +8,7 @@ void zstd_ruby_skippable_frame_init(void);
8
8
  void zstd_ruby_streaming_compress_init(void);
9
9
  void zstd_ruby_streaming_decompress_init(void);
10
10
 
11
- void
11
+ RUBY_FUNC_EXPORTED void
12
12
  Init_zstdruby(void)
13
13
  {
14
14
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
@@ -4,6 +4,7 @@ struct streaming_compress_t {
4
4
  ZSTD_CCtx* ctx;
5
5
  VALUE buf;
6
6
  size_t buf_size;
7
+ VALUE pending; /* accumulate compressed bytes produced by write() */
7
8
  };
8
9
 
9
10
  static void
@@ -12,8 +13,10 @@ streaming_compress_mark(void *p)
12
13
  struct streaming_compress_t *sc = p;
13
14
  #ifdef HAVE_RB_GC_MARK_MOVABLE
14
15
  rb_gc_mark_movable(sc->buf);
16
+ rb_gc_mark_movable(sc->pending);
15
17
  #else
16
18
  rb_gc_mark(sc->buf);
19
+ rb_gc_mark(sc->pending);
17
20
  #endif
18
21
  }
19
22
 
@@ -40,6 +43,7 @@ streaming_compress_compact(void *p)
40
43
  {
41
44
  struct streaming_compress_t *sc = p;
42
45
  sc->buf = rb_gc_location(sc->buf);
46
+ sc->pending = rb_gc_location(sc->pending);
43
47
  }
44
48
  #endif
45
49
 
@@ -64,6 +68,7 @@ rb_streaming_compress_allocate(VALUE klass)
64
68
  sc->ctx = NULL;
65
69
  sc->buf = Qnil;
66
70
  sc->buf_size = 0;
71
+ sc->pending = Qnil;
67
72
  return obj;
68
73
  }
69
74
 
@@ -71,8 +76,7 @@ static VALUE
71
76
  rb_streaming_compress_initialize(int argc, VALUE *argv, VALUE obj)
72
77
  {
73
78
  VALUE kwargs;
74
- VALUE compression_level_value;
75
- rb_scan_args(argc, argv, "01:", &compression_level_value, &kwargs);
79
+ rb_scan_args(argc, argv, "00:", &kwargs);
76
80
 
77
81
  struct streaming_compress_t* sc;
78
82
  TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
@@ -82,11 +86,12 @@ rb_streaming_compress_initialize(int argc, VALUE *argv, VALUE obj)
82
86
  if (ctx == NULL) {
83
87
  rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCCtx error");
84
88
  }
85
- set_compress_params(ctx, compression_level_value, kwargs);
89
+ set_compress_params(ctx, kwargs);
86
90
 
87
91
  sc->ctx = ctx;
88
92
  sc->buf = rb_str_new(NULL, buffOutSize);
89
93
  sc->buf_size = buffOutSize;
94
+ sc->pending = rb_str_new(0, 0);
90
95
 
91
96
  return obj;
92
97
  }
@@ -100,13 +105,13 @@ static VALUE
100
105
  no_compress(struct streaming_compress_t* sc, ZSTD_EndDirective endOp)
101
106
  {
102
107
  ZSTD_inBuffer input = { NULL, 0, 0 };
103
- const char* output_data = RSTRING_PTR(sc->buf);
104
108
  VALUE result = rb_str_new(0, 0);
105
109
  size_t ret;
106
110
  do {
111
+ const char* output_data = RSTRING_PTR(sc->buf);
107
112
  ZSTD_outBuffer output = { (void*)output_data, sc->buf_size, 0 };
108
113
 
109
- size_t const ret = zstd_stream_compress(sc->ctx, &output, &input, endOp, false);
114
+ ret = zstd_stream_compress(sc->ctx, &output, &input, endOp, false);
110
115
  if (ZSTD_isError(ret)) {
111
116
  rb_raise(rb_eRuntimeError, "flush error error code: %s", ZSTD_getErrorName(ret));
112
117
  }
@@ -126,9 +131,9 @@ rb_streaming_compress_compress(VALUE obj, VALUE src)
126
131
  struct streaming_compress_t* sc;
127
132
  TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
128
133
 
129
- const char* output_data = RSTRING_PTR(sc->buf);
130
134
  VALUE result = rb_str_new(0, 0);
131
135
  while (input.pos < input.size) {
136
+ const char* output_data = RSTRING_PTR(sc->buf);
132
137
  ZSTD_outBuffer output = { (void*)output_data, sc->buf_size, 0 };
133
138
  size_t const ret = zstd_stream_compress(sc->ctx, &output, &input, ZSTD_e_continue, false);
134
139
  if (ZSTD_isError(ret)) {
@@ -143,10 +148,8 @@ static VALUE
143
148
  rb_streaming_compress_write(int argc, VALUE *argv, VALUE obj)
144
149
  {
145
150
  size_t total = 0;
146
- VALUE result = rb_str_new(0, 0);
147
151
  struct streaming_compress_t* sc;
148
152
  TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
149
- const char* output_data = RSTRING_PTR(sc->buf);
150
153
 
151
154
  while (argc-- > 0) {
152
155
  VALUE str = *argv++;
@@ -156,14 +159,20 @@ rb_streaming_compress_write(int argc, VALUE *argv, VALUE obj)
156
159
  ZSTD_inBuffer input = { input_data, input_size, 0 };
157
160
 
158
161
  while (input.pos < input.size) {
162
+ const char* output_data = RSTRING_PTR(sc->buf);
159
163
  ZSTD_outBuffer output = { (void*)output_data, sc->buf_size, 0 };
160
164
  size_t const ret = zstd_stream_compress(sc->ctx, &output, &input, ZSTD_e_continue, false);
161
165
  if (ZSTD_isError(ret)) {
162
166
  rb_raise(rb_eRuntimeError, "compress error error code: %s", ZSTD_getErrorName(ret));
163
167
  }
164
- total += RSTRING_LEN(str);
168
+ /* Directly append to the pending buffer */
169
+ if (output.pos > 0) {
170
+ rb_str_cat(sc->pending, output.dst, output.pos);
171
+ }
165
172
  }
173
+ total += RSTRING_LEN(str);
166
174
  }
175
+
167
176
  return SIZET2NUM(total);
168
177
  }
169
178
 
@@ -193,8 +202,11 @@ rb_streaming_compress_flush(VALUE obj)
193
202
  {
194
203
  struct streaming_compress_t* sc;
195
204
  TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
196
- VALUE result = no_compress(sc, ZSTD_e_flush);
197
- return result;
205
+ VALUE drained = no_compress(sc, ZSTD_e_flush);
206
+ VALUE out = rb_str_dup(sc->pending);
207
+ rb_str_cat(out, RSTRING_PTR(drained), RSTRING_LEN(drained));
208
+ rb_str_resize(sc->pending, 0);
209
+ return out;
198
210
  }
199
211
 
200
212
  static VALUE
@@ -202,8 +214,11 @@ rb_streaming_compress_finish(VALUE obj)
202
214
  {
203
215
  struct streaming_compress_t* sc;
204
216
  TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
205
- VALUE result = no_compress(sc, ZSTD_e_end);
206
- return result;
217
+ VALUE drained = no_compress(sc, ZSTD_e_end);
218
+ VALUE out = rb_str_dup(sc->pending);
219
+ rb_str_cat(out, RSTRING_PTR(drained), RSTRING_LEN(drained));
220
+ rb_str_resize(sc->pending, 0);
221
+ return out;
207
222
  }
208
223
 
209
224
  extern VALUE rb_mZstd, cStreamingCompress;
@@ -100,19 +100,47 @@ rb_streaming_decompress_decompress(VALUE obj, VALUE src)
100
100
 
101
101
  struct streaming_decompress_t* sd;
102
102
  TypedData_Get_Struct(obj, struct streaming_decompress_t, &streaming_decompress_type, sd);
103
- const char* output_data = RSTRING_PTR(sd->buf);
104
103
  VALUE result = rb_str_new(0, 0);
104
+
105
105
  while (input.pos < input.size) {
106
+ const char* output_data = RSTRING_PTR(sd->buf);
106
107
  ZSTD_outBuffer output = { (void*)output_data, sd->buf_size, 0 };
107
108
  size_t const ret = zstd_stream_decompress(sd->dctx, &output, &input, false);
109
+
108
110
  if (ZSTD_isError(ret)) {
109
111
  rb_raise(rb_eRuntimeError, "decompress error error code: %s", ZSTD_getErrorName(ret));
110
112
  }
111
- rb_str_cat(result, output.dst, output.pos);
113
+ if (output.pos > 0) {
114
+ rb_str_cat(result, output.dst, output.pos);
115
+ }
116
+ if (ret == 0 && output.pos == 0) {
117
+ break;
118
+ }
112
119
  }
113
120
  return result;
114
121
  }
115
122
 
123
+ static VALUE
124
+ rb_streaming_decompress_decompress_with_pos(VALUE obj, VALUE src)
125
+ {
126
+ StringValue(src);
127
+ const char* input_data = RSTRING_PTR(src);
128
+ size_t input_size = RSTRING_LEN(src);
129
+ ZSTD_inBuffer input = { input_data, input_size, 0 };
130
+
131
+ struct streaming_decompress_t* sd;
132
+ TypedData_Get_Struct(obj, struct streaming_decompress_t, &streaming_decompress_type, sd);
133
+ const char* output_data = RSTRING_PTR(sd->buf);
134
+ VALUE result = rb_str_new(0, 0);
135
+ ZSTD_outBuffer output = { (void*)output_data, sd->buf_size, 0 };
136
+ size_t const ret = zstd_stream_decompress(sd->dctx, &output, &input, false);
137
+ if (ZSTD_isError(ret)) {
138
+ rb_raise(rb_eRuntimeError, "decompress error error code: %s", ZSTD_getErrorName(ret));
139
+ }
140
+ rb_str_cat(result, output.dst, output.pos);
141
+ return rb_ary_new_from_args(2, result, ULONG2NUM(input.pos));
142
+ }
143
+
116
144
  extern VALUE rb_mZstd, cStreamingDecompress;
117
145
  void
118
146
  zstd_ruby_streaming_decompress_init(void)
@@ -121,4 +149,5 @@ zstd_ruby_streaming_decompress_init(void)
121
149
  rb_define_alloc_func(cStreamingDecompress, rb_streaming_decompress_allocate);
122
150
  rb_define_method(cStreamingDecompress, "initialize", rb_streaming_decompress_initialize, -1);
123
151
  rb_define_method(cStreamingDecompress, "decompress", rb_streaming_decompress_decompress, 1);
152
+ rb_define_method(cStreamingDecompress, "decompress_with_pos", rb_streaming_decompress_decompress_with_pos, 1);
124
153
  }
@@ -11,16 +11,15 @@ static VALUE zstdVersion(VALUE self)
11
11
  static VALUE rb_compress(int argc, VALUE *argv, VALUE self)
12
12
  {
13
13
  VALUE input_value;
14
- VALUE compression_level_value;
15
14
  VALUE kwargs;
16
- rb_scan_args(argc, argv, "11:", &input_value, &compression_level_value, &kwargs);
15
+ rb_scan_args(argc, argv, "10:", &input_value, &kwargs);
17
16
 
18
17
  ZSTD_CCtx* const ctx = ZSTD_createCCtx();
19
18
  if (ctx == NULL) {
20
19
  rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCCtx error");
21
20
  }
22
21
 
23
- set_compress_params(ctx, compression_level_value, kwargs);
22
+ set_compress_params(ctx, kwargs);
24
23
 
25
24
  StringValue(input_value);
26
25
  char* input_data = RSTRING_PTR(input_value);
@@ -40,159 +39,91 @@ static VALUE rb_compress(int argc, VALUE *argv, VALUE self)
40
39
  return output;
41
40
  }
42
41
 
43
- static VALUE rb_compress_using_dict(int argc, VALUE *argv, VALUE self)
44
- {
45
- rb_warn("Zstd.compress_using_dict is deprecated; use Zstd.compress with `dict:` instead.");
46
- VALUE input_value;
47
- VALUE dict;
48
- VALUE compression_level_value;
49
- rb_scan_args(argc, argv, "21", &input_value, &dict, &compression_level_value);
50
- int compression_level = convert_compression_level(compression_level_value);
51
-
52
- StringValue(input_value);
53
- char* input_data = RSTRING_PTR(input_value);
54
- size_t input_size = RSTRING_LEN(input_value);
55
- size_t max_compressed_size = ZSTD_compressBound(input_size);
56
-
57
- char* dict_buffer = RSTRING_PTR(dict);
58
- size_t dict_size = RSTRING_LEN(dict);
42
+ static VALUE decode_one_frame(ZSTD_DCtx* dctx, const unsigned char* src, size_t size, VALUE kwargs) {
43
+ VALUE out = rb_str_buf_new(0);
44
+ size_t cap = ZSTD_DStreamOutSize();
45
+ char *buf = ALLOC_N(char, cap);
46
+ ZSTD_inBuffer in = (ZSTD_inBuffer){ src, size, 0 };
59
47
 
60
- ZSTD_CDict* const cdict = ZSTD_createCDict(dict_buffer, dict_size, compression_level);
61
- if (cdict == NULL) {
62
- rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCDict failed");
63
- }
64
- ZSTD_CCtx* const ctx = ZSTD_createCCtx();
65
- if (ctx == NULL) {
66
- ZSTD_freeCDict(cdict);
67
- rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCCtx failed");
68
- }
69
-
70
- VALUE output = rb_str_new(NULL, max_compressed_size);
71
- char* output_data = RSTRING_PTR(output);
72
- size_t const compressed_size = ZSTD_compress_usingCDict(ctx, (void*)output_data, max_compressed_size,
73
- (void*)input_data, input_size, cdict);
74
-
75
- if (ZSTD_isError(compressed_size)) {
76
- ZSTD_freeCDict(cdict);
77
- ZSTD_freeCCtx(ctx);
78
- rb_raise(rb_eRuntimeError, "%s: %s", "compress failed", ZSTD_getErrorName(compressed_size));
79
- }
80
-
81
- rb_str_resize(output, compressed_size);
82
- ZSTD_freeCDict(cdict);
83
- ZSTD_freeCCtx(ctx);
84
- return output;
85
- }
86
-
87
-
88
- static VALUE decompress_buffered(ZSTD_DCtx* dctx, const char* input_data, size_t input_size)
89
- {
90
- ZSTD_inBuffer input = { input_data, input_size, 0 };
91
- VALUE result = rb_str_new(0, 0);
92
-
93
- while (input.pos < input.size) {
94
- ZSTD_outBuffer output = { NULL, 0, 0 };
95
- output.size += ZSTD_DStreamOutSize();
96
- VALUE output_string = rb_str_new(NULL, output.size);
97
- output.dst = RSTRING_PTR(output_string);
48
+ ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only);
49
+ set_decompress_params(dctx, kwargs);
98
50
 
99
- size_t ret = zstd_stream_decompress(dctx, &output, &input, false);
51
+ for (;;) {
52
+ ZSTD_outBuffer o = (ZSTD_outBuffer){ buf, cap, 0 };
53
+ size_t ret = ZSTD_decompressStream(dctx, &o, &in);
100
54
  if (ZSTD_isError(ret)) {
101
- ZSTD_freeDCtx(dctx);
102
- rb_raise(rb_eRuntimeError, "%s: %s", "ZSTD_decompressStream failed", ZSTD_getErrorName(ret));
55
+ xfree(buf);
56
+ rb_raise(rb_eRuntimeError, "ZSTD_decompressStream failed: %s", ZSTD_getErrorName(ret));
57
+ }
58
+ if (o.pos) {
59
+ rb_str_cat(out, buf, o.pos);
60
+ }
61
+ if (ret == 0) {
62
+ break;
103
63
  }
104
- rb_str_cat(result, output.dst, output.pos);
105
64
  }
106
- ZSTD_freeDCtx(dctx);
107
- return result;
65
+ xfree(buf);
66
+ return out;
67
+ }
68
+
69
+ static VALUE decompress_buffered(ZSTD_DCtx* dctx, const char* data, size_t len) {
70
+ return decode_one_frame(dctx, (const unsigned char*)data, len, Qnil);
108
71
  }
109
72
 
110
73
  static VALUE rb_decompress(int argc, VALUE *argv, VALUE self)
111
74
  {
112
- VALUE input_value;
113
- VALUE kwargs;
75
+ VALUE input_value, kwargs;
114
76
  rb_scan_args(argc, argv, "10:", &input_value, &kwargs);
115
77
  StringValue(input_value);
116
- char* input_data = RSTRING_PTR(input_value);
117
- size_t input_size = RSTRING_LEN(input_value);
118
- ZSTD_DCtx* const dctx = ZSTD_createDCtx();
119
- if (dctx == NULL) {
120
- rb_raise(rb_eRuntimeError, "%s", "ZSTD_createDCtx failed");
121
- }
122
- set_decompress_params(dctx, kwargs);
123
-
124
- unsigned long long const uncompressed_size = ZSTD_getFrameContentSize(input_data, input_size);
125
- if (uncompressed_size == ZSTD_CONTENTSIZE_ERROR) {
126
- rb_raise(rb_eRuntimeError, "%s: %s", "not compressed by zstd", ZSTD_getErrorName(uncompressed_size));
127
- }
128
- // ZSTD_decompressStream may be called multiple times when ZSTD_CONTENTSIZE_UNKNOWN, causing slowness.
129
- // Therefore, we will not standardize on ZSTD_decompressStream
130
- if (uncompressed_size == ZSTD_CONTENTSIZE_UNKNOWN) {
131
- return decompress_buffered(dctx, input_data, input_size);
132
- }
133
-
134
- VALUE output = rb_str_new(NULL, uncompressed_size);
135
- char* output_data = RSTRING_PTR(output);
136
-
137
- size_t const decompress_size = zstd_decompress(dctx, output_data, uncompressed_size, input_data, input_size, false);
138
- if (ZSTD_isError(decompress_size)) {
139
- rb_raise(rb_eRuntimeError, "%s: %s", "decompress error", ZSTD_getErrorName(decompress_size));
140
- }
141
- ZSTD_freeDCtx(dctx);
142
- return output;
143
- }
144
78
 
145
- static VALUE rb_decompress_using_dict(int argc, VALUE *argv, VALUE self)
146
- {
147
- rb_warn("Zstd.decompress_using_dict is deprecated; use Zstd.decompress with `dict:` instead.");
148
- VALUE input_value;
149
- VALUE dict;
150
- rb_scan_args(argc, argv, "20", &input_value, &dict);
79
+ size_t in_size = RSTRING_LEN(input_value);
80
+ const unsigned char *in_r = (const unsigned char *)RSTRING_PTR(input_value);
81
+ unsigned char *in = ALLOC_N(unsigned char, in_size);
82
+ memcpy(in, in_r, in_size);
83
+
84
+ size_t off = 0;
85
+ const uint32_t ZSTD_MAGIC = 0xFD2FB528U;
86
+ const uint32_t SKIP_LO = 0x184D2A50U; /* ...5F */
87
+
88
+ while (off + 4 <= in_size) {
89
+ uint32_t magic = (uint32_t)in[off]
90
+ | ((uint32_t)in[off+1] << 8)
91
+ | ((uint32_t)in[off+2] << 16)
92
+ | ((uint32_t)in[off+3] << 24);
93
+
94
+ if ((magic & 0xFFFFFFF0U) == (SKIP_LO & 0xFFFFFFF0U)) {
95
+ if (off + 8 > in_size) break;
96
+ uint32_t skipLen = (uint32_t)in[off+4]
97
+ | ((uint32_t)in[off+5] << 8)
98
+ | ((uint32_t)in[off+6] << 16)
99
+ | ((uint32_t)in[off+7] << 24);
100
+ size_t adv = (size_t)8 + (size_t)skipLen;
101
+ if (off + adv > in_size) break;
102
+ off += adv;
103
+ continue;
104
+ }
151
105
 
152
- StringValue(input_value);
153
- char* input_data = RSTRING_PTR(input_value);
154
- size_t input_size = RSTRING_LEN(input_value);
106
+ if (magic == ZSTD_MAGIC) {
107
+ ZSTD_DCtx *dctx = ZSTD_createDCtx();
108
+ if (!dctx) {
109
+ xfree(in);
110
+ rb_raise(rb_eRuntimeError, "ZSTD_createDCtx failed");
111
+ }
155
112
 
156
- char* dict_buffer = RSTRING_PTR(dict);
157
- size_t dict_size = RSTRING_LEN(dict);
158
- ZSTD_DDict* const ddict = ZSTD_createDDict(dict_buffer, dict_size);
159
- if (ddict == NULL) {
160
- rb_raise(rb_eRuntimeError, "%s", "ZSTD_createDDict failed");
161
- }
162
- unsigned const expected_dict_id = ZSTD_getDictID_fromDDict(ddict);
163
- unsigned const actual_dict_id = ZSTD_getDictID_fromFrame(input_data, input_size);
164
- if (expected_dict_id != actual_dict_id) {
165
- ZSTD_freeDDict(ddict);
166
- rb_raise(rb_eRuntimeError, "DictID mismatch");
167
- }
113
+ VALUE out = decode_one_frame(dctx, in + off, in_size - off, kwargs);
168
114
 
169
- ZSTD_DCtx* const ctx = ZSTD_createDCtx();
170
- if (ctx == NULL) {
171
- ZSTD_freeDDict(ddict);
172
- rb_raise(rb_eRuntimeError, "%s", "ZSTD_createDCtx failed");
173
- }
115
+ ZSTD_freeDCtx(dctx);
116
+ xfree(in);
117
+ RB_GC_GUARD(input_value);
118
+ return out;
119
+ }
174
120
 
175
- unsigned long long const uncompressed_size = ZSTD_getFrameContentSize(input_data, input_size);
176
- if (uncompressed_size == ZSTD_CONTENTSIZE_ERROR) {
177
- ZSTD_freeDDict(ddict);
178
- ZSTD_freeDCtx(ctx);
179
- rb_raise(rb_eRuntimeError, "%s: %s", "not compressed by zstd", ZSTD_getErrorName(uncompressed_size));
180
- }
181
- if (uncompressed_size == ZSTD_CONTENTSIZE_UNKNOWN) {
182
- return decompress_buffered(ctx, input_data, input_size);
121
+ off += 1;
183
122
  }
184
123
 
185
- VALUE output = rb_str_new(NULL, uncompressed_size);
186
- char* output_data = RSTRING_PTR(output);
187
- size_t const decompress_size = ZSTD_decompress_usingDDict(ctx, output_data, uncompressed_size, input_data, input_size, ddict);
188
- if (ZSTD_isError(decompress_size)) {
189
- ZSTD_freeDDict(ddict);
190
- ZSTD_freeDCtx(ctx);
191
- rb_raise(rb_eRuntimeError, "%s: %s", "decompress error", ZSTD_getErrorName(decompress_size));
192
- }
193
- ZSTD_freeDDict(ddict);
194
- ZSTD_freeDCtx(ctx);
195
- return output;
124
+ xfree(in);
125
+ RB_GC_GUARD(input_value);
126
+ rb_raise(rb_eRuntimeError, "not a zstd frame (magic not found)");
196
127
  }
197
128
 
198
129
  static void free_cdict(void *dict)
@@ -284,9 +215,7 @@ zstd_ruby_init(void)
284
215
  {
285
216
  rb_define_module_function(rb_mZstd, "zstd_version", zstdVersion, 0);
286
217
  rb_define_module_function(rb_mZstd, "compress", rb_compress, -1);
287
- rb_define_module_function(rb_mZstd, "compress_using_dict", rb_compress_using_dict, -1);
288
218
  rb_define_module_function(rb_mZstd, "decompress", rb_decompress, -1);
289
- rb_define_module_function(rb_mZstd, "decompress_using_dict", rb_decompress_using_dict, -1);
290
219
 
291
220
  rb_define_alloc_func(rb_cCDict, rb_cdict_alloc);
292
221
  rb_define_private_method(rb_cCDict, "initialize", rb_cdict_initialize, -1);
@@ -1,3 +1,3 @@
1
1
  module Zstd
2
- VERSION = "1.5.7.0"
2
+ VERSION = "2.0.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zstd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.7.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - SpringMT
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-07-06 00:00:00.000000000 Z
10
+ date: 2025-09-29 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -100,6 +99,7 @@ files:
100
99
  - bin/console
101
100
  - bin/setup
102
101
  - ext/zstdruby/common.h
102
+ - ext/zstdruby/exports.txt
103
103
  - ext/zstdruby/extconf.rb
104
104
  - ext/zstdruby/libzstd/common/allocations.h
105
105
  - ext/zstdruby/libzstd/common/bits.h
@@ -187,7 +187,6 @@ homepage: https://github.com/SpringMT/zstd-ruby
187
187
  licenses:
188
188
  - MIT
189
189
  metadata: {}
190
- post_install_message:
191
190
  rdoc_options: []
192
191
  require_paths:
193
192
  - lib
@@ -202,8 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
201
  - !ruby/object:Gem::Version
203
202
  version: '0'
204
203
  requirements: []
205
- rubygems_version: 3.4.19
206
- signing_key:
204
+ rubygems_version: 3.6.2
207
205
  specification_version: 4
208
206
  summary: Ruby binding for zstd(Zstandard - Fast real-time compression algorithm)
209
207
  test_files: []