ruby-brs 1.1.5 → 1.2.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: a66abcbdc6c29138075e622a75c3af830c333b3534429d97c57d08f568e0c6ae
4
- data.tar.gz: 1326fc334ec25dcfef28777cbe9defa15ffde9d84df48773bfd5199d140510a1
3
+ metadata.gz: 239de67878a08e54f6b89af1381711ad94c70c1f29d99c8dca22ced06ebd850a
4
+ data.tar.gz: 2601159e2ede572b8bcdc8ce0ee7ca738f06f42cb0d6e59a159eb525e2f70420
5
5
  SHA512:
6
- metadata.gz: 690e7ac3f938f10028ea88d50933a5f6b59f7ad8040966356655760bc5dc3deaaa27a89fc3b98e85565edf373015075ca1424a6c894557c6d6dc7c52bee83a9f
7
- data.tar.gz: 2daa6c5c07566869cdb1f5826a8ab9584ff7c1371140edfba33dcd1bdbaf1ea02dcbebb2c0ee405c44692658bdbbaad326482c4069d761d05e86a4dfc8dcdb9d
6
+ metadata.gz: d12cfbf9280a63ad850ce4198e1bdaa044afc4502bccdeadeef8af746f1806182fb560bd580dd0bafafeaff93fd8e2e40435d7ac12079bad89a79b9cb22ef088
7
+ data.tar.gz: 13cb28b02d174fe3c448f291b85a80625cabb3f07ab32f5a5233a244425299ed04f248737be934d9f3171c6bc93c31062cbb1e7daf9f146e46a617163298a678
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Ruby bindings for brotli library
2
2
 
3
- | Travis | AppVeyor | Circle | Codecov |
4
- | :---: | :---: | :---: | :---: |
5
- | [![Travis test status](https://travis-ci.com/andrew-aladev/ruby-brs.svg?branch=master)](https://travis-ci.com/andrew-aladev/ruby-brs) | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-brs?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-brs/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-brs/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-brs/tree/master) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-brs/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-brs) |
3
+ | Travis | AppVeyor | Circle | Codecov | Gem |
4
+ | :---: | :---: | :---: | :---: | :---: |
5
+ | [![Travis test status](https://travis-ci.com/andrew-aladev/ruby-brs.svg?branch=master)](https://travis-ci.com/andrew-aladev/ruby-brs) | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/ruby-brs?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/ruby-brs/branch/master) | [![Circle test status](https://circleci.com/gh/andrew-aladev/ruby-brs/tree/master.svg?style=shield)](https://circleci.com/gh/andrew-aladev/ruby-brs/tree/master) | [![Codecov](https://codecov.io/gh/andrew-aladev/ruby-brs/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/ruby-brs) | [![Gem](https://img.shields.io/gem/v/ruby-brs.svg)](https://rubygems.org/gems/ruby-brs) |
6
6
 
7
7
  See [brotli library](https://github.com/google/brotli).
8
8
 
@@ -61,7 +61,7 @@ ensure
61
61
  end
62
62
  ```
63
63
 
64
- You can create and read `tar.br` archives with `minitar` for example.
64
+ You can create and read `tar.br` archives with [minitar](https://github.com/halostatue/minitar) for example.
65
65
 
66
66
  ```ruby
67
67
  require "brs"
@@ -83,12 +83,25 @@ BRS::Stream::Reader.open "file.tar.br" do |reader|
83
83
  end
84
84
  ```
85
85
 
86
+ You can also use `Content-Encoding: br` with [sinatra](http://sinatrarb.com):
87
+
88
+ ```ruby
89
+ require "brs"
90
+ require "sinatra"
91
+
92
+ get "/" do
93
+ headers["Content-Encoding"] = "br"
94
+ BRS::String.compress "sample string"
95
+ end
96
+ ```
97
+
86
98
  ## Options
87
99
 
88
100
  | Option | Values | Default | Description |
89
101
  |------------------------------------|------------|------------|-------------|
90
102
  | `source_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for source data |
91
103
  | `destination_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for description data |
104
+ | `gvl` | true/false | false | enables global VM lock where possible |
92
105
  | `mode` | `MODES` | `:generic` | compressor mode |
93
106
  | `quality` | 0 - 11 | 11 | compression level |
94
107
  | `lgwin` | 10 - 24 | 22 | compressor window size |
@@ -102,6 +115,10 @@ There are internal buffers for compressed and decompressed data.
102
115
  For example you want to use 1 KB as `source_buffer_length` for compressor - please use 256 B as `destination_buffer_length`.
103
116
  You want to use 256 B as `source_buffer_length` for decompressor - please use 1 KB as `destination_buffer_length`.
104
117
 
118
+ `gvl` is disabled by default, this mode allows running multiple compressors/decompressors in different threads simultaneously.
119
+ Please consider enabling `gvl` if you don't want to launch processors in separate threads.
120
+ If `gvl` is enabled ruby won't waste time on acquiring/releasing VM lock.
121
+
105
122
  `String` and `File` will set `:size_hint` automaticaly.
106
123
 
107
124
  You can also read brotli docs for more info about options.
@@ -117,6 +134,7 @@ Possible compressor options:
117
134
  ```
118
135
  :source_buffer_length
119
136
  :destination_buffer_length
137
+ :gvl
120
138
  :mode
121
139
  :quality
122
140
  :lgwin
@@ -130,6 +148,7 @@ Possible decompressor options:
130
148
  ```
131
149
  :source_buffer_length
132
150
  :destination_buffer_length
151
+ :gvl
133
152
  :disable_ring_buffer_reallocation
134
153
  :large_window
135
154
  ```
@@ -143,18 +162,6 @@ data = BRS::String.compress "sample string", :quality => 5
143
162
  puts BRS::String.decompress(data, :disable_ring_buffer_reallocation => true)
144
163
  ```
145
164
 
146
- HTTP encoding (`Content-Encoding: br`) using default options:
147
-
148
- ```ruby
149
- require "brs"
150
- require "sinatra"
151
-
152
- get "/" do
153
- headers["Content-Encoding"] = "br"
154
- BRS::String.compress "sample string"
155
- end
156
- ```
157
-
158
165
  ## String
159
166
 
160
167
  String maintains destination buffer only, so it accepts `destination_buffer_length` option only.
@@ -330,12 +337,18 @@ See [`IO`](https://ruby-doc.org/core-2.7.0/IO.html) docs.
330
337
 
331
338
  Typical helpers, see [`Zlib::GzipReader`](https://ruby-doc.org/stdlib-2.7.0/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
332
339
 
340
+ ## Thread safety
341
+
342
+ `:gvl` option is disabled by default, you can use bindings effectively in multiple threads.
343
+ Please be careful: bindings are not thread safe.
344
+ You should lock all shared data between threads.
345
+
333
346
  ## CI
334
347
 
335
- See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
336
348
  Please visit [scripts/test-images](scripts/test-images).
337
- You can run this test script using many native and cross images.
349
+ See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
350
+ You can run this script using many native and cross images.
338
351
 
339
352
  ## License
340
353
 
341
- MIT license, see LICENSE and AUTHORS.
354
+ MIT license, see [LICENSE](LICENSE) and [AUTHORS](AUTHORS).
@@ -10,10 +10,11 @@ VALUE brs_ext_create_string_buffer(VALUE length)
10
10
  return rb_str_new(NULL, NUM2SIZET(length));
11
11
  }
12
12
 
13
- VALUE brs_ext_resize_string_buffer(VALUE args)
13
+ VALUE brs_ext_resize_string_buffer(VALUE buffer_args)
14
14
  {
15
- VALUE buffer = rb_ary_entry(args, 0);
16
- VALUE length = rb_ary_entry(args, 1);
15
+ VALUE buffer = rb_ary_entry(buffer_args, 0);
16
+ VALUE length = rb_ary_entry(buffer_args, 1);
17
+
17
18
  return rb_str_resize(buffer, NUM2SIZET(length));
18
19
  }
19
20
 
@@ -21,8 +22,21 @@ void brs_ext_buffer_exports(VALUE root_module)
21
22
  {
22
23
  VALUE module = rb_define_module_under(root_module, "Buffer");
23
24
 
24
- rb_define_const(module, "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR", SIZET2NUM(BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR));
25
- rb_define_const(module, "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR", SIZET2NUM(BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR));
26
- rb_define_const(module, "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR", SIZET2NUM(BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR));
27
- rb_define_const(module, "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR", SIZET2NUM(BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR));
25
+ rb_define_const(
26
+ module, "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR", SIZET2NUM(BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR));
27
+
28
+ rb_define_const(
29
+ module,
30
+ "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR",
31
+ SIZET2NUM(BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR));
32
+
33
+ rb_define_const(
34
+ module,
35
+ "DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR",
36
+ SIZET2NUM(BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR));
37
+
38
+ rb_define_const(
39
+ module,
40
+ "DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR",
41
+ SIZET2NUM(BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR));
28
42
  }
@@ -6,10 +6,10 @@
6
6
 
7
7
  #include "ruby.h"
8
8
 
9
- #define BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR (1 << 18) // 256 KB
9
+ #define BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_COMPRESSOR (1 << 18) // 256 KB
10
10
  #define BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_COMPRESSOR (1 << 16) // 64 KB
11
11
 
12
- #define BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR (1 << 16) // 64 KB
12
+ #define BRS_DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR (1 << 16) // 64 KB
13
13
  #define BRS_DEFAULT_DESTINATION_BUFFER_LENGTH_FOR_DECOMPRESSOR (1 << 18) // 256 KB
14
14
 
15
15
  VALUE brs_ext_create_string_buffer(VALUE length);
@@ -17,12 +17,12 @@ VALUE brs_ext_create_string_buffer(VALUE length);
17
17
  #define BRS_EXT_CREATE_STRING_BUFFER(buffer, length, exception) \
18
18
  VALUE buffer = rb_protect(brs_ext_create_string_buffer, SIZET2NUM(length), &exception);
19
19
 
20
- VALUE brs_ext_resize_string_buffer(VALUE args);
20
+ VALUE brs_ext_resize_string_buffer(VALUE buffer_args);
21
21
 
22
- #define BRS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
23
- VALUE args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
24
- buffer = rb_protect(brs_ext_resize_string_buffer, args, &exception); \
25
- RB_GC_GUARD(args);
22
+ #define BRS_EXT_RESIZE_STRING_BUFFER(buffer, length, exception) \
23
+ VALUE buffer_args = rb_ary_new_from_args(2, buffer, SIZET2NUM(length)); \
24
+ buffer = rb_protect(brs_ext_resize_string_buffer, buffer_args, &exception); \
25
+ RB_GC_GUARD(buffer_args);
26
26
 
27
27
  void brs_ext_buffer_exports(VALUE root_module);
28
28
 
@@ -11,7 +11,8 @@
11
11
 
12
12
  // Results for errors listed in "lib/brs/error" used in c extension.
13
13
 
14
- enum {
14
+ enum
15
+ {
15
16
  BRS_EXT_ERROR_ALLOCATE_FAILED = 1,
16
17
  BRS_EXT_ERROR_VALIDATE_FAILED,
17
18
 
@@ -0,0 +1,24 @@
1
+ // Ruby bindings for brotli library.
2
+ // Copyright (c) 2019 AUTHORS, MIT License.
3
+
4
+ #if !defined(BRS_EXT_GVL_H)
5
+ #define BRS_EXT_GVL_H
6
+
7
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
8
+
9
+ #include "ruby/thread.h"
10
+
11
+ #define BRS_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 BRS_EXT_GVL_WRAP(_gvl, function, data) function((void*) data);
21
+
22
+ #endif
23
+
24
+ #endif // BRS_EXT_GVL_H
@@ -1,7 +1,7 @@
1
1
  // Ruby bindings for brotli library.
2
2
  // Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
- #include "ruby/io.h"
4
+ #include "brs_ext/io.h"
5
5
 
6
6
  #include <brotli/decode.h>
7
7
  #include <brotli/encode.h>
@@ -12,19 +12,22 @@
12
12
 
13
13
  #include "brs_ext/buffer.h"
14
14
  #include "brs_ext/error.h"
15
- #include "brs_ext/io.h"
15
+ #include "brs_ext/gvl.h"
16
16
  #include "brs_ext/macro.h"
17
17
  #include "brs_ext/option.h"
18
18
  #include "ruby.h"
19
+ #include "ruby/io.h"
19
20
 
20
21
  // Additional possible results:
21
- enum {
22
+ enum
23
+ {
22
24
  BRS_EXT_FILE_READ_FINISHED = 128
23
25
  };
24
26
 
25
27
  // -- file --
26
28
 
27
- static inline brs_ext_result_t read_file(FILE* source_file, brs_ext_byte_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
29
+ static inline brs_ext_result_t
30
+ read_file(FILE* source_file, brs_ext_byte_t* source_buffer, size_t* source_length_ptr, size_t source_buffer_length)
28
31
  {
29
32
  size_t read_length = fread(source_buffer, 1, source_buffer_length, source_file);
30
33
  if (read_length == 0 && feof(source_file)) {
@@ -40,7 +43,8 @@ static inline brs_ext_result_t read_file(FILE* source_file, brs_ext_byte_t* sour
40
43
  return 0;
41
44
  }
42
45
 
43
- static inline brs_ext_result_t write_file(FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_length)
46
+ static inline brs_ext_result_t
47
+ write_file(FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_length)
44
48
  {
45
49
  size_t written_length = fwrite(destination_buffer, 1, destination_length, destination_file);
46
50
  if (written_length != destination_length) {
@@ -53,8 +57,10 @@ static inline brs_ext_result_t write_file(FILE* destination_file, brs_ext_byte_t
53
57
  // -- buffer --
54
58
 
55
59
  static inline brs_ext_result_t create_buffers(
56
- brs_ext_byte_t** source_buffer_ptr, size_t source_buffer_length,
57
- brs_ext_byte_t** destination_buffer_ptr, size_t destination_buffer_length)
60
+ brs_ext_byte_t** source_buffer_ptr,
61
+ size_t source_buffer_length,
62
+ brs_ext_byte_t** destination_buffer_ptr,
63
+ size_t destination_buffer_length)
58
64
  {
59
65
  brs_ext_byte_t* source_buffer = malloc(source_buffer_length);
60
66
  if (source_buffer == NULL) {
@@ -81,8 +87,10 @@ static inline brs_ext_result_t create_buffers(
81
87
 
82
88
  static inline brs_ext_result_t read_more_source(
83
89
  FILE* source_file,
84
- const brs_ext_byte_t** source_ptr, size_t* source_length_ptr,
85
- brs_ext_byte_t* source_buffer, size_t source_buffer_length)
90
+ const brs_ext_byte_t** source_ptr,
91
+ size_t* source_length_ptr,
92
+ brs_ext_byte_t* source_buffer,
93
+ size_t source_buffer_length)
86
94
  {
87
95
  const brs_ext_byte_t* source = *source_ptr;
88
96
  size_t source_length = *source_length_ptr;
@@ -105,7 +113,9 @@ static inline brs_ext_result_t read_more_source(
105
113
  brs_ext_byte_t* remaining_source_buffer = source_buffer + source_length;
106
114
  size_t new_source_length;
107
115
 
108
- brs_ext_result_t ext_result = read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
116
+ brs_ext_result_t ext_result =
117
+ read_file(source_file, remaining_source_buffer, &new_source_length, remaining_source_buffer_length);
118
+
109
119
  if (ext_result != 0) {
110
120
  return ext_result;
111
121
  }
@@ -115,42 +125,37 @@ static inline brs_ext_result_t read_more_source(
115
125
  return 0;
116
126
  }
117
127
 
118
- #define BUFFERED_READ_SOURCE(function, ...) \
119
- do { \
120
- bool is_function_called = false; \
121
- \
122
- while (true) { \
123
- ext_result = read_more_source( \
124
- source_file, \
125
- &source, &source_length, \
126
- source_buffer, source_buffer_length); \
127
- \
128
- if (ext_result == BRS_EXT_FILE_READ_FINISHED) { \
129
- if (source_length != 0) { \
130
- /* Brotli won't provide any remainder by design. */ \
131
- return BRS_EXT_ERROR_READ_IO; \
132
- } \
133
- break; \
134
- } \
135
- else if (ext_result != 0) { \
136
- return ext_result; \
137
- } \
138
- \
139
- ext_result = function(__VA_ARGS__); \
140
- if (ext_result != 0) { \
141
- return ext_result; \
142
- } \
143
- \
144
- is_function_called = true; \
145
- } \
146
- \
147
- if (!is_function_called) { \
148
- /* Function should be called at least once. */ \
149
- ext_result = function(__VA_ARGS__); \
150
- if (ext_result != 0) { \
151
- return ext_result; \
152
- } \
153
- } \
128
+ #define BUFFERED_READ_SOURCE(function, ...) \
129
+ do { \
130
+ bool is_function_called = false; \
131
+ \
132
+ while (true) { \
133
+ ext_result = read_more_source(source_file, &source, &source_length, source_buffer, source_buffer_length); \
134
+ if (ext_result == BRS_EXT_FILE_READ_FINISHED) { \
135
+ if (source_length != 0) { \
136
+ /* Brotli won't provide any remainder by design. */ \
137
+ return BRS_EXT_ERROR_READ_IO; \
138
+ } \
139
+ break; \
140
+ } else if (ext_result != 0) { \
141
+ return ext_result; \
142
+ } \
143
+ \
144
+ ext_result = function(__VA_ARGS__); \
145
+ if (ext_result != 0) { \
146
+ return ext_result; \
147
+ } \
148
+ \
149
+ is_function_called = true; \
150
+ } \
151
+ \
152
+ if (!is_function_called) { \
153
+ /* Function should be called at least once. */ \
154
+ ext_result = function(__VA_ARGS__); \
155
+ if (ext_result != 0) { \
156
+ return ext_result; \
157
+ } \
158
+ } \
154
159
  } while (false);
155
160
 
156
161
  // Algorithm has written data into destination buffer.
@@ -159,7 +164,9 @@ static inline brs_ext_result_t read_more_source(
159
164
 
160
165
  static inline brs_ext_result_t flush_destination_buffer(
161
166
  FILE* destination_file,
162
- brs_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
167
+ brs_ext_byte_t* destination_buffer,
168
+ size_t* destination_length_ptr,
169
+ size_t destination_buffer_length)
163
170
  {
164
171
  if (*destination_length_ptr == 0) {
165
172
  // We want to write more data at once, than buffer has.
@@ -176,7 +183,8 @@ static inline brs_ext_result_t flush_destination_buffer(
176
183
  return 0;
177
184
  }
178
185
 
179
- static inline brs_ext_result_t write_remaining_destination(FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_length)
186
+ static inline brs_ext_result_t
187
+ write_remaining_destination(FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_length)
180
188
  {
181
189
  if (destination_length == 0) {
182
190
  return 0;
@@ -198,29 +206,57 @@ static inline brs_ext_result_t write_remaining_destination(FILE* destination_fil
198
206
  brs_ext_raise_error(BRS_EXT_ERROR_ACCESS_IO); \
199
207
  }
200
208
 
201
- // -- compress --
209
+ // -- buffered compress --
210
+
211
+ typedef struct
212
+ {
213
+ BrotliEncoderState* state_ptr;
214
+ const brs_ext_byte_t** source_ptr;
215
+ size_t* source_length_ptr;
216
+ brs_ext_byte_t* remaining_destination_buffer;
217
+ size_t* remaining_destination_buffer_length_ptr;
218
+ BROTLI_BOOL result;
219
+ } compress_args_t;
220
+
221
+ static inline void* compress_wrapper(void* data)
222
+ {
223
+ compress_args_t* args = data;
224
+
225
+ args->result = BrotliEncoderCompressStream(
226
+ args->state_ptr,
227
+ BROTLI_OPERATION_PROCESS,
228
+ args->source_length_ptr,
229
+ args->source_ptr,
230
+ args->remaining_destination_buffer_length_ptr,
231
+ &args->remaining_destination_buffer,
232
+ NULL);
233
+
234
+ return NULL;
235
+ }
202
236
 
203
237
  static inline brs_ext_result_t buffered_compress(
204
238
  BrotliEncoderState* state_ptr,
205
- const brs_ext_byte_t** source_ptr, size_t* source_length_ptr,
206
- FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
239
+ const brs_ext_byte_t** source_ptr,
240
+ size_t* source_length_ptr,
241
+ FILE* destination_file,
242
+ brs_ext_byte_t* destination_buffer,
243
+ size_t* destination_length_ptr,
244
+ size_t destination_buffer_length,
245
+ bool gvl)
207
246
  {
208
- BROTLI_BOOL result;
209
247
  brs_ext_result_t ext_result;
248
+ compress_args_t args = {.state_ptr = state_ptr, .source_ptr = source_ptr, .source_length_ptr = source_length_ptr};
210
249
 
211
250
  while (true) {
212
251
  brs_ext_byte_t* remaining_destination_buffer = destination_buffer + *destination_length_ptr;
213
252
  size_t remaining_destination_buffer_length = destination_buffer_length - *destination_length_ptr;
214
253
  size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
215
254
 
216
- result = BrotliEncoderCompressStream(
217
- state_ptr,
218
- BROTLI_OPERATION_PROCESS,
219
- source_length_ptr, source_ptr,
220
- &remaining_destination_buffer_length, &remaining_destination_buffer,
221
- NULL);
255
+ args.remaining_destination_buffer = remaining_destination_buffer;
256
+ args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length;
222
257
 
223
- if (!result) {
258
+ BRS_EXT_GVL_WRAP(gvl, compress_wrapper, &args);
259
+ if (!args.result) {
224
260
  return BRS_EXT_ERROR_UNEXPECTED;
225
261
  }
226
262
 
@@ -228,8 +264,7 @@ static inline brs_ext_result_t buffered_compress(
228
264
 
229
265
  if (BrotliEncoderHasMoreOutput(state_ptr)) {
230
266
  ext_result = flush_destination_buffer(
231
- destination_file,
232
- destination_buffer, destination_length_ptr, destination_buffer_length);
267
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
233
268
 
234
269
  if (ext_result != 0) {
235
270
  return ext_result;
@@ -244,29 +279,57 @@ static inline brs_ext_result_t buffered_compress(
244
279
  return 0;
245
280
  }
246
281
 
282
+ // -- buffered compressor finish --
283
+
284
+ typedef struct
285
+ {
286
+ BrotliEncoderState* state_ptr;
287
+ const brs_ext_byte_t** source_ptr;
288
+ size_t* source_length_ptr;
289
+ brs_ext_byte_t* remaining_destination_buffer;
290
+ size_t* remaining_destination_buffer_length_ptr;
291
+ BROTLI_BOOL result;
292
+ } compressor_finish_args_t;
293
+
294
+ static inline void* compressor_finish_wrapper(void* data)
295
+ {
296
+ compressor_finish_args_t* args = data;
297
+
298
+ args->result = BrotliEncoderCompressStream(
299
+ args->state_ptr,
300
+ BROTLI_OPERATION_FINISH,
301
+ args->source_length_ptr,
302
+ args->source_ptr,
303
+ args->remaining_destination_buffer_length_ptr,
304
+ &args->remaining_destination_buffer,
305
+ NULL);
306
+
307
+ return NULL;
308
+ }
309
+
247
310
  static inline brs_ext_result_t buffered_compressor_finish(
248
311
  BrotliEncoderState* state_ptr,
249
- FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
312
+ FILE* destination_file,
313
+ brs_ext_byte_t* destination_buffer,
314
+ size_t* destination_length_ptr,
315
+ size_t destination_buffer_length,
316
+ bool gvl)
250
317
  {
251
- BROTLI_BOOL result;
252
- brs_ext_result_t ext_result;
253
-
254
- const brs_ext_byte_t* source = NULL;
255
- size_t source_length = 0;
318
+ brs_ext_result_t ext_result;
319
+ const brs_ext_byte_t* source = NULL;
320
+ size_t source_length = 0;
321
+ compressor_finish_args_t args = {.state_ptr = state_ptr, .source_ptr = &source, .source_length_ptr = &source_length};
256
322
 
257
323
  while (true) {
258
324
  brs_ext_byte_t* remaining_destination_buffer = destination_buffer + *destination_length_ptr;
259
325
  size_t remaining_destination_buffer_length = destination_buffer_length - *destination_length_ptr;
260
326
  size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
261
327
 
262
- result = BrotliEncoderCompressStream(
263
- state_ptr,
264
- BROTLI_OPERATION_FINISH,
265
- &source_length, &source,
266
- &remaining_destination_buffer_length, &remaining_destination_buffer,
267
- NULL);
328
+ args.remaining_destination_buffer = remaining_destination_buffer;
329
+ args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length;
268
330
 
269
- if (!result) {
331
+ BRS_EXT_GVL_WRAP(gvl, compressor_finish_wrapper, &args);
332
+ if (!args.result) {
270
333
  return BRS_EXT_ERROR_UNEXPECTED;
271
334
  }
272
335
 
@@ -274,8 +337,7 @@ static inline brs_ext_result_t buffered_compressor_finish(
274
337
 
275
338
  if (BrotliEncoderHasMoreOutput(state_ptr) || !BrotliEncoderIsFinished(state_ptr)) {
276
339
  ext_result = flush_destination_buffer(
277
- destination_file,
278
- destination_buffer, destination_length_ptr, destination_buffer_length);
340
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
279
341
 
280
342
  if (ext_result != 0) {
281
343
  return ext_result;
@@ -290,13 +352,19 @@ static inline brs_ext_result_t buffered_compressor_finish(
290
352
  return 0;
291
353
  }
292
354
 
355
+ // -- compress --
356
+
293
357
  static inline brs_ext_result_t compress(
294
358
  BrotliEncoderState* state_ptr,
295
- FILE* source_file, brs_ext_byte_t* source_buffer, size_t source_buffer_length,
296
- FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_buffer_length)
359
+ FILE* source_file,
360
+ brs_ext_byte_t* source_buffer,
361
+ size_t source_buffer_length,
362
+ FILE* destination_file,
363
+ brs_ext_byte_t* destination_buffer,
364
+ size_t destination_buffer_length,
365
+ bool gvl)
297
366
  {
298
- brs_ext_result_t ext_result;
299
-
367
+ brs_ext_result_t ext_result;
300
368
  const brs_ext_byte_t* source = source_buffer;
301
369
  size_t source_length = 0;
302
370
  size_t destination_length = 0;
@@ -304,12 +372,16 @@ static inline brs_ext_result_t compress(
304
372
  BUFFERED_READ_SOURCE(
305
373
  buffered_compress,
306
374
  state_ptr,
307
- &source, &source_length,
308
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
375
+ &source,
376
+ &source_length,
377
+ destination_file,
378
+ destination_buffer,
379
+ &destination_length,
380
+ destination_buffer_length,
381
+ gvl);
309
382
 
310
383
  ext_result = buffered_compressor_finish(
311
- state_ptr,
312
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
384
+ state_ptr, destination_file, destination_buffer, &destination_length, destination_buffer_length, gvl);
313
385
 
314
386
  if (ext_result != 0) {
315
387
  return ext_result;
@@ -323,9 +395,10 @@ VALUE brs_ext_compress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE destin
323
395
  GET_FILE(source);
324
396
  GET_FILE(destination);
325
397
  Check_Type(options, T_HASH);
398
+ BRS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
399
+ BRS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
400
+ BRS_EXT_GET_BOOL_OPTION(options, gvl);
326
401
  BRS_EXT_GET_COMPRESSOR_OPTIONS(options);
327
- BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
328
- BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
329
402
 
330
403
  BrotliEncoderState* state_ptr = BrotliEncoderCreateInstance(NULL, NULL, NULL);
331
404
  if (state_ptr == NULL) {
@@ -348,10 +421,7 @@ VALUE brs_ext_compress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE destin
348
421
  brs_ext_byte_t* source_buffer;
349
422
  brs_ext_byte_t* destination_buffer;
350
423
 
351
- ext_result = create_buffers(
352
- &source_buffer, source_buffer_length,
353
- &destination_buffer, destination_buffer_length);
354
-
424
+ ext_result = create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
355
425
  if (ext_result != 0) {
356
426
  BrotliEncoderDestroyInstance(state_ptr);
357
427
  brs_ext_raise_error(ext_result);
@@ -359,8 +429,13 @@ VALUE brs_ext_compress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE destin
359
429
 
360
430
  ext_result = compress(
361
431
  state_ptr,
362
- source_file, source_buffer, source_buffer_length,
363
- destination_file, destination_buffer, destination_buffer_length);
432
+ source_file,
433
+ source_buffer,
434
+ source_buffer_length,
435
+ destination_file,
436
+ destination_buffer,
437
+ destination_buffer_length,
438
+ gvl);
364
439
 
365
440
  free(source_buffer);
366
441
  free(destination_buffer);
@@ -376,41 +451,68 @@ VALUE brs_ext_compress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE destin
376
451
  return Qnil;
377
452
  }
378
453
 
379
- // -- decompress --
454
+ // -- buffered decompress --
455
+
456
+ typedef struct
457
+ {
458
+ BrotliDecoderState* state_ptr;
459
+ const brs_ext_byte_t** source_ptr;
460
+ size_t* source_length_ptr;
461
+ brs_ext_byte_t* remaining_destination_buffer;
462
+ size_t* remaining_destination_buffer_length_ptr;
463
+ BrotliDecoderResult result;
464
+ } decompress_args_t;
465
+
466
+ static inline void* decompress_wrapper(void* data)
467
+ {
468
+ decompress_args_t* args = data;
469
+
470
+ args->result = BrotliDecoderDecompressStream(
471
+ args->state_ptr,
472
+ args->source_length_ptr,
473
+ args->source_ptr,
474
+ args->remaining_destination_buffer_length_ptr,
475
+ &args->remaining_destination_buffer,
476
+ NULL);
477
+
478
+ return NULL;
479
+ }
380
480
 
381
481
  static inline brs_ext_result_t buffered_decompress(
382
482
  BrotliDecoderState* state_ptr,
383
- const brs_ext_byte_t** source_ptr, size_t* source_length_ptr,
384
- FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t* destination_length_ptr, size_t destination_buffer_length)
483
+ const brs_ext_byte_t** source_ptr,
484
+ size_t* source_length_ptr,
485
+ FILE* destination_file,
486
+ brs_ext_byte_t* destination_buffer,
487
+ size_t* destination_length_ptr,
488
+ size_t destination_buffer_length,
489
+ bool gvl)
385
490
  {
386
- BrotliDecoderResult result;
387
- brs_ext_result_t ext_result;
491
+ brs_ext_result_t ext_result;
492
+ decompress_args_t args = {.state_ptr = state_ptr, .source_ptr = source_ptr, .source_length_ptr = source_length_ptr};
388
493
 
389
494
  while (true) {
390
495
  brs_ext_byte_t* remaining_destination_buffer = destination_buffer + *destination_length_ptr;
391
496
  size_t remaining_destination_buffer_length = destination_buffer_length - *destination_length_ptr;
392
497
  size_t prev_remaining_destination_buffer_length = remaining_destination_buffer_length;
393
498
 
394
- result = BrotliDecoderDecompressStream(
395
- state_ptr,
396
- source_length_ptr, source_ptr,
397
- &remaining_destination_buffer_length, &remaining_destination_buffer,
398
- NULL);
499
+ args.remaining_destination_buffer = remaining_destination_buffer;
500
+ args.remaining_destination_buffer_length_ptr = &remaining_destination_buffer_length;
501
+
502
+ BRS_EXT_GVL_WRAP(gvl, decompress_wrapper, &args);
399
503
 
400
504
  if (
401
- result != BROTLI_DECODER_RESULT_SUCCESS &&
402
- result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT &&
403
- result != BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
505
+ args.result != BROTLI_DECODER_RESULT_SUCCESS && args.result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT &&
506
+ args.result != BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
404
507
  BrotliDecoderErrorCode error_code = BrotliDecoderGetErrorCode(state_ptr);
405
508
  return brs_ext_get_decompressor_error(error_code);
406
509
  }
407
510
 
408
511
  *destination_length_ptr += prev_remaining_destination_buffer_length - remaining_destination_buffer_length;
409
512
 
410
- if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
513
+ if (args.result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
411
514
  ext_result = flush_destination_buffer(
412
- destination_file,
413
- destination_buffer, destination_length_ptr, destination_buffer_length);
515
+ destination_file, destination_buffer, destination_length_ptr, destination_buffer_length);
414
516
 
415
517
  if (ext_result != 0) {
416
518
  return ext_result;
@@ -425,13 +527,19 @@ static inline brs_ext_result_t buffered_decompress(
425
527
  return 0;
426
528
  }
427
529
 
530
+ // -- decompress --
531
+
428
532
  static inline brs_ext_result_t decompress(
429
533
  BrotliDecoderState* state_ptr,
430
- FILE* source_file, brs_ext_byte_t* source_buffer, size_t source_buffer_length,
431
- FILE* destination_file, brs_ext_byte_t* destination_buffer, size_t destination_buffer_length)
534
+ FILE* source_file,
535
+ brs_ext_byte_t* source_buffer,
536
+ size_t source_buffer_length,
537
+ FILE* destination_file,
538
+ brs_ext_byte_t* destination_buffer,
539
+ size_t destination_buffer_length,
540
+ bool gvl)
432
541
  {
433
- brs_ext_result_t ext_result;
434
-
542
+ brs_ext_result_t ext_result;
435
543
  const brs_ext_byte_t* source = source_buffer;
436
544
  size_t source_length = 0;
437
545
  size_t destination_length = 0;
@@ -439,8 +547,13 @@ static inline brs_ext_result_t decompress(
439
547
  BUFFERED_READ_SOURCE(
440
548
  buffered_decompress,
441
549
  state_ptr,
442
- &source, &source_length,
443
- destination_file, destination_buffer, &destination_length, destination_buffer_length);
550
+ &source,
551
+ &source_length,
552
+ destination_file,
553
+ destination_buffer,
554
+ &destination_length,
555
+ destination_buffer_length,
556
+ gvl);
444
557
 
445
558
  return write_remaining_destination(destination_file, destination_buffer, destination_length);
446
559
  }
@@ -450,9 +563,10 @@ VALUE brs_ext_decompress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE dest
450
563
  GET_FILE(source);
451
564
  GET_FILE(destination);
452
565
  Check_Type(options, T_HASH);
566
+ BRS_EXT_GET_SIZE_OPTION(options, source_buffer_length);
567
+ BRS_EXT_GET_SIZE_OPTION(options, destination_buffer_length);
568
+ BRS_EXT_GET_BOOL_OPTION(options, gvl);
453
569
  BRS_EXT_GET_DECOMPRESSOR_OPTIONS(options);
454
- BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, source_buffer_length);
455
- BRS_EXT_GET_BUFFER_LENGTH_OPTION(options, destination_buffer_length);
456
570
 
457
571
  BrotliDecoderState* state_ptr = BrotliDecoderCreateInstance(NULL, NULL, NULL);
458
572
  if (state_ptr == NULL) {
@@ -475,10 +589,7 @@ VALUE brs_ext_decompress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE dest
475
589
  brs_ext_byte_t* source_buffer;
476
590
  brs_ext_byte_t* destination_buffer;
477
591
 
478
- ext_result = create_buffers(
479
- &source_buffer, source_buffer_length,
480
- &destination_buffer, destination_buffer_length);
481
-
592
+ ext_result = create_buffers(&source_buffer, source_buffer_length, &destination_buffer, destination_buffer_length);
482
593
  if (ext_result != 0) {
483
594
  BrotliDecoderDestroyInstance(state_ptr);
484
595
  brs_ext_raise_error(ext_result);
@@ -486,8 +597,13 @@ VALUE brs_ext_decompress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE dest
486
597
 
487
598
  ext_result = decompress(
488
599
  state_ptr,
489
- source_file, source_buffer, source_buffer_length,
490
- destination_file, destination_buffer, destination_buffer_length);
600
+ source_file,
601
+ source_buffer,
602
+ source_buffer_length,
603
+ destination_file,
604
+ destination_buffer,
605
+ destination_buffer_length,
606
+ gvl);
491
607
 
492
608
  free(source_buffer);
493
609
  free(destination_buffer);
@@ -503,6 +619,8 @@ VALUE brs_ext_decompress_io(VALUE BRS_EXT_UNUSED(self), VALUE source, VALUE dest
503
619
  return Qnil;
504
620
  }
505
621
 
622
+ // -- exports --
623
+
506
624
  void brs_ext_io_exports(VALUE root_module)
507
625
  {
508
626
  rb_define_module_function(root_module, "_native_compress_io", RUBY_METHOD_FUNC(brs_ext_compress_io), 3);