Skip to content

Commit 8187f8e

Browse files
committed
feat: add skippable_frame
1 parent 52c05e3 commit 8187f8e

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
rb_scan_args(argc, argv, "2", &input_value, &skip_value);
10+
11+
StringValue(input_value);
12+
StringValue(skip_value);
13+
char* input_data = RSTRING_PTR(input_value);
14+
size_t input_size = RSTRING_LEN(input_value);
15+
char* skip_data = RSTRING_PTR(skip_value);
16+
size_t skip_size = RSTRING_LEN(skip_value);
17+
18+
size_t dst_size = input_size + ZSTD_SKIPPABLEHEADERSIZE + skip_size;
19+
VALUE output = rb_str_new(input_data, dst_size);
20+
char* output_data = RSTRING_PTR(output);
21+
size_t output_size = ZSTD_writeSkippableFrame((void*)output_data, dst_size, (const void*)skip_data, skip_size, (unsigned)0);
22+
if (ZSTD_isError(output_size)) {
23+
rb_raise(rb_eRuntimeError, "%s: %s", "write skippable frame failed", ZSTD_getErrorName(output_size));
24+
}
25+
26+
rb_str_resize(output, output_size);
27+
return output;
28+
}
29+
30+
static VALUE rb_read_skippable_frame(VALUE self, VALUE input_value)
31+
{
32+
char* input_data = RSTRING_PTR(input_value);
33+
size_t input_size = RSTRING_LEN(input_value);
34+
35+
if (ZSTD_isSkippableFrame(input_data, input_size) == 0) {
36+
return Qnil;
37+
}
38+
// ref https://github.com/facebook/zstd/blob/321490cd5b9863433b3d44816d04012874e5ecdb/tests/fuzzer.c#L2096
39+
size_t const skipLen = 129 * 1024;
40+
VALUE output = rb_str_new(NULL, skipLen);
41+
char* output_data = RSTRING_PTR(output);
42+
size_t output_size = ZSTD_readSkippableFrame((void*)output_data, skipLen, (unsigned int*)0, (const void*)input_data, input_size);
43+
if (ZSTD_isError(output_size)) {
44+
rb_raise(rb_eRuntimeError, "%s: %s", "read skippable frame failed", ZSTD_getErrorName(output_size));
45+
}
46+
rb_str_resize(output, output_size);
47+
return output;
48+
}
49+
50+
void
51+
zstd_ruby_skippable_frame_init(void)
52+
{
53+
rb_define_module_function(rb_mZstd, "write_skippable_frame", rb_write_skippable_frame, -1);
54+
rb_define_module_function(rb_mZstd, "read_skippable_frame", rb_read_skippable_frame, 1);
55+
}

spec/zstd-skippable_frame_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
end
25+
end

0 commit comments

Comments
 (0)