Skip to content

Commit cf9b448

Browse files
authored
Merge pull request #62 from AndrewCEmil/main
Add support for dictionary use with streaming operations
2 parents 7f9f22b + 2197611 commit cf9b448

File tree

5 files changed

+120
-0
lines changed

5 files changed

+120
-0
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ res << stream.compress("def")
6666
res << stream.finish
6767
```
6868

69+
### Streaming Compression using Dictionary
70+
```ruby
71+
stream = Zstd::StreamingCompress.new(dict: IO.read('dictionary_file'))
72+
stream << "abc" << "def"
73+
res = stream.flush
74+
stream << "ghi"
75+
res << stream.finish
76+
```
77+
6978
### Simple Decompression
7079

7180
```ruby
@@ -87,6 +96,15 @@ result << stream.decompress(cstr[0, 10])
8796
result << stream.decompress(cstr[10..-1])
8897
```
8998

99+
### Streaming Decompression using dictionary
100+
```ruby
101+
cstr = "" # Compressed data
102+
stream = Zstd::StreamingDecompress.new(dict: IO.read('dictionary_file'))
103+
result = ''
104+
result << stream.decompress(cstr[0, 10])
105+
result << stream.decompress(cstr[10..-1])
106+
```
107+
90108
### Skippable frame
91109

92110
```ruby

ext/zstdruby/streaming_compress.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ rb_streaming_compress_initialize(int argc, VALUE *argv, VALUE obj)
7575
rb_scan_args(argc, argv, "01", &compression_level_value);
7676
int compression_level = convert_compression_level(compression_level_value);
7777

78+
ID kwargs_keys[1];
79+
kwargs_keys[0] = rb_intern("dict");
80+
VALUE kwargs_values[1];
81+
rb_get_kwargs(kwargs, kwargs_keys, 0, 1, kwargs_values);
82+
7883
struct streaming_compress_t* sc;
7984
TypedData_Get_Struct(obj, struct streaming_compress_t, &streaming_compress_type, sc);
8085
size_t const buffOutSize = ZSTD_CStreamOutSize();
@@ -83,6 +88,14 @@ rb_streaming_compress_initialize(int argc, VALUE *argv, VALUE obj)
8388
if (ctx == NULL) {
8489
rb_raise(rb_eRuntimeError, "%s", "ZSTD_createCCtx error");
8590
}
91+
if (kwargs_values[0] != Qundef && kwargs_values[0] != Qnil) {
92+
char* dict_buffer = RSTRING_PTR(kwargs_values[0]);
93+
size_t dict_size = RSTRING_LEN(kwargs_values[0]);
94+
size_t load_dict_ret = ZSTD_CCtx_loadDictionary(ctx, dict_buffer, dict_size);
95+
if (ZSTD_isError(load_dict_ret)) {
96+
rb_raise(rb_eRuntimeError, "%s", "ZSTD_CCtx_loadDictionary failed");
97+
}
98+
}
8699
ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
87100
sc->ctx = ctx;
88101
sc->buf = rb_str_new(NULL, buffOutSize);

ext/zstdruby/streaming_decompress.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ rb_streaming_decompress_allocate(VALUE klass)
7070
static VALUE
7171
rb_streaming_decompress_initialize(VALUE obj)
7272
{
73+
VALUE kwargs;
74+
rb_scan_args(argc, argv, "00:", &kwargs);
75+
76+
ID kwargs_keys[1];
77+
kwargs_keys[0] = rb_intern("dict");
78+
VALUE kwargs_values[1];
79+
rb_get_kwargs(kwargs, kwargs_keys, 0, 1, kwargs_values);
80+
7381
struct streaming_decompress_t* sd;
7482
TypedData_Get_Struct(obj, struct streaming_decompress_t, &streaming_decompress_type, sd);
7583
size_t const buffOutSize = ZSTD_DStreamOutSize();
@@ -78,6 +86,14 @@ rb_streaming_decompress_initialize(VALUE obj)
7886
if (ctx == NULL) {
7987
rb_raise(rb_eRuntimeError, "%s", "ZSTD_createDCtx error");
8088
}
89+
if (kwargs_values[0] != Qundef && kwargs_values[0] != Qnil) {
90+
char* dict_buffer = RSTRING_PTR(kwargs_values[0]);
91+
size_t dict_size = RSTRING_LEN(kwargs_values[0]);
92+
size_t load_dict_ret = ZSTD_DCtx_loadDictionary(ctx, dict_buffer, dict_size);
93+
if (ZSTD_isError(load_dict_ret)) {
94+
rb_raise(rb_eRuntimeError, "%s", "ZSTD_DCtx_loadDictionary failed");
95+
}
96+
}
8197
sd->ctx = ctx;
8298
sd->buf = rb_str_new(NULL, buffOutSize);
8399
sd->buf_size = buffOutSize;

spec/zstd-ruby-streaming-compress_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,41 @@
5353
end
5454
end
5555

56+
describe 'dictionary' do
57+
let(:dictionary) do
58+
IO.read("#{__dir__}/dictionary")
59+
end
60+
let(:user_json) do
61+
IO.read("#{__dir__}/user_springmt.json")
62+
end
63+
it 'shoud work' do
64+
dict_stream = Zstd::StreamingCompress.new(5, dict: dictionary, no_gvl: no_gvl)
65+
dict_stream << user_json
66+
dict_res = dict_stream.finish
67+
stream = Zstd::StreamingCompress.new(5, no_gvl: no_gvl)
68+
stream << user_json
69+
res = stream.finish
70+
71+
expect(dict_res.length).to be < res.length
72+
end
73+
end
74+
75+
describe 'nil dictionary' do
76+
let(:user_json) do
77+
IO.read("#{__dir__}/user_springmt.json")
78+
end
79+
it 'shoud work' do
80+
dict_stream = Zstd::StreamingCompress.new(5, dict: nil, no_gvl: no_gvl)
81+
dict_stream << user_json
82+
dict_res = dict_stream.finish
83+
stream = Zstd::StreamingCompress.new(5, no_gvl: no_gvl)
84+
stream << user_json
85+
res = stream.finish
86+
87+
expect(dict_res.length).to eq(res.length)
88+
end
89+
end
90+
5691
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0')
5792
describe 'Ractor' do
5893
it 'should be supported' do

spec/zstd-ruby-streaming-decompress_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,44 @@
3232
end
3333
end
3434

35+
describe 'dictionary streaming decompress + GC.compact' do
36+
let(:dictionary) do
37+
IO.read("#{__dir__}/dictionary")
38+
end
39+
let(:user_json) do
40+
IO.read("#{__dir__}/user_springmt.json")
41+
end
42+
it 'shoud work' do
43+
compressed_json = Zstd.compress_using_dict(user_json, dictionary)
44+
stream = Zstd::StreamingDecompress.new(dict: dictionary, no_gvl: no_gvl)
45+
result = ''
46+
result << stream.decompress(compressed_json[0, 5])
47+
result << stream.decompress(compressed_json[5, 5])
48+
GC.compact
49+
result << stream.decompress(compressed_json[10..-1])
50+
expect(result).to eq(user_json)
51+
end
52+
end
53+
54+
describe 'nil dictionary streaming decompress + GC.compact' do
55+
let(:dictionary) do
56+
IO.read("#{__dir__}/dictionary")
57+
end
58+
let(:user_json) do
59+
IO.read("#{__dir__}/user_springmt.json")
60+
end
61+
it 'shoud work' do
62+
compressed_json = Zstd.compress(user_json)
63+
stream = Zstd::StreamingDecompress.new(dict: nil, no_gvl: no_gvl)
64+
result = ''
65+
result << stream.decompress(compressed_json[0, 5])
66+
result << stream.decompress(compressed_json[5, 5])
67+
GC.compact
68+
result << stream.decompress(compressed_json[10..-1])
69+
expect(result).to eq(user_json)
70+
end
71+
end
72+
3573
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0')
3674
describe 'Ractor' do
3775
it 'should be supported' do

0 commit comments

Comments
 (0)