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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/Dockerfile +38 -0
- data/README.md +163 -2
- data/Rakefile +20 -3
- data/brotli.gemspec +12 -12
- data/ext/brotli/brotli.c +902 -197
- data/ext/brotli/brotli.h +3 -0
- data/ext/brotli/buffer.c +46 -67
- data/ext/brotli/buffer.h +6 -9
- data/ext/brotli/extconf.rb +22 -19
- data/lib/brotli/reader.rb +167 -0
- data/lib/brotli/version.rb +1 -1
- data/lib/brotli/writer.rb +81 -0
- data/lib/brotli.rb +2 -0
- data/vendor/brotli/c/common/constants.c +7 -7
- data/vendor/brotli/c/common/constants.h +2 -5
- data/vendor/brotli/c/common/context.c +2 -2
- data/vendor/brotli/c/common/context.h +1 -2
- data/vendor/brotli/c/common/dictionary.c +4 -5856
- data/vendor/brotli/c/common/dictionary.h +1 -2
- data/vendor/brotli/c/common/dictionary_inc.h +5847 -0
- data/vendor/brotli/c/common/platform.c +0 -4
- data/vendor/brotli/c/common/platform.h +182 -43
- data/vendor/brotli/c/common/shared_dictionary.c +3 -7
- data/vendor/brotli/c/common/shared_dictionary_internal.h +1 -1
- data/vendor/brotli/c/common/static_init.h +56 -0
- data/vendor/brotli/c/common/transform.c +6 -4
- data/vendor/brotli/c/common/transform.h +1 -2
- data/vendor/brotli/c/common/version.h +3 -3
- data/vendor/brotli/c/dec/bit_reader.c +2 -3
- data/vendor/brotli/c/dec/bit_reader.h +0 -4
- data/vendor/brotli/c/dec/decode.c +128 -39
- data/vendor/brotli/c/dec/huffman.c +2 -5
- data/vendor/brotli/c/dec/huffman.h +0 -2
- data/vendor/brotli/c/dec/prefix.c +67 -0
- data/vendor/brotli/c/dec/prefix.h +18 -708
- data/vendor/brotli/c/dec/prefix_inc.h +707 -0
- data/vendor/brotli/c/dec/state.c +18 -15
- data/vendor/brotli/c/dec/state.h +2 -6
- data/vendor/brotli/c/dec/static_init.c +53 -0
- data/vendor/brotli/c/dec/static_init.h +30 -0
- data/vendor/brotli/c/enc/backward_references.c +32 -8
- data/vendor/brotli/c/enc/backward_references.h +1 -5
- data/vendor/brotli/c/enc/backward_references_hq.c +15 -15
- data/vendor/brotli/c/enc/backward_references_hq.h +1 -5
- data/vendor/brotli/c/enc/bit_cost.c +28 -4
- data/vendor/brotli/c/enc/bit_cost.h +8 -40
- data/vendor/brotli/c/enc/bit_cost_inc.h +1 -1
- data/vendor/brotli/c/enc/block_splitter.c +9 -12
- data/vendor/brotli/c/enc/block_splitter.h +0 -3
- data/vendor/brotli/c/enc/block_splitter_inc.h +14 -8
- data/vendor/brotli/c/enc/brotli_bit_stream.c +10 -9
- data/vendor/brotli/c/enc/brotli_bit_stream.h +0 -6
- data/vendor/brotli/c/enc/cluster.c +0 -2
- data/vendor/brotli/c/enc/cluster.h +0 -2
- data/vendor/brotli/c/enc/command.c +1 -1
- data/vendor/brotli/c/enc/command.h +8 -10
- data/vendor/brotli/c/enc/compound_dictionary.c +3 -5
- data/vendor/brotli/c/enc/compound_dictionary.h +1 -4
- data/vendor/brotli/c/enc/compress_fragment.c +3 -13
- data/vendor/brotli/c/enc/compress_fragment.h +0 -2
- data/vendor/brotli/c/enc/compress_fragment_two_pass.c +5 -15
- data/vendor/brotli/c/enc/compress_fragment_two_pass.h +0 -2
- data/vendor/brotli/c/enc/dictionary_hash.c +127 -1830
- data/vendor/brotli/c/enc/dictionary_hash.h +23 -3
- data/vendor/brotli/c/enc/dictionary_hash_inc.h +1829 -0
- data/vendor/brotli/c/enc/encode.c +77 -52
- data/vendor/brotli/c/enc/encoder_dict.c +9 -7
- data/vendor/brotli/c/enc/encoder_dict.h +2 -4
- data/vendor/brotli/c/enc/entropy_encode.c +3 -6
- data/vendor/brotli/c/enc/entropy_encode.h +2 -4
- data/vendor/brotli/c/enc/entropy_encode_static.h +18 -12
- data/vendor/brotli/c/enc/fast_log.c +1 -1
- data/vendor/brotli/c/enc/fast_log.h +2 -3
- data/vendor/brotli/c/enc/find_match_length.h +0 -2
- data/vendor/brotli/c/enc/hash.h +38 -31
- data/vendor/brotli/c/enc/hash_base.h +38 -0
- data/vendor/brotli/c/enc/hash_forgetful_chain_inc.h +11 -1
- data/vendor/brotli/c/enc/hash_longest_match64_inc.h +24 -7
- data/vendor/brotli/c/enc/hash_longest_match64_simd_inc.h +304 -0
- data/vendor/brotli/c/enc/hash_longest_match_inc.h +30 -11
- data/vendor/brotli/c/enc/hash_longest_match_quickly_inc.h +4 -0
- data/vendor/brotli/c/enc/hash_longest_match_simd_inc.h +278 -0
- data/vendor/brotli/c/enc/histogram.c +1 -0
- data/vendor/brotli/c/enc/histogram.h +0 -4
- data/vendor/brotli/c/enc/literal_cost.c +4 -6
- data/vendor/brotli/c/enc/literal_cost.h +0 -2
- data/vendor/brotli/c/enc/matching_tag_mask.h +69 -0
- data/vendor/brotli/c/enc/memory.c +0 -5
- data/vendor/brotli/c/enc/memory.h +0 -4
- data/vendor/brotli/c/enc/metablock.c +7 -9
- data/vendor/brotli/c/enc/metablock.h +3 -3
- data/vendor/brotli/c/enc/metablock_inc.h +4 -4
- data/vendor/brotli/c/enc/params.h +0 -1
- data/vendor/brotli/c/enc/prefix.h +0 -2
- data/vendor/brotli/c/enc/quality.h +17 -10
- data/vendor/brotli/c/enc/ringbuffer.h +1 -4
- data/vendor/brotli/c/enc/state.h +2 -2
- data/vendor/brotli/c/enc/static_dict.c +5 -11
- data/vendor/brotli/c/enc/static_dict.h +1 -3
- data/vendor/brotli/c/enc/static_dict_lut.c +224 -0
- data/vendor/brotli/c/enc/static_dict_lut.h +20 -5837
- data/vendor/brotli/c/enc/static_dict_lut_inc.h +5830 -0
- data/vendor/brotli/c/enc/static_init.c +59 -0
- data/vendor/brotli/c/enc/static_init.h +30 -0
- data/vendor/brotli/c/enc/static_init_lazy.cc +26 -0
- data/vendor/brotli/c/enc/utf8_util.c +1 -1
- data/vendor/brotli/c/enc/utf8_util.h +0 -2
- data/vendor/brotli/c/enc/write_bits.h +0 -2
- data/vendor/brotli/c/include/brotli/decode.h +1 -1
- data/vendor/brotli/c/include/brotli/encode.h +5 -1
- data/vendor/brotli/c/include/brotli/port.h +4 -7
- data/vendor/brotli/c/include/brotli/types.h +2 -2
- metadata +24 -14
- data/test/brotli_test.rb +0 -117
- data/test/brotli_writer_test.rb +0 -36
- data/test/test_helper.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 439249d900d92fdfbdba0c607d03b1cc52be123ed909bf7971d6c8b89c8134de
|
|
4
|
+
data.tar.gz: 3ae1816fee964948daedfa0835f2098efd4526def10f855f6a3fcc415ad56ce1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 330cc7d4176f2ede18d615c7c51862131182c5e3826d164b3cced606a490924d44ef349ab746677e8f62f547a87601ddc65c5f95197a7cc43f53e87f1a7f448d
|
|
7
|
+
data.tar.gz: 711194c375cd78bba734cb5ae4b89270ddcbaa8a457e3e45b055a6334a0a684d1f7351780c868a965c804cab306796949f5e915a60bbeb95f332b6900fea68a6
|
data/.github/workflows/main.yml
CHANGED
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
|
-
|
|
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
|
|
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 :
|
|
24
|
-
task :
|
|
25
|
-
task :
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
spec.files
|
|
23
|
-
|
|
24
|
-
|
|
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
|