brotli 0.2.3 → 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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +34 -0
  3. data/.github/workflows/publish.yml +34 -0
  4. data/Gemfile +6 -3
  5. data/Rakefile +16 -9
  6. data/brotli.gemspec +7 -13
  7. data/ext/brotli/brotli.c +209 -31
  8. data/ext/brotli/buffer.c +1 -7
  9. data/ext/brotli/buffer.h +1 -1
  10. data/ext/brotli/extconf.rb +20 -18
  11. data/lib/brotli/version.rb +1 -1
  12. data/test/brotli_test.rb +104 -0
  13. data/test/brotli_writer_test.rb +36 -0
  14. data/test/test_helper.rb +8 -0
  15. data/vendor/brotli/c/common/constants.c +15 -0
  16. data/vendor/brotli/c/common/constants.h +136 -0
  17. data/vendor/brotli/c/common/context.c +156 -0
  18. data/vendor/brotli/c/common/context.h +4 -152
  19. data/vendor/brotli/c/common/dictionary.bin.br +0 -0
  20. data/vendor/brotli/c/common/dictionary.c +10 -1
  21. data/vendor/brotli/c/common/platform.c +22 -0
  22. data/vendor/brotli/c/common/platform.h +43 -17
  23. data/vendor/brotli/c/common/transform.c +59 -3
  24. data/vendor/brotli/c/common/transform.h +5 -0
  25. data/vendor/brotli/c/common/version.h +2 -2
  26. data/vendor/brotli/c/dec/bit_reader.c +28 -0
  27. data/vendor/brotli/c/dec/bit_reader.h +58 -16
  28. data/vendor/brotli/c/dec/decode.c +353 -251
  29. data/vendor/brotli/c/dec/huffman.h +6 -12
  30. data/vendor/brotli/c/dec/prefix.h +0 -18
  31. data/vendor/brotli/c/dec/state.c +9 -14
  32. data/vendor/brotli/c/dec/state.h +144 -37
  33. data/vendor/brotli/c/enc/backward_references.c +8 -7
  34. data/vendor/brotli/c/enc/backward_references.h +5 -4
  35. data/vendor/brotli/c/enc/backward_references_hq.c +51 -33
  36. data/vendor/brotli/c/enc/backward_references_hq.h +11 -8
  37. data/vendor/brotli/c/enc/backward_references_inc.h +24 -14
  38. data/vendor/brotli/c/enc/block_splitter.c +3 -3
  39. data/vendor/brotli/c/enc/block_splitter_inc.h +15 -6
  40. data/vendor/brotli/c/enc/brotli_bit_stream.c +13 -30
  41. data/vendor/brotli/c/enc/cluster_inc.h +6 -3
  42. data/vendor/brotli/c/enc/command.c +28 -0
  43. data/vendor/brotli/c/enc/command.h +12 -12
  44. data/vendor/brotli/c/enc/compress_fragment_two_pass.c +1 -1
  45. data/vendor/brotli/c/enc/dictionary_hash.c +1826 -1100
  46. data/vendor/brotli/c/enc/dictionary_hash.h +2 -1
  47. data/vendor/brotli/c/enc/encode.c +104 -39
  48. data/vendor/brotli/c/enc/encoder_dict.c +3 -2
  49. data/vendor/brotli/c/enc/encoder_dict.h +3 -1
  50. data/vendor/brotli/c/enc/entropy_encode.c +2 -0
  51. data/vendor/brotli/c/enc/entropy_encode.h +2 -2
  52. data/vendor/brotli/c/enc/fast_log.c +105 -0
  53. data/vendor/brotli/c/enc/fast_log.h +19 -100
  54. data/vendor/brotli/c/enc/find_match_length.h +2 -3
  55. data/vendor/brotli/c/enc/hash.h +80 -90
  56. data/vendor/brotli/c/enc/hash_composite_inc.h +52 -63
  57. data/vendor/brotli/c/enc/hash_forgetful_chain_inc.h +88 -49
  58. data/vendor/brotli/c/enc/hash_longest_match64_inc.h +50 -50
  59. data/vendor/brotli/c/enc/hash_longest_match_inc.h +53 -50
  60. data/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h +91 -60
  61. data/vendor/brotli/c/enc/hash_rolling_inc.h +23 -27
  62. data/vendor/brotli/c/enc/hash_to_binary_tree_inc.h +39 -38
  63. data/vendor/brotli/c/enc/memory.h +24 -12
  64. data/vendor/brotli/c/enc/metablock.c +23 -27
  65. data/vendor/brotli/c/enc/metablock_inc.h +1 -1
  66. data/vendor/brotli/c/enc/params.h +3 -1
  67. data/vendor/brotli/c/enc/ringbuffer.h +4 -1
  68. data/vendor/brotli/c/enc/utf8_util.c +1 -1
  69. data/vendor/brotli/c/enc/write_bits.h +27 -25
  70. data/vendor/brotli/c/include/brotli/encode.h +22 -1
  71. data/vendor/brotli/c/include/brotli/port.h +14 -0
  72. metadata +17 -97
  73. data/.travis.yml +0 -31
  74. data/docs/Brotli.html +0 -485
  75. data/docs/Brotli/Error.html +0 -124
  76. data/docs/_index.html +0 -122
  77. data/docs/class_list.html +0 -51
  78. data/docs/css/common.css +0 -1
  79. data/docs/css/full_list.css +0 -58
  80. data/docs/css/style.css +0 -496
  81. data/docs/file.README.html +0 -127
  82. data/docs/file_list.html +0 -56
  83. data/docs/frames.html +0 -17
  84. data/docs/index.html +0 -127
  85. data/docs/js/app.js +0 -292
  86. data/docs/js/full_list.js +0 -216
  87. data/docs/js/jquery.js +0 -4
  88. data/docs/method_list.html +0 -67
  89. data/docs/top-level-namespace.html +0 -110
  90. data/spec/brotli_spec.rb +0 -88
  91. data/spec/inflate_spec.rb +0 -75
  92. data/spec/spec_helper.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab7474a5a27fb0167098bc9838f8e57bc78082d596c5e38a7c86764fdf62bf13
4
- data.tar.gz: 5a0b4d731bfd0fdbdd19a2cec36973cfc04460237acf58ac03dda51e8da82fe1
3
+ metadata.gz: 410f0b599c6bf42f90cb7fa83c6710304ad0fb018b1f4b5560bfadadaef165d9
4
+ data.tar.gz: 03fbcca525c9705b99935ab2ea4db0bb80e4c6a960d3010450a173c5b305eb43
5
5
  SHA512:
6
- metadata.gz: ae940a4cb85d44b0ad84a01fde9e8aaf285366dfcbccca4ba68619966005e1f003046de298b5d69098a4d3fcfd2a020ef49fa6eb5a3a0c790f59cd3060138bb6
7
- data.tar.gz: 92b9e926ad5b98f4afc7522c0b7a1a7490f32ed474d0881643f09ae8747ada70f25aa77360dc4707c369c6e58bfa747fc0eca7efb8cc05ee2f752e802202b017
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,7 +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'
7
- gem 'yard'
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,18 +1,25 @@
1
+ require "bundler/setup"
1
2
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
+ require "rake/clean"
4
+ require "rake/testtask"
3
5
  require "rake/extensiontask"
4
- require "yard"
5
6
 
6
- RSpec::Core::RakeTask.new(:spec)
7
+ CLEAN.include("ext/brotli/common")
8
+ CLEAN.include("ext/brotli/dec")
9
+ CLEAN.include("ext/brotli/enc")
10
+ CLEAN.include("ext/brotli/include")
7
11
 
8
- task :default => [:compile, :spec]
9
-
10
- task :build => :compile
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
11
18
 
12
19
  Rake::ExtensionTask.new("brotli") do |ext|
13
20
  ext.lib_dir = "lib/brotli"
14
21
  end
15
22
 
16
- YARD::Rake::YardocTask.new do |t|
17
- t.options = ["--no-progress", "--output-dir", "docs"]
18
- end
23
+ task :build => :compile
24
+ task :test => :compile
25
+ task :default => :test
@@ -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"
29
- spec.add_development_dependency "rake"
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 {
@@ -241,26 +237,6 @@ brotli_deflate_no_gvl(void *arg)
241
237
  return arg;
242
238
  }
243
239
 
244
- /*
245
- * call-seq:
246
- * Brotli.deflate(str, opts=nil) -> String
247
- * @param [String] str
248
- * string
249
- * @param [Hash] opts
250
- * options
251
- * @option opts [Symbol] :mode
252
- * Deflate mode
253
- * * :generic
254
- * * :text
255
- * * :font
256
- * @option opts [Integer] :quality
257
- * quality 0-11
258
- * @option opts [Integer] :lgwin
259
- * lgwin 10-24
260
- * @option opts [Integer] :lgblock
261
- * lgblock 16-24 or 0
262
- * @return [String] Deflated string
263
- */
264
240
  static VALUE
265
241
  brotli_deflate(int argc, VALUE *argv, VALUE self)
266
242
  {
@@ -268,6 +244,10 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
268
244
  brotli_deflate_args_t args;
269
245
 
270
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
+ }
271
251
  StringValue(str);
272
252
 
273
253
  args.str = (uint8_t*)RSTRING_PTR(str);
@@ -293,6 +273,185 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
293
273
  return value;
294
274
  }
295
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
+
296
455
  /*******************************************************************************
297
456
  * entry
298
457
  ******************************************************************************/
@@ -300,8 +459,27 @@ brotli_deflate(int argc, VALUE *argv, VALUE self)
300
459
  void
301
460
  Init_brotli(void)
302
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;
303
468
  rb_mBrotli = rb_define_module("Brotli");
304
469
  rb_eBrotli = rb_define_class_under(rb_mBrotli, "Error", rb_eStandardError);
470
+ rb_global_variable(&rb_eBrotli);
305
471
  rb_define_singleton_method(rb_mBrotli, "deflate", RUBY_METHOD_FUNC(brotli_deflate), -1);
306
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);
307
485
  }