ruby-zstds 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 359ef05325e4af260831df1c3d71b70d64d1b4f235631ad3cd0bf10be63be308
4
- data.tar.gz: 39b138930c23a914317db548081db091a607b00867300788d3de3c22e7e56616
3
+ metadata.gz: f860451f1cf45988053d7d5512e26e18a5c5b446f9f75fbf06f3a9ef3103c806
4
+ data.tar.gz: a224e7b3e16b3f2e1d2785fc024241813e3fb934351e5a851744eb1e2ae82783
5
5
  SHA512:
6
- metadata.gz: 0c76ce3fe240fb963dab49e55ff167a2cfea690e1bb48b107d164821d5bba1b5090eb81b73ec221a86aa4c94c05f9082d540d6c0423ae99326face5deee199d8
7
- data.tar.gz: 00db3a70938e3ca436d253f9c2e715941837c252569bb6d72d1ddb94b1c8c9e85aeeb1bd67e9c85a0ab965c5393f785f273e67a0a3869a1ae770a915ae23ca9e
6
+ metadata.gz: 76ee0c342ebb71bf4ae6ac0a6139d3a8d7966da4843eece7c819b84e531fab6e501fa7ebafaa1c864f1200771b2b119ef885a827ba50da27b6112803a2858c7a
7
+ data.tar.gz: 575a15688165bccf52dc6f73a26fa1ac0f3a1ba0bf010384e9e34ecda8183bb904efb369635f75052420f86c84f59b906d7e46a65f301ab19e2c888d9ca96983
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Ruby bindings for zstd library
2
2
 
3
- | Travis | AppVeyor | Circle | Codecov |
4
- | :---: | :---: | :---: | :---: |
5
- | [![Travis test status](https://travis-ci.com/andrew-aladev/ruby-zstds.svg?branch=master)](https://travis-ci.com/andrew-aladev/ruby-zstds) | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-zstds?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-zstds/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-zstds/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-zstds/tree/master) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-zstds/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-zstds) |
3
+ | Travis | AppVeyor | Circle | Codecov | Gem |
4
+ | :---: | :---: | :---: | :---: | :---: |
5
+ | [![Travis test status](https://travis-ci.com/andrew-aladev/ruby-zstds.svg?branch=master)](https://travis-ci.com/andrew-aladev/ruby-zstds) | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-zstds?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-zstds/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-zstds/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-zstds/tree/master) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-zstds/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-zstds) | [![Gem](https://img.shields.io/gem/v/ruby-zstds.svg)](https://rubygems.org/gems/ruby-zstds) |
6
6
 
7
7
  See [zstd library](https://github.com/facebook/zstd).
8
8
 
@@ -79,7 +79,7 @@ data = ZSTDS::String.compress "sample string", :dictionary => dictionary
79
79
  puts ZSTDS::String.decompress(data, :dictionary => dictionary)
80
80
  ```
81
81
 
82
- You can create and read `tar.zst` archives with `minitar` for example.
82
+ You can create and read `tar.zst` archives with [minitar](https://github.com/halostatue/minitar) for example.
83
83
 
84
84
  ```ruby
85
85
  require "zstds"
@@ -101,12 +101,25 @@ ZSTDS::Stream::Reader.open "file.tar.zst" do |reader|
101
101
  end
102
102
  ```
103
103
 
104
+ You can also use `Content-Encoding: zstd` with [sinatra](http://sinatrarb.com):
105
+
106
+ ```ruby
107
+ require "zstds"
108
+ require "sinatra"
109
+
110
+ get "/" do
111
+ headers["Content-Encoding"] = "zstd"
112
+ ZSTDS::String.compress "sample string"
113
+ end
114
+ ```
115
+
104
116
  ## Options
105
117
 
106
118
  | Option | Values | Default | Description |
107
119
  |---------------------------------|----------------|------------|-------------|
108
120
  | `source_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for source data |
109
121
  | `destination_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for description data |
122
+ | `gvl` | true/false | false | enables global VM lock where possible |
110
123
  | `compression_level` | -131072 - 22 | 0 (auto) | compression level |
111
124
  | `window_log` | 10 - 31 | 0 (auto) | maximum back-reference distance (power of 2) |
112
125
  | `hash_log` | 6 - 30 | 0 (auto) | size of the initial probe table (power of 2) |
@@ -134,6 +147,10 @@ There are internal buffers for compressed and decompressed data.
134
147
  For example you want to use 1 KB as `source_buffer_length` for compressor - please use 256 B as `destination_buffer_length`.
135
148
  You want to use 256 B as `source_buffer_length` for decompressor - please use 1 KB as `destination_buffer_length`.
136
149
 
150
+ `gvl` is disabled by default, this mode allows running multiple compressors/decompressors in different threads simultaneously.
151
+ Please consider enabling `gvl` if you don't want to launch processors in separate threads.
152
+ If `gvl` is enabled ruby won't waste time on acquiring/releasing VM lock.
153
+
137
154
  `String` and `File` will set `:pledged_size` automaticaly.
138
155
 
139
156
  You can also read zstd docs for more info about options.
@@ -161,6 +178,7 @@ Possible compressor options:
161
178
  ```
162
179
  :source_buffer_length
163
180
  :destination_buffer_length
181
+ :gvl
164
182
  :compression_level
165
183
  :window_log
166
184
  :hash_log
@@ -188,6 +206,7 @@ Possible decompressor options:
188
206
  ```
189
207
  :source_buffer_length
190
208
  :destination_buffer_length
209
+ :gvl
191
210
  :window_log_max
192
211
  :dictionary
193
212
  ```
@@ -201,18 +220,6 @@ data = ZSTDS::String.compress "sample string", :compression_level => 5
201
220
  puts ZSTDS::String.decompress(data, :window_log_max => 11)
202
221
  ```
203
222
 
204
- HTTP encoding (`Content-Encoding: zstd`) using default options:
205
-
206
- ```ruby
207
- require "zstds"
208
- require "sinatra"
209
-
210
- get "/" do
211
- headers["Content-Encoding"] = "zstd"
212
- ZSTDS::String.compress "sample string"
213
- end
214
- ```
215
-
216
223
  ## String
217
224
 
218
225
  String maintains destination buffer only, so it accepts `destination_buffer_length` option only.
@@ -418,12 +425,18 @@ Please use regular constructor to create dictionary from buffer.
418
425
 
419
426
  Read dictionary id from buffer.
420
427
 
428
+ ## Thread safety
429
+
430
+ `:gvl` option is disabled by default, you can use bindings effectively in multiple threads.
431
+ Please be careful: bindings are not thread safe.
432
+ You should lock all shared data between threads.
433
+
421
434
  ## CI
422
435
 
423
- See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
424
436
  Please visit [scripts/test-images](scripts/test-images).
425
- You can run this test script using many native and cross images.
437
+ See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
438
+ You can run this script using many native and cross images.
426
439
 
427
440
  ## License
428
441
 
429
- MIT license, see LICENSE and AUTHORS.
442
+ MIT license, see [LICENSE](LICENSE) and [AUTHORS](AUTHORS).
@@ -3,6 +3,10 @@
3
3
 
4
4
  require "mkmf"
5
5
 
6
+ have_func "rb_thread_call_without_gvl", "ruby/thread.h"
7
+
8
+ # Old zstd versions has bug: underlinking against pthreads.
9
+ # https://bugs.gentoo.org/713940
6
10
  $LDFLAGS << " -pthread" # rubocop:disable Style/GlobalVars
7
11
 
8
12
  def require_header(name, types = [])
@@ -52,6 +56,7 @@ require_library(
52
56
  ZSTD_DStreamOutSize
53
57
  ZSTD_compressStream2
54
58
  ZSTD_decompressStream
59
+ ZDICT_isError
55
60
  ZDICT_getDictID
56
61
  ZDICT_trainFromBuffer
57
62
  ]
@@ -12,10 +12,11 @@ VALUE zstds_ext_create_string_buffer(VALUE length)
12
12
  return rb_str_new(NULL, NUM2SIZET(length));
13
13
  }
14
14
 
15
- VALUE zstds_ext_resize_string_buffer(VALUE args)
15
+ VALUE zstds_ext_resize_string_buffer(VALUE buffer_args)
16
16
  {
17
- VALUE buffer = rb_ary_entry(args, 0);
18
- VALUE length = rb_ary_entry(args, 1);
17
+ VALUE buffer = rb_ary_entry(buffer_args, 0);
18
+ VALUE length = rb_ary_entry(buffer_args, 1);
19
+
19
20
  return rb_str_resize(buffer, NUM2SIZET(length));
20
21
  }
21
22
 
@@ -11,12 +11,12 @@ VALUE zstds_ext_create_string_buffer(VALUE length);
11
11
  #define ZSTDS_EXT_CREATE_STRING_BUFFER(buffer, length, exception) \
12
12
  VALUE buffer = rb_protect(zstds_ext_create_string_buffer, SIZET2NUM(length), &exception);
13
13
 
14
- VALUE zstds_ext_resize_string_buffer(VALUE args);
14
+ VALUE zstds_ext_resize_string_buffer(VALUE buffer_args);
15
15
 
16
- #define ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
17
- VALUE args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
18
- buffer = rb_protect(zstds_ext_resize_string_buffer, args, &exception); \
19
- RB_GC_GUARD(args);
16
+ #define ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
17
+ VALUE buffer_args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
18
+ buffer = rb_protect(zstds_ext_resize_string_buffer, buffer_args, &exception); \
19
+ RB_GC_GUARD(buffer_args);
20
20
 
21
21
  void zstds_ext_buffer_exports(VALUE root_module);
22
22
 
@@ -9,36 +9,92 @@
9
9
  #include "ruby.h"
10
10
  #include "zstds_ext/buffer.h"
11
11
  #include "zstds_ext/error.h"
12
+ #include "zstds_ext/gvl.h"
12
13
  #include "zstds_ext/macro.h"
13
14
  #include "zstds_ext/option.h"
14
15
 
15
- VALUE zstds_ext_get_dictionary_buffer_id(VALUE ZSTDS_EXT_UNUSED(self), VALUE buffer)
16
+ // -- initialization --
17
+
18
+ typedef struct
16
19
  {
17
- unsigned int id = ZDICT_getDictID(RSTRING_PTR(buffer), RSTRING_LEN(buffer));
18
- if (id == 0) {
19
- zstds_ext_raise_error(ZSTDS_EXT_ERROR_VALIDATE_FAILED);
20
+ const char* data;
21
+ size_t size;
22
+ } sample_t;
23
+
24
+ typedef struct
25
+ {
26
+ const sample_t* samples;
27
+ size_t length;
28
+ char* buffer;
29
+ size_t capacity;
30
+ zstds_result_t result;
31
+ zstds_ext_result_t ext_result;
32
+ } train_args_t;
33
+
34
+ static inline void* train_wrapper(void* data)
35
+ {
36
+ train_args_t* args = data;
37
+ const sample_t* samples = args->samples;
38
+ size_t length = args->length;
39
+ size_t size = 0;
40
+
41
+ for (size_t index = 0; index < length; index++) {
42
+ size += samples[index].size;
20
43
  }
21
44
 
22
- return UINT2NUM(id);
45
+ zstds_ext_byte_t* group = malloc(size);
46
+ if (group == NULL) {
47
+ args->ext_result = ZSTDS_EXT_ERROR_ALLOCATE_FAILED;
48
+ return NULL;
49
+ }
50
+
51
+ size_t* sizes = malloc(length * sizeof(size_t));
52
+ if (sizes == NULL) {
53
+ free(group);
54
+ args->ext_result = ZSTDS_EXT_ERROR_ALLOCATE_FAILED;
55
+ return NULL;
56
+ }
57
+
58
+ size_t offset = 0;
59
+
60
+ for (size_t index = 0; index < length; index++) {
61
+ const sample_t* sample_ptr = &samples[index];
62
+ size_t sample_size = sample_ptr->size;
63
+
64
+ memmove(group + offset, sample_ptr->data, sample_size);
65
+ offset += sample_size;
66
+
67
+ sizes[index] = sample_size;
68
+ }
69
+
70
+ args->result = ZDICT_trainFromBuffer((void*) args->buffer, args->capacity, group, sizes, (unsigned int) length);
71
+
72
+ free(group);
73
+ free(sizes);
74
+
75
+ if (ZDICT_isError(args->result)) {
76
+ args->ext_result = zstds_ext_get_error(ZSTD_getErrorCode(args->result));
77
+ return NULL;
78
+ }
79
+
80
+ args->ext_result = 0;
81
+
82
+ return NULL;
23
83
  }
24
84
 
25
- VALUE zstds_ext_train_dictionary_buffer(VALUE ZSTDS_EXT_UNUSED(self), VALUE samples, VALUE options)
85
+ VALUE zstds_ext_train_dictionary_buffer(VALUE ZSTDS_EXT_UNUSED(self), VALUE raw_samples, VALUE options)
26
86
  {
27
- Check_Type(samples, T_ARRAY);
87
+ Check_Type(raw_samples, T_ARRAY);
28
88
 
29
- size_t sample_index;
30
- unsigned int samples_length = (unsigned int)RARRAY_LEN(samples);
31
- size_t samples_size = 0;
89
+ size_t length = RARRAY_LEN(raw_samples);
32
90
 
33
- for (sample_index = 0; sample_index < samples_length; sample_index++) {
34
- VALUE sample = rb_ary_entry(samples, sample_index);
35
- Check_Type(sample, T_STRING);
36
-
37
- samples_size += RSTRING_LEN(sample);
91
+ for (size_t index = 0; index < length; index++) {
92
+ Check_Type(rb_ary_entry(raw_samples, index), T_STRING);
38
93
  }
39
94
 
40
95
  Check_Type(options, T_HASH);
41
- ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, capacity);
96
+ ZSTDS_EXT_GET_BOOL_OPTION(options, gvl);
97
+ ZSTDS_EXT_GET_SIZE_OPTION(options, capacity);
42
98
 
43
99
  if (capacity == 0) {
44
100
  capacity = ZSTDS_EXT_DEFAULT_DICTIONARY_CAPACITY;
@@ -51,42 +107,34 @@ VALUE zstds_ext_train_dictionary_buffer(VALUE ZSTDS_EXT_UNUSED(self), VALUE samp
51
107
  zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
52
108
  }
53
109
 
54
- zstds_ext_byte_t* samples_buffer = malloc(samples_size);
55
- if (samples_buffer == NULL) {
110
+ sample_t* samples = malloc(sizeof(sample_t) * length);
111
+ if (samples == NULL) {
56
112
  zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
57
113
  }
58
114
 
59
- size_t* samples_sizes = malloc(samples_length * sizeof(size_t));
60
- if (samples_sizes == NULL) {
61
- free(samples_buffer);
62
- zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
63
- }
115
+ for (size_t index = 0; index < length; index++) {
116
+ VALUE raw_sample = rb_ary_entry(raw_samples, index);
117
+ sample_t* sample = &samples[index];
64
118
 
65
- size_t sample_offset = 0;
66
-
67
- for (sample_index = 0; sample_index < samples_length; sample_index++) {
68
- VALUE sample = rb_ary_entry(samples, sample_index);
69
- const char* sample_data = RSTRING_PTR(sample);
70
- size_t sample_size = RSTRING_LEN(sample);
71
-
72
- memmove(samples_buffer + sample_offset, sample_data, sample_size);
73
- sample_offset += sample_size;
74
-
75
- samples_sizes[sample_index] = sample_size;
119
+ sample->data = RSTRING_PTR(raw_sample);
120
+ sample->size = RSTRING_LEN(raw_sample);
76
121
  }
77
122
 
78
- zstds_result_t result = ZDICT_trainFromBuffer(
79
- RSTRING_PTR(buffer), capacity,
80
- samples_buffer, samples_sizes, samples_length);
123
+ train_args_t args = {
124
+ .samples = samples,
125
+ .length = length,
126
+ .buffer = RSTRING_PTR(buffer),
127
+ .capacity = capacity,
128
+ };
81
129
 
82
- free(samples_buffer);
83
- free(samples_sizes);
130
+ ZSTDS_EXT_GVL_WRAP(gvl, train_wrapper, &args);
131
+ free(samples);
84
132
 
85
- if (ZSTD_isError(result)) {
86
- zstds_ext_raise_error(zstds_ext_get_error(ZSTD_getErrorCode(result)));
133
+ if (args.ext_result != 0) {
134
+ zstds_ext_raise_error(args.ext_result);
87
135
  }
88
136
 
89
- ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, result, exception);
137
+ ZSTDS_EXT_RESIZE_STRING_BUFFER(buffer, args.result, exception);
90
138
  if (exception != 0) {
91
139
  zstds_ext_raise_error(ZSTDS_EXT_ERROR_ALLOCATE_FAILED);
92
140
  }
@@ -94,6 +142,20 @@ VALUE zstds_ext_train_dictionary_buffer(VALUE ZSTDS_EXT_UNUSED(self), VALUE samp
94
142
  return buffer;
95
143
  }
96
144
 
145
+ // -- other --
146
+
147
+ VALUE zstds_ext_get_dictionary_buffer_id(VALUE ZSTDS_EXT_UNUSED(self), VALUE buffer)
148
+ {
149
+ unsigned int id = ZDICT_getDictID(RSTRING_PTR(buffer), RSTRING_LEN(buffer));
150
+ if (id == 0) {
151
+ zstds_ext_raise_error(ZSTDS_EXT_ERROR_VALIDATE_FAILED);
152
+ }
153
+
154
+ return UINT2NUM(id);
155
+ }
156
+
157
+ // -- exports --
158
+
97
159
  void zstds_ext_dictionary_exports(VALUE root_module)
98
160
  {
99
161
  VALUE dictionary = rb_define_class_under(root_module, "Dictionary", rb_cObject);
@@ -8,8 +8,8 @@
8
8
 
9
9
  #define ZSTDS_EXT_DEFAULT_DICTIONARY_CAPACITY (1 << 17); // 128 KB
10
10
 
11
- VALUE zstds_ext_get_dictionary_buffer_id(VALUE self, VALUE buffer);
12
11
  VALUE zstds_ext_train_dictionary_buffer(VALUE self, VALUE samples, VALUE options);
12
+ VALUE zstds_ext_get_dictionary_buffer_id(VALUE self, VALUE buffer);
13
13
 
14
14
  void zstds_ext_dictionary_exports(VALUE root_module);
15
15
 
@@ -40,7 +40,7 @@ zstds_ext_result_t zstds_ext_get_error(ZSTD_ErrorCode error_code)
40
40
  }
41
41
  }
42
42
 
43
- static inline NORETURN(void raise(const char *name, const char *description))
43
+ static inline NORETURN(void raise(const char* name, const char* description))
44
44
  {
45
45
  VALUE module = rb_define_module(ZSTDS_EXT_MODULE_NAME);
46
46
  VALUE error = rb_const_get(module, rb_intern(name));
@@ -11,7 +11,8 @@
11
11
 
12
12
  // Results for errors listed in "lib/zstds/error" used in c extension.
13
13
 
14
- enum {
14
+ enum
15
+ {
15
16
  ZSTDS_EXT_ERROR_ALLOCATE_FAILED = 1,
16
17
  ZSTDS_EXT_ERROR_VALIDATE_FAILED,
17
18
 
@@ -0,0 +1,24 @@
1
+ // Ruby bindings for zstd library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(ZSTDS_EXT_GVL_H)
5
+ #define ZSTDS_EXT_GVL_H
6
+
7
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
8
+
9
+ #include "ruby/thread.h"
10
+
11
+ #define ZSTDS_EXT_GVL_WRAP(gvl, function, data) \
12
+ if (gvl) { \
13
+ function((void*) data); \
14
+ } else { \
15
+ rb_thread_call_without_gvl(function, (void*) data, RUBY_UBF_IO, NULL); \
16
+ }
17
+
18
+ #else
19
+
20
+ #define ZSTDS_EXT_GVL_WRAP(_gvl, function, data) function((void*) data);
21
+
22
+ #endif
23
+
24
+ #endif // ZSTDS_EXT_GVL_H
@@ -1,26 +1,29 @@
1
1
  // Ruby bindings for zstd library.
2
2
  // Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
- #include "ruby/io.h"
4
+ #include "zstds_ext/io.h"
5
5
 
6
6
  #include <stdio.h>
7
7
  #include <string.h>
8
8
  #include <zstd.h>
9
9
 
10
10
  #include "ruby.h"
11
+ #include "ruby/io.h"
11
12
  #include "zstds_ext/error.h"
12
- #include "zstds_ext/io.h"
13
+ #include "zstds_ext/gvl.h"
13
14
  #include "zstds_ext/macro.h"
14
15
  #include "zstds_ext/option.h"
15
16
 
16
17
  // Additional possible results:
17
- enum {
18
+ enum
19
+ {
18
20
  ZSTDS_EXT_FILE_READ_FINISHED = 128
19
21
  };
20
22
 
21
23
  // -- file --
22
24
 
23
- static inline zstds_ext_result_t read_file(FILE* source_file, zstds_ext_byte_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
25
+ static inline zstds_ext_result_t
26
+ read_file(FILE* source_file, zstds_ext_byte_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
24
27
  {
25
28
  size_t read_length = fread(source_buffer, 1, source_buffer_length, source_file);
26
29
  if (read_length == 0 && feof(source_file)) {
@@ -36,7 +39,8 @@ static inline zstds_ext_result_t read_file(FILE* source_file, zstds_ext_byte_t*
36
39
  return 0;
37
40
  }
38
41
 
39
- static inline zstds_ext_result_t write_file(FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_length)
42
+ static inline zstds_ext_result_t
43
+ write_file(FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_length)
40
44
  {
41
45
  size_t written_length = fwrite(destination_buffer, 1, destination_length, destination_file);
42
46
  if (written_length != destination_length) {
@@ -49,8 +53,10 @@ static inline zstds_ext_result_t write_file(FILE* destination_file, zstds_ext_by
49
53
  // -- buffer --
50
54
 
51
55
  static inline zstds_ext_result_t create_buffers(
52
- zstds_ext_byte_t** source_buffer_ptr, size_t source_buffer_length,
53
- zstds_ext_byte_t** destination_buffer_ptr, size_t destination_buffer_length)
56
+ zstds_ext_byte_t** source_buffer_ptr,
57
+ size_t source_buffer_length,
58
+ zstds_ext_byte_t** destination_buffer_ptr,
59
+ size_t destination_buffer_length)
54
60
  {
55
61
  zstds_ext_byte_t* source_buffer = malloc(source_buffer_length);
56
62
  if (source_buffer == NULL) {
@@ -77,8 +83,10 @@ static inline zstds_ext_result_t create_buffers(
77
83
 
78
84
  static inline zstds_ext_result_t read_more_source(
79
85
  FILE* source_file,
80
- const zstds_ext_byte_t** source_ptr, size_t* source_length_ptr,
81
- zstds_ext_byte_t* source_buffer, size_t source_buffer_length)
86
+ const zstds_ext_byte_t** source_ptr,
87
+ size_t* source_length_ptr,
88
+ zstds_ext_byte_t* source_buffer,
89
+ size_t source_buffer_length)
82
90
  {
83
91
  const zstds_ext_byte_t* source = *source_ptr;
84
92
  size_t source_length = *source_length_ptr;
@@ -101,7 +109,9 @@ static inline zstds_ext_result_t read_more_source(
101
109
  zstds_ext_byte_t* remaining_source_buffer = source_buffer + source_length;
102
110
  size_t new_source_length;
103
111
 
104
- zstds_ext_result_t ext_result = read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
112
+ zstds_ext_result_t ext_result =
113
+ read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
114
+
105
115
  if (ext_result != 0) {
106
116
  return ext_result;
107
117
  }
@@ -111,42 +121,37 @@ static inline zstds_ext_result_t read_more_source(
111
121
  return 0;
112
122
  }
113
123
 
114
- #define BUFFERED_READ_SOURCE(function, ...) \
115
- do { \
116
- bool is_function_called = false; \
117
- \
118
- while (true) { \
119
- ext_result = read_more_source( \
120
- source_file, \
121
- &source, &source_length, \
122
- source_buffer, source_buffer_length); \
123
- \
124
- if (ext_result == ZSTDS_EXT_FILE_READ_FINISHED) { \
125
- if (source_length != 0) { \
126
- /* ZSTD won't provide any remainder by design. */ \
127
- return ZSTDS_EXT_ERROR_READ_IO; \
128
- } \
129
- break; \
130
- } \
131
- else if (ext_result != 0) { \
132
- return ext_result; \
133
- } \
134
- \
135
- ext_result = function(__VA_ARGS__); \
136
- if (ext_result != 0) { \
137
- return ext_result; \
138
- } \
139
- \
140
- is_function_called = true; \
141
- } \
142
- \
143
- if (!is_function_called) { \
144
- /* Function should be called at least once. */ \
145
- ext_result = function(__VA_ARGS__); \
146
- if (ext_result != 0) { \
147
- return ext_result; \
148
- } \
149
- } \
124
+ #define BUFFERED_READ_SOURCE(function, ...) \
125
+ do { \
126
+ bool is_function_called = false; \
127
+ \
128
+ while (true) { \
129
+ ext_result = read_more_source(source_file, &source, &source_length, source_buffer, source_buffer_length); \
130
+ if (ext_result == ZSTDS_EXT_FILE_READ_FINISHED) { \
131
+ if (source_length != 0) { \
132
+ /* ZSTD won't provide any remainder by design. */ \
133
+ return ZSTDS_EXT_ERROR_READ_IO; \
134
+ } \
135
+ break; \
136
+ } else if (ext_result != 0) { \
137
+ return ext_result; \
138
+ } \
139
+ \
140
+ ext_result = function(__VA_ARGS__); \
141
+ if (ext_result != 0) { \
142
+ return ext_result; \
143
+ } \
144
+ \
145
+ is_function_called = true; \
146
+ } \
147
+ \
148
+ if (!is_function_called) { \
149
+ /* Function should be called at least once. */ \
150
+ ext_result = function(__VA_ARGS__); \
151
+ if (ext_result != 0) { \
152
+ return ext_result; \
153
+ } \
154
+ } \
150
155
  } while (false);
151
156
 
152
157
  // Algorithm has written data into destination buffer.
@@ -155,7 +160,9 @@ static inline zstds_ext_result_t read_more_source(
155
160
 
156
161
  static inline zstds_ext_result_t flush_destination_buffer(
157
162
  FILE* destination_file,
158
- zstds_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
163
+ zstds_ext_byte_t* destination_buffer,
164
+ size_t* destination_length_ptr,
165
+ size_t destination_buffer_length)
159
166
  {
160
167
  if (*destination_length_ptr == 0) {
161
168
  // We want to write more data at once, than buffer has.
@@ -172,7 +179,8 @@ static inline zstds_ext_result_t flush_destination_buffer(
172
179
  return 0;
173
180
  }
174
181
 
175
- static inline zstds_ext_result_t write_remaining_destination(FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_length)
182
+ static inline zstds_ext_result_t
183
+ write_remaining_destination(FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_length)
176
184
  {
177
185
  if (destination_length == 0) {
178
186
  return 0;
@@ -194,39 +202,57 @@ static inline zstds_ext_result_t write_remaining_destination(FILE* destination_f
194
202
  zstds_ext_raise_error(ZSTDS_EXT_ERROR_ACCESS_IO); \
195
203
  }
196
204
 
197
- // -- compress --
205
+ // -- buffered compress --
206
+
207
+ typedef struct
208
+ {
209
+ ZSTD_CCtx* ctx;
210
+ ZSTD_inBuffer* in_buffer_ptr;
211
+ ZSTD_outBuffer* out_buffer_ptr;
212
+ zstds_result_t result;
213
+ } compress_args_t;
214
+
215
+ static inline void* compress_wrapper(void* data)
216
+ {
217
+ compress_args_t* args = data;
218
+
219
+ args->result = ZSTD_compressStream2(args->ctx, args->out_buffer_ptr, args->in_buffer_ptr, ZSTD_e_continue);
220
+
221
+ return NULL;
222
+ }
198
223
 
199
224
  static inline zstds_ext_result_t buffered_compress(
200
225
  ZSTD_CCtx* ctx,
201
- const zstds_ext_byte_t** source_ptr, size_t* source_length_ptr,
202
- FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
226
+ const zstds_ext_byte_t** source_ptr,
227
+ size_t* source_length_ptr,
228
+ FILE* destination_file,
229
+ zstds_ext_byte_t* destination_buffer,
230
+ size_t* destination_length_ptr,
231
+ size_t destination_buffer_length,
232
+ bool gvl)
203
233
  {
204
- zstds_result_t result;
205
234
  zstds_ext_result_t ext_result;
206
-
207
- ZSTD_inBuffer in_buffer;
208
- in_buffer.src = *source_ptr;
209
- in_buffer.size = *source_length_ptr;
210
- in_buffer.pos = 0;
211
-
212
- ZSTD_outBuffer out_buffer;
235
+ ZSTD_inBuffer in_buffer = {.src = *source_ptr, .size = *source_length_ptr, .pos = 0};
236
+ compress_args_t args = {.ctx = ctx, .in_buffer_ptr = &in_buffer};
213
237
 
214
238
  while (true) {
215
- out_buffer.dst = destination_buffer + *destination_length_ptr;
216
- out_buffer.size = destination_buffer_length - *destination_length_ptr;
217
- out_buffer.pos = 0;
239
+ ZSTD_outBuffer out_buffer = {
240
+ .dst = destination_buffer + *destination_length_ptr,
241
+ .size = destination_buffer_length - *destination_length_ptr,
242
+ .pos = 0};
218
243
 
219
- result = ZSTD_compressStream2(ctx, &out_buffer, &in_buffer, ZSTD_e_continue);
220
- if (ZSTD_isError(result)) {
221
- return zstds_ext_get_error(ZSTD_getErrorCode(result));
244
+ args.out_buffer_ptr = &out_buffer;
245
+
246
+ ZSTDS_EXT_GVL_WRAP(gvl, compress_wrapper, &args);
247
+ if (ZSTD_isError(args.result)) {
248
+ return zstds_ext_get_error(ZSTD_getErrorCode(args.result));
222
249
  }
223
250
 
224
251
  *destination_length_ptr += out_buffer.pos;
225
252
 
226
253
  if (*destination_length_ptr == destination_buffer_length) {
227
254
  ext_result = flush_destination_buffer(
228
- destination_file,
229
- destination_buffer, destination_length_ptr, destination_buffer_length);
255
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
230
256
 
231
257
  if (ext_result != 0) {
232
258
  return ext_result;
@@ -244,36 +270,55 @@ static inline zstds_ext_result_t buffered_compress(
244
270
  return 0;
245
271
  }
246
272
 
247
- static inline zstds_ext_result_t buffered_compressor_finish(
248
- ZSTD_CCtx* ctx,
249
- FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
273
+ // -- buffered compressor finish --
274
+
275
+ typedef struct
250
276
  {
251
- zstds_result_t result;
252
- zstds_ext_result_t ext_result;
277
+ ZSTD_CCtx* ctx;
278
+ ZSTD_inBuffer* in_buffer_ptr;
279
+ ZSTD_outBuffer* out_buffer_ptr;
280
+ zstds_result_t result;
281
+ } compressor_finish_args_t;
253
282
 
254
- ZSTD_inBuffer in_buffer;
255
- in_buffer.src = NULL;
256
- in_buffer.size = 0;
257
- in_buffer.pos = 0;
283
+ static inline void* compressor_finish_wrapper(void* data)
284
+ {
285
+ compressor_finish_args_t* args = data;
258
286
 
259
- ZSTD_outBuffer out_buffer;
287
+ args->result = ZSTD_compressStream2(args->ctx, args->out_buffer_ptr, args->in_buffer_ptr, ZSTD_e_end);
288
+
289
+ return NULL;
290
+ }
291
+
292
+ static inline zstds_ext_result_t buffered_compressor_finish(
293
+ ZSTD_CCtx* ctx,
294
+ FILE* destination_file,
295
+ zstds_ext_byte_t* destination_buffer,
296
+ size_t* destination_length_ptr,
297
+ size_t destination_buffer_length,
298
+ bool gvl)
299
+ {
300
+ zstds_ext_result_t ext_result;
301
+ ZSTD_inBuffer in_buffer = {in_buffer.src = NULL, in_buffer.size = 0, in_buffer.pos = 0};
302
+ compressor_finish_args_t args = {.ctx = ctx, .in_buffer_ptr = &in_buffer};
260
303
 
261
304
  while (true) {
262
- out_buffer.dst = destination_buffer + *destination_length_ptr;
263
- out_buffer.size = destination_buffer_length - *destination_length_ptr;
264
- out_buffer.pos = 0;
305
+ ZSTD_outBuffer out_buffer = {
306
+ out_buffer.dst = destination_buffer + *destination_length_ptr,
307
+ out_buffer.size = destination_buffer_length - *destination_length_ptr,
308
+ out_buffer.pos = 0};
265
309
 
266
- result = ZSTD_compressStream2(ctx, &out_buffer, &in_buffer, ZSTD_e_end);
267
- if (ZSTD_isError(result)) {
268
- return zstds_ext_get_error(ZSTD_getErrorCode(result));
310
+ args.out_buffer_ptr = &out_buffer;
311
+
312
+ ZSTDS_EXT_GVL_WRAP(gvl, compressor_finish_wrapper, &args);
313
+ if (ZSTD_isError(args.result)) {
314
+ return zstds_ext_get_error(ZSTD_getErrorCode(args.result));
269
315
  }
270
316
 
271
317
  *destination_length_ptr += out_buffer.pos;
272
318
 
273
- if (result != 0) {
319
+ if (args.result != 0) {
274
320
  ext_result = flush_destination_buffer(
275
- destination_file,
276
- destination_buffer, destination_length_ptr, destination_buffer_length);
321
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
277
322
 
278
323
  if (ext_result != 0) {
279
324
  return ext_result;
@@ -288,13 +333,19 @@ static inline zstds_ext_result_t buffered_compressor_finish(
288
333
  return 0;
289
334
  }
290
335
 
336
+ // -- compress --
337
+
291
338
  static inline zstds_ext_result_t compress(
292
- ZSTD_CCtx* ctx,
293
- FILE* source_file, zstds_ext_byte_t* source_buffer, size_t source_buffer_length,
294
- FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_buffer_length)
339
+ ZSTD_CCtx* ctx,
340
+ FILE* source_file,
341
+ zstds_ext_byte_t* source_buffer,
342
+ size_t source_buffer_length,
343
+ FILE* destination_file,
344
+ zstds_ext_byte_t* destination_buffer,
345
+ size_t destination_buffer_length,
346
+ bool gvl)
295
347
  {
296
- zstds_ext_result_t ext_result;
297
-
348
+ zstds_ext_result_t ext_result;
298
349
  const zstds_ext_byte_t* source = source_buffer;
299
350
  size_t source_length = 0;
300
351
  size_t destination_length = 0;
@@ -302,12 +353,16 @@ static inline zstds_ext_result_t compress(
302
353
  BUFFERED_READ_SOURCE(
303
354
  buffered_compress,
304
355
  ctx,
305
- &source, &source_length,
306
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
356
+ &source,
357
+ &source_length,
358
+ destination_file,
359
+ destination_buffer,
360
+ &destination_length,
361
+ destination_buffer_length,
362
+ gvl);
307
363
 
308
364
  ext_result = buffered_compressor_finish(
309
- ctx,
310
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
365
+ ctx, destination_file, destination_buffer, &destination_length, destination_buffer_length, gvl);
311
366
 
312
367
  if (ext_result != 0) {
313
368
  return ext_result;
@@ -321,9 +376,10 @@ VALUE zstds_ext_compress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE de
321
376
  GET_FILE(source);
322
377
  GET_FILE(destination);
323
378
  Check_Type(options, T_HASH);
379
+ ZSTDS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
380
+ ZSTDS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
381
+ ZSTDS_EXT_GET_BOOL_OPTION(options, gvl);
324
382
  ZSTDS_EXT_GET_COMPRESSOR_OPTIONS(options);
325
- ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
326
- ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
327
383
 
328
384
  ZSTD_CCtx* ctx = ZSTD_createCCtx();
329
385
  if (ctx == NULL) {
@@ -346,10 +402,7 @@ VALUE zstds_ext_compress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE de
346
402
  zstds_ext_byte_t* source_buffer;
347
403
  zstds_ext_byte_t* destination_buffer;
348
404
 
349
- ext_result = create_buffers(
350
- &source_buffer, source_buffer_length,
351
- &destination_buffer, destination_buffer_length);
352
-
405
+ ext_result = create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
353
406
  if (ext_result != 0) {
354
407
  ZSTD_freeCCtx(ctx);
355
408
  zstds_ext_raise_error(ext_result);
@@ -357,8 +410,13 @@ VALUE zstds_ext_compress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE de
357
410
 
358
411
  ext_result = compress(
359
412
  ctx,
360
- source_file, source_buffer, source_buffer_length,
361
- destination_file, destination_buffer, destination_buffer_length);
413
+ source_file,
414
+ source_buffer,
415
+ source_buffer_length,
416
+ destination_file,
417
+ destination_buffer,
418
+ destination_buffer_length,
419
+ gvl);
362
420
 
363
421
  free(source_buffer);
364
422
  free(destination_buffer);
@@ -374,39 +432,57 @@ VALUE zstds_ext_compress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE de
374
432
  return Qnil;
375
433
  }
376
434
 
377
- // -- decompress --
435
+ // -- buffered decompress --
436
+
437
+ typedef struct
438
+ {
439
+ ZSTD_DCtx* ctx;
440
+ ZSTD_inBuffer* in_buffer_ptr;
441
+ ZSTD_outBuffer* out_buffer_ptr;
442
+ zstds_result_t result;
443
+ } decompress_args_t;
444
+
445
+ static inline void* decompress_wrapper(void* data)
446
+ {
447
+ decompress_args_t* args = data;
448
+
449
+ args->result = ZSTD_decompressStream(args->ctx, args->out_buffer_ptr, args->in_buffer_ptr);
450
+
451
+ return NULL;
452
+ }
378
453
 
379
454
  static inline zstds_ext_result_t buffered_decompress(
380
455
  ZSTD_DCtx* ctx,
381
- const zstds_ext_byte_t** source_ptr, size_t* source_length_ptr,
382
- FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
456
+ const zstds_ext_byte_t** source_ptr,
457
+ size_t* source_length_ptr,
458
+ FILE* destination_file,
459
+ zstds_ext_byte_t* destination_buffer,
460
+ size_t* destination_length_ptr,
461
+ size_t destination_buffer_length,
462
+ bool gvl)
383
463
  {
384
- zstds_result_t result;
385
464
  zstds_ext_result_t ext_result;
386
-
387
- ZSTD_inBuffer in_buffer;
388
- in_buffer.src = *source_ptr;
389
- in_buffer.size = *source_length_ptr;
390
- in_buffer.pos = 0;
391
-
392
- ZSTD_outBuffer out_buffer;
465
+ ZSTD_inBuffer in_buffer = {.src = *source_ptr, .size = *source_length_ptr, .pos = 0};
466
+ decompress_args_t args = {.ctx = ctx, .in_buffer_ptr = &in_buffer};
393
467
 
394
468
  while (true) {
395
- out_buffer.dst = destination_buffer + *destination_length_ptr;
396
- out_buffer.size = destination_buffer_length - *destination_length_ptr;
397
- out_buffer.pos = 0;
469
+ ZSTD_outBuffer out_buffer = {
470
+ .dst = destination_buffer + *destination_length_ptr,
471
+ .size = destination_buffer_length - *destination_length_ptr,
472
+ .pos = 0};
473
+
474
+ args.out_buffer_ptr = &out_buffer;
398
475
 
399
- result = ZSTD_decompressStream(ctx, &out_buffer, &in_buffer);
400
- if (ZSTD_isError(result)) {
401
- return zstds_ext_get_error(ZSTD_getErrorCode(result));
476
+ ZSTDS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
477
+ if (ZSTD_isError(args.result)) {
478
+ return zstds_ext_get_error(ZSTD_getErrorCode(args.result));
402
479
  }
403
480
 
404
481
  *destination_length_ptr += out_buffer.pos;
405
482
 
406
483
  if (*destination_length_ptr == destination_buffer_length) {
407
484
  ext_result = flush_destination_buffer(
408
- destination_file,
409
- destination_buffer, destination_length_ptr, destination_buffer_length);
485
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
410
486
 
411
487
  if (ext_result != 0) {
412
488
  return ext_result;
@@ -424,13 +500,19 @@ static inline zstds_ext_result_t buffered_decompress(
424
500
  return 0;
425
501
  }
426
502
 
503
+ // -- decompress --
504
+
427
505
  static inline zstds_ext_result_t decompress(
428
- ZSTD_DCtx* ctx,
429
- FILE* source_file, zstds_ext_byte_t* source_buffer, size_t source_buffer_length,
430
- FILE* destination_file, zstds_ext_byte_t* destination_buffer, size_t destination_buffer_length)
506
+ ZSTD_DCtx* ctx,
507
+ FILE* source_file,
508
+ zstds_ext_byte_t* source_buffer,
509
+ size_t source_buffer_length,
510
+ FILE* destination_file,
511
+ zstds_ext_byte_t* destination_buffer,
512
+ size_t destination_buffer_length,
513
+ bool gvl)
431
514
  {
432
- zstds_ext_result_t ext_result;
433
-
515
+ zstds_ext_result_t ext_result;
434
516
  const zstds_ext_byte_t* source = source_buffer;
435
517
  size_t source_length = 0;
436
518
  size_t destination_length = 0;
@@ -438,8 +520,13 @@ static inline zstds_ext_result_t decompress(
438
520
  BUFFERED_READ_SOURCE(
439
521
  buffered_decompress,
440
522
  ctx,
441
- &source, &source_length,
442
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
523
+ &source,
524
+ &source_length,
525
+ destination_file,
526
+ destination_buffer,
527
+ &destination_length,
528
+ destination_buffer_length,
529
+ gvl);
443
530
 
444
531
  return write_remaining_destination(destination_file, destination_buffer, destination_length);
445
532
  }
@@ -449,9 +536,10 @@ VALUE zstds_ext_decompress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE
449
536
  GET_FILE(source);
450
537
  GET_FILE(destination);
451
538
  Check_Type(options, T_HASH);
539
+ ZSTDS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
540
+ ZSTDS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
541
+ ZSTDS_EXT_GET_BOOL_OPTION(options, gvl);
452
542
  ZSTDS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
453
- ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
454
- ZSTDS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
455
543
 
456
544
  ZSTD_DCtx* ctx = ZSTD_createDCtx();
457
545
  if (ctx == NULL) {
@@ -474,10 +562,7 @@ VALUE zstds_ext_decompress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE
474
562
  zstds_ext_byte_t* source_buffer;
475
563
  zstds_ext_byte_t* destination_buffer;
476
564
 
477
- ext_result = create_buffers(
478
- &source_buffer, source_buffer_length,
479
- &destination_buffer, destination_buffer_length);
480
-
565
+ ext_result = create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
481
566
  if (ext_result != 0) {
482
567
  ZSTD_freeDCtx(ctx);
483
568
  zstds_ext_raise_error(ext_result);
@@ -485,8 +570,13 @@ VALUE zstds_ext_decompress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE
485
570
 
486
571
  ext_result = decompress(
487
572
  ctx,
488
- source_file, source_buffer, source_buffer_length,
489
- destination_file, destination_buffer, destination_buffer_length);
573
+ source_file,
574
+ source_buffer,
575
+ source_buffer_length,
576
+ destination_file,
577
+ destination_buffer,
578
+ destination_buffer_length,
579
+ gvl);
490
580
 
491
581
  free(source_buffer);
492
582
  free(destination_buffer);
@@ -502,6 +592,8 @@ VALUE zstds_ext_decompress_io(VALUE ZSTDS_EXT_UNUSED(self), VALUE source, VALUE
502
592
  return Qnil;
503
593
  }
504
594
 
595
+ // -- exports --
596
+
505
597
  void zstds_ext_io_exports(VALUE root_module)
506
598
  {
507
599
  rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(zstds_ext_compress_io), 3);