brotli 0.6.0 → 0.8.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/Dockerfile +38 -0
  4. data/README.md +163 -2
  5. data/Rakefile +20 -3
  6. data/brotli.gemspec +12 -12
  7. data/ext/brotli/brotli.c +902 -197
  8. data/ext/brotli/brotli.h +3 -0
  9. data/ext/brotli/buffer.c +46 -67
  10. data/ext/brotli/buffer.h +6 -9
  11. data/ext/brotli/extconf.rb +22 -19
  12. data/lib/brotli/reader.rb +167 -0
  13. data/lib/brotli/version.rb +1 -1
  14. data/lib/brotli/writer.rb +81 -0
  15. data/lib/brotli.rb +2 -0
  16. data/vendor/brotli/c/common/constants.c +7 -7
  17. data/vendor/brotli/c/common/constants.h +2 -5
  18. data/vendor/brotli/c/common/context.c +2 -2
  19. data/vendor/brotli/c/common/context.h +1 -2
  20. data/vendor/brotli/c/common/dictionary.c +4 -5856
  21. data/vendor/brotli/c/common/dictionary.h +1 -2
  22. data/vendor/brotli/c/common/dictionary_inc.h +5847 -0
  23. data/vendor/brotli/c/common/platform.c +0 -4
  24. data/vendor/brotli/c/common/platform.h +182 -43
  25. data/vendor/brotli/c/common/shared_dictionary.c +3 -7
  26. data/vendor/brotli/c/common/shared_dictionary_internal.h +1 -1
  27. data/vendor/brotli/c/common/static_init.h +56 -0
  28. data/vendor/brotli/c/common/transform.c +6 -4
  29. data/vendor/brotli/c/common/transform.h +1 -2
  30. data/vendor/brotli/c/common/version.h +3 -3
  31. data/vendor/brotli/c/dec/bit_reader.c +2 -3
  32. data/vendor/brotli/c/dec/bit_reader.h +0 -4
  33. data/vendor/brotli/c/dec/decode.c +128 -39
  34. data/vendor/brotli/c/dec/huffman.c +2 -5
  35. data/vendor/brotli/c/dec/huffman.h +0 -2
  36. data/vendor/brotli/c/dec/prefix.c +67 -0
  37. data/vendor/brotli/c/dec/prefix.h +18 -708
  38. data/vendor/brotli/c/dec/prefix_inc.h +707 -0
  39. data/vendor/brotli/c/dec/state.c +18 -15
  40. data/vendor/brotli/c/dec/state.h +2 -6
  41. data/vendor/brotli/c/dec/static_init.c +53 -0
  42. data/vendor/brotli/c/dec/static_init.h +30 -0
  43. data/vendor/brotli/c/enc/backward_references.c +32 -8
  44. data/vendor/brotli/c/enc/backward_references.h +1 -5
  45. data/vendor/brotli/c/enc/backward_references_hq.c +15 -15
  46. data/vendor/brotli/c/enc/backward_references_hq.h +1 -5
  47. data/vendor/brotli/c/enc/bit_cost.c +28 -4
  48. data/vendor/brotli/c/enc/bit_cost.h +8 -40
  49. data/vendor/brotli/c/enc/bit_cost_inc.h +1 -1
  50. data/vendor/brotli/c/enc/block_splitter.c +9 -12
  51. data/vendor/brotli/c/enc/block_splitter.h +0 -3
  52. data/vendor/brotli/c/enc/block_splitter_inc.h +14 -8
  53. data/vendor/brotli/c/enc/brotli_bit_stream.c +10 -9
  54. data/vendor/brotli/c/enc/brotli_bit_stream.h +0 -6
  55. data/vendor/brotli/c/enc/cluster.c +0 -2
  56. data/vendor/brotli/c/enc/cluster.h +0 -2
  57. data/vendor/brotli/c/enc/command.c +1 -1
  58. data/vendor/brotli/c/enc/command.h +8 -10
  59. data/vendor/brotli/c/enc/compound_dictionary.c +3 -5
  60. data/vendor/brotli/c/enc/compound_dictionary.h +1 -4
  61. data/vendor/brotli/c/enc/compress_fragment.c +3 -13
  62. data/vendor/brotli/c/enc/compress_fragment.h +0 -2
  63. data/vendor/brotli/c/enc/compress_fragment_two_pass.c +5 -15
  64. data/vendor/brotli/c/enc/compress_fragment_two_pass.h +0 -2
  65. data/vendor/brotli/c/enc/dictionary_hash.c +127 -1830
  66. data/vendor/brotli/c/enc/dictionary_hash.h +23 -3
  67. data/vendor/brotli/c/enc/dictionary_hash_inc.h +1829 -0
  68. data/vendor/brotli/c/enc/encode.c +77 -52
  69. data/vendor/brotli/c/enc/encoder_dict.c +9 -7
  70. data/vendor/brotli/c/enc/encoder_dict.h +2 -4
  71. data/vendor/brotli/c/enc/entropy_encode.c +3 -6
  72. data/vendor/brotli/c/enc/entropy_encode.h +2 -4
  73. data/vendor/brotli/c/enc/entropy_encode_static.h +18 -12
  74. data/vendor/brotli/c/enc/fast_log.c +1 -1
  75. data/vendor/brotli/c/enc/fast_log.h +2 -3
  76. data/vendor/brotli/c/enc/find_match_length.h +0 -2
  77. data/vendor/brotli/c/enc/hash.h +38 -31
  78. data/vendor/brotli/c/enc/hash_base.h +38 -0
  79. data/vendor/brotli/c/enc/hash_forgetful_chain_inc.h +11 -1
  80. data/vendor/brotli/c/enc/hash_longest_match64_inc.h +24 -7
  81. data/vendor/brotli/c/enc/hash_longest_match64_simd_inc.h +304 -0
  82. data/vendor/brotli/c/enc/hash_longest_match_inc.h +30 -11
  83. data/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h +4 -0
  84. data/vendor/brotli/c/enc/hash_longest_match_simd_inc.h +278 -0
  85. data/vendor/brotli/c/enc/histogram.c +1 -0
  86. data/vendor/brotli/c/enc/histogram.h +0 -4
  87. data/vendor/brotli/c/enc/literal_cost.c +4 -6
  88. data/vendor/brotli/c/enc/literal_cost.h +0 -2
  89. data/vendor/brotli/c/enc/matching_tag_mask.h +69 -0
  90. data/vendor/brotli/c/enc/memory.c +0 -5
  91. data/vendor/brotli/c/enc/memory.h +0 -4
  92. data/vendor/brotli/c/enc/metablock.c +7 -9
  93. data/vendor/brotli/c/enc/metablock.h +3 -3
  94. data/vendor/brotli/c/enc/metablock_inc.h +4 -4
  95. data/vendor/brotli/c/enc/params.h +0 -1
  96. data/vendor/brotli/c/enc/prefix.h +0 -2
  97. data/vendor/brotli/c/enc/quality.h +17 -10
  98. data/vendor/brotli/c/enc/ringbuffer.h +1 -4
  99. data/vendor/brotli/c/enc/state.h +2 -2
  100. data/vendor/brotli/c/enc/static_dict.c +5 -11
  101. data/vendor/brotli/c/enc/static_dict.h +1 -3
  102. data/vendor/brotli/c/enc/static_dict_lut.c +224 -0
  103. data/vendor/brotli/c/enc/static_dict_lut.h +20 -5837
  104. data/vendor/brotli/c/enc/static_dict_lut_inc.h +5830 -0
  105. data/vendor/brotli/c/enc/static_init.c +59 -0
  106. data/vendor/brotli/c/enc/static_init.h +30 -0
  107. data/vendor/brotli/c/enc/static_init_lazy.cc +26 -0
  108. data/vendor/brotli/c/enc/utf8_util.c +1 -1
  109. data/vendor/brotli/c/enc/utf8_util.h +0 -2
  110. data/vendor/brotli/c/enc/write_bits.h +0 -2
  111. data/vendor/brotli/c/include/brotli/decode.h +1 -1
  112. data/vendor/brotli/c/include/brotli/encode.h +5 -1
  113. data/vendor/brotli/c/include/brotli/port.h +4 -7
  114. data/vendor/brotli/c/include/brotli/types.h +2 -2
  115. metadata +24 -14
  116. data/test/brotli_test.rb +0 -117
  117. data/test/brotli_writer_test.rb +0 -36
  118. data/test/test_helper.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfcc8a08b7351d54f9472ca2b549733eac9d4be139c3ba9f1ebfe41c3f8fb057
4
- data.tar.gz: 98764c09827125ba6304a6910a84e1674f4dcd6a3e13793b4438ea5c9a96f815
3
+ metadata.gz: 439249d900d92fdfbdba0c607d03b1cc52be123ed909bf7971d6c8b89c8134de
4
+ data.tar.gz: 3ae1816fee964948daedfa0835f2098efd4526def10f855f6a3fcc415ad56ce1
5
5
  SHA512:
6
- metadata.gz: d24c3f2595c9578a71e9d1a3208bcd74e95197e17f796ba3df72dd36818189a0838e79b9ae5ffcf668f258718a2c260142866e419993b4282bada394199d025b
7
- data.tar.gz: 891d4714d76a902d38898e9287063e97e8161be0f4e7af3f91477af5a7412c25d73b881dbd0d1031457458a69b3c4f0b466ab953a519318664acd3332c057fb7
6
+ metadata.gz: 330cc7d4176f2ede18d615c7c51862131182c5e3826d164b3cced606a490924d44ef349ab746677e8f62f547a87601ddc65c5f95197a7cc43f53e87f1a7f448d
7
+ data.tar.gz: 711194c375cd78bba734cb5ae4b89270ddcbaa8a457e3e45b055a6334a0a684d1f7351780c868a965c804cab306796949f5e915a60bbeb95f332b6900fea68a6
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- ruby: [2.7, 3.1, 3.2, 3.3]
10
+ ruby: [3.1, 3.2, 3.3, 3.4]
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - uses: actions/checkout@v4
data/Dockerfile ADDED
@@ -0,0 +1,38 @@
1
+ ARG GCC_VERSION=14
2
+ FROM gcc:${GCC_VERSION}
3
+
4
+ ARG USE_SYSTEM_BROTLI=true
5
+
6
+ # Install Ruby and development dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ git \
9
+ libbrotli-dev \
10
+ pkg-config \
11
+ ruby \
12
+ ruby-dev \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Verify GCC version
16
+ RUN gcc --version
17
+
18
+ # Set working directory
19
+ WORKDIR /app
20
+
21
+ # Copy source code
22
+ COPY . .
23
+
24
+ # Install bundler and dependencies
25
+ RUN gem install bundler
26
+ RUN bundle install
27
+
28
+ # Build and test
29
+ RUN if [ "${USE_SYSTEM_BROTLI}" = "true" ]; then \
30
+ echo "Building with system Brotli" && \
31
+ bundle exec rake compile; \
32
+ else \
33
+ echo "Building with vendor Brotli" && \
34
+ bundle exec rake compile -- --enable-vendor; \
35
+ fi
36
+ RUN bundle exec rake test
37
+
38
+ CMD ["bash"]
data/README.md CHANGED
@@ -25,19 +25,180 @@ Or install it yourself as:
25
25
 
26
26
  ## Usage
27
27
 
28
+ ### Basic Compression/Decompression
29
+
28
30
  ```ruby
29
31
  require 'brotli'
30
32
  compressed = Brotli.deflate(string)
31
33
  decompressed = Brotli.inflate(compressed)
32
34
  ```
33
35
 
34
- See test/brotli_test.rb
36
+ ### Custom Dictionary Support
37
+
38
+ Brotli supports using custom dictionaries to improve compression ratio when you have repetitive data patterns:
39
+
40
+ ```ruby
41
+ # Using a dictionary that contains common patterns in your data
42
+ dictionary = "common patterns in my data"
43
+ data = "This text contains common patterns in my data multiple times"
44
+
45
+ # Compress with dictionary
46
+ compressed = Brotli.deflate(data, dictionary: dictionary)
47
+
48
+ # Decompress with the same dictionary
49
+ decompressed = Brotli.inflate(compressed, dictionary: dictionary)
50
+ ```
51
+
52
+ ### Compression Options
53
+
54
+ ```ruby
55
+ # Combine dictionary with other compression options
56
+ compressed = Brotli.deflate(data,
57
+ dictionary: dictionary,
58
+ quality: 11, # 0-11, higher = better compression but slower
59
+ mode: :text, # :generic (default), :text, or :font
60
+ lgwin: 22, # window size (10-24)
61
+ lgblock: 0 # block size (0 or 16-24)
62
+ )
63
+ ```
64
+
65
+ ### Streaming Compression with Writer
66
+
67
+ ```ruby
68
+ # Basic usage
69
+ File.open('output.br', 'wb') do |file|
70
+ writer = Brotli::Writer.new(file)
71
+ writer.write(data)
72
+ writer.close
73
+ end
74
+
75
+ # With dictionary
76
+ File.open('output.br', 'wb') do |file|
77
+ writer = Brotli::Writer.new(file, dictionary: dictionary)
78
+ writer.write(data)
79
+ writer.close
80
+ end
81
+ ```
82
+
83
+ ### Streaming Decompression with Reader
84
+
85
+ ```ruby
86
+ # Basic usage
87
+ File.open('output.br', 'rb') do |file|
88
+ reader = Brotli::Reader.new(file)
89
+ data = reader.read
90
+ reader.close
91
+ end
92
+
93
+ # With dictionary
94
+ File.open('output.br', 'rb') do |file|
95
+ reader = Brotli::Reader.new(file, dictionary: dictionary)
96
+ data = reader.read
97
+ reader.close
98
+ end
99
+ ```
100
+
101
+ `Brotli::Reader` is the preferred API when you already have an IO-like input.
102
+ It handles incremental decompression for you, including small `read` and
103
+ `readpartial` calls.
104
+
105
+ ### Low-level Streaming with Compressor and Decompressor
106
+
107
+ `Brotli::Writer` and `Brotli::Reader` are the preferred high-level streaming
108
+ interfaces. `Brotli::Compressor` and `Brotli::Decompressor` are lower-level
109
+ primitives intended for integrations which already manage their own buffering
110
+ and chunk boundaries.
111
+
112
+ Use `Brotli::Writer` and `Brotli::Reader` when you have an IO-like stream and
113
+ want Brotli to handle the streaming lifecycle for you.
114
+
115
+ Use `Brotli::Compressor` and `Brotli::Decompressor` when your application
116
+ already owns the buffering model, for example when working with framed
117
+ protocols, event loops, chunked transports, or custom body transcoders.
118
+
119
+ This is a typical chunk-by-chunk compression pattern:
120
+
121
+ ```ruby
122
+ compressor = Brotli::Compressor.new
123
+
124
+ compressed_chunk = compressor.process(input_chunk)
125
+ compressed_chunk << compressor.flush
126
+
127
+ # when the input stream is finished
128
+ compressed_tail = compressor.finish
129
+ ```
130
+
131
+ And this is the corresponding incremental decompression pattern:
132
+
133
+ ```ruby
134
+ decompressor = Brotli::Decompressor.new
135
+ output = +""
136
+
137
+ compressed_chunks.each do |input_chunk|
138
+ output << decompressor.process(input_chunk)
139
+ end
140
+
141
+ unless decompressor.finished?
142
+ raise Brotli::Error, "Unexpected end of compressed stream"
143
+ end
144
+ ```
145
+
146
+ For fully chunked or frame-based transports, call `#process` once per received
147
+ compressed chunk and append the returned output. `#finished?` tells you whether
148
+ the full Brotli stream has been decoded, and `#can_accept_more_data` indicates
149
+ whether the decompressor is ready for more input or still has buffered output to
150
+ drain first.
151
+
152
+ These low-level classes are useful when you need to plug Brotli into an
153
+ existing incremental pipeline without wrapping the stream in an IO-like object.
154
+
155
+ ### `output_buffer_limit`
156
+
157
+ `Brotli::Decompressor#process` also accepts `output_buffer_limit:`:
158
+
159
+ ```ruby
160
+ decompressor = Brotli::Decompressor.new
161
+ output = +""
162
+
163
+ compressed_chunks.each do |chunk|
164
+ output << decompressor.process(chunk, output_buffer_limit: 16 * 1024)
165
+
166
+ until decompressor.can_accept_more_data || decompressor.finished?
167
+ output << decompressor.process("", output_buffer_limit: 16 * 1024)
168
+ end
169
+ end
170
+
171
+ unless decompressor.finished?
172
+ raise Brotli::Error, "Unexpected end of compressed stream"
173
+ end
174
+ ```
175
+
176
+ `output_buffer_limit:` caps how many decompressed bytes are returned from a
177
+ single `#process` call. When the limit is reached, the decompressor may still
178
+ have buffered state to drain. In that case `#can_accept_more_data` returns
179
+ `false`, and you should keep calling `#process("")` until it becomes `true`
180
+ again or the stream is finished.
181
+
182
+ Use `output_buffer_limit:` when:
183
+
184
+ - you want to bound memory usage or work per iteration
185
+ - you are feeding decompressed bytes into a downstream consumer with backpressure
186
+ - you are integrating Brotli into an event loop, framed protocol, or chunked transport
187
+ - you are reading small slices from a large compressed payload and do not want a single call to return a very large output buffer
188
+
189
+ You usually do not need `output_buffer_limit:` when:
190
+
191
+ - you are using `Brotli.inflate` for one-shot decompression
192
+ - you are using `Brotli::Reader`, which already handles incremental buffering for IO-style reads
193
+ - you are happy for each `#process` call to return all currently available output
194
+
195
+ See test/brotli_test.rb for more examples.
35
196
 
36
197
  ## Development
37
198
 
38
199
  After checking out the repo, run `bin/setup` to install bundle and Brotli C library dependencies.
39
200
 
40
- Run `rake build` to build brotli extension for ruby. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
201
+ Run `rake build` to build the Brotli extension for Ruby. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
202
 
42
203
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
204
 
data/Rakefile CHANGED
@@ -20,6 +20,23 @@ Rake::ExtensionTask.new("brotli") do |ext|
20
20
  ext.lib_dir = "lib/brotli"
21
21
  end
22
22
 
23
- task :build => :compile
24
- task :test => :compile
25
- task :default => :test
23
+ task build: :compile
24
+ task test: :compile
25
+ task default: :test
26
+
27
+ task :docker do
28
+ gcc_versions = ["14", "15"]
29
+ brotli_configs = [true, false]
30
+ gcc_versions.product(brotli_configs).each do |gcc_version, use_system_brotli|
31
+ command = "docker build "\
32
+ "--progress=plain "\
33
+ "--build-arg GCC_VERSION=#{gcc_version} "\
34
+ "--build-arg USE_SYSTEM_BROTLI=#{use_system_brotli} "\
35
+ "-t brotli:#{gcc_version}#{use_system_brotli ? "-use_system_brotli" : ""} ."
36
+ puts "Running: #{command}"
37
+ system command
38
+ unless $?.exited?
39
+ raise "Docker build failed for GCC version #{gcc_version} with use_system_brotli=#{use_system_brotli}"
40
+ end
41
+ end
42
+ end
data/brotli.gemspec CHANGED
@@ -1,7 +1,4 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "brotli/version"
1
+ require_relative "lib/brotli/version"
5
2
 
6
3
  Gem::Specification.new do |spec|
7
4
  spec.name = "brotli"
@@ -14,14 +11,17 @@ Gem::Specification.new do |spec|
14
11
  spec.homepage = "https://github.com/miyucy/brotli"
15
12
  spec.license = "MIT"
16
13
 
17
- spec.test_files = `git ls-files -z -- test`.split("\x0")
18
- spec.files = `git ls-files -z`.split("\x0")
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"]
23
- spec.bindir = "exe"
24
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
14
+ tracked_files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
15
+ vendored_brotli_files = Dir.chdir(__dir__) do
16
+ Dir["vendor/brotli/c/{common,enc,dec,include}/**/*"].select { |path| File.file?(path) }
17
+ end
18
+
19
+ spec.files = tracked_files
20
+ .reject { |path| path == "vendor/brotli" || path.start_with?("test/") }
21
+ .concat(vendored_brotli_files)
22
+ .append("vendor/brotli/LICENSE")
23
+ .sort
24
+ .uniq
25
25
  spec.require_paths = ["lib"]
26
26
  spec.extensions = ["ext/brotli/extconf.rb"]
27
27
  end