brotli 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/main.yml +34 -0
  3. data/.github/workflows/publish.yml +34 -0
  4. data/Gemfile +6 -2
  5. data/Rakefile +18 -6
  6. data/bin/before_install.sh +9 -0
  7. data/brotli.gemspec +7 -13
  8. data/ext/brotli/brotli.c +209 -11
  9. data/ext/brotli/buffer.c +1 -7
  10. data/ext/brotli/buffer.h +1 -1
  11. data/ext/brotli/extconf.rb +45 -26
  12. data/lib/brotli/version.rb +1 -1
  13. data/smoke.sh +1 -1
  14. data/test/brotli_test.rb +104 -0
  15. data/test/brotli_writer_test.rb +36 -0
  16. data/test/test_helper.rb +8 -0
  17. data/vendor/brotli/c/common/constants.c +15 -0
  18. data/vendor/brotli/c/common/constants.h +149 -6
  19. data/vendor/brotli/c/{dec/context.h → common/context.c} +91 -186
  20. data/vendor/brotli/c/common/context.h +113 -0
  21. data/vendor/brotli/c/common/dictionary.bin +0 -0
  22. data/vendor/brotli/c/common/dictionary.bin.br +0 -0
  23. data/vendor/brotli/c/common/dictionary.c +11 -2
  24. data/vendor/brotli/c/common/dictionary.h +4 -4
  25. data/vendor/brotli/c/common/platform.c +22 -0
  26. data/vendor/brotli/c/common/platform.h +594 -0
  27. data/vendor/brotli/c/common/transform.c +291 -0
  28. data/vendor/brotli/c/common/transform.h +85 -0
  29. data/vendor/brotli/c/common/version.h +8 -1
  30. data/vendor/brotli/c/dec/bit_reader.c +29 -1
  31. data/vendor/brotli/c/dec/bit_reader.h +91 -100
  32. data/vendor/brotli/c/dec/decode.c +665 -437
  33. data/vendor/brotli/c/dec/huffman.c +65 -84
  34. data/vendor/brotli/c/dec/huffman.h +67 -14
  35. data/vendor/brotli/c/dec/prefix.h +1 -20
  36. data/vendor/brotli/c/dec/state.c +32 -45
  37. data/vendor/brotli/c/dec/state.h +173 -55
  38. data/vendor/brotli/c/enc/backward_references.c +27 -16
  39. data/vendor/brotli/c/enc/backward_references.h +7 -7
  40. data/vendor/brotli/c/enc/backward_references_hq.c +155 -116
  41. data/vendor/brotli/c/enc/backward_references_hq.h +22 -23
  42. data/vendor/brotli/c/enc/backward_references_inc.h +32 -22
  43. data/vendor/brotli/c/enc/bit_cost.c +1 -1
  44. data/vendor/brotli/c/enc/bit_cost.h +5 -5
  45. data/vendor/brotli/c/enc/block_encoder_inc.h +7 -6
  46. data/vendor/brotli/c/enc/block_splitter.c +5 -6
  47. data/vendor/brotli/c/enc/block_splitter.h +1 -1
  48. data/vendor/brotli/c/enc/block_splitter_inc.h +26 -17
  49. data/vendor/brotli/c/enc/brotli_bit_stream.c +107 -123
  50. data/vendor/brotli/c/enc/brotli_bit_stream.h +19 -38
  51. data/vendor/brotli/c/enc/cluster.c +1 -1
  52. data/vendor/brotli/c/enc/cluster.h +1 -1
  53. data/vendor/brotli/c/enc/cluster_inc.h +6 -3
  54. data/vendor/brotli/c/enc/command.c +28 -0
  55. data/vendor/brotli/c/enc/command.h +52 -42
  56. data/vendor/brotli/c/enc/compress_fragment.c +21 -22
  57. data/vendor/brotli/c/enc/compress_fragment.h +1 -1
  58. data/vendor/brotli/c/enc/compress_fragment_two_pass.c +102 -69
  59. data/vendor/brotli/c/enc/compress_fragment_two_pass.h +1 -1
  60. data/vendor/brotli/c/enc/dictionary_hash.c +1827 -1101
  61. data/vendor/brotli/c/enc/dictionary_hash.h +2 -1
  62. data/vendor/brotli/c/enc/encode.c +358 -195
  63. data/vendor/brotli/c/enc/encoder_dict.c +33 -0
  64. data/vendor/brotli/c/enc/encoder_dict.h +43 -0
  65. data/vendor/brotli/c/enc/entropy_encode.c +16 -14
  66. data/vendor/brotli/c/enc/entropy_encode.h +7 -7
  67. data/vendor/brotli/c/enc/entropy_encode_static.h +3 -3
  68. data/vendor/brotli/c/enc/fast_log.c +105 -0
  69. data/vendor/brotli/c/enc/fast_log.h +20 -99
  70. data/vendor/brotli/c/enc/find_match_length.h +5 -6
  71. data/vendor/brotli/c/enc/hash.h +145 -103
  72. data/vendor/brotli/c/enc/hash_composite_inc.h +125 -0
  73. data/vendor/brotli/c/enc/hash_forgetful_chain_inc.h +93 -53
  74. data/vendor/brotli/c/enc/hash_longest_match64_inc.h +54 -53
  75. data/vendor/brotli/c/enc/hash_longest_match_inc.h +58 -54
  76. data/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h +95 -63
  77. data/vendor/brotli/c/enc/hash_rolling_inc.h +212 -0
  78. data/vendor/brotli/c/enc/hash_to_binary_tree_inc.h +46 -43
  79. data/vendor/brotli/c/enc/histogram.c +9 -6
  80. data/vendor/brotli/c/enc/histogram.h +6 -3
  81. data/vendor/brotli/c/enc/histogram_inc.h +1 -1
  82. data/vendor/brotli/c/enc/literal_cost.c +5 -5
  83. data/vendor/brotli/c/enc/literal_cost.h +2 -2
  84. data/vendor/brotli/c/enc/memory.c +5 -16
  85. data/vendor/brotli/c/enc/memory.h +52 -1
  86. data/vendor/brotli/c/enc/metablock.c +171 -36
  87. data/vendor/brotli/c/enc/metablock.h +13 -8
  88. data/vendor/brotli/c/enc/metablock_inc.h +2 -2
  89. data/vendor/brotli/c/enc/params.h +46 -0
  90. data/vendor/brotli/c/enc/prefix.h +3 -4
  91. data/vendor/brotli/c/enc/quality.h +29 -24
  92. data/vendor/brotli/c/enc/ringbuffer.h +19 -12
  93. data/vendor/brotli/c/enc/static_dict.c +49 -45
  94. data/vendor/brotli/c/enc/static_dict.h +4 -3
  95. data/vendor/brotli/c/enc/static_dict_lut.h +1 -1
  96. data/vendor/brotli/c/enc/utf8_util.c +21 -21
  97. data/vendor/brotli/c/enc/utf8_util.h +1 -1
  98. data/vendor/brotli/c/enc/write_bits.h +35 -38
  99. data/vendor/brotli/c/include/brotli/decode.h +13 -8
  100. data/vendor/brotli/c/include/brotli/encode.h +54 -8
  101. data/vendor/brotli/c/include/brotli/port.h +225 -83
  102. data/vendor/brotli/c/include/brotli/types.h +0 -7
  103. metadata +28 -87
  104. data/.travis.yml +0 -30
  105. data/spec/brotli_spec.rb +0 -88
  106. data/spec/inflate_spec.rb +0 -75
  107. data/spec/spec_helper.rb +0 -4
  108. data/vendor/brotli/c/dec/port.h +0 -168
  109. data/vendor/brotli/c/dec/transform.h +0 -300
  110. data/vendor/brotli/c/enc/context.h +0 -184
  111. data/vendor/brotli/c/enc/port.h +0 -184
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 28b044ea8f0192f41b544f72eea2855d490f2722
4
- data.tar.gz: 6face28127a2acde37dd8a72a203d277f72f78a9
2
+ SHA256:
3
+ metadata.gz: 410f0b599c6bf42f90cb7fa83c6710304ad0fb018b1f4b5560bfadadaef165d9
4
+ data.tar.gz: 03fbcca525c9705b99935ab2ea4db0bb80e4c6a960d3010450a173c5b305eb43
5
5
  SHA512:
6
- metadata.gz: 12b573800edcd717571dff187a05cb9b71d8e6d33c64a8c9c358b3b6e332152b80035c66e050ae83846d02378abf63a3df778afd3af04ca17dc6ea3cd124f80f
7
- data.tar.gz: 1932d518c3345ab655a52bc75a0066304eb88ad1207d830a94ad6d97148cedb4094859691730329e34ad442bb4c089c1c1a51d430eb2c12ad20177633b5360ad
6
+ metadata.gz: fe26f6b7a9a2f3a0bf0ab5d1e79ff89ff8dc2a763e3b8beb073feb365f6ae222dcf277672bbbe2ac7aeaa71f2b024edd6ce49a6a7254452897fdbfef61f422a5
7
+ data.tar.gz: c1952c3b820fa2ec0b5a1e1567acfe831fcc1a6131f07e12acf3b26595c3bda90c89182abfcc57730febe5311906632ea8c19a21e16e866a5e620a8b85cfa2d4
@@ -0,0 +1,34 @@
1
+ name: Ruby
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby: [2.5, 2.6, 2.7, 3.0]
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ with:
15
+ submodules: true
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - name: Run the default task
22
+ run: |
23
+ bundle exec rake clobber test build
24
+ gem install --no-document "$(ls pkg/brotli-*.gem)"
25
+ cat <<EOF | ruby
26
+ require "brotli"
27
+ if Brotli.inflate(Brotli.deflate(File.read("README.md"))) == File.read("README.md")
28
+ puts "OK"
29
+ exit 0
30
+ else
31
+ puts "NG"
32
+ exit 0
33
+ end
34
+ EOF
@@ -0,0 +1,34 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ build:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ ruby: [2.7]
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ with:
18
+ submodules: true
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ bundler-cache: true
24
+ - name: Run release task
25
+ run: |
26
+ mkdir -p ~/.gem
27
+ cat << EOF > ~/.gem/credentials
28
+ ---
29
+ :github: Bearer ${{secrets.GITHUB_TOKEN}}
30
+ :rubygems_api_key: ${{secrets.RUBYGEMS_API_KEY}}
31
+ EOF
32
+ chmod 600 ~/.gem/credentials
33
+ bundle exec rake release[remote]
34
+ rm -f ~/.gem/credentials
data/Gemfile CHANGED
@@ -1,6 +1,10 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in brotli.gemspec
4
4
  gemspec
5
5
 
6
- gem 'travis'
6
+ gem "rake", "~> 13.0"
7
+ gem "rake-compiler"
8
+ gem "test-unit", "~> 3.0"
9
+ gem "test-unit-rr"
10
+ gem "rantly"
data/Rakefile CHANGED
@@ -1,13 +1,25 @@
1
+ require "bundler/setup"
1
2
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => [:compile, :spec]
3
+ require "rake/clean"
4
+ require "rake/testtask"
7
5
  require "rake/extensiontask"
8
6
 
9
- task :build => :compile
7
+ CLEAN.include("ext/brotli/common")
8
+ CLEAN.include("ext/brotli/dec")
9
+ CLEAN.include("ext/brotli/enc")
10
+ CLEAN.include("ext/brotli/include")
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.test_files = FileList["test/**/*_test.rb"]
15
+ t.warning = true
16
+ t.verbose = true
17
+ end
10
18
 
11
19
  Rake::ExtensionTask.new("brotli") do |ext|
12
20
  ext.lib_dir = "lib/brotli"
13
21
  end
22
+
23
+ task :build => :compile
24
+ task :test => :compile
25
+ task :default => :test
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ set -ev
3
+ gem update --system || gem update --system '2.7.8'
4
+ if [ "$(gem --version | cut -b 1)" = 3 ]; then
5
+ echo "gem: -N" >> ~/.gemrc
6
+ else
7
+ echo "gem: --no-ri --no-rdoc" >> ~/.gemrc
8
+ fi
9
+ gem install bundler || gem install bundler -v '< 2'
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'brotli/version'
4
+ require "brotli/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "brotli"
@@ -9,25 +9,19 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["miyucy"]
10
10
  spec.email = ["fistfvck@gmail.com"]
11
11
 
12
- spec.summary = %q{Brotli compressor/decompressor}
13
- spec.description = %q{Brotli compressor/decompressor}
12
+ spec.summary = "Brotli compressor/decompressor"
13
+ spec.description = "Brotli compressor/decompressor"
14
14
  spec.homepage = "https://github.com/miyucy/brotli"
15
15
  spec.license = "MIT"
16
16
 
17
- spec.test_files = `git ls-files -z -- spec`.split("\x0")
17
+ spec.test_files = `git ls-files -z -- test`.split("\x0")
18
18
  spec.files = `git ls-files -z`.split("\x0")
19
19
  spec.files -= spec.test_files
20
- spec.files -= ['vendor/brotli']
21
- spec.files += Dir['vendor/brotli/c/{common,enc,dec,include}/**/*']
22
- spec.files += ['vendor/brotli/LICENSE']
20
+ spec.files -= ["vendor/brotli"]
21
+ spec.files += Dir["vendor/brotli/c/{common,enc,dec,include}/**/*"]
22
+ spec.files += ["vendor/brotli/LICENSE"]
23
23
  spec.bindir = "exe"
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
  spec.extensions = ["ext/brotli/extconf.rb"]
27
-
28
- spec.add_development_dependency "bundler", "~> 1.10"
29
- spec.add_development_dependency "rake", "~> 10.0"
30
- spec.add_development_dependency "rake-compiler"
31
- spec.add_development_dependency "rspec"
32
- spec.add_development_dependency "rantly"
33
27
  end
@@ -2,20 +2,19 @@
2
2
 
3
3
  #define CSTR2SYM(x) ID2SYM(rb_intern(x))
4
4
 
5
- static VALUE rb_mBrotli;
6
5
  static VALUE rb_eBrotli;
7
6
 
8
- static void*
7
+ static inline void*
9
8
  brotli_alloc(void* opaque, size_t size)
10
9
  {
11
- return malloc(size);
10
+ return ruby_xmalloc(size);
12
11
  }
13
12
 
14
- static void
13
+ static inline void
15
14
  brotli_free(void* opaque, void* address)
16
15
  {
17
16
  if (address) {
18
- free(address);
17
+ ruby_xfree(address);
19
18
  }
20
19
  }
21
20
 
@@ -138,11 +137,10 @@ brotli_deflate_set_mode(BrotliEncoderState* s, VALUE value)
138
137
  static void
139
138
  brotli_deflate_set_quality(BrotliEncoderState* s, VALUE value)
140
139
  {
141
- uint32_t param;
142
140
  if (NIL_P(value)) {
143
141
  return;
144
142
  } else {
145
- param = NUM2INT(value);
143
+ int32_t param = NUM2INT(value);
146
144
  if (0 <= param && param <= 11) {
147
145
  BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, param);
148
146
  } else {
@@ -154,11 +152,10 @@ brotli_deflate_set_quality(BrotliEncoderState* s, VALUE value)
154
152
  static void
155
153
  brotli_deflate_set_lgwin(BrotliEncoderState* s, VALUE value)
156
154
  {
157
- uint32_t param;
158
155
  if (NIL_P(value)) {
159
156
  return;
160
157
  } else {
161
- param = NUM2INT(value);
158
+ int32_t param = NUM2INT(value);
162
159
  if (10 <= param && param <= 24) {
163
160
  BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, param);
164
161
  } else {
@@ -170,11 +167,10 @@ brotli_deflate_set_lgwin(BrotliEncoderState* s, VALUE value)
170
167
  static void
171
168
  brotli_deflate_set_lgblock(BrotliEncoderState* s, VALUE value)
172
169
  {
173
- uint32_t param;
174
170
  if (NIL_P(value)) {
175
171
  return;
176
172
  } else {
177
- param = NUM2INT(value);
173
+ int32_t param = NUM2INT(value);
178
174
  if ((param == 0) || (16 <= param && param <= 24)) {
179
175
  BrotliEncoderSetParameter(s, BROTLI_PARAM_LGBLOCK, param);
180
176
  } else {
@@ -248,6 +244,10 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
248
244
  brotli_deflate_args_t args;
249
245
 
250
246
  rb_scan_args(argc, argv, "11", &str, &opts);
247
+ if (NIL_P(str)) {
248
+ rb_raise(rb_eArgError, "input should not be nil");
249
+ return Qnil;
250
+ }
251
251
  StringValue(str);
252
252
 
253
253
  args.str = (uint8_t*)RSTRING_PTR(str);
@@ -273,6 +273,185 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
273
273
  return value;
274
274
  }
275
275
 
276
+ /*******************************************************************************
277
+ * version
278
+ ******************************************************************************/
279
+
280
+ static VALUE brotli_version(VALUE klass) {
281
+ uint32_t ver = BrotliEncoderVersion();
282
+ char version[255];
283
+ snprintf(version, sizeof(version), "%u.%u.%u", ver >> 24, (ver >> 12) & 0xFFF, ver & 0xFFF);
284
+ return rb_str_new2(version);
285
+ }
286
+
287
+ /*******************************************************************************
288
+ * Writer
289
+ ******************************************************************************/
290
+
291
+ static ID id_write, id_flush, id_close;
292
+
293
+ struct brotli {
294
+ VALUE io;
295
+ BrotliEncoderState* state;
296
+ };
297
+
298
+ static void br_mark(void *p)
299
+ {
300
+ struct brotli *br = p;
301
+ rb_gc_mark(br->io);
302
+ }
303
+
304
+ static void br_free(void *p)
305
+ {
306
+ struct brotli* br = p;
307
+ BrotliEncoderDestroyInstance(br->state);
308
+ br->state = NULL;
309
+ br->io = Qnil;
310
+ ruby_xfree(br);
311
+ }
312
+
313
+ static size_t br_memsize(const void *p)
314
+ {
315
+ return sizeof(struct brotli);
316
+ }
317
+
318
+ static const rb_data_type_t brotli_data_type = {
319
+ "brotli",
320
+ { br_mark, br_free, br_memsize },
321
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
322
+ };
323
+
324
+ typedef struct {
325
+ BrotliEncoderState* state;
326
+ BrotliEncoderOperation op;
327
+ size_t available_in;
328
+ const uint8_t* next_in;
329
+ } brotli_encoder_args_t;
330
+
331
+ static void* compress_no_gvl(void *ptr) {
332
+ brotli_encoder_args_t *args = ptr;
333
+ size_t zero = 0;
334
+ if (!BrotliEncoderCompressStream(args->state, args->op,
335
+ &args->available_in, &args->next_in,
336
+ &zero, NULL, NULL)) {
337
+ rb_raise(rb_eBrotli, "BrotliEncoderCompressStream failed");
338
+ }
339
+ return NULL;
340
+ }
341
+
342
+ static size_t push_output(struct brotli *br) {
343
+ size_t len = 0;
344
+ if (BrotliEncoderHasMoreOutput(br->state)) {
345
+ const uint8_t* out = BrotliEncoderTakeOutput(br->state, &len);
346
+ if (len > 0) {
347
+ rb_funcall(br->io, id_write, 1, rb_str_new((const char*)out, len));
348
+ }
349
+ }
350
+ return len;
351
+ }
352
+
353
+ static VALUE rb_writer_alloc(VALUE klass) {
354
+ struct brotli *br;
355
+ VALUE obj = TypedData_Make_Struct(klass, struct brotli, &brotli_data_type, br);
356
+ br->io = Qnil;
357
+ br->state = BrotliEncoderCreateInstance(brotli_alloc, brotli_free, NULL);
358
+ if (!br->state) {
359
+ rb_raise(rb_eNoMemError, "BrotliEncoderCreateInstance failed");
360
+ return Qnil;
361
+ }
362
+ return obj;
363
+ }
364
+
365
+ static VALUE rb_writer_initialize(int argc, VALUE* argv, VALUE self) {
366
+ VALUE io = Qnil;
367
+ VALUE opts = Qnil;
368
+ rb_scan_args(argc, argv, "11", &io, &opts);
369
+ if (NIL_P(io)) {
370
+ rb_raise(rb_eArgError, "io should not be nil");
371
+ return Qnil;
372
+ }
373
+
374
+ struct brotli *br;
375
+ TypedData_Get_Struct(self, struct brotli, &brotli_data_type, br);
376
+ brotli_deflate_parse_options(br->state, opts);
377
+ br->io = io;
378
+ return self;
379
+ }
380
+
381
+ static VALUE rb_writer_write(VALUE self, VALUE buf) {
382
+ struct brotli* br;
383
+ TypedData_Get_Struct(self, struct brotli, &brotli_data_type, br);
384
+ StringValue(buf);
385
+
386
+ const size_t total = (size_t)RSTRING_LEN(buf);
387
+
388
+ brotli_encoder_args_t args = {
389
+ .state = br->state,
390
+ .op = BROTLI_OPERATION_PROCESS,
391
+ .available_in = total,
392
+ .next_in = (uint8_t*)RSTRING_PTR(buf)
393
+ };
394
+
395
+ while (args.available_in > 0) {
396
+ rb_thread_call_without_gvl(compress_no_gvl, (void*)&args, NULL, NULL);
397
+ push_output(br);
398
+ }
399
+
400
+ return SIZET2NUM(total);
401
+ }
402
+
403
+ static VALUE rb_writer_finish(VALUE self) {
404
+ struct brotli* br;
405
+ TypedData_Get_Struct(self, struct brotli, &brotli_data_type, br);
406
+
407
+ brotli_encoder_args_t args = {
408
+ .state = br->state,
409
+ .op = BROTLI_OPERATION_FINISH,
410
+ .available_in = 0,
411
+ .next_in = NULL
412
+ };
413
+
414
+ while (!BrotliEncoderIsFinished(br->state)) {
415
+ rb_thread_call_without_gvl(compress_no_gvl, (void*)&args, NULL, NULL);
416
+ push_output(br);
417
+ }
418
+ return br->io;
419
+ }
420
+
421
+ static VALUE rb_writer_flush(VALUE self) {
422
+ struct brotli *br;
423
+ TypedData_Get_Struct(self, struct brotli, &brotli_data_type, br);
424
+
425
+ brotli_encoder_args_t args = {
426
+ .state = br->state,
427
+ .op = BROTLI_OPERATION_FLUSH,
428
+ .available_in = 0,
429
+ .next_in = NULL
430
+ };
431
+
432
+ do {
433
+ rb_thread_call_without_gvl(compress_no_gvl, (void*)&args, NULL, NULL);
434
+ push_output(br);
435
+ } while (BrotliEncoderHasMoreOutput(br->state));
436
+
437
+ if (rb_respond_to(br->io, id_flush)) {
438
+ rb_funcall(br->io, id_flush, 0);
439
+ }
440
+ return self;
441
+ }
442
+
443
+ static VALUE rb_writer_close(VALUE self) {
444
+ struct brotli* br;
445
+ TypedData_Get_Struct(self, struct brotli, &brotli_data_type, br);
446
+
447
+ rb_writer_finish(self);
448
+
449
+ if (rb_respond_to(br->io, id_close)) {
450
+ rb_funcall(br->io, id_close, 0);
451
+ }
452
+ return br->io;
453
+ }
454
+
276
455
  /*******************************************************************************
277
456
  * entry
278
457
  ******************************************************************************/
@@ -280,8 +459,27 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
280
459
  void
281
460
  Init_brotli(void)
282
461
  {
462
+ #if HAVE_RB_EXT_RACTOR_SAFE
463
+ rb_ext_ractor_safe(true);
464
+ #endif
465
+
466
+ VALUE rb_mBrotli;
467
+ VALUE rb_Writer;
283
468
  rb_mBrotli = rb_define_module("Brotli");
284
469
  rb_eBrotli = rb_define_class_under(rb_mBrotli, "Error", rb_eStandardError);
470
+ rb_global_variable(&rb_eBrotli);
285
471
  rb_define_singleton_method(rb_mBrotli, "deflate", RUBY_METHOD_FUNC(brotli_deflate), -1);
286
472
  rb_define_singleton_method(rb_mBrotli, "inflate", RUBY_METHOD_FUNC(brotli_inflate), 1);
473
+ rb_define_singleton_method(rb_mBrotli, "version", RUBY_METHOD_FUNC(brotli_version), 0);
474
+ // Brotli::Writer
475
+ id_write = rb_intern("write");
476
+ id_flush = rb_intern("flush");
477
+ id_close = rb_intern("close");
478
+ rb_Writer = rb_define_class_under(rb_mBrotli, "Writer", rb_cObject);
479
+ rb_define_alloc_func(rb_Writer, rb_writer_alloc);
480
+ rb_define_method(rb_Writer, "initialize", RUBY_METHOD_FUNC(rb_writer_initialize), -1);
481
+ rb_define_method(rb_Writer, "write", RUBY_METHOD_FUNC(rb_writer_write), 1);
482
+ rb_define_method(rb_Writer, "finish", RUBY_METHOD_FUNC(rb_writer_finish), 0);
483
+ rb_define_method(rb_Writer, "flush", RUBY_METHOD_FUNC(rb_writer_flush), 0);
484
+ rb_define_method(rb_Writer, "close", RUBY_METHOD_FUNC(rb_writer_close), 0);
287
485
  }