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 +4 -4
- data/README.md +28 -0
- data/ext/zstdruby/common.h +1 -4
- data/ext/zstdruby/exports.txt +1 -0
- data/ext/zstdruby/extconf.rb +6 -1
- data/ext/zstdruby/main.c +1 -1
- data/ext/zstdruby/streaming_compress.c +28 -13
- data/ext/zstdruby/streaming_decompress.c +31 -2
- data/ext/zstdruby/zstdruby.c +69 -140
- data/lib/zstd-ruby/version.rb +1 -1
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91a2e54ff93b0c602d6b2d064f6f8c615a59f0ef963c20b24e4db6d17643163e
|
4
|
+
data.tar.gz: cbeb4c7c21187336a284cb714765fb80b02b982c6b4fafa60784d8f7950fe528
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/ext/zstdruby/common.h
CHANGED
@@ -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
|
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
|
data/ext/zstdruby/extconf.rb
CHANGED
@@ -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
@@ -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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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
|
197
|
-
|
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
|
206
|
-
|
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
|
-
|
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
|
}
|
data/ext/zstdruby/zstdruby.c
CHANGED
@@ -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, "
|
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,
|
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
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
102
|
-
rb_raise(rb_eRuntimeError, "
|
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
|
-
|
107
|
-
return
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
115
|
+
ZSTD_freeDCtx(dctx);
|
116
|
+
xfree(in);
|
117
|
+
RB_GC_GUARD(input_value);
|
118
|
+
return out;
|
119
|
+
}
|
174
120
|
|
175
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
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);
|
data/lib/zstd-ruby/version.rb
CHANGED
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:
|
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-
|
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.
|
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: []
|