zstd-ruby 2.0.8 → 2.0.10
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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/Rakefile +26 -0
- data/ext/zstdruby/common.h +13 -34
- data/ext/zstdruby/extconf.rb +1 -1
- data/ext/zstdruby/streaming_compress.c +11 -1
- data/ext/zstdruby/streaming_decompress.c +7 -1
- data/ext/zstdruby/zstdruby.c +11 -6
- data/lib/zstd-ruby/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02555a47928cf342248cbe5335c2a6b4aad14e3c7f9d78bbb65bf83fa3791c16
|
|
4
|
+
data.tar.gz: b58b8db365ce2490998468d785f77b5500023d9f4840aa49ade89413308c9672
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2a700c515e5073d5a20b7383a8c890007aa2792db0fc27e5187bbfd2a3ff806929349d988e8809a29ac0d6616a12f3fab41583c8ab10afe4fd220a35da35e8b
|
|
7
|
+
data.tar.gz: e560ce637e53be29601f6cb610461a8c137808019902c1c2e461390400845a021e22a5749020ec3e6f842a94d02ecfae9fe155ef39485db6dcc7503b23d53967
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -15,6 +15,32 @@ end
|
|
|
15
15
|
|
|
16
16
|
task :default => [:clobber, :compile, :spec]
|
|
17
17
|
|
|
18
|
+
begin
|
|
19
|
+
require 'ruby_memcheck'
|
|
20
|
+
require 'ruby_memcheck/rspec/rake_task'
|
|
21
|
+
|
|
22
|
+
RubyMemcheck.config(
|
|
23
|
+
binary_name: 'zstdruby',
|
|
24
|
+
# Valgrind and YJIT interfere with each other, adding noise and slowdown,
|
|
25
|
+
# so keep YJIT disabled while running under Valgrind.
|
|
26
|
+
ruby: "#{FileUtils::RUBY} --disable-yjit"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
namespace :spec do
|
|
30
|
+
task :check_valgrind do
|
|
31
|
+
unless system('command -v valgrind > /dev/null 2>&1')
|
|
32
|
+
abort("\nValgrind is required for `rake spec:valgrind` but was not found.\n" \
|
|
33
|
+
"Install it first (Linux only), e.g. `sudo apt-get install valgrind`.\n")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
RubyMemcheck::RSpec::RakeTask.new(valgrind: [:check_valgrind, :compile])
|
|
38
|
+
end
|
|
39
|
+
rescue LoadError
|
|
40
|
+
# ruby_memcheck is an optional development dependency, absent on the platforms
|
|
41
|
+
# and Ruby versions the Gemfile excludes. Skip the task instead of breaking.
|
|
42
|
+
end
|
|
43
|
+
|
|
18
44
|
desc 'Sync zstd libs dirs to ext/zstdruby/libzstd'
|
|
19
45
|
task :zstd_update do
|
|
20
46
|
FileUtils.rm_r("ext/zstdruby/libzstd")
|
data/ext/zstdruby/common.h
CHANGED
|
@@ -24,7 +24,11 @@ static int convert_compression_level(ZSTD_CCtx* ctx, VALUE compression_level_val
|
|
|
24
24
|
return NUM2INT(compression_level_value);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
/* Returns the Zstd::CDict given as `dict:`, or Qnil. ZSTD_CCtx_refCDict only
|
|
28
|
+
borrows the pointer, so a caller that keeps the ZSTD_CCtx alive beyond this
|
|
29
|
+
call has to keep the returned object reachable for just as long. A String
|
|
30
|
+
dictionary needs no such handling: ZSTD_CCtx_loadDictionary copies it. */
|
|
31
|
+
static VALUE set_compress_params(ZSTD_CCtx* const ctx, VALUE kwargs)
|
|
28
32
|
{
|
|
29
33
|
ID kwargs_keys[2];
|
|
30
34
|
kwargs_keys[0] = rb_intern("level");
|
|
@@ -46,6 +50,7 @@ static void set_compress_params(ZSTD_CCtx* const ctx, VALUE kwargs)
|
|
|
46
50
|
ZSTD_freeCCtx(ctx);
|
|
47
51
|
rb_raise(rb_eRuntimeError, "%s", "ZSTD_CCtx_refCDict failed");
|
|
48
52
|
}
|
|
53
|
+
return kwargs_values[1];
|
|
49
54
|
} else if (TYPE(kwargs_values[1]) == T_STRING) {
|
|
50
55
|
char* dict_buffer = RSTRING_PTR(kwargs_values[1]);
|
|
51
56
|
size_t dict_size = RSTRING_LEN(kwargs_values[1]);
|
|
@@ -59,6 +64,7 @@ static void set_compress_params(ZSTD_CCtx* const ctx, VALUE kwargs)
|
|
|
59
64
|
rb_raise(rb_eArgError, "`dict:` must be a Zstd::CDict or a String");
|
|
60
65
|
}
|
|
61
66
|
}
|
|
67
|
+
return Qnil;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
struct stream_compress_params {
|
|
@@ -122,7 +128,9 @@ static size_t zstd_compress(ZSTD_CCtx* const ctx, char* output_data, size_t outp
|
|
|
122
128
|
#endif
|
|
123
129
|
}
|
|
124
130
|
|
|
125
|
-
|
|
131
|
+
/* Returns the Zstd::DDict given as `dict:`, or Qnil. See set_compress_params:
|
|
132
|
+
ZSTD_DCtx_refDDict borrows, ZSTD_DCtx_loadDictionary copies. */
|
|
133
|
+
static VALUE set_decompress_params(ZSTD_DCtx* const dctx, VALUE kwargs)
|
|
126
134
|
{
|
|
127
135
|
ID kwargs_keys[1];
|
|
128
136
|
kwargs_keys[0] = rb_intern("dict");
|
|
@@ -137,19 +145,21 @@ static void set_decompress_params(ZSTD_DCtx* const dctx, VALUE kwargs)
|
|
|
137
145
|
ZSTD_freeDCtx(dctx);
|
|
138
146
|
rb_raise(rb_eRuntimeError, "%s", "ZSTD_DCtx_refDDict failed");
|
|
139
147
|
}
|
|
148
|
+
return kwargs_values[0];
|
|
140
149
|
} else if (TYPE(kwargs_values[0]) == T_STRING) {
|
|
141
150
|
char* dict_buffer = RSTRING_PTR(kwargs_values[0]);
|
|
142
151
|
size_t dict_size = RSTRING_LEN(kwargs_values[0]);
|
|
143
152
|
size_t load_dict_ret = ZSTD_DCtx_loadDictionary(dctx, dict_buffer, dict_size);
|
|
144
153
|
if (ZSTD_isError(load_dict_ret)) {
|
|
145
154
|
ZSTD_freeDCtx(dctx);
|
|
146
|
-
rb_raise(rb_eRuntimeError, "%s", "
|
|
155
|
+
rb_raise(rb_eRuntimeError, "%s", "ZSTD_DCtx_loadDictionary failed");
|
|
147
156
|
}
|
|
148
157
|
} else {
|
|
149
158
|
ZSTD_freeDCtx(dctx);
|
|
150
159
|
rb_raise(rb_eArgError, "`dict:` must be a Zstd::DDict or a String");
|
|
151
160
|
}
|
|
152
161
|
}
|
|
162
|
+
return Qnil;
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
struct stream_decompress_params {
|
|
@@ -181,35 +191,4 @@ static size_t zstd_stream_decompress(ZSTD_DCtx* const dctx, ZSTD_outBuffer* outp
|
|
|
181
191
|
#endif
|
|
182
192
|
}
|
|
183
193
|
|
|
184
|
-
struct decompress_params {
|
|
185
|
-
ZSTD_DCtx* dctx;
|
|
186
|
-
char* output_data;
|
|
187
|
-
size_t output_size;
|
|
188
|
-
char* input_data;
|
|
189
|
-
size_t input_size;
|
|
190
|
-
size_t ret;
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
static void* decompress_wrapper(void* args)
|
|
194
|
-
{
|
|
195
|
-
struct decompress_params* params = args;
|
|
196
|
-
params->ret = ZSTD_decompressDCtx(params->dctx, params->output_data, params->output_size, params->input_data, params->input_size);
|
|
197
|
-
return NULL;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
static size_t zstd_decompress(ZSTD_DCtx* const dctx, char* output_data, size_t output_size, char* input_data, size_t input_size, bool gvl)
|
|
201
|
-
{
|
|
202
|
-
#ifdef HAVE_RUBY_THREAD_H
|
|
203
|
-
if (gvl) {
|
|
204
|
-
return ZSTD_decompressDCtx(dctx, output_data, output_size, input_data, input_size);
|
|
205
|
-
} else {
|
|
206
|
-
struct decompress_params params = { dctx, output_data, output_size, input_data, input_size };
|
|
207
|
-
rb_thread_call_without_gvl(decompress_wrapper, ¶ms, NULL, NULL);
|
|
208
|
-
return params.ret;
|
|
209
|
-
}
|
|
210
|
-
#else
|
|
211
|
-
return ZSTD_decompressDCtx(dctx, output_data, output_size, input_data, input_size);
|
|
212
|
-
#endif
|
|
213
|
-
}
|
|
214
|
-
|
|
215
194
|
#endif /* ZSTD_RUBY_H */
|
data/ext/zstdruby/extconf.rb
CHANGED
|
@@ -26,7 +26,7 @@ def ext_export_filename
|
|
|
26
26
|
name
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
$CFLAGS
|
|
29
|
+
$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")))\''
|
|
30
30
|
$CPPFLAGS += " -fdeclspec" if CONFIG['CXX'] =~ /clang/
|
|
31
31
|
|
|
32
32
|
# macOS specific: Use exported_symbols_list to control symbol visibility
|
|
@@ -5,6 +5,7 @@ struct streaming_compress_t {
|
|
|
5
5
|
VALUE buf;
|
|
6
6
|
size_t buf_size;
|
|
7
7
|
VALUE pending; /* accumulate compressed bytes produced by write() */
|
|
8
|
+
VALUE dict; /* Zstd::CDict the ctx borrows a pointer into, or Qnil */
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
static void
|
|
@@ -14,9 +15,11 @@ streaming_compress_mark(void *p)
|
|
|
14
15
|
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
15
16
|
rb_gc_mark_movable(sc->buf);
|
|
16
17
|
rb_gc_mark_movable(sc->pending);
|
|
18
|
+
rb_gc_mark_movable(sc->dict);
|
|
17
19
|
#else
|
|
18
20
|
rb_gc_mark(sc->buf);
|
|
19
21
|
rb_gc_mark(sc->pending);
|
|
22
|
+
rb_gc_mark(sc->dict);
|
|
20
23
|
#endif
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -44,6 +47,7 @@ streaming_compress_compact(void *p)
|
|
|
44
47
|
struct streaming_compress_t *sc = p;
|
|
45
48
|
sc->buf = rb_gc_location(sc->buf);
|
|
46
49
|
sc->pending = rb_gc_location(sc->pending);
|
|
50
|
+
sc->dict = rb_gc_location(sc->dict);
|
|
47
51
|
}
|
|
48
52
|
#endif
|
|
49
53
|
|
|
@@ -69,6 +73,7 @@ rb_streaming_compress_allocate(VALUE klass)
|
|
|
69
73
|
RB_OBJ_WRITE(obj, &sc->buf, Qnil);
|
|
70
74
|
sc->buf_size = 0;
|
|
71
75
|
RB_OBJ_WRITE(obj, &sc->pending, Qnil);
|
|
76
|
+
RB_OBJ_WRITE(obj, &sc->dict, Qnil);
|
|
72
77
|
return obj;
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -86,9 +91,10 @@ rb_streaming_compress_initialize(int argc, VALUE *argv, VALUE obj)
|
|
|
86
91
|
if (ctx == NULL) {
|
|
87
92
|
rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCCtx error");
|
|
88
93
|
}
|
|
89
|
-
set_compress_params(ctx, kwargs);
|
|
94
|
+
VALUE dict = set_compress_params(ctx, kwargs);
|
|
90
95
|
|
|
91
96
|
sc->ctx = ctx;
|
|
97
|
+
RB_OBJ_WRITE(obj, &sc->dict, dict);
|
|
92
98
|
RB_OBJ_WRITE(obj, &sc->buf, rb_str_new(NULL, buffOutSize));
|
|
93
99
|
sc->buf_size = buffOutSize;
|
|
94
100
|
RB_OBJ_WRITE(obj, &sc->pending, rb_str_new(0, 0));
|
|
@@ -205,6 +211,8 @@ rb_streaming_compress_flush(VALUE obj)
|
|
|
205
211
|
VALUE drained = no_compress(sc, ZSTD_e_flush);
|
|
206
212
|
VALUE out = rb_str_dup(sc->pending);
|
|
207
213
|
rb_str_cat(out, RSTRING_PTR(drained), RSTRING_LEN(drained));
|
|
214
|
+
/* prevent `drained` from being freed by GC inside rb_str_cat */
|
|
215
|
+
RB_GC_GUARD(drained);
|
|
208
216
|
rb_str_resize(sc->pending, 0);
|
|
209
217
|
return out;
|
|
210
218
|
}
|
|
@@ -217,6 +225,8 @@ rb_streaming_compress_finish(VALUE obj)
|
|
|
217
225
|
VALUE drained = no_compress(sc, ZSTD_e_end);
|
|
218
226
|
VALUE out = rb_str_dup(sc->pending);
|
|
219
227
|
rb_str_cat(out, RSTRING_PTR(drained), RSTRING_LEN(drained));
|
|
228
|
+
/* prevent `drained` from being freed by GC inside rb_str_cat */
|
|
229
|
+
RB_GC_GUARD(drained);
|
|
220
230
|
rb_str_resize(sc->pending, 0);
|
|
221
231
|
return out;
|
|
222
232
|
}
|
|
@@ -4,6 +4,7 @@ struct streaming_decompress_t {
|
|
|
4
4
|
ZSTD_DCtx* dctx;
|
|
5
5
|
VALUE buf;
|
|
6
6
|
size_t buf_size;
|
|
7
|
+
VALUE dict; /* Zstd::DDict the dctx borrows a pointer into, or Qnil */
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
static void
|
|
@@ -12,8 +13,10 @@ streaming_decompress_mark(void *p)
|
|
|
12
13
|
struct streaming_decompress_t *sd = p;
|
|
13
14
|
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
14
15
|
rb_gc_mark_movable(sd->buf);
|
|
16
|
+
rb_gc_mark_movable(sd->dict);
|
|
15
17
|
#else
|
|
16
18
|
rb_gc_mark(sd->buf);
|
|
19
|
+
rb_gc_mark(sd->dict);
|
|
17
20
|
#endif
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -40,6 +43,7 @@ streaming_decompress_compact(void *p)
|
|
|
40
43
|
{
|
|
41
44
|
struct streaming_decompress_t *sd = p;
|
|
42
45
|
sd->buf = rb_gc_location(sd->buf);
|
|
46
|
+
sd->dict = rb_gc_location(sd->dict);
|
|
43
47
|
}
|
|
44
48
|
#endif
|
|
45
49
|
|
|
@@ -64,6 +68,7 @@ rb_streaming_decompress_allocate(VALUE klass)
|
|
|
64
68
|
sd->dctx = NULL;
|
|
65
69
|
RB_OBJ_WRITE(obj, &sd->buf, Qnil);
|
|
66
70
|
sd->buf_size = 0;
|
|
71
|
+
RB_OBJ_WRITE(obj, &sd->dict, Qnil);
|
|
67
72
|
return obj;
|
|
68
73
|
}
|
|
69
74
|
|
|
@@ -81,9 +86,10 @@ rb_streaming_decompress_initialize(int argc, VALUE *argv, VALUE obj)
|
|
|
81
86
|
if (dctx == NULL) {
|
|
82
87
|
rb_raise(rb_eRuntimeError, "%s", "ZSTD_createDCtx error");
|
|
83
88
|
}
|
|
84
|
-
set_decompress_params(dctx, kwargs);
|
|
89
|
+
VALUE dict = set_decompress_params(dctx, kwargs);
|
|
85
90
|
|
|
86
91
|
sd->dctx = dctx;
|
|
92
|
+
RB_OBJ_WRITE(obj, &sd->dict, dict);
|
|
87
93
|
RB_OBJ_WRITE(obj, &sd->buf, rb_str_new(NULL, buffOutSize));
|
|
88
94
|
sd->buf_size = buffOutSize;
|
|
89
95
|
|
data/ext/zstdruby/zstdruby.c
CHANGED
|
@@ -51,7 +51,8 @@ static VALUE decode_one_frame(ZSTD_DCtx* dctx, const unsigned char* src, size_t
|
|
|
51
51
|
|
|
52
52
|
for (;;) {
|
|
53
53
|
ZSTD_outBuffer o = (ZSTD_outBuffer){ buf, cap, 0 };
|
|
54
|
-
size_t
|
|
54
|
+
size_t const in_pos_before = in.pos;
|
|
55
|
+
size_t ret = zstd_stream_decompress(dctx, &o, &in, false);
|
|
55
56
|
if (ZSTD_isError(ret)) {
|
|
56
57
|
xfree(buf);
|
|
57
58
|
rb_raise(rb_eRuntimeError, "ZSTD_decompressStream failed: %s", ZSTD_getErrorName(ret));
|
|
@@ -62,6 +63,13 @@ static VALUE decode_one_frame(ZSTD_DCtx* dctx, const unsigned char* src, size_t
|
|
|
62
63
|
if (ret == 0) {
|
|
63
64
|
break;
|
|
64
65
|
}
|
|
66
|
+
/* A non-zero return is a "need more input" hint, not an error, and libzstd's
|
|
67
|
+
own noForwardProgress guard is bypassed by the early return it takes on a
|
|
68
|
+
truncated frame header -- so the stall has to be detected here. */
|
|
69
|
+
if (o.pos == 0 && in.pos == in_pos_before) {
|
|
70
|
+
xfree(buf);
|
|
71
|
+
rb_raise(rb_eRuntimeError, "ZSTD_decompressStream failed: truncated or incomplete frame");
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
xfree(buf);
|
|
67
75
|
if (consumed) {
|
|
@@ -70,10 +78,6 @@ static VALUE decode_one_frame(ZSTD_DCtx* dctx, const unsigned char* src, size_t
|
|
|
70
78
|
return out;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
|
-
static VALUE decompress_buffered(ZSTD_DCtx* dctx, const char* data, size_t len) {
|
|
74
|
-
return decode_one_frame(dctx, (const unsigned char*)data, len, Qnil, NULL);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
81
|
static VALUE rb_decompress(int argc, VALUE *argv, VALUE self)
|
|
78
82
|
{
|
|
79
83
|
VALUE input_value, kwargs;
|
|
@@ -124,6 +128,7 @@ static VALUE rb_decompress(int argc, VALUE *argv, VALUE self)
|
|
|
124
128
|
result = out;
|
|
125
129
|
} else {
|
|
126
130
|
rb_str_cat(result, RSTRING_PTR(out), RSTRING_LEN(out));
|
|
131
|
+
RB_GC_GUARD(out);
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
if (consumed == 0) {
|
|
@@ -208,7 +213,7 @@ static VALUE rb_cdict_initialize(int argc, VALUE *argv, VALUE self)
|
|
|
208
213
|
|
|
209
214
|
static VALUE rb_ddict_alloc(VALUE self)
|
|
210
215
|
{
|
|
211
|
-
|
|
216
|
+
ZSTD_DDict* ddict = NULL;
|
|
212
217
|
return TypedData_Wrap_Struct(self, &ddict_type, ddict);
|
|
213
218
|
}
|
|
214
219
|
|
data/lib/zstd-ruby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zstd-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SpringMT
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|