Skip to content

Commit 0b38c7c

Browse files
authored
Merge pull request #56 from SpringMT/feature/add-skippable_frame
feat: add skippable_frame
2 parents 52c05e3 + 317c12c commit 0b38c7c

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

ext/zstdruby/extconf.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require "mkmf"
22

3-
$CFLAGS = '-I. -O3 -std=c99'
3+
$CFLAGS = '-I. -O3 -std=c99 -DZSTD_STATIC_LINKING_ONLY'
44
$CPPFLAGS += " -fdeclspec" if CONFIG['CXX'] =~ /clang/
55

66
Dir.chdir File.expand_path('..', __FILE__) do

ext/zstdruby/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <common.h>
22
VALUE rb_mZstd;
33
void zstd_ruby_init(void);
4+
void zstd_ruby_skippable_frame_init(void);
45
void zstd_ruby_streaming_compress_init(void);
56
void zstd_ruby_streaming_decompress_init(void);
67

@@ -13,6 +14,7 @@ Init_zstdruby(void)
1314

1415
rb_mZstd = rb_define_module("Zstd");
1516
zstd_ruby_init();
17+
zstd_ruby_skippable_frame_init();
1618
zstd_ruby_streaming_compress_init();
1719
zstd_ruby_streaming_decompress_init();
1820
}

ext/zstdruby/skippable_frame.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <common.h>
2+
3+
extern VALUE rb_mZstd;
4+
5+
static VALUE rb_write_skippable_frame(int argc, VALUE *argv, VALUE self)
6+
{
7+
VALUE input_value;
8+
VALUE skip_value;
9+
VALUE kwargs;
10+
rb_scan_args(argc, argv, "2:", &input_value, &skip_value, &kwargs);
11+
12+
ID kwargs_keys[1];
13+
kwargs_keys[0] = rb_intern("magic_variant");
14+
VALUE kwargs_values[1];
15+
rb_get_kwargs(kwargs, kwargs_keys, 0, 1, kwargs_values);
16+
unsigned magic_variant = (kwargs_values[0] != Qundef) ? (NUM2INT(kwargs_values[0])) : 0;
17+
18+
StringValue(input_value);
19+
StringValue(skip_value);
20+
char* input_data = RSTRING_PTR(input_value);
21+
size_t input_size = RSTRING_LEN(input_value);
22+
char* skip_data = RSTRING_PTR(skip_value);
23+
size_t skip_size = RSTRING_LEN(skip_value);
24+
25+
size_t dst_size = input_size + ZSTD_SKIPPABLEHEADERSIZE + skip_size;
26+
VALUE output = rb_str_new(input_data, dst_size);
27+
char* output_data = RSTRING_PTR(output);
28+
size_t output_size = ZSTD_writeSkippableFrame((void*)output_data, dst_size, (const void*)skip_data, skip_size, magic_variant);
29+
if (ZSTD_isError(output_size)) {
30+
rb_raise(rb_eRuntimeError, "%s: %s", "write skippable frame failed", ZSTD_getErrorName(output_size));
31+
}
32+
33+
rb_str_resize(output, output_size);
34+
return output;
35+
}
36+
37+
static VALUE rb_read_skippable_frame(VALUE self, VALUE input_value)
38+
{
39+
char* input_data = RSTRING_PTR(input_value);
40+
size_t input_size = RSTRING_LEN(input_value);
41+
42+
if (ZSTD_isSkippableFrame(input_data, input_size) == 0) {
43+
return Qnil;
44+
}
45+
// ref https://github.com/facebook/zstd/blob/321490cd5b9863433b3d44816d04012874e5ecdb/tests/fuzzer.c#L2096
46+
size_t const skipLen = 129 * 1024;
47+
VALUE output = rb_str_new(NULL, skipLen);
48+
char* output_data = RSTRING_PTR(output);
49+
unsigned readMagic;
50+
size_t output_size = ZSTD_readSkippableFrame((void*)output_data, skipLen, &readMagic, (const void*)input_data, input_size);
51+
if (ZSTD_isError(output_size)) {
52+
rb_raise(rb_eRuntimeError, "%s: %s", "read skippable frame failed", ZSTD_getErrorName(output_size));
53+
}
54+
rb_str_resize(output, output_size);
55+
return output;
56+
}
57+
58+
void
59+
zstd_ruby_skippable_frame_init(void)
60+
{
61+
rb_define_module_function(rb_mZstd, "write_skippable_frame", rb_write_skippable_frame, -1);
62+
rb_define_module_function(rb_mZstd, "read_skippable_frame", rb_read_skippable_frame, 1);
63+
}

spec/zstd-skippable_frame_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require "spec_helper"
2+
require 'zstd-ruby'
3+
require 'securerandom'
4+
5+
RSpec.describe Zstd do
6+
describe 'read_skippable_frame' do
7+
context 'simple string' do
8+
it '' do
9+
expect(Zstd.read_skippable_frame('abc')).to eq nil
10+
end
11+
end
12+
context 'compressed string' do
13+
it '' do
14+
expect(Zstd.read_skippable_frame(Zstd.compress(SecureRandom.hex(150)))).to eq nil
15+
end
16+
end
17+
context 'compressed string + skippable frame' do
18+
it '' do
19+
compressed_data = Zstd.compress(SecureRandom.hex(150))
20+
compressed_data_with_skippable_frame = Zstd.write_skippable_frame(compressed_data, "sample data")
21+
expect(Zstd.read_skippable_frame(compressed_data_with_skippable_frame)).to eq "sample data"
22+
end
23+
end
24+
25+
context 'compressed string + skippable frame + magic_variant' do
26+
it '' do
27+
compressed_data = Zstd.compress(SecureRandom.hex(150))
28+
compressed_data_with_skippable_frame = Zstd.write_skippable_frame(compressed_data, "sample data", magic_variant: 1)
29+
expect(Zstd.read_skippable_frame(compressed_data_with_skippable_frame)).to eq "sample data"
30+
end
31+
end
32+
33+
end
34+
end

0 commit comments

Comments
 (0)