Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "9651aa3ebfd23be83ca14d914fba2d7c12b32d2f"
},
"path_in_vcs": ""
}
+121
View File
@@ -0,0 +1,121 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "brotli"
version = "8.0.4"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
"sha2",
]
[[package]]
name = "brotli-decompressor"
version = "5.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+117
View File
@@ -0,0 +1,117 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2015"
rust-version = "1.59.0"
name = "brotli"
version = "8.0.4"
authors = [
"Daniel Reiter Horn <danielrh@dropbox.com>",
"The Brotli Authors",
]
build = false
include = [
"/src/**/*.rs",
"/examples/**/*.rs",
"/Cargo.toml",
"/README.md",
"/LICENSE.BSD-3-Clause",
"/LICENSE.MIT",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A brotli compressor and decompressor that with an interface avoiding the rust stdlib. This makes it suitable for embedded devices and kernels. It is designed with a pluggable allocator so that the standard lib's allocator may be employed. The default build also includes a stdlib allocator and stream interface. Disable this with --features=no-stdlib. All included code is safe."
homepage = "https://github.com/dropbox/rust-brotli"
documentation = "https://docs.rs/brotli/"
readme = "README.md"
keywords = [
"brotli",
"decompression",
"lz77",
"huffman",
"nostd",
]
categories = [
"compression",
"no-std",
]
license = "BSD-3-Clause AND MIT"
repository = "https://github.com/dropbox/rust-brotli"
[features]
benchmark = ["brotli-decompressor/benchmark"]
billing = []
default = ["std"]
disable-timer = ["brotli-decompressor/disable-timer"]
disallow_large_window_size = []
external-literal-probability = []
ffi-api = ["brotli-decompressor/ffi-api"]
float64 = []
floating_point_context_mixing = []
no-stdlib-ffi-binding = []
pass-through-ffi-panics = []
seccomp = ["brotli-decompressor/seccomp"]
simd = []
std = [
"alloc-stdlib",
"brotli-decompressor/std",
]
validation = ["sha2"]
vector_scratch_space = []
[lib]
name = "brotli"
path = "src/lib.rs"
[[bin]]
name = "brotli"
path = "src/bin/brotli.rs"
doc = false
[[bin]]
name = "catbrotli"
path = "src/bin/catbrotli.rs"
doc = false
[[example]]
name = "compress"
path = "examples/compress.rs"
[[example]]
name = "decompress"
path = "examples/decompress.rs"
[dependencies.alloc-no-stdlib]
version = ">=2.0.4, <3"
[dependencies.alloc-stdlib]
version = "~0.2"
optional = true
[dependencies.brotli-decompressor]
version = "~5.0"
default-features = false
[dependencies.sha2]
version = "~0.10"
optional = true
[dev-dependencies.alloc-no-stdlib]
version = ">=2.0.4, <3"
features = ["unsafe"]
[profile.release]
lto = true
incremental = false
+66
View File
@@ -0,0 +1,66 @@
[package]
name = "brotli"
version = "8.0.4"
authors = ["Daniel Reiter Horn <danielrh@dropbox.com>", "The Brotli Authors"]
description = "A brotli compressor and decompressor that with an interface avoiding the rust stdlib. This makes it suitable for embedded devices and kernels. It is designed with a pluggable allocator so that the standard lib's allocator may be employed. The default build also includes a stdlib allocator and stream interface. Disable this with --features=no-stdlib. All included code is safe."
license = "BSD-3-Clause AND MIT"
documentation = "https://docs.rs/brotli/"
homepage = "https://github.com/dropbox/rust-brotli"
repository = "https://github.com/dropbox/rust-brotli"
keywords = ["brotli", "decompression", "lz77", "huffman", "nostd"]
categories = ["compression", "no-std"]
readme = "README.md"
autobins = false
edition = "2015"
rust-version = "1.59.0"
include = [
"/src/**/*.rs",
"/examples/**/*.rs",
"/Cargo.toml",
"/README.md",
"/LICENSE.BSD-3-Clause",
"/LICENSE.MIT",
]
[[bin]]
doc = false
name = "brotli"
[[bin]]
doc = false
name = "catbrotli"
[profile.release]
lto = true
incremental = false
[dependencies]
"alloc-no-stdlib" = { version = ">=2.0.4, <3" }
"alloc-stdlib" = { version = "~0.2", optional = true }
"brotli-decompressor" = { version = "~5.0", default-features = false }
"sha2" = { version = "~0.10", optional = true }
[dev-dependencies]
# The test suite (src/enc/test.rs) builds calloc-backed memory pools, which on
# alloc-no-stdlib 3.x live behind its "unsafe" feature (always present on 2.x).
# Enable it for tests only; the library itself uses no calloc path.
"alloc-no-stdlib" = { version = ">=2.0.4, <3", features = ["unsafe"] }
[features]
default = ["std"]
benchmark = ["brotli-decompressor/benchmark"]
billing = []
disable-timer = ["brotli-decompressor/disable-timer"]
disallow_large_window_size = []
external-literal-probability = []
ffi-api = ["brotli-decompressor/ffi-api"]
float64 = []
floating_point_context_mixing = []
no-stdlib-ffi-binding = []
pass-through-ffi-panics = []
seccomp = ["brotli-decompressor/seccomp"]
simd = []
std = ["alloc-stdlib", "brotli-decompressor/std"]
validation = ["sha2"]
vector_scratch_space = []
+12
View File
@@ -0,0 +1,12 @@
Copyright (c) 2016 Dropbox, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+320
View File
@@ -0,0 +1,320 @@
# rust-brotli
[![crates.io](https://img.shields.io/crates/v/brotli.svg)](https://crates.io/crates/brotli)
[![Build Status](https://travis-ci.org/dropbox/rust-brotli.svg?branch=master)](https://travis-ci.org/dropbox/rust-brotli)
# What's new in 8.0.4
Fix: adjust versions of rust-decompressor and rust-alloc-no-stdlib and
alloc-stdlib so the Allocator<> trait is identical for all associated crates.
Return BrotliFileNotCraftedForConcatenation when a new stream header advertises more whole source bytes than have been buffered. This prevents the unsigned subtraction in shift_and_check_new_stream_header from underflowing on truncated metadata headers.
Return NULL from BrotliEncoderCreateInstance and BrotliEncoderCreateWorkPool when a caller-provided allocator returns NULL, rather than writing state through a NULL pointer.
Wrap the mutable Broccoli FFI entry points in a local catch_unwind helper, matching the encoder FFI convention so Rust panics do not unwind across extern C when std panic catching is available.
Return BrotliFileNotCraftedForConcatenation on caught panics and keep the existing pass-through behavior for no-std or pass-through-ffi-panics builds. Add regression coverage for a crafted stream input that previously panicked through BroccoliConcatStream.
Reject serialized BroCatli buffers with out-of-range live state fields before constructing the state. This keeps deserialize_from_buffer on its existing Result<BroCatli, ()> API while returning Err(()) for corrupt buffers that would otherwise panic on later use
## What's new in 8.0.3
Fix: avoid panic across Broccoli FFI boundary with BroCatLi
Fix: CompressMulti worker joins on errors
## What's new in 8.0.2
Fix for memory leak in ffi API
## What's new in 8.0.1
Compatibility for ffi builds
## What's new in 8.0.0
* Fixed LZ77 to comply with the specification
* No longer seed the context by the end of the lz77 dictionary. Instead use
zero for the seed as would happen without a dictionary. This matches the
behavior of brotli with a custom dictionary as specified in
* https://datatracker.ietf.org/doc/draft-vandevenne-shared-brotli-format/
## What's new in 7.0.0
* Fix error with short writes
* allow quality=10 for certain APIs and make it default to 9.5
## What's new in 6.0.0
* Remove unused SIMD use statements
* hide a few warnings - these are TODOs, and should be fixed in separate PRs
* do NOT build SIMD as part of MSRV -- doesn't make any sense to combine nightly with MSRV
## What's new in 5.0.0
* The FFI is no longer active by default to avoid ODR issues if multiple versions of brotli are included in several dependent crates.
## What's new in 4.0.0
Pinned to a rust-brotli-decompressor that can disable the ffi with the ffi-api
flag.
This can help avoid symbol conflicts with other brotli libs.
## What's new in 3.5
Updated SIMD support.
Better CI integration.
Cleaned up some of the clippy warnings.
## What's new in 3.4
Brotli decompressor's reader and writer has better behavior when operating upon brotli streams with extra bits at the end.
Optional features like stdsimd are now tested or disabled for now.
## What's new in 3.2
* into_inner conversions for both Reader and Writer classes
## What's new in 3.0
* A fully compatible FFI for drop-in compatibiltiy with the https://github.com/google/brotli binaries
* custom allocators fully supported
* Multithreaded compression so multiple threads can operate in unison on a single file
* Concatenatability mode to add the feature requested in https://github.com/google/brotli/issues/628
* binary tool catbrotli can accomplish this if the first file was specified with -apendable and the second with -catable
* validation mode where a file is double-checked to be able to be decompressed with the same settings; useful for benchmarking or fuzzing
* Magic Number: where the brotli file can have a useful header with a few magic bytes, concatability info and a final output size for pre-allocating memory
## What's new in 2.5
* In 2.5 The callback also passes down an allocator to make new StaticCommands and PDFs and 256 bit floating point vectors.
* In 2.4 The callback with the compression intermediate representation now passes a full metablock at a time. Also these items are mutable
in case futher optimization is desired
## What's new in 2.3
* Flush now produces output instead of calling finish on the stream. This allows you to use the writer abstraction to
get immediate output without having to resort to the CompressStream internal abstraction
## Project Requirements
Direct no-stdlib port of the C brotli compressor to Rust
no dependency on the Rust stdlib: this library would be ideal for decompressing within a rust kernel among other things.
This is useful to see how C and Rust compare in an apples-to-apples
comparison where the same algorithms and data structures and
optimizations are employed.
## Compression Usage
Rust brotli currently supports compression levels 0 - 11
They should be bitwise identical to the brotli C compression engine at compression levels 0-9
Recommended lg_window_size is between 20 and 22
### With the io::Read abstraction
```rust
let mut input = brotli::CompressorReader::new(&mut io::stdin(), 4096 /* buffer size */,
quality as u32, lg_window_size as u32);
```
then you can simply read input as you would any other io::Read class
### With the io::Write abstraction
```rust
let mut writer = brotli::Compressor::new(&mut io::stdout(), 4096 /* buffer size */,
quality as u32, lg_window_size as u32);
```
There are also methods to build Compressor Readers or Writers using the with_params static function
eg:
```rust
let params = BrotliEncoderParams::default();
// modify params to fit the application needs
let mut writer = brotli::Compressor::with_params(&mut io::stdout(), 4096 /* buffer size */,
params);
```
or for the reader
```rust
let params = BrotliEncoderParams::default();
// modify params to fit the application needs
let mut writer = brotli::CompressorReader::with_params(&mut io::stdin(), 4096 /* buffer size */,
params);
```
### With the Stream Copy abstraction
```rust
match brotli::BrotliCompress(&mut io::stdin(), &mut io::stdout(), &brotli_encoder_params) {
Ok(_) => {},
Err(e) => panic!("Error {:?}", e),
}
```
## Decompression Usage
### With the io::Read abstraction
```rust
let mut input = brotli::Decompressor::new(&mut io::stdin(), 4096 /* buffer size */);
```
then you can simply read input as you would any other io::Read class
### With the io::Write abstraction
```rust
let mut writer = brotli::DecompressorWriter::new(&mut io::stdout(), 4096 /* buffer size */);
```
### With the Stream Copy abstraction
```rust
match brotli::BrotliDecompress(&mut io::stdin(), &mut io::stdout()) {
Ok(_) => {},
Err(e) => panic!("Error {:?}", e),
}
```
### With manual memory management
There are 3 steps to using brotli without stdlib
1. setup the memory manager
2. setup the BrotliState
3. in a loop, call BrotliDecompressStream
in Detail
```rust
// at global scope declare a MemPool type -- in this case we'll choose the heap to
// avoid unsafe code, and avoid restrictions of the stack size
declare_stack_allocator_struct!(MemPool, heap);
// at local scope, make a heap allocated buffers to hold uint8's uint32's and huffman codes
let mut u8_buffer = define_allocator_memory_pool!(4096, u8, [0; 32 * 1024 * 1024], heap);
let mut u32_buffer = define_allocator_memory_pool!(4096, u32, [0; 1024 * 1024], heap);
let mut hc_buffer = define_allocator_memory_pool!(4096, HuffmanCode, [0; 4 * 1024 * 1024], heap);
let heap_u8_allocator = HeapPrealloc::<u8>::new_allocator(4096, &mut u8_buffer, bzero);
let heap_u32_allocator = HeapPrealloc::<u32>::new_allocator(4096, &mut u32_buffer, bzero);
let heap_hc_allocator = HeapPrealloc::<HuffmanCode>::new_allocator(4096, &mut hc_buffer, bzero);
// At this point no more syscalls are going to be needed since everything can come from the allocators.
// Feel free to activate SECCOMP jailing or other mechanisms to secure your application if you wish.
// Now it's possible to setup the decompressor state
let mut brotli_state = BrotliState::new(heap_u8_allocator, heap_u32_allocator, heap_hc_allocator);
// at this point the decompressor simply needs an input and output buffer and the ability to track
// the available data left in each buffer
loop {
result = BrotliDecompressStream(&mut available_in, &mut input_offset, &input.slice(),
&mut available_out, &mut output_offset, &mut output.slice_mut(),
&mut written, &mut brotli_state);
// just end the decompression if result is BrotliResult::ResultSuccess or BrotliResult::ResultFailure
}
```
This interface is the same interface that the C brotli decompressor uses
Also feel free to use custom allocators that invoke Box directly.
This example illustrates a mechanism to avoid subsequent syscalls after the initial allocation
## Using the C interface
rust-brotli is a drop-in replacement for the official https://github.com/google/brotli C
implementation. That means you can use it from any place that supports that library.
To build rust-brotli in this manner enter the c subdirectory and run make there
cd c && make
this should build c/target/release/libbrotli.so and should build the vanilla
command line tool in C for compressing and decompressing any brotli file.
the libbrotli.so in c/target/release should be able to replace any other libbrotli.so
file, but with all the advantages of using safe rust (except in the FFI bindings)
The code also allows a wider range of options, including forcing the prediction mode
(eg UTF8 vs signed vs MSB vs LSB) and changing the weight of the literal cost from 540
to other values.
## Stream Concatenation
Brotli supports creating streams that can be concatenated together, useful for streaming
scenarios where you want to compress chunks independently but decompress as a single stream.
### Simple Concatenation (Fast)
Use `-bare -appendable` for the first file and `-bare -catable` for subsequent files.
These can be combined using plain byte concatenation without special tools, with a
finalization byte (`0x03`) added at the end:
```bash
# Create the base file with header but no trailer (must specify window size)
brotli -c -bare -appendable -w22 input1.txt > base.br
# Create bare-catable streams (no header, no trailer, same window size!)
brotli -c -bare -catable -w22 input2.txt > part2.br
brotli -c -bare -catable -w22 input3.txt > part3.br
# Simple concatenation with finalization byte
# Note: printf '\x03' adds the required final byte
(cat base.br part2.br part3.br; printf '\x03') > combined.br
# Decompress normally
brotli -d combined.br -o output.txt
```
**Advantages:**
- Instant concatenation (no processing)
- No special tools required
- Bare streams can be appended in any order
**Requirements:**
- All files must use the same window size (`-w22` recommended)
- First file: `-bare -appendable` (has header, no trailer)
- Subsequent files: `-bare -catable` (no header, no trailer, no dictionary refs)
- A final `0x03` byte must be appended to complete the stream
### Efficient Concatenation (Size-optimized)
Use the `catbrotli` tool with `-catable` and `-appendable` flags for better compression
at the cost of processing time:
```bash
# Create files for catbrotli tool
brotli -c -appendable input1.txt > appendable.br
brotli -c -catable input2.txt > catable1.br
brotli -c -catable input3.txt > catable2.br
# Concatenate using catbrotli tool
catbrotli appendable.br catable1.br catable2.br > combined.br
```
**Tradeoff:** `catbrotli` produces smaller output but requires CPU time to process the
streams intelligently. Use this when size matters more than concatenation speed.
### Technical Reference: Stream Parameter Interactions
**Stream Types and Their Parameters:**
| Stream Type | bare_stream | byte_align | appendable | catable | use_dictionary | Description |
|-------------|-------------|------------|------------|---------|----------------|-------------|
| Standard | false | false | false | false | true | Normal brotli stream with header and trailer |
| First (simple concat) | true | true | true | false | true | Has header, no trailer - for simple `cat` concatenation |
| Subsequent (simple concat) | true | true | true | true | false | No header, no trailer, no dict refs - append to first |
| Appendable (catbrotli) | false | varies | true | false | true | For use with `catbrotli` tool |
| Catable (catbrotli) | false | varies | true | true | false | For use with `catbrotli` tool |
**Important Notes:**
- **Parameter dependencies are applied automatically** by the library in both CLI and API usage
- The library's `SanitizeParams` function ensures:
- `catable = true` → automatically sets `appendable = true` and `use_dictionary = false`
- `bare_stream = true` → automatically sets `byte_align = true`
- `!appendable` → automatically sets `byte_align = false`
- When using `set_parameter()`, dependencies are applied immediately
- When setting fields directly (e.g., `params.catable = true`), dependencies are applied during compression initialization
- **No manual fixups needed** - the library handles all parameter dependencies
- The `use_dictionary = false` for catable streams prevents references to bytes before the chunk boundary
- Simple concatenation requires a final `0x03` byte to complete the stream
- All concatenated streams must use the same window size
**Example API Usage:**
```rust
// First file: -bare -appendable equivalent
params.bare_stream = true; // Sets bare_stream=true, byte_align=true (automatic)
params.appendable = true; // Sets appendable=true
// Subsequent files: -bare -catable equivalent
params.bare_stream = true; // Sets bare_stream=true, byte_align=true (automatic)
params.catable = true; // Sets catable=true, appendable=true, use_dictionary=false (automatic)
// All parameter dependencies are handled automatically by the library.
// No manual fixups required - just set the primary flags you want.
```
+42
View File
@@ -0,0 +1,42 @@
extern crate brotli;
#[cfg(not(feature = "std"))]
fn main() {
panic!("For no-stdlib examples please see the tests")
}
#[cfg(feature = "std")]
fn main() {
use std::io;
use std::io::{Read, Write};
let stdout = &mut io::stdout();
{
let mut writer = brotli::CompressorWriter::new(stdout, 4096, 11, 22);
let mut buf = [0u8; 4096];
loop {
match io::stdin().read(&mut buf[..]) {
Err(e) => {
if let io::ErrorKind::Interrupted = e.kind() {
continue;
}
panic!("{}", e);
}
Ok(size) => {
if size == 0 {
match writer.flush() {
Err(e) => {
if let io::ErrorKind::Interrupted = e.kind() {
continue;
}
panic!("{}", e)
}
Ok(_) => break,
}
}
match writer.write_all(&buf[..size]) {
Err(e) => panic!("{}", e),
Ok(_) => {}
}
}
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
extern crate brotli;
#[cfg(not(feature = "std"))]
fn main() {
panic!("For no-stdlib examples please see the tests")
}
#[cfg(feature = "std")]
fn main() {
use std::io;
let stdin = &mut io::stdin();
{
use std::io::{Read, Write};
let mut reader = brotli::Decompressor::new(
stdin, 4096, // buffer size
);
let mut buf = [0u8; 4096];
loop {
match reader.read(&mut buf[..]) {
Err(e) => {
if let io::ErrorKind::Interrupted = e.kind() {
continue;
}
panic!("{}", e);
}
Ok(size) => {
if size == 0 {
break;
}
match io::stdout().write_all(&buf[..size]) {
Err(e) => panic!("{}", e),
Ok(_) => {}
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
+170
View File
@@ -0,0 +1,170 @@
extern crate brotli;
extern crate core;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::{env, io};
use brotli::concat::{BroCatli, BroCatliResult};
fn usage() {
writeln!(
&mut ::std::io::stderr(),
"Usage: [-w<window_size>] filename0 filename1 filename2..."
)
.unwrap();
}
fn read_no_interrupt<R: Read>(r: &mut R, buf: &mut [u8]) -> Result<usize, io::Error> {
loop {
match r.read(buf) {
Err(e) => match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => return Err(e),
},
Ok(cur_read) => return Ok(cur_read),
}
}
}
fn write_no_interrupt<W: Write>(w: &mut W, mut buf: &[u8]) -> Result<usize, io::Error> {
let mut total_read = 0usize;
loop {
match w.write(buf) {
Err(e) => match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => return Err(e),
},
Ok(cur_read) => {
buf = &buf[cur_read..];
total_read += cur_read;
if buf.is_empty() {
return Ok(total_read);
}
}
}
}
}
fn main() {
let mut window_size: Option<u8> = None;
let mut double_dash = false;
let mut buffer_size = 4096usize;
let mut filenames = Vec::<String>::new();
let mut ostream = io::stdout();
if env::args_os().len() > 1 {
for argument in env::args().skip(1) {
if argument.starts_with("-w") && !double_dash {
window_size = Some(
argument
.trim_matches('-')
.trim_matches('w')
.parse::<i32>()
.unwrap() as u8,
);
continue;
}
if argument.starts_with("-bs") && !double_dash {
buffer_size = argument
.trim_matches('-')
.trim_matches('b')
.trim_matches('s')
.parse::<usize>()
.unwrap();
continue;
}
if argument == "--" {
double_dash = true;
continue;
}
filenames.push(argument);
}
} else {
usage();
return;
}
let mut ibuffer = vec![0u8; buffer_size];
let mut obuffer = vec![0u8; buffer_size];
let mut ooffset = 0;
let mut ioffset;
let mut bro_cat_li = match window_size {
Some(ws) => BroCatli::new_with_window_size(ws),
None => BroCatli::new(),
};
for filename in filenames {
bro_cat_li.new_brotli_file();
let mut input_file = match File::open(Path::new(&filename)) {
Err(why) => panic!("couldn't open {:}\n{:}", filename, why),
Ok(file) => file,
};
loop {
ioffset = 0;
match read_no_interrupt(&mut input_file, &mut ibuffer[..]) {
Err(e) => panic!("{}", e),
Ok(cur_read) => {
if cur_read == 0 {
break;
}
loop {
match bro_cat_li.stream(
&ibuffer[..cur_read],
&mut ioffset,
&mut obuffer[..],
&mut ooffset,
) {
BroCatliResult::NeedsMoreOutput => {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
break;
}
BroCatliResult::Success => {
panic!("Unexpected state: Success when streaming before finish");
}
failure => {
panic!(
"Failed to concatenate files on {:} {:?}",
filename, failure
);
}
}
}
}
}
}
}
loop {
match bro_cat_li.finish(&mut obuffer[..], &mut ooffset) {
BroCatliResult::NeedsMoreOutput => {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
panic!("Unexpected EOF");
}
BroCatliResult::Success => {
if ooffset != 0 {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
}
break;
}
failure => {
panic!("{:?}", failure)
}
}
}
}
File diff suppressed because it is too large Load Diff
+702
View File
@@ -0,0 +1,702 @@
#![cfg(test)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
extern crate brotli_decompressor;
extern crate core;
use core::cmp::{max, min};
use brotli_decompressor::{CustomRead, CustomWrite};
use super::brotli::concat::{BroCatli, BroCatliResult};
use super::brotli::enc::BrotliEncoderParams;
use super::integration_tests::UnlimitedBuffer;
use super::Rebox;
static RANDOM_THEN_UNICODE: &[u8] = include_bytes!("../../testdata/random_then_unicode");
static ALICE: &[u8] = include_bytes!("../../testdata/alice29.txt");
static UKKONOOA: &[u8] = include_bytes!("../../testdata/ukkonooa");
static ASYOULIKE: &[u8] = include_bytes!("../../testdata/asyoulik.txt");
static BACKWARD65536: &[u8] = include_bytes!("../../testdata/backward65536");
static DICTWORD: &[u8] = include_bytes!("../../testdata/ends_with_truncated_dictionary");
static RANDOM10K: &[u8] = include_bytes!("../../testdata/random_org_10k.bin");
static RANDOMTHENUNICODE: &[u8] = include_bytes!("../../testdata/random_then_unicode");
static QUICKFOX: &[u8] = include_bytes!("../../testdata/quickfox_repeated");
static EMPTY: &[u8] = &[];
fn concat(
files: &mut [UnlimitedBuffer],
brotli_files: &mut [UnlimitedBuffer],
window_override: Option<u8>,
bs: usize,
) {
let mut obuffer = vec![0u8; bs];
let mut ibuffer = vec![0u8; bs];
let mut ooffset = 0usize;
let mut ioffset;
let mut uboutput = UnlimitedBuffer::new(&[]);
{
let mut output = super::IoWriterWrapper(&mut uboutput);
let mut bro_cat_li = match window_override {
Some(ws) => BroCatli::new_with_window_size(ws),
None => BroCatli::new(),
};
for brotli in brotli_files.iter_mut() {
bro_cat_li.new_brotli_file();
{
let mut input = super::IoReaderWrapper(brotli);
loop {
ioffset = 0;
match input.read(&mut ibuffer[..]) {
Err(e) => panic!("{}", e),
Ok(cur_read) => {
if cur_read == 0 {
break;
}
loop {
match bro_cat_li.stream(
&ibuffer[..cur_read],
&mut ioffset,
&mut obuffer[..],
&mut ooffset,
) {
BroCatliResult::NeedsMoreOutput => {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
break;
}
BroCatliResult::Success => {
panic!("Unexpected state: Success when streaming before finish");
}
failure => {
panic!("{:?}", failure);
}
}
}
}
}
}
}
brotli.reset_read();
}
loop {
match bro_cat_li.finish(&mut obuffer[..], &mut ooffset) {
BroCatliResult::NeedsMoreOutput => {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write\n{:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
panic!("Unexpected EOF");
}
BroCatliResult::Success => {
if ooffset != 0 {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write\n{:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
}
break;
}
failure => {
panic!("{:?}", failure)
}
}
}
}
let mut rt = UnlimitedBuffer::new(&[]);
match super::decompress(&mut uboutput, &mut rt, 65536, Rebox::default()) {
Ok(_) => {}
Err(e) => panic!("Error {:?}", e),
}
let mut offset = 0;
for file in files {
assert_eq!(&rt.data()[offset..offset + file.data().len()], file.data());
offset += file.data().len();
}
assert_eq!(offset, rt.data().len());
}
fn concat_many_subsets(
files: &mut [UnlimitedBuffer],
brotli_files: &mut [UnlimitedBuffer],
window_override: Option<u8>,
) {
let test_plans: [(usize, usize); 4] =
[(brotli_files.len(), 4096 * 1024), (4, 1), (3, 3), (2, 4096)];
for plan_bs in test_plans.iter() {
let files_len = files.len();
for index in 0..(brotli_files.len() - min(plan_bs.0 - 1, files_len)) {
let file_subset = &mut files[index..min(index + plan_bs.0, files_len)];
let brotli_subset = &mut brotli_files[index..min(index + plan_bs.0, files_len)];
concat(file_subset, brotli_subset, window_override, plan_bs.1);
}
}
}
#[cfg(debug_assertions)]
fn light_debug_test(params: &mut BrotliEncoderParams) {
params.quality = 5;
}
#[cfg(not(debug_assertions))]
fn light_debug_test(_params: &mut BrotliEncoderParams) {}
#[cfg(debug_assertions)]
fn medium_debug_test(params: &mut BrotliEncoderParams) {
params.quality = 9;
params.q9_5 = false;
}
#[cfg(not(debug_assertions))]
fn medium_debug_test(_params: &mut BrotliEncoderParams) {}
#[test]
#[should_panic]
fn test_appendonly_twice_fails() {
let mut files = [
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(QUICKFOX),
];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = true;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_append_then_empty_works() {
let mut files = [UnlimitedBuffer::new(UKKONOOA), UnlimitedBuffer::new(&[])];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_append_then_cat_works() {
let mut files = [
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(QUICKFOX),
];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_one_byte_works() {
let mut files = [UnlimitedBuffer::new(UKKONOOA), UnlimitedBuffer::new(&[8])];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_one_byte_before_works() {
let mut files = [UnlimitedBuffer::new(&[8]), UnlimitedBuffer::new(UKKONOOA)];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_two_byte_works() {
let mut files = [
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(&[8, 9]),
];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_two_byte_before_works() {
let mut files = [
UnlimitedBuffer::new(&[8, 9]),
UnlimitedBuffer::new(UKKONOOA),
];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_empty_then_cat_works() {
let mut files = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(QUICKFOX)];
let mut ufiles = [UnlimitedBuffer::new(&[]), UnlimitedBuffer::new(&[])];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
let mut params0 = BrotliEncoderParams::default();
params0.appendable = first;
params0.catable = !first;
params0.use_dictionary = first;
super::compress(src, dst, 4096, &params0, &[], 1).unwrap();
first = false;
}
concat(&mut files[..], &mut ufiles[..], None, 2);
}
#[test]
fn test_concat() {
let mut files = [
UnlimitedBuffer::new(ALICE),
UnlimitedBuffer::new(RANDOMTHENUNICODE),
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(ASYOULIKE),
UnlimitedBuffer::new(BACKWARD65536),
UnlimitedBuffer::new(EMPTY),
UnlimitedBuffer::new(DICTWORD),
UnlimitedBuffer::new(RANDOM10K),
UnlimitedBuffer::new(QUICKFOX),
];
let mut params0 = BrotliEncoderParams::default();
light_debug_test(&mut params0);
let mut params1 = params0.clone();
params1.quality = 9;
params1.q9_5 = true;
params1.lgwin = 22;
params1.magic_number = true;
medium_debug_test(&mut params1);
let mut params2 = params0.clone();
params2.quality = if params1.q9_5 || params1.quality > 9 {
9
} else {
8
};
params2.lgwin = 19;
let mut params3 = params0.clone();
params3.quality = 8;
params3.lgwin = 16;
params3.magic_number = true;
let mut params4 = params0.clone();
params4.quality = 7;
params4.lgwin = 14;
let mut params4 = params0.clone();
params4.quality = 1;
params4.lgwin = 10;
let mut params5 = params0.clone();
params5.quality = 0;
params5.lgwin = 10;
params5.magic_number = true;
params0.lgwin = 26;
params0.large_window = true;
let mut options = [params0, params1, params2, params3, params4, params5];
for option in options.iter_mut().skip(3) {
let mut ufiles = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
let mut first = true;
for (src, dst) in files.iter_mut().zip(ufiles.iter_mut()) {
if first {
option.appendable = true;
} else {
option.appendable = false;
option.catable = true;
option.use_dictionary = false;
}
super::compress(src, dst, 4096, option, &[], 1).unwrap();
src.reset_read();
first = false;
}
concat_many_subsets(&mut files[..], &mut ufiles[..], None);
return;
}
let mut ufiles = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
let options_len = options.len();
for (index, (src, dst)) in files.iter_mut().zip(ufiles.iter_mut()).enumerate() {
options[min(index, options_len - 1)].catable = true;
options[min(index, options_len - 1)].use_dictionary = false;
options[min(index, options_len - 1)].appendable = false;
options[min(index, options_len - 1)].quality =
max(2, options[min(index, options_len - 1)].quality);
// ^^^ there's an artificial limitation of using 18 as the minimum window size for quality 0,1
// since this test depends on different window sizes for each stream, exclude q={0,1}
super::compress(
src,
dst,
4096,
&options[min(index, options_len - 1)],
&[],
1,
)
.unwrap();
src.reset_read();
}
concat_many_subsets(&mut files[..], &mut ufiles[..], None);
concat_many_subsets(&mut files[..], &mut ufiles[..], Some(28)); // FIXME: make this 28
}
// Helper function for simple byte concatenation
fn byte_concat_decompress(files: &mut [UnlimitedBuffer], brotli_files: &mut [UnlimitedBuffer]) {
// Simple byte concatenation with proper finalization:
// 1. First file is -bare -appendable (header, no trailer)
// 2. Subsequent files are -bare -catable (no header, no trailer)
// 3. Add final byte (0x03) at the end
let mut concatenated = UnlimitedBuffer::new(&[]);
// All files: add as-is
for brotli_file in brotli_files.iter_mut() {
concatenated.data.extend_from_slice(brotli_file.data());
brotli_file.reset_read();
}
// Add finalization byte
concatenated.data.push(0x03);
concatenated.reset_read(); // Reset read offset before decompression
let mut decompressed = UnlimitedBuffer::new(&[]);
match super::decompress(
&mut concatenated,
&mut decompressed,
65536,
Rebox::default(),
) {
Ok(_) => {}
Err(e) => panic!("Error decompressing concatenated stream: {:?}", e),
}
// Verify output matches original files in order
let mut offset = 0;
for file in files {
assert_eq!(
&decompressed.data()[offset..offset + file.data().len()],
file.data(),
"Decompressed content doesn't match original"
);
offset += file.data().len();
}
assert_eq!(
offset,
decompressed.data().len(),
"Decompressed size mismatch"
);
}
#[test]
fn test_bytealign_appendable_with_bare() {
// Test: appendable + bytealign base file with bare streams
let mut files = [
UnlimitedBuffer::new(ALICE),
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(QUICKFOX),
];
let mut brotli_files = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
// First file: bare + appendable (header, no trailer)
let mut params_base = BrotliEncoderParams::default();
params_base.bare_stream = true;
params_base.byte_align = true; // implied by -bare
params_base.appendable = true;
params_base.lgwin = 22;
super::compress(
&mut files[0],
&mut brotli_files[0],
4096,
&params_base,
&[],
1,
)
.unwrap();
files[0].reset_read();
// Subsequent files: bare streams (no header)
for i in 1..files.len() {
let mut params_bare = BrotliEncoderParams::default();
params_bare.bare_stream = true;
params_bare.byte_align = true; // implied by -bare
params_bare.catable = true;
params_bare.use_dictionary = false; // implied by -catable
params_bare.appendable = true; // implied by -catable
params_bare.lgwin = 22;
super::compress(
&mut files[i],
&mut brotli_files[i],
4096,
&params_bare,
&[],
1,
)
.unwrap();
files[i].reset_read();
}
// Test simple byte concatenation
byte_concat_decompress(&mut files[..], &mut brotli_files[..]);
}
#[test]
fn test_bare_any_order() {
// Test: bare streams can be appended in any order
let mut files = [
UnlimitedBuffer::new(ALICE),
UnlimitedBuffer::new(UKKONOOA),
UnlimitedBuffer::new(QUICKFOX),
];
let mut brotli_files = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
// Base file
let mut params_base = BrotliEncoderParams::default();
params_base.bare_stream = true;
params_base.byte_align = true;
params_base.appendable = true;
params_base.lgwin = 22;
super::compress(
&mut files[0],
&mut brotli_files[0],
4096,
&params_base,
&[],
1,
)
.unwrap();
files[0].reset_read();
// Create bare streams
for i in 1..files.len() {
let mut params_bare = BrotliEncoderParams::default();
params_bare.bare_stream = true;
params_bare.byte_align = true;
params_bare.catable = true;
params_bare.use_dictionary = false;
params_bare.appendable = true;
params_bare.lgwin = 22;
super::compress(
&mut files[i],
&mut brotli_files[i],
4096,
&params_bare,
&[],
1,
)
.unwrap();
files[i].reset_read();
}
// Test original order
byte_concat_decompress(&mut files[..], &mut brotli_files[..]);
// Test reordered bare streams (base always first, swap the other two)
let mut files_reordered = [
UnlimitedBuffer::new(files[0].data()),
UnlimitedBuffer::new(files[2].data()),
UnlimitedBuffer::new(files[1].data()),
];
for file in files_reordered.iter_mut() {
file.reset_read();
}
let mut brotli_reordered = [
UnlimitedBuffer::new(brotli_files[0].data()),
UnlimitedBuffer::new(brotli_files[2].data()),
UnlimitedBuffer::new(brotli_files[1].data()),
];
for brotli_file in brotli_reordered.iter_mut() {
brotli_file.reset_read();
}
byte_concat_decompress(&mut files_reordered[..], &mut brotli_reordered[..]);
}
#[test]
fn test_bytealign_with_empty() {
// Test: bytealign with empty files
let mut files = [
UnlimitedBuffer::new(ALICE),
UnlimitedBuffer::new(EMPTY),
UnlimitedBuffer::new(QUICKFOX),
];
let mut brotli_files = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
// First file: appendable + bytealign
let mut params_base = BrotliEncoderParams::default();
params_base.bare_stream = true;
params_base.byte_align = true;
params_base.appendable = true;
params_base.lgwin = 22;
super::compress(
&mut files[0],
&mut brotli_files[0],
4096,
&params_base,
&[],
1,
)
.unwrap();
files[0].reset_read();
// Remaining files: bare
for i in 1..files.len() {
let mut params_bare = BrotliEncoderParams::default();
params_bare.bare_stream = true;
params_bare.byte_align = true;
params_bare.catable = true;
params_bare.use_dictionary = false;
params_bare.appendable = true;
params_bare.lgwin = 22;
super::compress(
&mut files[i],
&mut brotli_files[i],
4096,
&params_bare,
&[],
1,
)
.unwrap();
files[i].reset_read();
}
byte_concat_decompress(&mut files[..], &mut brotli_files[..]);
}
#[test]
fn test_bytealign_various_data() {
// Test: bytealign with various data types
let mut files = [
UnlimitedBuffer::new(RANDOM10K),
UnlimitedBuffer::new(RANDOMTHENUNICODE),
UnlimitedBuffer::new(ASYOULIKE),
UnlimitedBuffer::new(BACKWARD65536),
];
let mut brotli_files = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
// First file: appendable + bytealign
let mut params_base = BrotliEncoderParams::default();
params_base.bare_stream = true;
params_base.byte_align = true;
params_base.appendable = true;
params_base.lgwin = 22;
light_debug_test(&mut params_base);
super::compress(
&mut files[0],
&mut brotli_files[0],
4096,
&params_base,
&[],
1,
)
.unwrap();
files[0].reset_read();
// Remaining files: bare
for i in 1..files.len() {
let mut params_bare = BrotliEncoderParams::default();
params_bare.bare_stream = true;
params_bare.byte_align = true;
params_bare.catable = true;
params_bare.use_dictionary = false;
params_bare.appendable = true;
params_bare.lgwin = 22;
light_debug_test(&mut params_bare);
super::compress(
&mut files[i],
&mut brotli_files[i],
4096,
&params_bare,
&[],
1,
)
.unwrap();
files[i].reset_read();
}
byte_concat_decompress(&mut files[..], &mut brotli_files[..]);
}
+333
View File
@@ -0,0 +1,333 @@
#![cfg(test)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
extern crate brotli_decompressor;
extern crate core;
use std::io::{Read, Write};
use super::brotli::concat::{BroCatli, BroCatliResult};
use super::brotli::enc::BrotliEncoderParams;
use super::integration_tests::UnlimitedBuffer;
use super::Rebox;
static RANDOM_THEN_UNICODE: &[u8] = include_bytes!("../../testdata/random_then_unicode");
static ALICE: &[u8] = include_bytes!("../../testdata/alice29.txt");
#[test]
fn test_custom_dict_minimal() {
let mut raw = UnlimitedBuffer::new("\012345656789abcde".as_bytes());
let mut params = BrotliEncoderParams::default();
params.quality = 10;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let dict = "123456789abcde".as_bytes();
super::compress(&mut raw, &mut br, 4096, &params, dict, 1).unwrap();
raw.reset_read();
eprintln!("Compressed: {:?}", &br);
let mut vec = Vec::<u8>::new();
vec.extend(dict);
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
}
#[test]
fn test_custom_dict_large() {
let mut params = BrotliEncoderParams::default();
params.quality = 11;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let mut dict = [0u8; 256];
for (index, val) in dict[..].iter_mut().enumerate() {
*val = index as u8;
}
let mut data_source = [0u8; 10823];
let mut counter = 0usize;
for (index, val) in data_source[..].iter_mut().enumerate() {
*val = counter as u8;
counter += 1;
if counter * 10 > index {
counter -= index / 11;
}
}
eprintln!("Uncompressed: {:?}", &data_source);
let mut raw = UnlimitedBuffer::new(&data_source);
super::compress(&mut raw, &mut br, 4096, &params, &dict, 1).unwrap();
raw.reset_read();
eprintln!("Compressed: {:?}", &br);
// Write debug files to temp directory (works on all platforms)
if let Ok(temp_dir) = std::env::var("TMPDIR").or_else(|_| std::env::var("TEMP")) {
let _ = std::fs::File::create(format!("{}/compressed.br", temp_dir))
.and_then(|mut f| f.write_all(&br.data));
let _ = std::fs::File::create(format!("{}/compressed.dict", temp_dir))
.and_then(|mut f| f.write_all(&dict));
let _ = std::fs::File::create(format!("{}/compressed.txt", temp_dir))
.and_then(|mut f| f.write_all(&data_source));
}
let mut vec = Vec::<u8>::new();
vec.extend(dict);
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
}
#[test]
fn test_custom_dict_medium() {
let mut params = BrotliEncoderParams::default();
params.quality = 11;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let mut dict = [0u8; 256];
for (index, val) in dict[..].iter_mut().enumerate() {
*val = index as u8;
}
let mut data_source = [0u8; 323];
let mut counter = 0usize;
for (index, val) in data_source[..].iter_mut().enumerate() {
*val = counter as u8;
counter += 1;
if counter * 10 > index {
counter -= index / 11;
counter = counter.wrapping_sub(43);
counter = usize::from(counter as u8);
}
}
eprintln!("Uncompressed: {:?}", &data_source[72..]);
let new_data_source = [
148, 100, 52, 4, 5, 6, 7, 214, 165, 116, 67, 18, 225, 176, 127, 78, 29, 235, 185, 135, 85,
35, 241, 191, 141, 91, 41, 247, 196, 145, 94, 43, 248, 197, 146, 95, 44, 249, 198, 146, 94,
42, 246, 194, 142, 90, 38, 242, 190, 138, 85, 32, 235, 182, 129, 76, 23, 226, 173, 120, 67,
13, 215, 161, 107, 53, 255, 201, 147, 93, 39, 241, 186, 131, 76, 21, 222, 167, 112, 57, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 215, 158, 101, 44, 243, 186, 129, 72, 15,
16, 17, 215, 157, 99, 41, 239, 181, 123, 65, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 215, 155, 95, 35, 231, 171, 111, 51, 247, 187, 127, 66, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 216, 154, 92, 29, 222, 159, 96, 33, 226, 163, 100, 37,
230, 167, 103, 39, 231, 167, 103, 39, 231, 167, 103, 39, 231, 166, 101, 36, 227, 162, 97,
32, 223, 158, 93, 28, 218, 152, 86, 20, 21, 22, 23, 24, 25, 26, 27, 216, 149, 82, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 217, 149, 81, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 217, 147, 77, 7, 8, 9, 10, 11,
];
let mut raw = UnlimitedBuffer::new(&new_data_source);
super::compress(&mut raw, &mut br, 4096, &params, &dict, 1).unwrap();
raw.reset_read();
eprintln!("Compressed: {:?}", &br);
let mut vec = Vec::<u8>::new();
vec.extend(dict);
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
}
#[test]
fn test_custom_dict_alice() {
let mut raw = UnlimitedBuffer::new(ALICE);
let mut params = BrotliEncoderParams::default();
params.quality = 11;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let dict = &ALICE[12515..23411];
super::compress(&mut raw, &mut br, 4096, &params, dict, 1).unwrap();
raw.reset_read();
eprintln!("Dict {:?}", dict);
eprintln!("Compressed: {:?}", &br);
let mut vec = Vec::<u8>::new();
vec.extend(dict);
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
// Platform-specific compression sizes - exact size varies by architecture and optimizations
if br.data().len() != 43860 && br.data().len() != 43836 && br.data().len() != 43857 {
panic!(
"Unexpected compressed size: {} (expected 43860, 43836, or 43857)",
br.data().len()
);
}
}
#[test]
fn test_custom_dict_alice_9_5() {
let mut raw = UnlimitedBuffer::new(ALICE);
let mut params = BrotliEncoderParams::default();
params.quality = 11;
params.q9_5 = true;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let dict = &ALICE[12515..23411];
super::compress(&mut raw, &mut br, 4096, &params, dict, 1).unwrap();
raw.reset_read();
eprintln!("Dict {:?}", dict);
eprintln!("Compressed: {:?}", &br);
let mut vec = Vec::<u8>::new();
vec.extend(dict);
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
if br.data().len() != 45710 {
assert_eq!(br.data().len(), 45698);
}
}
#[test]
fn test_custom_wrong_dict_fails() {
let mut raw = UnlimitedBuffer::new(ALICE);
let mut params = BrotliEncoderParams::default();
params.quality = 10;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let dict = &ALICE[12515..19515];
super::compress(&mut raw, &mut br, 4096, &params, dict, 1).unwrap();
raw.reset_read();
let mut vec = Vec::<u8>::new();
vec.extend(&dict[1..]); // slightly offset dictionary to be wrong, and ensure the dict was being used above
match super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)) {
Ok(_) => panic!("Decompression should have failed"),
Err(_) => {}
}
if rt.data() == raw.data() {
panic!("they should be unequal");
}
}
#[test]
fn test_custom_wrong_dict_fails_but_doesnt_disrupt_compression_strategy() {
let mut raw = UnlimitedBuffer::new(ALICE);
let mut params = BrotliEncoderParams::default();
params.quality = 6;
let mut br = UnlimitedBuffer::new(&[]);
let mut rt = UnlimitedBuffer::new(&[]);
let dict = &ALICE[12515..19515];
super::compress(&mut raw, &mut br, 4096, &params, dict, 1).unwrap();
raw.reset_read();
let mut vec = Vec::<u8>::new();
vec.extend(&dict[1..]); // slightly offset dictionary to be wrong, and ensure the dict was being used above
super::decompress(&mut br, &mut rt, 4096, Rebox::from(vec)).unwrap();
if rt.data() == raw.data() {
panic!("they should be unequal");
}
}
#[ignore = "LZ77 mode no longer is compatible with manual file splitting since it sets context to 0 instead of leaving it to the end of the dict"]
#[test]
fn test_custom_dict_for_multithreading() {
let mut raws = [
UnlimitedBuffer::new(&ALICE[..ALICE.len() / 3]),
UnlimitedBuffer::new(&ALICE[ALICE.len() / 3..2 * ALICE.len() / 3]),
UnlimitedBuffer::new(&ALICE[2 * ALICE.len() / 3..]),
];
let mut params = BrotliEncoderParams::default();
params.quality = 10;
params.appendable = true;
let mut brs = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
let mut rts = [
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
UnlimitedBuffer::new(&[]),
];
let dicts = [
&ALICE[..0],
&ALICE[..ALICE.len() / 3],
&ALICE[..2 * ALICE.len() / 3],
];
for (raw, (br, (rt, dict))) in raws
.iter_mut()
.zip(brs.iter_mut().zip(rts.iter_mut().zip(dicts.iter())))
{
super::compress(raw, br, 4096, &params, &[], 1).unwrap();
raw.reset_read();
let mut vec = Vec::<u8>::new();
vec.extend(*dict);
super::decompress(br, rt, 4096, Rebox::from(vec)).unwrap();
assert_eq!(rt.data(), raw.data());
params.catable = true;
params.use_dictionary = false;
}
let mut bro_cat_li = BroCatli::new();
let mut output = UnlimitedBuffer::new(&[]);
let mut ibuffer = [0u8; 1];
let mut obuffer = [0u8; 1];
let mut ooffset = 0usize;
for brotli in brs.iter_mut() {
brotli.reset_read();
bro_cat_li.new_brotli_file();
let input = brotli;
loop {
let mut ioffset = 0usize;
match input.read(&mut ibuffer[..]) {
Err(e) => panic!("{:?}", e),
Ok(cur_read) => {
if cur_read == 0 {
break;
}
loop {
match bro_cat_li.stream(
&ibuffer[..cur_read],
&mut ioffset,
&mut obuffer[..],
&mut ooffset,
) {
BroCatliResult::NeedsMoreOutput => {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
break;
}
BroCatliResult::Success => {
panic!("Unexpected state: Success when streaming before finish");
}
failure => {
panic!("{:?}", failure);
}
}
}
}
}
}
}
loop {
match bro_cat_li.finish(&mut obuffer[..], &mut ooffset) {
BroCatliResult::NeedsMoreOutput => {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write\n{:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
ooffset = 0;
}
BroCatliResult::NeedsMoreInput => {
panic!("Unexpected EOF");
}
BroCatliResult::Success => {
if ooffset != 0 {
match output.write(&obuffer[..ooffset]) {
Err(why) => panic!("couldn't write\n{:}", why),
Ok(count) => {
assert_eq!(count, ooffset);
}
}
}
break;
}
failure => {
panic!("{}", failure as i32)
}
}
}
let mut rt = UnlimitedBuffer::new(&[]);
output.reset_read();
super::decompress(&mut output, &mut rt, 4096, Rebox::default()).unwrap();
assert_eq!(rt.data(), ALICE);
// without setting std flag: approximation make it 4 bytes bigger
if output.data().len() != 48568 {
assert_eq!(output.data().len(), 48563); // as opposed to 46487 with standard settings
}
}
+216
View File
@@ -0,0 +1,216 @@
#![cfg(test)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
extern crate brotli_decompressor;
extern crate core;
use brotli::enc::threading::{Owned, SendAlloc};
use brotli_decompressor::{SliceWrapper, SliceWrapperMut};
use super::brotli::enc::{
compress_multi, compress_multi_no_threadpool, BrotliEncoderMaxCompressedSizeMulti,
BrotliEncoderParams, UnionHasher,
};
use super::integration_tests::UnlimitedBuffer;
use super::{new_brotli_heap_alloc, Rebox};
static RANDOM_THEN_UNICODE: &[u8] = include_bytes!("../../testdata/random_then_unicode");
static ALICE: &[u8] = include_bytes!("../../testdata/alice29.txt");
struct SliceRef<'a>(&'a [u8]);
impl<'a> SliceWrapper<u8> for SliceRef<'a> {
fn slice(&self) -> &[u8] {
self.0
}
}
fn multi_threaded_split_compression_test(
input_data: &'static [u8],
num_threads: usize,
quality: i32,
catable: bool,
expected_size: usize,
) {
let mut params = BrotliEncoderParams::default();
params.quality = quality;
params.magic_number = true;
if catable {
params.catable = true;
params.use_dictionary = false;
}
let mut output = Rebox::from(vec![
0u8;
BrotliEncoderMaxCompressedSizeMulti(
input_data.len(),
num_threads
)
]);
let mut alloc_per_thread = [
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
];
if num_threads > alloc_per_thread.len() {
panic!(
"Too many threads requested {} > {}",
num_threads,
alloc_per_thread.len()
);
}
let res = compress_multi(
&params,
&mut Owned::new(SliceRef(input_data)),
output.slice_mut(),
&mut alloc_per_thread[..num_threads],
);
let observed_size = res.unwrap();
if observed_size > expected_size {
assert_eq!(observed_size, expected_size);
}
assert_ne!(observed_size, 0);
let mut compressed_version = UnlimitedBuffer::new(&output.slice()[..observed_size]);
let mut rt = UnlimitedBuffer::new(&[]);
match super::decompress(&mut compressed_version, &mut rt, 65536, Rebox::default()) {
Ok(_) => {}
Err(e) => panic!("Error {:?}", e),
}
assert_eq!(rt.data(), input_data);
}
#[test]
fn multi_threaded_split_compression_test_1() {
multi_threaded_split_compression_test(RANDOM_THEN_UNICODE, 1, 3, false, 155808)
}
#[test]
fn multi_threaded_split_compression_test_2() {
multi_threaded_split_compression_test(RANDOM_THEN_UNICODE, 2, 4, false, 151857)
}
#[test]
fn multi_threaded_split_compression_test_3() {
multi_threaded_split_compression_test(RANDOM_THEN_UNICODE, 3, 5, false, 144325)
}
#[test]
fn multi_threaded_split_compression_test_4() {
multi_threaded_split_compression_test(RANDOM_THEN_UNICODE, 4, 10, true, 136812)
}
#[test]
fn multi_threaded_split_compression_test_5() {
multi_threaded_split_compression_test(RANDOM_THEN_UNICODE, 5, 9, false, 139126)
}
#[test]
fn multi_threaded_split_compression_test_1b1() {
multi_threaded_split_compression_test(&RANDOM_THEN_UNICODE[..1], 5, 9, false, 139126)
}
#[test]
fn multi_threaded_split_compression_test_1b5() {
multi_threaded_split_compression_test(&RANDOM_THEN_UNICODE[..1], 5, 9, false, 139125)
}
#[test]
fn multi_threaded_split_compression_test_0b1() {
multi_threaded_split_compression_test(&[], 5, 9, false, 139125)
}
#[test]
fn multi_threaded_split_compression_test_0b5() {
multi_threaded_split_compression_test(&[], 5, 9, false, 139125)
}
fn thread_spawn_per_job_split_compression_test(
input_data: &'static [u8],
num_threads: usize,
quality: i32,
catable: bool,
expected_size: usize,
) {
let mut params = BrotliEncoderParams::default();
params.quality = quality;
params.magic_number = true;
if catable {
params.catable = true;
params.use_dictionary = false;
}
params.favor_cpu_efficiency = true; // this should test both paths
let mut output = Rebox::from(vec![
0u8;
BrotliEncoderMaxCompressedSizeMulti(
input_data.len(),
num_threads
)
]);
let mut alloc_per_thread = [
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
SendAlloc::new(new_brotli_heap_alloc(), UnionHasher::Uninit),
];
if num_threads > alloc_per_thread.len() {
panic!(
"Too many threads requested {} > {}",
num_threads,
alloc_per_thread.len()
);
}
let res = compress_multi_no_threadpool(
&params,
&mut Owned::new(SliceRef(input_data)),
output.slice_mut(),
&mut alloc_per_thread[..num_threads],
);
let observed_size = res.unwrap();
if observed_size > expected_size {
assert_eq!(observed_size, expected_size);
}
assert_ne!(observed_size, 0);
let mut compressed_version = UnlimitedBuffer::new(&output.slice()[..observed_size]);
let mut rt = UnlimitedBuffer::new(&[]);
match super::decompress(&mut compressed_version, &mut rt, 65536, Rebox::default()) {
Ok(_) => {}
Err(e) => panic!("Error {:?}", e),
}
assert_eq!(rt.data(), input_data);
}
#[test]
fn thread_spawn_per_job_split_compression_test_1() {
thread_spawn_per_job_split_compression_test(RANDOM_THEN_UNICODE, 1, 3, false, 155808)
}
#[test]
fn thread_spawn_per_job_split_compression_test_3() {
thread_spawn_per_job_split_compression_test(RANDOM_THEN_UNICODE, 3, 5, false, 144325)
}
#[test]
fn thread_spawn_per_job_split_compression_test_1b1() {
thread_spawn_per_job_split_compression_test(&RANDOM_THEN_UNICODE[..1], 1, 3, false, 155808)
}
#[test]
fn thread_spawn_per_job_split_compression_test_1b3() {
thread_spawn_per_job_split_compression_test(&RANDOM_THEN_UNICODE[..1], 3, 5, false, 144325)
}
#[test]
fn thread_spawn_per_job_split_compression_test_0b1() {
thread_spawn_per_job_split_compression_test(&[], 1, 3, false, 155808)
}
#[test]
fn thread_spawn_per_job_split_compression_test_0b3() {
thread_spawn_per_job_split_compression_test(&[], 3, 5, false, 144325)
}
+115
View File
@@ -0,0 +1,115 @@
#![cfg(test)]
extern crate core;
use core::cmp::min;
use std::io;
struct Buffer {
data: Vec<u8>,
read_offset: usize,
}
impl Buffer {
pub fn new(buf: &[u8]) -> Buffer {
let mut ret = Buffer {
data: Vec::<u8>::new(),
read_offset: 0,
};
ret.data.extend(buf);
ret
}
}
impl io::Read for Buffer {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let bytes_to_read = min(buf.len(), self.data.len() - self.read_offset);
if bytes_to_read > 0 {
buf[0..bytes_to_read]
.clone_from_slice(&self.data[self.read_offset..self.read_offset + bytes_to_read]);
}
self.read_offset += bytes_to_read;
Ok(bytes_to_read)
}
}
impl io::Write for Buffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.data.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
fn copy_from_to<R: io::Read, W: io::Write>(mut r: R, mut w: W) -> io::Result<usize> {
let mut buffer: [u8; 65536] = [0; 65536];
let mut out_size: usize = 0;
loop {
match r.read(&mut buffer[..]) {
Err(e) => {
match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => {}
}
return Err(e);
}
Ok(size) => {
if size == 0 {
break;
} else {
match w.write_all(&buffer[..size]) {
Err(e) => {
match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => {}
}
return Err(e);
}
Ok(_) => out_size += size,
}
}
}
}
}
Ok(out_size)
}
#[test]
fn test_10x_10y() {
let in_buf: [u8; 12] = [
0x1b, 0x13, 0x00, 0x00, 0xa4, 0xb0, 0xb2, 0xea, 0x81, 0x47, 0x02, 0x8a,
];
let mut output = Buffer::new(&[]);
let mut input = super::BrotliDecompressor::new(Buffer::new(&in_buf), 4096);
match copy_from_to(&mut input, &mut output) {
Ok(_) => {}
Err(e) => panic!("Error {:?}", e),
}
let mut i: usize = 0;
while i < 10 {
assert_eq!(output.data[i], b'X');
assert_eq!(output.data[i + 10], b'Y');
i += 1;
}
assert_eq!(output.data.len(), 20);
}
#[test]
fn test_alice() {
let in_buf = include_bytes!("../../testdata/alice29.txt.compressed");
let mut output = Buffer::new(&[]);
let mut input = super::BrotliDecompressor::new(Buffer::new(in_buf), 1);
match copy_from_to(&mut input, &mut output) {
Ok(_) => {}
Err(e) => panic!("Error {:?}", e),
}
let mut i: usize = 0;
let truth = include_bytes!("../../testdata/alice29.txt");
while i < truth.len() {
assert_eq!(output.data[i], truth[i]);
i += 1;
}
assert_eq!(truth.len(), output.data.len());
}
+325
View File
@@ -0,0 +1,325 @@
use core::marker::PhantomData;
use core::mem;
use std;
use std::collections::BTreeMap;
use std::fmt;
use std::thread::JoinHandle;
use alloc_no_stdlib::{Allocator, SliceWrapper};
use brotli::dictionary::{
kBrotliDictionary, kBrotliDictionaryOffsetsByLength, kBrotliDictionarySizeBitsByLength,
};
use brotli::enc::threading::{
AnyBoxConstructor, BatchSpawnable, BatchSpawnableLite, BrotliEncoderThreadError, InternalOwned,
InternalSendAlloc, Joinable, Owned, OwnedRetriever, PoisonedThreadError, SendAlloc,
};
use brotli::enc::BrotliAlloc;
use brotli::interface;
use brotli::transform::TransformDictionaryWord;
struct HexSlice<'a>(&'a [u8]);
impl<'a> fmt::Display for HexSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.0 {
if let Err(e) = write!(f, "{:02X}", byte) {
return Err(e);
}
}
Ok(())
}
}
pub fn permute_dictionary() -> BTreeMap<Vec<u8>, ()> {
let mut ret = BTreeMap::<Vec<u8>, ()>::new();
let mut transformed = [0u8; 38];
for wordlen in 4..kBrotliDictionaryOffsetsByLength.len() {
let offset = kBrotliDictionaryOffsetsByLength[wordlen] as usize;
for index in 0..(1 << kBrotliDictionarySizeBitsByLength[wordlen]) {
let word = &kBrotliDictionary[offset + index..offset + index + wordlen];
for transform in 0..121 {
let final_size =
TransformDictionaryWord(&mut transformed[..], word, wordlen as i32, transform)
as usize;
let vec: Vec<u8> = transformed[..final_size].to_vec();
ret.insert(vec, ());
}
}
}
ret
}
pub fn print_dictionary(dict: BTreeMap<Vec<u8>, ()>) {
for (key, _) in dict {
println!("{}", HexSlice(&key[..]));
}
}
macro_rules! println_stderr(
($($val:tt)*) => { {
writeln!(&mut ::std::io::stderr(), $($val)*).unwrap();
} }
);
fn prediction_mode_str(
prediction_mode_nibble: interface::LiteralPredictionModeNibble,
) -> &'static str {
match prediction_mode_nibble.prediction_mode() {
interface::LITERAL_PREDICTION_MODE_SIGN => "sign",
interface::LITERAL_PREDICTION_MODE_LSB6 => "lsb6",
interface::LITERAL_PREDICTION_MODE_MSB6 => "msb6",
interface::LITERAL_PREDICTION_MODE_UTF8 => "utf8",
_ => "unknown",
}
}
struct SliceU8Ref<'a>(pub &'a [u8]);
impl<'a> fmt::LowerHex for SliceU8Ref<'a> {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> Result<(), fmt::Error> {
for item in self.0 {
fmtr.write_fmt(format_args!("{:02x}", item))?
}
Ok(())
}
}
pub fn write_one<T: SliceWrapper<u8>>(cmd: &interface::Command<T>) {
use std::io::Write;
match cmd {
interface::Command::BlockSwitchLiteral(bsl) => {
println_stderr!("ltype {} {}", bsl.0.block_type(), bsl.1);
}
interface::Command::BlockSwitchCommand(bsc) => {
println_stderr!("ctype {}", bsc.0);
}
interface::Command::BlockSwitchDistance(bsd) => {
println_stderr!("dtype {}", bsd.0);
}
interface::Command::PredictionMode(prediction) => {
let prediction_mode = prediction_mode_str(prediction.literal_prediction_mode());
let lit_cm = prediction
.literal_context_map
.slice()
.iter()
.fold(::std::string::String::new(), |res, &val| {
res + " " + &val.to_string()
});
let dist_cm = prediction
.distance_context_map()
.iter()
.fold(::std::string::String::new(), |res, &val| {
res + " " + &val.to_string()
});
let mixing_values = prediction
.get_mixing_values()
.iter()
.fold(::std::string::String::new(), |res, &val| {
res + " " + &val.to_string()
});
if prediction.has_context_speeds() {
println_stderr!("prediction {} lcontextmap{} dcontextmap{} mixingvalues{} cmspeedinc {} {} cmspeedmax {} {} stspeedinc {} {} stspeedmax {} {} mxspeedinc {} {} mxspeedmax {} {}",
prediction_mode,
lit_cm,
dist_cm,
mixing_values,
prediction.context_map_speed()[0].0,
prediction.context_map_speed()[1].0,
prediction.context_map_speed()[0].1,
prediction.context_map_speed()[1].1,
prediction.stride_context_speed()[0].0,
prediction.stride_context_speed()[1].0,
prediction.stride_context_speed()[0].1,
prediction.stride_context_speed()[1].1,
prediction.combined_stride_context_speed()[0].0,
prediction.combined_stride_context_speed()[1].0,
prediction.combined_stride_context_speed()[0].1,
prediction.combined_stride_context_speed()[0].1,
);
} else {
println_stderr!(
"prediction {} lcontextmap{} dcontextmap{} mixingvalues{}",
prediction_mode,
lit_cm,
dist_cm,
mixing_values,
);
}
}
interface::Command::Copy(copy) => {
println_stderr!("copy {} from {}", copy.num_bytes, copy.distance);
}
interface::Command::Dict(dict) => {
let mut transformed_word = [0u8; 38];
let word_index = dict.word_id as usize * dict.word_size as usize
+ kBrotliDictionaryOffsetsByLength[dict.word_size as usize] as usize;
let raw_word = &kBrotliDictionary[word_index..(word_index + dict.word_size as usize)];
let actual_copy_len = TransformDictionaryWord(
&mut transformed_word[..],
raw_word,
dict.word_size as i32,
dict.transform as i32,
) as usize;
assert_eq!(dict.final_size as usize, actual_copy_len);
println_stderr!(
"dict {} word {},{} {:x} func {} {:x}",
actual_copy_len,
dict.word_size,
dict.word_id,
SliceU8Ref(raw_word),
dict.transform,
SliceU8Ref(transformed_word.split_at(actual_copy_len).0)
);
}
interface::Command::Literal(lit) => {
println_stderr!(
"{} {} {:x}",
if lit.high_entropy { "rndins" } else { "insert" },
lit.data.slice().len(),
SliceU8Ref(lit.data.slice())
);
}
}
}
// in-place thread create
use std::sync::RwLock;
pub struct MTJoinable<T: Send + 'static, U: Send + 'static>(JoinHandle<T>, PhantomData<U>);
#[cfg(not(feature = "std"))]
impl<T: Send + 'static, U: Send + 'static + AnyBoxConstructor> Joinable<T, U> for MTJoinable<T, U> {
fn join(self) -> Result<T, U> {
match self.0.join() {
Ok(t) => Ok(t),
Err(_e) => Err(<U as AnyBoxConstructor>::new(())),
}
}
}
#[cfg(feature = "std")]
impl<T: Send + 'static, U: Send + 'static + AnyBoxConstructor> Joinable<T, U> for MTJoinable<T, U> {
fn join(self) -> Result<T, U> {
match self.0.join() {
Ok(t) => Ok(t),
Err(e) => Err(<U as AnyBoxConstructor>::new(e)),
}
}
}
pub struct MTOwnedRetriever<U: Send + 'static>(std::sync::Arc<RwLock<U>>);
impl<U: Send + 'static> Clone for MTOwnedRetriever<U> {
fn clone(&self) -> Self {
MTOwnedRetriever(self.0.clone())
}
}
impl<U: Send + 'static> OwnedRetriever<U> for MTOwnedRetriever<U> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError> {
match self.0.read() {
Ok(u) => Ok(f(&*u)),
Err(_) => Err(PoisonedThreadError::default()),
}
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
match std::sync::Arc::try_unwrap(self.0) {
Ok(rwlock) => match rwlock.into_inner() {
Ok(u) => Ok(u),
Err(_) => Err(PoisonedThreadError::default()),
},
Err(_) => Err(PoisonedThreadError::default()),
}
}
}
#[derive(Default)]
pub struct MTSpawner {}
fn spawn_work<
T: Send + 'static,
ExtraInput: Send + 'static,
F: Fn(ExtraInput, usize, usize, &U, Alloc) -> T + Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
>(
extra_input: ExtraInput,
index: usize,
num_threads: usize,
locked_input: MTOwnedRetriever<U>,
alloc: Alloc,
f: F,
) -> std::thread::JoinHandle<T>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
std::thread::spawn(move || {
locked_input
.view(move |guard: &U| -> T { f(extra_input, index, num_threads, guard, alloc) })
.unwrap()
})
}
impl<
T: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnable<T, ExtraInput, Alloc, U> for MTSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle = MTJoinable<T, BrotliEncoderThreadError>;
type FinalJoinHandle = MTOwnedRetriever<U>;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
MTOwnedRetriever(std::sync::Arc::<RwLock<U>>::new(RwLock::new(
mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap(),
)))
}
fn spawn<F: Fn(ExtraInput, usize, usize, &U, Alloc) -> T + Send + 'static + Copy>(
&mut self,
locked_input: &mut Self::FinalJoinHandle,
work: &mut SendAlloc<T, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: F,
) {
let (alloc, extra_input) = work.replace_with_default();
let ret = spawn_work(
extra_input,
index,
num_threads,
locked_input.clone(),
alloc,
f,
);
*work = SendAlloc(InternalSendAlloc::Join(MTJoinable(ret, PhantomData)));
}
}
impl<
T: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnableLite<T, ExtraInput, Alloc, U> for MTSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle = <MTSpawner as BatchSpawnable<T, ExtraInput, Alloc, U>>::JoinHandle;
type FinalJoinHandle = <MTSpawner as BatchSpawnable<T, ExtraInput, Alloc, U>>::FinalJoinHandle;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
<Self as BatchSpawnable<T, ExtraInput, Alloc, U>>::make_spawner(self, input)
}
fn spawn(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<T, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: fn(ExtraInput, usize, usize, &U, Alloc) -> T,
) {
<Self as BatchSpawnable<T, ExtraInput, Alloc, U>>::spawn(
self,
handle,
alloc_per_thread,
index,
num_threads,
f,
)
}
}
+179
View File
@@ -0,0 +1,179 @@
#[cfg(feature = "validation")]
use core;
use std::io::{self, Error, ErrorKind, Read, Write};
use alloc_no_stdlib::{Allocator, SliceWrapper};
use brotli::enc::BrotliEncoderParams;
use brotli::{CustomWrite, DecompressorWriterCustomIo};
#[cfg(feature = "validation")]
use sha2::{Digest, Sha256};
use super::{HeapAllocator, IoWriterWrapper, Rebox};
#[cfg(feature = "validation")]
type Checksum = Sha256;
struct Tee<OutputA: Write, OutputB: Write>(OutputA, OutputB);
impl<OutputA: Write, OutputB: Write> Write for Tee<OutputA, OutputB> {
fn write(&mut self, data: &[u8]) -> Result<usize, io::Error> {
match self.0.write(data) {
Err(err) => Err(err),
Ok(size) => match self.1.write_all(&data[..size]) {
Ok(_) => Ok(size),
Err(err) => Err(err),
},
}
}
fn flush(&mut self) -> Result<(), io::Error> {
match self.0.flush() {
Err(err) => Err(err),
Ok(_) => loop {
match self.1.flush() {
Err(e) => match e.kind() {
ErrorKind::Interrupted => continue,
_ => return Err(e),
},
Ok(e) => return Ok(e),
}
},
}
}
}
struct DecompressAndValidate<'a, OutputType: Write + 'a>(
DecompressorWriterCustomIo<
io::Error,
IoWriterWrapper<'a, OutputType>,
Rebox<u8>, // buffer type
HeapAllocator,
HeapAllocator,
HeapAllocator,
>,
);
impl<'a, OutputType: Write> Write for DecompressAndValidate<'a, OutputType> {
fn write(&mut self, data: &[u8]) -> Result<usize, io::Error> {
self.0.write(data)
}
fn flush(&mut self) -> Result<(), io::Error> {
self.0.flush()
}
}
#[cfg(not(feature = "validation"))]
fn make_sha_writer() -> io::Sink {
io::sink()
}
#[cfg(not(feature = "validation"))]
fn make_sha_reader<InputType: Read>(r: &mut InputType) -> &mut InputType {
r
}
#[cfg(not(feature = "validation"))]
fn sha_ok<InputType: Read>(_writer: &mut io::Sink, _reader: &mut InputType) -> bool {
false
}
#[cfg(feature = "validation")]
struct ShaReader<'a, InputType: Read + 'a> {
reader: &'a mut InputType,
checksum: Checksum,
}
#[cfg(feature = "validation")]
impl<'a, InputType: Read + 'a> Read for ShaReader<'a, InputType> {
fn read(&mut self, data: &mut [u8]) -> Result<usize, io::Error> {
match self.reader.read(data) {
Err(e) => Err(e),
Ok(size) => {
self.checksum.update(&data[..size]);
Ok(size)
}
}
}
}
#[cfg(feature = "validation")]
fn make_sha_reader<InputType: Read>(r: &mut InputType) -> ShaReader<InputType> {
ShaReader {
reader: r,
checksum: Checksum::default(),
}
}
#[cfg(feature = "validation")]
fn sha_ok<InputType: Read>(writer: &mut ShaWriter, reader: &mut ShaReader<InputType>) -> bool {
core::mem::replace(&mut writer.0, Checksum::default()).finalize()
== core::mem::replace(&mut reader.checksum, Checksum::default()).finalize()
}
#[cfg(feature = "validation")]
#[derive(Default)]
struct ShaWriter(Checksum);
#[cfg(feature = "validation")]
impl Write for ShaWriter {
fn write(&mut self, data: &[u8]) -> Result<usize, io::Error> {
self.0.update(data);
Ok(data.len())
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
#[cfg(feature = "validation")]
fn make_sha_writer() -> ShaWriter {
ShaWriter::default()
}
#[cfg(feature = "validation")]
const VALIDATION_FAILED: &'static str = "Validation failed";
#[cfg(not(feature = "validation"))]
const VALIDATION_FAILED: &str =
"Validation module not enabled: build with cargo build --features=validation";
pub fn compress_validate<InputType: Read, OutputType: Write>(
r: &mut InputType,
w: &mut OutputType,
buffer_size: usize,
params: &BrotliEncoderParams,
custom_dictionary: Rebox<u8>,
num_threads: usize,
) -> Result<(), io::Error> {
let mut m8 = HeapAllocator::default();
let buffer = m8.alloc_cell(buffer_size);
// FIXME: could reuse the dictionary to seed the compressor, but that violates the abstraction right now
// also dictionaries are not very popular since they are mostly an internal concept, given their deprecation in
// the standard brotli spec
let mut dict = Vec::<u8>::new();
dict.extend_from_slice(custom_dictionary.slice());
let mut sha_writer = make_sha_writer();
let mut sha_reader = make_sha_reader(r);
let ret;
{
let validate_writer =
DecompressAndValidate(DecompressorWriterCustomIo::new_with_custom_dictionary(
IoWriterWrapper(&mut sha_writer),
buffer,
m8,
HeapAllocator::default(),
HeapAllocator::default(),
custom_dictionary,
Error::new(ErrorKind::InvalidData, "Invalid Data"),
));
let mut overarching_writer = Tee(validate_writer, w);
ret = super::compress(
&mut sha_reader,
&mut overarching_writer,
buffer_size,
params,
&dict[..],
num_threads,
);
}
match ret {
Ok(_ret) => {
if sha_ok(&mut sha_writer, &mut sha_reader) {
Ok(())
} else {
Err(Error::new(ErrorKind::InvalidData, VALIDATION_FAILED))
}
}
Err(e) => Err(e),
}
}
+838
View File
@@ -0,0 +1,838 @@
use core::cmp::min;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BroCatliResult {
Success = 0,
NeedsMoreInput = 1,
NeedsMoreOutput = 2,
BrotliFileNotCraftedForAppend = 124,
InvalidWindowSize = 125,
WindowSizeLargerThanPreviousFile = 126,
BrotliFileNotCraftedForConcatenation = 127,
}
const NUM_STREAM_HEADER_BYTES: usize = 5;
#[derive(Clone, Copy)]
struct NewStreamData {
bytes_so_far: [u8; NUM_STREAM_HEADER_BYTES],
num_bytes_read: u8,
num_bytes_written: Option<u8>,
}
impl NewStreamData {
pub fn new() -> NewStreamData {
NewStreamData {
bytes_so_far: [0, 0, 0, 0, 0],
num_bytes_read: 0,
num_bytes_written: None,
}
}
fn sufficient(&self) -> bool {
if self.num_bytes_read == 4 && (127 & self.bytes_so_far[0]) != 17 {
return true;
}
self.num_bytes_read == 5
}
}
fn parse_window_size(bytes_so_far: &[u8]) -> Result<(u8, usize), ()> {
// returns window_size and offset in stream in bits
if bytes_so_far[0] & 1 == 0 {
return Ok((16, 1));
}
match bytes_so_far[0] & 15 {
0x3 => return Ok((18, 4)),
0x5 => return Ok((19, 4)),
0x7 => return Ok((20, 4)),
0x9 => return Ok((21, 4)),
0xb => return Ok((22, 4)),
0xd => return Ok((23, 4)),
0xf => return Ok((24, 4)),
_ => match bytes_so_far[0] & 127 {
0x71 => return Ok((15, 7)),
0x61 => return Ok((14, 7)),
0x51 => return Ok((13, 7)),
0x41 => return Ok((12, 7)),
0x31 => return Ok((11, 7)),
0x21 => return Ok((10, 7)),
0x1 => return Ok((17, 7)),
_ => {}
},
}
if (bytes_so_far[0] & 0x80) != 0 {
return Err(());
}
let ret = bytes_so_far[1] & 0x3f;
if !(10..=30).contains(&ret) {
return Err(());
}
Ok((ret, 14))
}
fn detect_varlen_offset(bytes_so_far: &[u8]) -> Result<(usize), ()> {
// returns offfset in bits
let (_, mut offset) = match parse_window_size(bytes_so_far) {
Ok(x) => x,
Err(_) => return Err(()),
};
let mut bytes = 0u64;
for (index, item) in bytes_so_far.iter().enumerate() {
bytes |= u64::from(*item) << (index * 8);
}
bytes >>= offset;
offset += 1;
if (bytes & 1) != 0 {
// ISLAST
bytes >>= 1;
offset += 1;
if (bytes & 1) != 0 {
// ISLASTEMPTY
return Ok(offset);
}
}
bytes >>= 1;
let mut mnibbles = bytes & 3;
bytes >>= 2;
offset += 2;
if mnibbles == 3 {
// metadata block
if (bytes & 1) != 0 {
return Err(()); // reserved, must be zero
}
bytes >>= 1;
offset += 1;
let mskipbytes = bytes & ((1 << 2) - 1);
offset += 2;
offset += (mskipbytes as usize) * 8; // next item is byte aligned
return Ok(offset);
}
mnibbles += 4;
offset += (mnibbles as usize) * 4;
bytes >>= mnibbles * 4;
offset += 1;
if (bytes & 1) == 0 {
// not UNCOMPRESSED
Err(()) // not valid bitstream for concatenation
} else {
// UNCOMPRESSED: now things are aligend
Ok(offset)
}
}
// eat your vegetables
#[derive(Default)]
pub struct BroCatli {
last_bytes: [u8; 2],
last_bytes_len: u8,
last_byte_sanitized: bool,
any_bytes_emitted: bool,
last_byte_bit_offset: u8,
// need to make sure that window sizes stay similar or get smaller
window_size: u8,
new_stream_pending: Option<NewStreamData>,
}
impl BroCatli {
pub fn new() -> Self {
Self::default()
}
pub fn deserialize_from_buffer(buffer: &[u8]) -> Result<BroCatli, ()> {
if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() {
return Err(());
}
let last_bytes_len = buffer[8];
let last_byte_bit_offset = buffer[10];
let window_size = buffer[11];
let has_new_stream_pending = (buffer[9] & (1 << 6)) != 0;
let has_num_bytes_written = (buffer[9] & (1 << 7)) != 0;
if last_bytes_len > 2 || last_byte_bit_offset >= 8 {
return Err(());
}
if window_size != 0 && BroCatli::try_new_with_window_size(window_size).is_err() {
return Err(());
}
if has_new_stream_pending {
if usize::from(buffer[12]) > NUM_STREAM_HEADER_BYTES {
return Err(());
}
if has_num_bytes_written && buffer[13] > buffer[12] {
return Err(());
}
}
let mut possible_new_stream_pending = NewStreamData {
num_bytes_read: buffer[12],
num_bytes_written: if has_num_bytes_written {
Some(buffer[13])
} else {
None
},
bytes_so_far: [0; NUM_STREAM_HEADER_BYTES],
};
let xlen = possible_new_stream_pending.bytes_so_far.len();
possible_new_stream_pending
.bytes_so_far
.clone_from_slice(&buffer[16..16 + xlen]);
let new_stream_pending: Option<NewStreamData> = if has_new_stream_pending {
Some(possible_new_stream_pending)
} else {
None
};
let mut ret = BroCatli {
last_bytes: [0, 0],
last_bytes_len,
last_byte_sanitized: (buffer[9] & 0x1) != 0,
last_byte_bit_offset,
any_bytes_emitted: (buffer[9] & (1 << 5)) != 0,
window_size,
new_stream_pending,
};
if ret.last_bytes.len() > 8 {
return Err(());
}
let xlen = ret.last_bytes.len();
ret.last_bytes.clone_from_slice(&buffer[..xlen]);
Ok(ret)
}
#[inline(always)]
pub fn serialize_to_buffer(&self, buffer: &mut [u8]) -> Result<(), ()> {
if 16 + NUM_STREAM_HEADER_BYTES > buffer.len() {
return Err(());
}
buffer[..self.last_bytes.len()].clone_from_slice(&self.last_bytes[..]);
buffer[8] = self.last_bytes_len;
buffer[9] = (self.last_byte_sanitized as u8)
| ((self.new_stream_pending.is_some() as u8) << 6)
| ((self.any_bytes_emitted as u8) << 5);
buffer[10] = self.last_byte_bit_offset;
buffer[11] = self.window_size;
if let Some(new_stream_pending) = self.new_stream_pending {
if new_stream_pending.num_bytes_written.is_some() {
buffer[9] |= (1 << 7);
}
buffer[12] = new_stream_pending.num_bytes_read;
buffer[13] = new_stream_pending.num_bytes_written.unwrap_or(0);
// 14, 15 reserved
buffer[16..16 + new_stream_pending.bytes_so_far.len()]
.clone_from_slice(&new_stream_pending.bytes_so_far[..]);
}
Ok(())
}
/// Creates a `BroCatli` with an initial window size.
///
/// Panics for invalid window sizes. Use `try_new_with_window_size` to detect
/// invalid input without panicking.
pub fn new_with_window_size(log_window_size: u8) -> BroCatli {
Self::try_new_with_window_size(log_window_size).expect("invalid brotli window size")
}
/// Creates a `BroCatli` with an initial window size, or returns an error for invalid sizes.
pub fn try_new_with_window_size(log_window_size: u8) -> Result<BroCatli, BroCatliResult> {
// in this case setup the last_bytes of the stream to perfectly mimic what would
// appear in an empty stream with the selected window size...
// this means the window size followed by 2 sequential 1 bits (LAST_METABLOCK, EMPTY)
// the new_stream code should naturally find the sequential 1 bits and mask them
// out and then prepend the window size... then the following window sizes should
// be checked to be shorter
let last_bytes_len;
let last_bytes;
if log_window_size > 24 {
last_bytes = [17u8, log_window_size | 64 | 128];
last_bytes_len = 2;
} else if log_window_size == 16 {
last_bytes = [1 | 2 | 4, 0];
last_bytes_len = 1;
} else if log_window_size > 17 {
last_bytes = [(3 + (log_window_size - 18) * 2) | (16 | 32), 0];
last_bytes_len = 1;
} else {
match log_window_size {
15 => last_bytes = [0x71 | 0x80, 1],
14 => last_bytes = [0x61 | 0x80, 1],
13 => last_bytes = [0x51 | 0x80, 1],
12 => last_bytes = [0x41 | 0x80, 1],
11 => last_bytes = [0x31 | 0x80, 1],
10 => last_bytes = [0x21 | 0x80, 1],
17 => last_bytes = [0x1 | 0x80, 1],
_ => return Err(BroCatliResult::InvalidWindowSize),
}
last_bytes_len = 2;
}
Ok(BroCatli {
last_bytes,
last_bytes_len,
last_byte_bit_offset: 0,
last_byte_sanitized: false,
any_bytes_emitted: false,
new_stream_pending: None,
window_size: log_window_size,
})
}
pub fn new_brotli_file(&mut self) {
self.new_stream_pending = Some(NewStreamData::new());
}
fn flush_previous_stream(
&mut self,
out_bytes: &mut [u8],
out_offset: &mut usize,
) -> BroCatliResult {
if !self.last_byte_sanitized {
// if the previous stream hasn't had the last metablock (bit 1,1) sanitized
if self.last_bytes_len == 0 {
// first stream or otherwise sanitized
self.last_byte_sanitized = true;
return BroCatliResult::Success;
}
// create a 16 bit integer with the last 2 bytes of data
let mut last_bytes = self.last_bytes[0] as u16 + ((self.last_bytes[1] as u16) << 8);
let max = self.last_bytes_len * 8;
let mut index = max - 1;
for i in 0..max {
index = max - 1 - i;
if ((1 << index) & last_bytes) != 0 {
break; // find the highest set bit
}
}
if index == 0 {
// if the bit is too low, return failure, since both bits could not possibly have been set
return BroCatliResult::BrotliFileNotCraftedForAppend;
}
if (last_bytes >> (index - 1)) != 3 {
// last two bits need to be set for the final metablock
return BroCatliResult::BrotliFileNotCraftedForAppend;
}
index -= 1; // discard the final two bits
last_bytes &= (1 << index) - 1; // mask them out
self.last_bytes[0] = last_bytes as u8; // reset the last_bytes pair
self.last_bytes[1] = (last_bytes >> 8) as u8;
if index >= 8 {
// if both bits and one useful bit were in the second block, then write that
if out_bytes.len() > *out_offset {
out_bytes[*out_offset] = self.last_bytes[0];
self.last_bytes[0] = self.last_bytes[1];
*out_offset += 1;
self.any_bytes_emitted = true;
index -= 8;
self.last_bytes_len -= 1;
} else {
return BroCatliResult::NeedsMoreOutput;
}
}
self.last_byte_bit_offset = index;
assert!(index < 8);
self.last_byte_sanitized = true;
}
BroCatliResult::Success
}
fn shift_and_check_new_stream_header(
&mut self,
mut new_stream_pending: NewStreamData,
out_bytes: &mut [u8],
out_offset: &mut usize,
) -> BroCatliResult {
if new_stream_pending.num_bytes_written.is_none() {
let (window_size, window_offset) = if let Ok(results) = parse_window_size(
&new_stream_pending.bytes_so_far[..usize::from(new_stream_pending.num_bytes_read)],
) {
results
} else {
return BroCatliResult::InvalidWindowSize;
};
if self.window_size == 0 {
// parse window size and just copy everything
self.window_size = window_size;
assert_eq!(self.last_byte_bit_offset, 0); // we are first stream
out_bytes[*out_offset] = new_stream_pending.bytes_so_far[0];
new_stream_pending.num_bytes_written = Some(1);
self.any_bytes_emitted = true;
*out_offset += 1;
} else {
if window_size > self.window_size {
return BroCatliResult::WindowSizeLargerThanPreviousFile;
}
let mut realigned_header: [u8; NUM_STREAM_HEADER_BYTES + 1] =
[self.last_bytes[0], 0, 0, 0, 0, 0];
let varlen_offset = if let Ok(voffset) = detect_varlen_offset(
&new_stream_pending.bytes_so_far
[..usize::from(new_stream_pending.num_bytes_read)],
) {
voffset
} else {
return BroCatliResult::BrotliFileNotCraftedForConcatenation;
};
let mut bytes_so_far = 0u64;
for index in 0..usize::from(new_stream_pending.num_bytes_read) {
bytes_so_far |=
u64::from(new_stream_pending.bytes_so_far[index]) << (index * 8);
}
bytes_so_far >>= window_offset; // mask out the window size
bytes_so_far &= (1u64 << (varlen_offset - window_offset)) - 1;
let var_len_bytes = (((varlen_offset - window_offset) + 7) / 8);
for byte_index in 0..var_len_bytes {
let cur_byte = (bytes_so_far >> (byte_index * 8));
realigned_header[byte_index] |=
((cur_byte & ((1 << (8 - self.last_byte_bit_offset)) - 1))
<< self.last_byte_bit_offset) as u8;
realigned_header[byte_index + 1] =
(cur_byte >> (8 - self.last_byte_bit_offset)) as u8;
}
let whole_byte_destination =
((usize::from(self.last_byte_bit_offset) + varlen_offset - window_offset) + 7)
/ 8;
let whole_byte_source = (varlen_offset + 7) / 8;
if whole_byte_source > usize::from(new_stream_pending.num_bytes_read) {
return BroCatliResult::BrotliFileNotCraftedForConcatenation;
}
let num_whole_bytes_to_copy =
usize::from(new_stream_pending.num_bytes_read) - whole_byte_source;
for aligned_index in 0..num_whole_bytes_to_copy {
realigned_header[whole_byte_destination + aligned_index] =
new_stream_pending.bytes_so_far[whole_byte_source + aligned_index];
}
out_bytes[*out_offset] = realigned_header[0];
self.any_bytes_emitted = true;
*out_offset += 1;
// subtract one since that has just been written out and we're only copying realigned_header[1..]
new_stream_pending.num_bytes_read =
(whole_byte_destination + num_whole_bytes_to_copy) as u8 - 1;
new_stream_pending.num_bytes_written = Some(0);
new_stream_pending
.bytes_so_far
.clone_from_slice(&realigned_header[1..]);
}
} else {
assert_ne!(self.window_size, 0);
}
let to_copy = min(
out_bytes.len() - *out_offset,
usize::from(
new_stream_pending.num_bytes_read - new_stream_pending.num_bytes_written.unwrap(),
),
);
out_bytes
.split_at_mut(*out_offset)
.1
.split_at_mut(to_copy)
.0
.clone_from_slice(
new_stream_pending
.bytes_so_far
.split_at(usize::from(new_stream_pending.num_bytes_written.unwrap()))
.1
.split_at(to_copy)
.0,
);
*out_offset += to_copy;
if to_copy != 0 {
self.any_bytes_emitted = true;
}
new_stream_pending.num_bytes_written =
Some((new_stream_pending.num_bytes_written.unwrap() + to_copy as u8));
if new_stream_pending.num_bytes_written.unwrap() != new_stream_pending.num_bytes_read {
self.new_stream_pending = Some(new_stream_pending);
return BroCatliResult::NeedsMoreOutput;
}
self.new_stream_pending = None;
self.last_byte_sanitized = false;
self.last_byte_bit_offset = 0;
self.last_bytes_len = 0;
self.last_bytes = [0, 0];
//now unwrite from the stream, since the last byte may need to be adjusted to be EOF
*out_offset -= 1;
self.last_bytes[0] = out_bytes[*out_offset];
self.last_bytes_len = 1;
BroCatliResult::Success
}
pub fn stream(
&mut self,
in_bytes: &[u8],
in_offset: &mut usize,
out_bytes: &mut [u8],
out_offset: &mut usize,
) -> BroCatliResult {
if let Some(mut new_stream_pending) = self.new_stream_pending {
let flush_result = self.flush_previous_stream(out_bytes, out_offset);
if let BroCatliResult::Success = flush_result {
if usize::from(new_stream_pending.num_bytes_read)
< new_stream_pending.bytes_so_far.len()
{
{
let dst = &mut new_stream_pending.bytes_so_far
[usize::from(new_stream_pending.num_bytes_read)..];
let to_copy = min(dst.len(), in_bytes.len() - *in_offset);
dst[..to_copy]
.clone_from_slice(in_bytes.split_at(*in_offset).1.split_at(to_copy).0);
*in_offset += to_copy;
new_stream_pending.num_bytes_read += to_copy as u8;
}
self.new_stream_pending = Some(new_stream_pending); // write back changes
}
if !new_stream_pending.sufficient() {
return BroCatliResult::NeedsMoreInput;
}
if out_bytes.len() == *out_offset {
return BroCatliResult::NeedsMoreOutput;
}
let shift_result = self.shift_and_check_new_stream_header(
new_stream_pending,
out_bytes,
out_offset,
);
if let BroCatliResult::Success = shift_result {
} else {
return shift_result;
}
} else {
return flush_result;
}
if *out_offset == out_bytes.len() {
return BroCatliResult::NeedsMoreOutput; // need to be able to write at least one byte of data to make progress
}
}
assert!(self.new_stream_pending.is_none()); // this should have been handled above
if self.last_bytes_len != 2 {
if out_bytes.len() == *out_offset {
return BroCatliResult::NeedsMoreOutput;
}
if in_bytes.len() == *in_offset {
return BroCatliResult::NeedsMoreInput;
}
self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
*in_offset += 1;
self.last_bytes_len += 1;
if self.last_bytes_len != 2 {
if out_bytes.len() == *out_offset {
return BroCatliResult::NeedsMoreOutput;
}
if in_bytes.len() == *in_offset {
return BroCatliResult::NeedsMoreInput;
}
self.last_bytes[usize::from(self.last_bytes_len)] = in_bytes[*in_offset];
self.last_bytes_len += 1;
*in_offset += 1;
}
}
if out_bytes.len() == *out_offset {
return BroCatliResult::NeedsMoreOutput;
}
if in_bytes.len() == *in_offset {
return BroCatliResult::NeedsMoreInput;
}
let mut to_copy = min(out_bytes.len() - *out_offset, in_bytes.len() - *in_offset);
assert_ne!(to_copy, 0);
if to_copy == 1 {
out_bytes[*out_offset] = self.last_bytes[0];
self.last_bytes[0] = self.last_bytes[1];
self.last_bytes[1] = in_bytes[*in_offset];
*in_offset += 1;
*out_offset += 1;
if *out_offset == out_bytes.len() {
return BroCatliResult::NeedsMoreOutput;
}
return BroCatliResult::NeedsMoreInput;
}
out_bytes
.split_at_mut(*out_offset)
.1
.split_at_mut(2)
.0
.clone_from_slice(&self.last_bytes[..]);
*out_offset += 2;
let (new_in_offset, last_two) = in_bytes
.split_at(*in_offset)
.1
.split_at(to_copy)
.0
.split_at(to_copy - 2);
self.last_bytes.clone_from_slice(last_two);
*in_offset += 2; // add this after the clone since we grab the last 2 bytes, not the first
to_copy -= 2;
out_bytes
.split_at_mut(*out_offset)
.1
.split_at_mut(to_copy)
.0
.clone_from_slice(new_in_offset);
*out_offset += to_copy;
*in_offset += to_copy;
if *out_offset == out_bytes.len() {
return BroCatliResult::NeedsMoreOutput;
}
BroCatliResult::NeedsMoreInput
}
fn append_eof_metablock_to_last_bytes(&mut self) {
assert!(self.last_byte_sanitized);
let mut last_bytes = self.last_bytes[0] as u16 | ((self.last_bytes[1] as u16) << 8);
let bit_end = (self.last_bytes_len - 1) * 8 + self.last_byte_bit_offset;
last_bytes |= 3 << bit_end;
self.last_bytes[0] = last_bytes as u8;
self.last_bytes[1] = (last_bytes >> 8) as u8;
self.last_byte_sanitized = false;
self.last_byte_bit_offset += 2;
if self.last_byte_bit_offset >= 8 {
self.last_byte_bit_offset -= 8;
self.last_bytes_len += 1;
}
}
pub fn finish(&mut self, out_bytes: &mut [u8], out_offset: &mut usize) -> BroCatliResult {
if self.last_byte_sanitized && self.last_bytes_len != 0 {
self.append_eof_metablock_to_last_bytes();
}
while self.last_bytes_len != 0 {
if *out_offset == out_bytes.len() {
return BroCatliResult::NeedsMoreOutput;
}
out_bytes[*out_offset] = self.last_bytes[0];
*out_offset += 1;
self.last_bytes_len -= 1;
self.last_bytes[0] = self.last_bytes[1];
self.any_bytes_emitted = true;
}
if !self.any_bytes_emitted {
if out_bytes.len() == *out_offset {
return BroCatliResult::NeedsMoreOutput;
}
self.any_bytes_emitted = true;
out_bytes[*out_offset] = b';';
*out_offset += 1;
}
BroCatliResult::Success
}
}
#[cfg(test)]
mod test {
use super::BroCatli;
fn make_valid_serialized_buffer(buffer: &mut [u8]) {
buffer[8] = 2;
buffer[9] = (1 << 6) | (1 << 7);
buffer[10] = 7;
buffer[11] = 22;
buffer[12] = super::NUM_STREAM_HEADER_BYTES as u8;
buffer[13] = 3;
}
#[test]
fn test_deserialization() {
let broccoli = BroCatli {
new_stream_pending: Some(super::NewStreamData {
bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
num_bytes_read: super::NUM_STREAM_HEADER_BYTES as u8,
num_bytes_written: Some(3),
}),
last_bytes: [0x45, 0x46],
last_bytes_len: 1,
last_byte_sanitized: true,
any_bytes_emitted: false,
last_byte_bit_offset: 7,
window_size: 22,
};
let mut buffer = [0u8; 248];
broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
assert_eq!(broccoli.last_bytes, bc.last_bytes);
assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
assert_eq!(broccoli.window_size, bc.window_size);
assert_eq!(
broccoli.new_stream_pending.unwrap().bytes_so_far,
bc.new_stream_pending.unwrap().bytes_so_far
);
assert_eq!(
broccoli.new_stream_pending.unwrap().num_bytes_read,
bc.new_stream_pending.unwrap().num_bytes_read
);
assert_eq!(
broccoli.new_stream_pending.unwrap().num_bytes_written,
bc.new_stream_pending.unwrap().num_bytes_written
);
}
#[test]
fn test_deserialization_any_written() {
let broccoli = BroCatli {
new_stream_pending: Some(super::NewStreamData {
bytes_so_far: [0x33; super::NUM_STREAM_HEADER_BYTES],
num_bytes_read: super::NUM_STREAM_HEADER_BYTES as u8,
num_bytes_written: Some(3),
}),
last_bytes: [0x45, 0x46],
last_bytes_len: 1,
last_byte_sanitized: true,
any_bytes_emitted: true,
last_byte_bit_offset: 7,
window_size: 22,
};
let mut buffer = [0u8; 248];
broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
let bc = BroCatli::deserialize_from_buffer(&buffer[..]).unwrap();
assert_eq!(broccoli.last_bytes, bc.last_bytes);
assert_eq!(broccoli.last_bytes_len, bc.last_bytes_len);
assert_eq!(broccoli.last_byte_sanitized, bc.last_byte_sanitized);
assert_eq!(broccoli.last_byte_bit_offset, bc.last_byte_bit_offset);
assert_eq!(broccoli.window_size, bc.window_size);
assert_eq!(
broccoli.new_stream_pending.unwrap().bytes_so_far,
bc.new_stream_pending.unwrap().bytes_so_far
);
assert_eq!(
broccoli.new_stream_pending.unwrap().num_bytes_read,
bc.new_stream_pending.unwrap().num_bytes_read
);
assert_eq!(
broccoli.new_stream_pending.unwrap().num_bytes_written,
bc.new_stream_pending.unwrap().num_bytes_written
);
}
#[test]
fn test_serialization() {
let mut buffer = [0u8; 248];
let mut broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
let mut buffer2 = [0u8; 248];
broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
assert_eq!(&buffer[..], &buffer2[..]);
for (index, item) in buffer.iter_mut().enumerate() {
*item = index as u8;
}
make_valid_serialized_buffer(&mut buffer[..]);
broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
for (_index, item) in buffer.iter_mut().enumerate() {
*item = 0;
}
broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
assert_eq!(&buffer[..], &buffer2[..]);
for (index, item) in buffer.iter_mut().enumerate() {
*item = 0xff ^ index as u8;
}
make_valid_serialized_buffer(&mut buffer[..]);
broccoli = BroCatli::deserialize_from_buffer(&buffer).unwrap();
broccoli.serialize_to_buffer(&mut buffer2[..]).unwrap();
broccoli = BroCatli::deserialize_from_buffer(&buffer2).unwrap();
for (_index, item) in buffer.iter_mut().enumerate() {
*item = 0;
}
broccoli.serialize_to_buffer(&mut buffer[..]).unwrap();
assert_eq!(&buffer[..], &buffer2[..]);
}
#[test]
fn test_deserialization_rejects_invalid_state_fields() {
let mut buffer = [0u8; 248];
make_valid_serialized_buffer(&mut buffer[..]);
let mut invalid = buffer;
invalid[8] = 3;
assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
invalid = buffer;
invalid[10] = 8;
assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
invalid = buffer;
invalid[11] = 9;
assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
invalid = buffer;
invalid[12] = super::NUM_STREAM_HEADER_BYTES as u8 + 1;
assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
invalid = buffer;
invalid[13] = buffer[12] + 1;
assert!(BroCatli::deserialize_from_buffer(&invalid[..]).is_err());
}
#[test]
fn test_cat_empty_stream() {
let empty_catable = [b';'];
let mut bcat = super::BroCatli::default();
let mut in_offset = 0usize;
let mut out_bytes = [0u8; 32];
let mut out_offset = 0usize;
bcat.new_brotli_file();
let mut res = bcat.stream(
&empty_catable[..],
&mut in_offset,
&mut out_bytes[..],
&mut out_offset,
);
assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
bcat.new_brotli_file();
in_offset = 0;
res = bcat.stream(
&empty_catable[..],
&mut in_offset,
&mut out_bytes[..],
&mut out_offset,
);
assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
res = bcat.finish(&mut out_bytes[..], &mut out_offset);
assert_eq!(res, super::BroCatliResult::Success);
assert_ne!(out_offset, 0);
assert_eq!(&out_bytes[..out_offset], &[b';']);
}
#[test]
fn test_cat_truncated_metadata_header_fails() {
let empty_catable = [b';'];
let mut bcat = super::BroCatli::new_with_window_size(22);
let mut in_offset = 0usize;
let mut out_bytes = [0u8; 32];
let mut out_offset = 0usize;
let mut res = bcat.stream(
&empty_catable[..],
&mut in_offset,
&mut out_bytes[..],
&mut out_offset,
);
assert_eq!(res, super::BroCatliResult::NeedsMoreInput);
let truncated_metadata = [0x71, 0x1b, 0, 0];
bcat.new_brotli_file();
in_offset = 0;
out_offset = 0;
res = bcat.stream(
&truncated_metadata[..],
&mut in_offset,
&mut out_bytes[..],
&mut out_offset,
);
assert_eq!(
res,
super::BroCatliResult::BrotliFileNotCraftedForConcatenation
);
}
#[test]
fn test_try_new_with_window_size_invalid_returns_error() {
use super::BroCatliResult;
// Values 0..=9 are invalid window sizes and must return an error, not panic.
for ws in 0u8..=9 {
match BroCatli::try_new_with_window_size(ws) {
Err(BroCatliResult::InvalidWindowSize) => {}
Err(_) => panic!("window_size {} returned wrong error variant", ws),
Ok(_) => panic!("window_size {} should be rejected", ws),
}
#[cfg(feature = "std")]
assert!(
std::panic::catch_unwind(|| BroCatli::new_with_window_size(ws)).is_err(),
"window_size {} should panic through the legacy constructor",
ws
);
}
}
#[test]
fn test_new_with_window_size_valid() {
// Values 10..=24 and >24 are valid window sizes.
for ws in 10u8..=30 {
let _ = BroCatli::new_with_window_size(ws);
assert!(
BroCatli::try_new_with_window_size(ws).is_ok(),
"window_size {} should be accepted",
ws
);
}
}
}
@@ -0,0 +1,414 @@
#![cfg(feature = "benchmark")]
#![cfg(feature = "std")]
extern crate test;
use alloc_stdlib::StandardAlloc;
use super::*;
static RANDOM_THEN_UNICODE: &'static [u8] = include_bytes!("../../../testdata/random_then_unicode");
static FINALIZE_DATA: &'static [u8] = &[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20,
];
const TEST_LEN: usize = 256 * 1024;
const DISTANCE_CACHE: &'static [i32] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // distance cache
fn make_generic_hasher() -> AdvHasher<H5Sub, StandardAlloc> {
let params_hasher = BrotliHasherParams {
type_: 5,
block_bits: 6,
bucket_bits: 15,
num_last_distances_to_check: 10,
hash_len: 4,
literal_byte_score: 540,
};
let block_size = 1u64 << params_hasher.block_bits;
let bucket_size = 1u64 << params_hasher.bucket_bits;
let mut alloc = StandardAlloc::default();
AdvHasher::<H5Sub, StandardAlloc> {
buckets: alloc.alloc_cell((bucket_size * block_size) as usize),
h9_opts: H9Opts::new(&params_hasher),
num: alloc.alloc_cell(bucket_size as usize),
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: H5Sub {
hash_shift_: 32i32 - params_hasher.bucket_bits,
bucket_size_: bucket_size as u32,
block_bits_: params_hasher.block_bits as i32,
block_mask_: block_size.wrapping_sub(1) as u32,
},
}
}
fn make_specialized_hasher() -> AdvHasher<HQ7Sub, StandardAlloc> {
let params_hasher = BrotliHasherParams {
type_: 5,
block_bits: 6,
bucket_bits: 15,
num_last_distances_to_check: 10,
hash_len: 4,
literal_byte_score: 540,
};
let block_size = 1u64 << params_hasher.block_bits;
let bucket_size = 1u64 << params_hasher.bucket_bits;
let mut alloc = StandardAlloc::default();
AdvHasher::<HQ7Sub, StandardAlloc> {
buckets: alloc.alloc_cell((bucket_size * block_size) as usize),
h9_opts: H9Opts::new(&params_hasher),
num: alloc.alloc_cell(bucket_size as usize),
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: HQ7Sub {},
}
}
#[bench]
fn bench_256k_basic_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
for i in 0..TEST_LEN {
hasher.Store(testdata, usize::MAX, i);
}
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_basic_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
for i in 0..TEST_LEN {
hasher.Store(testdata, usize::MAX, i);
}
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_opt_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptBatch(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_opt_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptBatch(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_fetch_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptMemFetch(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_fetch_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptMemFetch(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_lazy_dupe_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptMemFetchLazyDupeUpdate(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_lazy_dupe_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptMemFetchLazyDupeUpdate(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_random_dupe_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptRandomDupeUpdate(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_mem_random_dupe_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRangeOptRandomDupeUpdate(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_cur_generic(bench: &mut test::Bencher) {
let mut hasher = make_generic_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRange(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
#[bench]
fn bench_256k_cur_specialized(bench: &mut test::Bencher) {
let mut hasher = make_specialized_hasher();
bench.iter(|| {
let testdata = test::black_box(RANDOM_THEN_UNICODE.split_at(TEST_LEN + 8).0);
hasher.BulkStoreRange(testdata, usize::MAX, 0, TEST_LEN);
let mut output = super::HasherSearchResult {
len: 0,
len_x_code: 0,
distance: 0,
score: 0,
};
hasher.FindLongestMatch(
None,
&[],
test::black_box(FINALIZE_DATA), // data
15, // ring mask
DISTANCE_CACHE,
8, // cur_x
8,
4,
4,
4,
&mut output,
);
});
}
@@ -0,0 +1,530 @@
use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core;
use core::cmp::min;
use super::{
fix_unbroken_len, kHashMul32, AnyHasher, BrotliEncoderParams, CloneWithAlloc, H9Opts,
HasherSearchResult, HowPrepared, Struct1,
};
use crate::enc::combined_alloc::allocate;
use crate::enc::static_dict::{
BrotliDictionary, FindMatchLengthWithLimit, BROTLI_UNALIGNED_LOAD32,
};
use crate::enc::util::floatX;
pub const kInfinity: floatX = 1.7e38;
#[derive(Clone, Copy, Debug)]
pub enum Union1 {
cost(floatX),
next(u32),
shortcut(u32),
}
#[derive(Clone, Copy, Debug)]
pub struct ZopfliNode {
//highest 7 bit is used to reconstruct the length code
pub length: u32,
// distance associated with the length
pub distance: u32,
// number of literal inserts before the copy; highest 5 bits contain distance short code + 1 (or zero if no short code)
pub dcode_insert_length: u32,
pub u: Union1,
}
impl Default for ZopfliNode {
fn default() -> Self {
ZopfliNode {
length: 1,
distance: 0,
dcode_insert_length: 0,
u: Union1::cost(kInfinity),
}
}
}
pub trait Allocable<T: Copy, AllocT: Allocator<T>> {
fn new(m: &mut AllocT, init: T) -> Self;
fn new_uninit(m: &mut AllocT) -> Self;
fn free(&mut self, m: &mut AllocT);
}
pub trait H10Params {
fn max_tree_search_depth() -> u32;
fn max_tree_comp_length() -> u32;
}
pub struct H10DefaultParams {}
impl H10Params for H10DefaultParams {
#[inline(always)]
fn max_tree_search_depth() -> u32 {
64
}
#[inline(always)]
fn max_tree_comp_length() -> u32 {
128
}
}
const BUCKET_BITS: usize = 17;
pub struct H10Buckets<AllocU32: Allocator<u32>>(AllocU32::AllocatedMemory);
impl<AllocU32: Allocator<u32>> Allocable<u32, AllocU32> for H10Buckets<AllocU32> {
fn new(m: &mut AllocU32, initializer: u32) -> H10Buckets<AllocU32> {
let mut ret = m.alloc_cell(1 << BUCKET_BITS);
for item in ret.slice_mut().iter_mut() {
*item = initializer;
}
H10Buckets::<AllocU32>(ret)
}
fn new_uninit(m: &mut AllocU32) -> H10Buckets<AllocU32> {
H10Buckets::<AllocU32>(m.alloc_cell(1 << BUCKET_BITS))
}
fn free(&mut self, m: &mut AllocU32) {
m.free_cell(core::mem::take(&mut self.0));
}
}
impl<AllocU32: Allocator<u32>> PartialEq<H10Buckets<AllocU32>> for H10Buckets<AllocU32> {
fn eq(&self, other: &H10Buckets<AllocU32>) -> bool {
return self.0.slice() == other.0.slice();
}
}
impl<AllocU32: Allocator<u32>> SliceWrapper<u32> for H10Buckets<AllocU32> {
#[inline(always)]
fn slice(&self) -> &[u32] {
self.0.slice()
}
}
impl<AllocU32: Allocator<u32>> SliceWrapperMut<u32> for H10Buckets<AllocU32> {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [u32] {
self.0.slice_mut()
}
}
pub struct H10<
AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
> where
Buckets: PartialEq<Buckets>,
{
pub window_mask_: usize,
pub ringbuffer_break: Option<core::num::NonZeroUsize>,
pub common: Struct1,
pub buckets_: Buckets,
pub invalid_pos_: u32,
pub forest: AllocU32::AllocatedMemory,
pub _params: core::marker::PhantomData<Params>,
}
impl<
AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
> PartialEq<H10<AllocU32, Buckets, Params>> for H10<AllocU32, Buckets, Params>
where
Buckets: PartialEq<Buckets>,
{
fn eq(&self, other: &H10<AllocU32, Buckets, Params>) -> bool {
self.window_mask_ == other.window_mask_
&& self.common == other.common
&& self.buckets_ == other.buckets_
&& self.invalid_pos_ == other.invalid_pos_
&& self.forest.slice() == other.forest.slice()
&& self._params == other._params
&& self.ringbuffer_break == other.ringbuffer_break
}
}
pub fn InitializeH10<AllocU32: Allocator<u32>>(
m32: &mut AllocU32,
one_shot: bool,
params: &BrotliEncoderParams,
ringbuffer_break: Option<core::num::NonZeroUsize>,
input_size: usize,
) -> H10<AllocU32, H10Buckets<AllocU32>, H10DefaultParams> {
initialize_h10::<AllocU32, H10Buckets<AllocU32>>(
m32,
one_shot,
params,
input_size,
ringbuffer_break,
)
}
fn initialize_h10<
AllocU32: Allocator<u32>,
Buckets: SliceWrapperMut<u32> + SliceWrapper<u32> + Allocable<u32, AllocU32>,
>(
m32: &mut AllocU32,
one_shot: bool,
params: &BrotliEncoderParams,
input_size: usize,
ringbuffer_break: Option<core::num::NonZeroUsize>,
) -> H10<AllocU32, Buckets, H10DefaultParams>
where
Buckets: PartialEq<Buckets>,
{
let mut num_nodes = 1 << params.lgwin;
if one_shot && input_size < num_nodes {
num_nodes = input_size;
}
let window_mask = (1 << params.lgwin) - 1;
let invalid_pos = 0u32.wrapping_sub(window_mask);
let buckets = <Buckets as Allocable<u32, AllocU32>>::new(m32, invalid_pos);
H10::<AllocU32, Buckets, H10DefaultParams> {
common: Struct1 {
params: params.hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
_params: core::marker::PhantomData::<H10DefaultParams>,
window_mask_: window_mask as usize,
invalid_pos_: invalid_pos,
buckets_: buckets,
forest: m32.alloc_cell(num_nodes * 2),
ringbuffer_break,
}
}
impl<
AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
> H10<AllocU32, Buckets, Params>
where
Buckets: PartialEq<Buckets>,
{
pub fn free(&mut self, m32: &mut AllocU32) {
m32.free_cell(core::mem::take(&mut self.forest));
self.buckets_.free(m32);
}
}
impl<
Alloc: Allocator<u16> + Allocator<u32>,
Buckets: Allocable<u32, Alloc> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
> CloneWithAlloc<Alloc> for H10<Alloc, Buckets, Params>
where
Buckets: PartialEq<Buckets>,
{
fn clone_with_alloc(&self, m: &mut Alloc) -> Self {
let mut ret = H10::<Alloc, Buckets, Params> {
window_mask_: self.window_mask_,
common: self.common.clone(),
buckets_: Buckets::new_uninit(m),
invalid_pos_: self.invalid_pos_,
forest: allocate::<u32, _>(m, self.forest.len()),
_params: core::marker::PhantomData::<Params>,
ringbuffer_break: self.ringbuffer_break,
};
ret.buckets_
.slice_mut()
.clone_from_slice(self.buckets_.slice());
ret.forest.slice_mut().clone_from_slice(self.forest.slice());
ret
}
}
impl<
AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
> AnyHasher for H10<AllocU32, Buckets, Params>
where
Buckets: PartialEq<Buckets>,
{
/* fn GetH10Tree(&mut self) -> Option<&mut H10<AllocU32, Buckets, H10Params>> {
Some(self)
}*/
#[inline(always)]
fn Opts(&self) -> H9Opts {
H9Opts {
literal_byte_score: 340,
}
}
#[inline(always)]
fn PrepareDistanceCache(&self, _distance_cache: &mut [i32]) {}
#[inline(always)]
fn HashTypeLength(&self) -> usize {
4
}
#[inline(always)]
fn StoreLookahead(&self) -> usize {
Params::max_tree_comp_length() as usize
}
fn StitchToPreviousBlock(
&mut self,
num_bytes: usize,
position: usize,
ringbuffer: &[u8],
ringbuffer_mask: usize,
) {
super::hq::StitchToPreviousBlockH10(
self,
num_bytes,
position,
ringbuffer,
ringbuffer_mask,
self.ringbuffer_break,
)
}
#[inline(always)]
fn GetHasherCommon(&mut self) -> &mut Struct1 {
&mut self.common
}
#[inline(always)]
fn HashBytes(&self, data: &[u8]) -> usize {
let h = BROTLI_UNALIGNED_LOAD32(data).wrapping_mul(kHashMul32);
(h >> (32i32 - BUCKET_BITS as i32)) as usize
}
#[inline(always)]
fn Store(&mut self, data: &[u8], mask: usize, ix: usize) {
let max_backward: usize = self.window_mask_.wrapping_sub(16).wrapping_add(1);
StoreAndFindMatchesH10(
self,
data,
ix,
mask,
self.ringbuffer_break,
Params::max_tree_comp_length() as usize,
max_backward,
&mut 0,
&mut [],
);
}
fn StoreRange(&mut self, data: &[u8], mask: usize, ix_start: usize, ix_end: usize) {
let mut i: usize = ix_start;
let mut j: usize = ix_start;
if ix_start.wrapping_add(63) <= ix_end {
i = ix_end.wrapping_sub(63);
}
if ix_start.wrapping_add(512) <= i {
while j < i {
{
self.Store(data, mask, j);
}
j = j.wrapping_add(8);
}
}
while i < ix_end {
{
self.Store(data, mask, i);
}
i = i.wrapping_add(1);
}
}
fn BulkStoreRange(&mut self, data: &[u8], mask: usize, ix_start: usize, ix_end: usize) {
for i in ix_start..ix_end {
self.Store(data, mask, i);
}
}
fn Prepare(&mut self, _one_shot: bool, _input_size: usize, _data: &[u8]) -> HowPrepared {
if self.common.is_prepared_ != 0 {
return HowPrepared::ALREADY_PREPARED;
}
let invalid_pos = self.invalid_pos_;
for bucket in self.buckets_.slice_mut().iter_mut() {
*bucket = invalid_pos;
}
self.common.is_prepared_ = 1;
HowPrepared::NEWLY_PREPARED
}
fn FindLongestMatch(
&mut self,
_dictionary: Option<&BrotliDictionary>,
_dictionary_hash: &[u16],
_data: &[u8],
_ring_buffer_mask: usize,
_ring_buffer_break: Option<core::num::NonZeroUsize>,
_distance_cache: &[i32],
_cur_ix: usize,
_max_length: usize,
_max_backward: usize,
_gap: usize,
_max_distance: usize,
_out: &mut HasherSearchResult,
) -> bool {
unimplemented!();
}
}
pub struct BackwardMatch(pub u64);
// pub distance : u32,
// pub length_and_code : u32,
impl BackwardMatch {
#[inline(always)]
pub fn distance(&self) -> u32 {
self.0 as u32
}
#[inline(always)]
pub fn length_and_code(&self) -> u32 {
(self.0 >> 32) as u32
}
}
pub struct BackwardMatchMut<'a>(pub &'a mut u64);
// pub distance : u32,
// pub length_and_code : u32,
impl<'a> BackwardMatchMut<'a> {
#[inline(always)]
pub fn distance(&self) -> u32 {
*self.0 as u32
}
#[inline(always)]
pub fn length_and_code(&self) -> u32 {
(*self.0 >> 32) as u32
}
#[inline(always)]
pub fn set_distance(&mut self, data: u32) {
*self.0 &= 0xffffffff00000000;
*self.0 |= u64::from(data)
}
#[inline(always)]
pub fn set_length_and_code(&mut self, data: u32) {
*self.0 = u64::from((*self.0) as u32) | (u64::from(data) << 32);
}
#[inline(always)]
pub fn init(&mut self, dist: usize, len: usize) {
self.set_distance(dist as u32);
self.set_length_and_code((len << 5) as u32);
}
#[inline(always)]
pub(crate) fn init_dictionary(&mut self, dist: usize, len: usize, len_code: usize) {
self.set_distance(dist as u32);
self.set_length_and_code((len << 5 | if len == len_code { 0 } else { len_code }) as u32);
}
}
macro_rules! LeftChildIndexH10 {
($xself: expr, $pos: expr) => {
(2usize).wrapping_mul($pos & (*$xself).window_mask_)
};
}
macro_rules! RightChildIndexH10 {
($xself: expr, $pos: expr) => {
(2usize)
.wrapping_mul($pos & (*$xself).window_mask_)
.wrapping_add(1)
};
}
/*
fn LeftChildIndexH10<AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32>+SliceWrapperMut<u32>+SliceWrapper<u32>,
Params:H10Params>(
mut xself : &mut H10<AllocU32, Buckets, Params>, pos : usize
) -> usize {
(2usize).wrapping_mul(pos & xself.window_mask_)
}
fn RightChildIndexH10<AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32>+SliceWrapperMut<u32>+SliceWrapper<u32>,
Params:H10Params>(
mut xself : &mut H10<AllocU32, Buckets, Params>, pos : usize
) -> usize {
(2usize).wrapping_mul(
pos & xself.window_mask_
).wrapping_add(
1
)
}
*/
pub fn StoreAndFindMatchesH10<
AllocU32: Allocator<u32>,
Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
Params: H10Params,
>(
xself: &mut H10<AllocU32, Buckets, Params>,
data: &[u8],
cur_ix: usize,
ring_buffer_mask: usize,
ringbuffer_break: Option<core::num::NonZeroUsize>,
max_length: usize,
max_backward: usize,
best_len: &mut usize,
matches: &mut [u64],
) -> usize
where
Buckets: PartialEq<Buckets>,
{
let mut matches_offset = 0_usize;
let cur_ix_masked = cur_ix & ring_buffer_mask;
let max_comp_len = min(max_length, 128);
let should_reroot_tree = max_length >= 128;
let key = xself.HashBytes(&data[cur_ix_masked..]);
let forest = xself.forest.slice_mut();
let mut prev_ix = xself.buckets_.slice()[key] as usize;
let mut node_left = LeftChildIndexH10!(xself, cur_ix);
let mut node_right = RightChildIndexH10!(xself, cur_ix);
let mut best_len_left = 0_usize;
let mut best_len_right = 0_usize;
let mut depth_remaining = 64_usize;
if should_reroot_tree {
xself.buckets_.slice_mut()[key] = cur_ix as u32;
}
loop {
let backward = cur_ix.wrapping_sub(prev_ix);
let prev_ix_masked = prev_ix & ring_buffer_mask;
if backward == 0 || backward > max_backward || depth_remaining == 0 {
if should_reroot_tree {
forest[node_left] = xself.invalid_pos_;
forest[node_right] = xself.invalid_pos_;
}
break;
}
let cur_len = min(best_len_left, best_len_right);
let len = fix_unbroken_len(
cur_len.wrapping_add(FindMatchLengthWithLimit(
&data[cur_ix_masked.wrapping_add(cur_len)..],
&data[prev_ix_masked.wrapping_add(cur_len)..],
max_length.wrapping_sub(cur_len),
)),
prev_ix_masked,
cur_ix_masked,
ringbuffer_break,
);
if matches_offset != matches.len() && len > *best_len {
*best_len = len;
BackwardMatchMut(&mut matches[matches_offset]).init(backward, len);
matches_offset += 1;
}
if len >= max_comp_len {
if should_reroot_tree {
forest[node_left] = forest[LeftChildIndexH10!(xself, prev_ix)];
forest[node_right] = forest[RightChildIndexH10!(xself, prev_ix)];
}
break;
}
if data[cur_ix_masked.wrapping_add(len)] > data[prev_ix_masked.wrapping_add(len)] {
best_len_left = len;
if should_reroot_tree {
forest[node_left] = prev_ix as u32;
}
node_left = RightChildIndexH10!(xself, prev_ix);
prev_ix = forest[node_left] as usize;
} else {
best_len_right = len;
if should_reroot_tree {
forest[node_right] = prev_ix as u32;
}
node_right = LeftChildIndexH10!(xself, prev_ix);
prev_ix = forest[node_right] as usize;
}
depth_remaining = depth_remaining.wrapping_sub(1);
}
matches_offset
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,294 @@
#![cfg(test)]
#![cfg(feature = "std")]
use alloc_stdlib::StandardAlloc;
use super::{
AdvHasher, AnyHasher, BrotliHasherParams, CloneWithAlloc, H5Sub, H9Opts, HQ7Sub, Struct1,
};
use crate::enc::combined_alloc::allocate;
use crate::enc::{Allocator, SliceWrapper};
static RANDOM_THEN_UNICODE: &[u8] = include_bytes!("../../../testdata/random_then_unicode"); //&[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55];
#[cfg(feature = "std")]
#[test]
fn test_bulk_store_range() {
let params_hasher = BrotliHasherParams {
type_: 5,
block_bits: 6,
bucket_bits: 15,
num_last_distances_to_check: 10,
hash_len: 4,
literal_byte_score: 540,
};
let block_size = 1u64 << params_hasher.block_bits;
let bucket_size = 1u64 << params_hasher.bucket_bits;
let mut alloc = StandardAlloc::default();
let mut buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
let mut num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_a = AdvHasher::<H5Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: H5Sub {
hash_shift_: 32i32 - params_hasher.bucket_bits,
bucket_size_: bucket_size as u32,
block_bits_: params_hasher.block_bits,
block_mask_: block_size.wrapping_sub(1) as u32,
},
};
buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_b = hasher_a.clone_with_alloc(&mut alloc);
assert!(hasher_a == hasher_b);
let mut hasher_e = hasher_a.clone_with_alloc(&mut alloc);
let mut hasher_c = AdvHasher::<HQ7Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: HQ7Sub {},
};
let mut hasher_d = hasher_c.clone_with_alloc(&mut alloc);
assert!(hasher_d == hasher_c);
hasher_a.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
hasher_c.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
for i in 15..RANDOM_THEN_UNICODE.len() - 8 {
hasher_b.Store(RANDOM_THEN_UNICODE, usize::MAX, i);
}
hasher_d.StoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
let ret_start = hasher_e.StoreRangeOptBatch(
RANDOM_THEN_UNICODE,
usize::MAX,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
assert!(ret_start > 15);
hasher_e.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
ret_start,
RANDOM_THEN_UNICODE.len() - 8,
);
assert_eq!(hasher_a.buckets.slice(), hasher_c.buckets.slice());
assert_eq!(hasher_b.buckets.slice(), hasher_d.buckets.slice());
assert_eq!(hasher_a.num.slice(), hasher_c.num.slice());
assert_eq!(hasher_b.num.slice(), hasher_d.num.slice());
assert_eq!(hasher_a.buckets.slice(), hasher_b.buckets.slice());
assert_eq!(hasher_c.buckets.slice(), hasher_d.buckets.slice());
assert_eq!(hasher_a.num.slice(), hasher_b.num.slice());
assert_eq!(hasher_c.num.slice(), hasher_d.num.slice());
assert!(hasher_a == hasher_b);
assert!(hasher_d == hasher_c);
assert!(hasher_a == hasher_e);
}
#[cfg(feature = "std")]
#[test]
// does not use the fancy optimizations for q7
fn test_bulk_store_range_off_spec() {
let params_hasher = BrotliHasherParams {
type_: 5,
block_bits: 6,
bucket_bits: 15,
num_last_distances_to_check: 10,
hash_len: 4,
literal_byte_score: 540,
};
let block_size = 1u64 << params_hasher.block_bits;
let bucket_size = 1u64 << params_hasher.bucket_bits;
let mut alloc = StandardAlloc::default();
let mut buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
let mut num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_a = AdvHasher::<H5Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: H5Sub {
hash_shift_: 32i32 - params_hasher.bucket_bits,
bucket_size_: bucket_size as u32,
block_bits_: params_hasher.block_bits,
block_mask_: block_size.wrapping_sub(1) as u32,
},
};
buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_b = hasher_a.clone_with_alloc(&mut alloc);
assert!(hasher_a == hasher_b);
let mut hasher_c = AdvHasher::<HQ7Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: HQ7Sub {},
};
let mut hasher_d = hasher_c.clone_with_alloc(&mut alloc);
assert!(hasher_d == hasher_c);
hasher_a.BulkStoreRange(
RANDOM_THEN_UNICODE,
0x0fff,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
hasher_c.BulkStoreRange(
RANDOM_THEN_UNICODE,
0x0fff,
15,
RANDOM_THEN_UNICODE.len() - 8,
);
hasher_c.BulkStoreRange(
RANDOM_THEN_UNICODE,
0x0fff,
RANDOM_THEN_UNICODE.len(),
RANDOM_THEN_UNICODE.len() - 8,
); // noop
for i in 15..RANDOM_THEN_UNICODE.len() - 8 {
hasher_b.Store(RANDOM_THEN_UNICODE, 0x0fff, i);
hasher_d.Store(RANDOM_THEN_UNICODE, 0x0fff, i);
}
assert_eq!(hasher_a.buckets.slice(), hasher_c.buckets.slice());
assert_eq!(hasher_b.buckets.slice(), hasher_d.buckets.slice());
assert_eq!(hasher_a.num.slice(), hasher_c.num.slice());
assert_eq!(hasher_b.num.slice(), hasher_d.num.slice());
assert!(hasher_a == hasher_b);
assert!(hasher_d == hasher_c);
}
#[cfg(feature = "std")]
#[test]
fn test_bulk_store_range_pow2() {
let params_hasher = BrotliHasherParams {
type_: 5,
block_bits: 6,
bucket_bits: 15,
num_last_distances_to_check: 10,
hash_len: 4,
literal_byte_score: 540,
};
let block_size = 1u64 << params_hasher.block_bits;
let bucket_size = 1u64 << params_hasher.bucket_bits;
let mut alloc = StandardAlloc::default();
let mut buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
let mut num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_a = AdvHasher::<H5Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: H5Sub {
hash_shift_: 32i32 - params_hasher.bucket_bits,
bucket_size_: bucket_size as u32,
block_bits_: params_hasher.block_bits,
block_mask_: block_size.wrapping_sub(1) as u32,
},
};
buckets = allocate::<u32, _>(&mut alloc, (bucket_size * block_size) as usize);
num = alloc.alloc_cell(bucket_size as usize);
let mut hasher_b = hasher_a.clone_with_alloc(&mut alloc);
assert!(hasher_a == hasher_b);
let mut hasher_e = hasher_a.clone_with_alloc(&mut alloc);
let mut hasher_c = AdvHasher::<HQ7Sub, StandardAlloc> {
buckets,
h9_opts: H9Opts::new(&params_hasher),
num,
GetHasherCommon: Struct1 {
params: params_hasher,
is_prepared_: 1,
dict_num_lookups: 0,
dict_num_matches: 0,
},
specialization: HQ7Sub {},
};
let mut hasher_d = hasher_c.clone_with_alloc(&mut alloc);
assert!(hasher_d == hasher_c);
hasher_a.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
RANDOM_THEN_UNICODE.len() - 64 - 3,
RANDOM_THEN_UNICODE.len() - 3,
);
hasher_c.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
RANDOM_THEN_UNICODE.len() - 64 - 3,
RANDOM_THEN_UNICODE.len() - 3,
);
for i in RANDOM_THEN_UNICODE.len() - 64 - 3..RANDOM_THEN_UNICODE.len() - 3 {
hasher_b.Store(RANDOM_THEN_UNICODE, usize::MAX, i);
}
hasher_d.StoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
RANDOM_THEN_UNICODE.len() - 64 - 3,
RANDOM_THEN_UNICODE.len() - 3,
);
let ret_start = hasher_e.StoreRangeOptBatch(
RANDOM_THEN_UNICODE,
usize::MAX,
RANDOM_THEN_UNICODE.len() - 64 - 3,
RANDOM_THEN_UNICODE.len() - 3,
);
assert!(ret_start > 15);
hasher_e.BulkStoreRange(
RANDOM_THEN_UNICODE,
usize::MAX,
ret_start,
RANDOM_THEN_UNICODE.len() - 3,
);
assert_eq!(hasher_a.buckets.slice(), hasher_c.buckets.slice());
assert_eq!(hasher_b.buckets.slice(), hasher_d.buckets.slice());
assert_eq!(hasher_a.num.slice(), hasher_c.num.slice());
assert_eq!(hasher_b.num.slice(), hasher_d.num.slice());
assert_eq!(hasher_a.buckets.slice(), hasher_b.buckets.slice());
assert_eq!(hasher_c.buckets.slice(), hasher_d.buckets.slice());
assert_eq!(hasher_a.num.slice(), hasher_b.num.slice());
assert_eq!(hasher_c.num.slice(), hasher_d.num.slice());
assert!(hasher_a == hasher_b);
assert!(hasher_d == hasher_c);
assert!(hasher_a == hasher_e);
}
+211
View File
@@ -0,0 +1,211 @@
use alloc::SliceWrapperMut;
use core::cmp::{max, min};
use super::super::alloc::SliceWrapper;
use super::histogram::CostAccessors;
use super::util::{FastLog2, FastLog2u16};
use super::vectorization::Mem256i;
use crate::enc::floatX;
const BROTLI_REPEAT_ZERO_CODE_LENGTH: usize = 17;
const BROTLI_CODE_LENGTH_CODES: usize = BROTLI_REPEAT_ZERO_CODE_LENGTH + 1;
pub(crate) fn shannon_entropy(mut population: &[u32], size: usize) -> (floatX, usize) {
let mut sum: usize = 0;
let mut retval: floatX = 0.0;
if (size & 1) != 0 && !population.is_empty() {
let p = population[0] as usize;
population = population.split_at(1).1;
sum = sum.wrapping_add(p);
retval -= p as floatX * FastLog2u16(p as u16);
}
for pop_iter in population.split_at((size >> 1) << 1).0 {
let p = *pop_iter as usize;
sum = sum.wrapping_add(p);
retval -= p as floatX * FastLog2u16(p as u16);
}
if sum != 0 {
retval += sum as floatX * FastLog2(sum as u64); // not sure it's 16 bit
}
(retval, sum)
}
#[inline(always)]
pub fn BitsEntropy(population: &[u32], size: usize) -> floatX {
let (mut retval, sum) = shannon_entropy(population, size);
if retval < sum as floatX {
retval = sum as floatX;
}
retval
}
#[allow(clippy::excessive_precision)]
fn CostComputation<T: SliceWrapper<Mem256i>>(
depth_histo: &mut [u32; BROTLI_CODE_LENGTH_CODES],
nnz_data: &T,
nnz: usize,
_total_count: floatX,
log2total: floatX,
) -> floatX {
let mut bits: floatX = 0.0;
let mut max_depth: usize = 1;
for i in 0..nnz {
// Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) =
// = log2(total_count) - log2(count(symbol))
let element = nnz_data.slice()[i >> 3][i & 7];
let log2p = log2total - FastLog2u16(element as u16);
// Approximate the bit depth by round(-log2(P(symbol)))
let depth = min((log2p + 0.5) as u8, 15u8);
bits += (element as floatX) * log2p;
if (depth as usize) > max_depth {
max_depth = depth as usize;
}
depth_histo[depth as usize] += 1;
}
// Add the estimated encoding cost of the code length code histogram.
bits += (18 + 2 * max_depth) as floatX;
// Add the entropy of the code length code histogram.
bits += BitsEntropy(depth_histo, BROTLI_CODE_LENGTH_CODES);
//println_stderr!("{:?} {:?}", &depth_histo[..], bits);
bits
}
pub fn BrotliPopulationCost<HistogramType: SliceWrapper<u32> + CostAccessors>(
histogram: &HistogramType,
nnz_data: &mut HistogramType::i32vec,
) -> floatX {
static kOneSymbolHistogramCost: floatX = 12.0;
static kTwoSymbolHistogramCost: floatX = 20.0;
static kThreeSymbolHistogramCost: floatX = 28.0;
static kFourSymbolHistogramCost: floatX = 37.0;
let data_size: usize = histogram.slice().len();
let mut count = 0;
let mut s: [usize; 5] = [0; 5];
let mut bits: floatX = 0.0;
if histogram.total_count() == 0 {
return kOneSymbolHistogramCost;
}
for i in 0..data_size {
if histogram.slice()[i] > 0 {
s[count] = i;
count += 1;
if count > 4 {
break;
}
}
}
match count {
1 => return kOneSymbolHistogramCost,
2 => return kTwoSymbolHistogramCost + histogram.total_count() as floatX,
3 => {
let histo0: u32 = histogram.slice()[s[0]];
let histo1: u32 = histogram.slice()[s[1]];
let histo2: u32 = histogram.slice()[s[2]];
let histomax: u32 = max(histo0, max(histo1, histo2));
return kThreeSymbolHistogramCost
+ (2u32).wrapping_mul(histo0.wrapping_add(histo1).wrapping_add(histo2)) as floatX
- histomax as floatX;
}
4 => {
let mut histo: [u32; 4] = [0; 4];
for i in 0..4 {
histo[i] = histogram.slice()[s[i]];
}
for i in 0..4 {
for j in i + 1..4 {
if histo[j] > histo[i] {
histo.swap(j, i);
}
}
}
let h23: u32 = histo[2].wrapping_add(histo[3]);
let histomax: u32 = max(h23, histo[0]);
return kFourSymbolHistogramCost
+ (3u32).wrapping_mul(h23) as floatX
+ (2u32).wrapping_mul(histo[0].wrapping_add(histo[1])) as floatX
- histomax as floatX;
}
_ => {}
}
if cfg!(feature = "vector_scratch_space") {
// vectorization failed: it's faster to do things inline than split into two loops
let mut nnz: usize = 0;
let mut depth_histo = [0u32; 18];
let total_count = histogram.total_count() as floatX;
let log2total = FastLog2(histogram.total_count() as u64);
let mut i: usize = 0;
while i < data_size {
if histogram.slice()[i] > 0 {
let nnz_val = &mut nnz_data.slice_mut()[nnz >> 3];
nnz_val[nnz & 7] = histogram.slice()[i] as i32;
i += 1;
nnz += 1;
} else {
let mut reps: u32 = 1;
for hd in histogram.slice()[i + 1..data_size].iter() {
if *hd != 0 {
break;
}
reps += 1
}
i += reps as usize;
if i == data_size {
break;
}
if reps < 3 {
depth_histo[0] += reps;
} else {
reps -= 2;
let mut depth_histo_adds: u32 = 0;
while reps > 0 {
depth_histo_adds += 1;
bits += 3.0;
reps >>= 3;
}
depth_histo[BROTLI_REPEAT_ZERO_CODE_LENGTH] += depth_histo_adds;
}
}
}
bits += CostComputation(&mut depth_histo, nnz_data, nnz, total_count, log2total);
} else {
let mut max_depth: usize = 1;
let mut depth_histo = [0u32; 18];
let log2total: floatX = FastLog2(histogram.total_count() as u64); // 64 bit here
let mut reps: u32 = 0;
for histo in histogram.slice()[..data_size].iter() {
if *histo != 0 {
if reps != 0 {
if reps < 3 {
depth_histo[0] += reps;
} else {
reps -= 2;
while reps > 0 {
depth_histo[17] += 1;
bits += 3.0;
reps >>= 3;
}
}
reps = 0;
}
let log2p = log2total - FastLog2u16(*histo as u16);
let mut depth = (log2p + 0.5) as usize;
bits += *histo as floatX * log2p;
depth = min(depth, 15);
max_depth = max(depth, max_depth);
depth_histo[depth] += 1;
} else {
reps += 1;
}
}
bits += (18usize).wrapping_add((2usize).wrapping_mul(max_depth)) as floatX;
bits += BitsEntropy(&depth_histo[..], 18);
}
bits
}
+39
View File
@@ -0,0 +1,39 @@
use super::super::alloc;
use super::super::alloc::{Allocator, SliceWrapper};
use crate::enc::combined_alloc::alloc_default;
pub struct BlockSplit<Alloc: alloc::Allocator<u8> + alloc::Allocator<u32>> {
pub num_types: usize,
pub num_blocks: usize,
pub types: <Alloc as Allocator<u8>>::AllocatedMemory,
pub lengths: <Alloc as Allocator<u32>>::AllocatedMemory,
}
impl<Alloc: alloc::Allocator<u8> + alloc::Allocator<u32>> Default for BlockSplit<Alloc> {
fn default() -> Self {
Self {
num_types: 0,
num_blocks: 0,
types: alloc_default::<u8, Alloc>(),
lengths: alloc_default::<u32, Alloc>(),
}
}
}
impl<Alloc: alloc::Allocator<u8> + alloc::Allocator<u32>> BlockSplit<Alloc> {
pub fn new() -> BlockSplit<Alloc> {
Self::default()
}
pub fn destroy(&mut self, m: &mut Alloc) {
<Alloc as Allocator<u8>>::free_cell(m, core::mem::take(&mut self.types));
<Alloc as Allocator<u32>>::free_cell(m, core::mem::take(&mut self.lengths));
self.num_blocks = 0;
self.num_types = 0;
}
pub fn types_alloc_size(&self) -> usize {
self.types.slice().len()
}
pub fn lengths_alloc_size(&self) -> usize {
self.lengths.slice().len()
}
}
+929
View File
@@ -0,0 +1,929 @@
use core;
use core::cmp::{max, min};
#[cfg(feature = "simd")]
use core::simd::prelude::{SimdFloat, SimdPartialOrd};
use super::super::alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use super::backward_references::BrotliEncoderParams;
use super::bit_cost::BrotliPopulationCost;
use super::block_split::BlockSplit;
use super::cluster::{BrotliHistogramBitCostDistance, BrotliHistogramCombine, HistogramPair};
use super::command::Command;
use super::histogram::{
ClearHistograms, CostAccessors, HistogramAddHistogram, HistogramAddItem, HistogramAddVector,
HistogramClear, HistogramCommand, HistogramDistance, HistogramLiteral,
};
use super::util::FastLog2;
use super::vectorization::{sum8i, v256, v256i, Mem256f};
use crate::enc::combined_alloc::allocate;
use crate::enc::floatX;
static kMaxLiteralHistograms: usize = 100usize;
static kMaxCommandHistograms: usize = 50usize;
static kLiteralBlockSwitchCost: floatX = 28.1;
static kCommandBlockSwitchCost: floatX = 13.5;
static kDistanceBlockSwitchCost: floatX = 14.6;
static kLiteralStrideLength: usize = 70usize;
static kCommandStrideLength: usize = 40usize;
static kSymbolsPerLiteralHistogram: usize = 544usize;
static kSymbolsPerCommandHistogram: usize = 530usize;
static kSymbolsPerDistanceHistogram: usize = 544usize;
static kMinLengthForBlockSplitting: usize = 128usize;
static kIterMulForRefining: usize = 2usize;
static kMinItersForRefining: usize = 100usize;
#[inline(always)]
fn update_cost_and_signal(
num_histograms32: u32,
ix: usize,
min_cost: floatX,
block_switch_cost: floatX,
cost: &mut [Mem256f],
switch_signal: &mut [u8],
) {
let ymm_min_cost = v256::splat(min_cost);
let ymm_block_switch_cost = v256::splat(block_switch_cost);
let ymm_and_mask = v256i::from([
1 << 0,
1 << 1,
1 << 2,
1 << 3,
1 << 4,
1 << 5,
1 << 6,
1 << 7,
]);
for (index, cost_it) in cost[..((num_histograms32 as usize + 7) >> 3)]
.iter_mut()
.enumerate()
{
let mut ymm_cost = *cost_it;
let costk_minus_min_cost = ymm_cost - ymm_min_cost;
let ymm_cmpge: v256i = costk_minus_min_cost.simd_ge(ymm_block_switch_cost).to_int();
let ymm_bits = ymm_cmpge & ymm_and_mask;
let result = sum8i(ymm_bits);
//super::vectorization::sum8(ymm_bits) as u8;
switch_signal[ix + index] |= result as u8;
ymm_cost = costk_minus_min_cost.simd_min(ymm_block_switch_cost);
*cost_it = Mem256f::from(ymm_cost);
//println_stderr!("{:} ss {:} c {:?}", (index << 3) + 7, switch_signal[ix + index],*cost_it);
}
}
fn CountLiterals(cmds: &[Command], num_commands: usize) -> usize {
let mut total_length: usize = 0usize;
for i in 0usize..num_commands {
total_length = total_length.wrapping_add((cmds[i]).insert_len_ as usize);
}
total_length
}
fn CopyLiteralsToByteArray(
cmds: &[Command],
num_commands: usize,
data: &[u8],
offset: usize,
mask: usize,
literals: &mut [u8],
) {
let mut pos: usize = 0usize;
let mut from_pos: usize = offset & mask;
for i in 0usize..num_commands {
let mut insert_len: usize = (cmds[i]).insert_len_ as usize;
if from_pos.wrapping_add(insert_len) > mask {
let head_size: usize = mask.wrapping_add(1).wrapping_sub(from_pos);
literals[pos..(pos + head_size)]
.clone_from_slice(&data[from_pos..(from_pos + head_size)]);
from_pos = 0usize;
pos = pos.wrapping_add(head_size);
insert_len = insert_len.wrapping_sub(head_size);
}
if insert_len > 0usize {
literals[pos..(pos + insert_len)]
.clone_from_slice(&data[from_pos..(from_pos + insert_len)]);
pos = pos.wrapping_add(insert_len);
}
from_pos = from_pos
.wrapping_add(insert_len)
.wrapping_add(cmds[i].copy_len() as usize)
& mask;
}
}
fn MyRand(seed: &mut u32) -> u32 {
*seed = seed.wrapping_mul(16807);
if *seed == 0u32 {
*seed = 1u32;
}
*seed
}
fn InitialEntropyCodes<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors,
IntegerType: Sized + Clone,
>(
data: &[IntegerType],
length: usize,
stride: usize,
num_histograms: usize,
histograms: &mut [HistogramType],
) where
u64: core::convert::From<IntegerType>,
{
let mut seed: u32 = 7u32;
let block_length: usize = length.wrapping_div(num_histograms);
ClearHistograms(histograms, num_histograms);
for i in 0usize..num_histograms {
let mut pos: usize = length.wrapping_mul(i).wrapping_div(num_histograms);
if i != 0usize {
pos = pos.wrapping_add((MyRand(&mut seed) as usize).wrapping_rem(block_length));
}
if pos.wrapping_add(stride) >= length {
pos = length.wrapping_sub(stride).wrapping_sub(1);
}
HistogramAddVector(&mut histograms[i], &data[pos..], stride);
}
}
fn RandomSample<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors,
IntegerType: Sized + Clone,
>(
seed: &mut u32,
data: &[IntegerType],
length: usize,
mut stride: usize,
sample: &mut HistogramType,
) where
u64: core::convert::From<IntegerType>,
{
let pos: usize;
if stride >= length {
pos = 0usize;
stride = length;
} else {
pos = (MyRand(seed) as usize).wrapping_rem(length.wrapping_sub(stride).wrapping_add(1));
}
HistogramAddVector(sample, &data[pos..], stride);
}
fn RefineEntropyCodes<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors + core::default::Default,
IntegerType: Sized + Clone,
>(
data: &[IntegerType],
length: usize,
stride: usize,
num_histograms: usize,
histograms: &mut [HistogramType],
) where
u64: core::convert::From<IntegerType>,
{
let mut iters: usize = kIterMulForRefining
.wrapping_mul(length)
.wrapping_div(stride)
.wrapping_add(kMinItersForRefining);
let mut seed: u32 = 7u32;
iters = iters
.wrapping_add(num_histograms)
.wrapping_sub(1)
.wrapping_div(num_histograms)
.wrapping_mul(num_histograms);
for iter in 0usize..iters {
let mut sample = HistogramType::default();
HistogramClear(&mut sample);
RandomSample(&mut seed, data, length, stride, &mut sample);
HistogramAddHistogram(
&mut histograms[iter.wrapping_rem(num_histograms)],
&mut sample,
);
}
}
fn BitCost(count: usize) -> floatX {
if count == 0usize {
-2.0
} else {
FastLog2(count as u64)
}
}
fn FindBlocks<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors,
IntegerType: Sized + Clone,
>(
data: &[IntegerType],
length: usize,
block_switch_bitcost: floatX,
num_histograms: usize,
histograms: &[HistogramType],
insert_cost: &mut [floatX],
cost: &mut [Mem256f],
switch_signal: &mut [u8],
block_id: &mut [u8],
) -> usize
where
u64: core::convert::From<IntegerType>,
{
if num_histograms == 0 {
return 0;
}
let data_size: usize = histograms[0].slice().len();
let bitmaplen: usize = num_histograms.wrapping_add(7) >> 3;
let mut num_blocks: usize = 1;
let mut i: usize;
if num_histograms <= 1 {
for i in 0usize..length {
block_id[i] = 0u8;
}
return 1;
}
for item in insert_cost[..(data_size * num_histograms)].iter_mut() {
*item = 0.0;
}
for i in 0usize..num_histograms {
insert_cost[i] = FastLog2((histograms[i]).total_count() as u32 as (u64));
}
i = data_size;
while i != 0usize {
i = i.wrapping_sub(1);
for j in 0usize..num_histograms {
insert_cost[i.wrapping_mul(num_histograms).wrapping_add(j)] =
insert_cost[j] - BitCost((histograms[j]).slice()[i] as usize);
}
}
for item in cost.iter_mut() {
*item = Mem256f::default();
}
for item in switch_signal[..(length * bitmaplen)].iter_mut() {
*item = 0;
}
for (byte_ix, data_byte_ix) in data[..length].iter().enumerate() {
let block_id_ptr = &mut block_id[byte_ix];
let ix: usize = byte_ix.wrapping_mul(bitmaplen);
let insert_cost_ix: usize =
u64::from(data_byte_ix.clone()).wrapping_mul(num_histograms as u64) as usize;
let mut min_cost: floatX = 1e38;
let mut block_switch_cost: floatX = block_switch_bitcost;
// main (vectorized) loop
let insert_cost_slice = insert_cost.split_at(insert_cost_ix).1;
for (v_index, cost_iter) in cost
.split_at_mut(num_histograms >> 3)
.0
.iter_mut()
.enumerate()
{
let base_index = v_index << 3;
let mut local_insert_cost = [0.0; 8];
local_insert_cost
.clone_from_slice(insert_cost_slice.split_at(base_index).1.split_at(8).0);
for sub_index in 0usize..8usize {
cost_iter[sub_index] += local_insert_cost[sub_index];
let final_cost = cost_iter[sub_index];
if final_cost < min_cost {
min_cost = final_cost;
*block_id_ptr = (base_index + sub_index) as u8;
}
}
}
let vectorized_offset = ((num_histograms >> 3) << 3);
let mut k = vectorized_offset;
//remainder loop for
for insert_cost_iter in insert_cost
.split_at(insert_cost_ix + vectorized_offset)
.1
.split_at(num_histograms & 7)
.0
.iter()
{
let cost_iter = &mut cost[(k >> 3)];
cost_iter[k & 7] += *insert_cost_iter;
if cost_iter[k & 7] < min_cost {
min_cost = cost_iter[k & 7];
*block_id_ptr = k as u8;
}
k += 1;
}
if byte_ix < 2000usize {
block_switch_cost *= (0.77 + 0.07 * (byte_ix as floatX) / 2000.0);
}
update_cost_and_signal(
num_histograms as u32,
ix,
min_cost,
block_switch_cost,
cost,
switch_signal,
);
}
{
let mut byte_ix: usize = length.wrapping_sub(1);
let mut ix: usize = byte_ix.wrapping_mul(bitmaplen);
let mut cur_id: u8 = block_id[byte_ix];
while byte_ix > 0usize {
let mask: u8 = (1u32 << (cur_id as i32 & 7i32)) as u8;
byte_ix -= 1;
ix = ix.wrapping_sub(bitmaplen);
if switch_signal[ix.wrapping_add((cur_id as i32 >> 3) as usize)] as i32 & mask as i32
!= 0
&& cur_id as i32 != block_id[byte_ix] as i32
{
cur_id = block_id[byte_ix];
num_blocks = num_blocks.wrapping_add(1);
}
block_id[byte_ix] = cur_id;
}
}
num_blocks
}
fn RemapBlockIds(
block_ids: &mut [u8],
length: usize,
new_id: &mut [u16],
num_histograms: usize,
) -> usize {
static kInvalidId: u16 = 256u16;
let mut next_id: u16 = 0u16;
for i in 0usize..num_histograms {
new_id[i] = kInvalidId;
}
for i in 0usize..length {
if new_id[(block_ids[i] as usize)] as i32 == kInvalidId as i32 {
new_id[(block_ids[i] as usize)] = {
let _old = next_id;
next_id = (next_id as i32 + 1) as u16;
_old
};
}
}
for i in 0usize..length {
block_ids[i] = new_id[(block_ids[i] as usize)] as u8;
}
next_id as usize
}
fn BuildBlockHistograms<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors,
IntegerType: Sized + Clone,
>(
data: &[IntegerType],
length: usize,
block_ids: &[u8],
num_histograms: usize,
histograms: &mut [HistogramType],
) where
u64: core::convert::From<IntegerType>,
{
ClearHistograms(histograms, num_histograms);
for i in 0usize..length {
HistogramAddItem(
&mut histograms[(block_ids[i] as usize)],
u64::from(data[i].clone()) as usize,
);
}
}
fn ClusterBlocks<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors + core::default::Default + Clone,
Alloc: alloc::Allocator<u8>
+ alloc::Allocator<u32>
+ alloc::Allocator<HistogramType>
+ alloc::Allocator<HistogramPair>,
IntegerType: Sized + Clone,
>(
alloc: &mut Alloc,
data: &[IntegerType],
length: usize,
num_blocks: usize,
scratch_space: &mut HistogramType::i32vec,
block_ids: &mut [u8],
split: &mut BlockSplit<Alloc>,
) where
u64: core::convert::From<IntegerType>,
{
let mut histogram_symbols = allocate::<u32, _>(alloc, num_blocks);
let mut block_lengths = allocate::<u32, _>(alloc, num_blocks);
let expected_num_clusters: usize = (16usize)
.wrapping_mul(num_blocks.wrapping_add(64).wrapping_sub(1))
.wrapping_div(64);
let mut all_histograms_size: usize = 0usize;
let mut all_histograms_capacity: usize = expected_num_clusters;
let mut all_histograms = allocate::<HistogramType, _>(alloc, all_histograms_capacity);
let mut cluster_size_size: usize = 0usize;
let mut cluster_size_capacity: usize = expected_num_clusters;
let mut cluster_size = allocate::<u32, _>(alloc, cluster_size_capacity);
let mut num_clusters: usize = 0usize;
let mut histograms = allocate::<HistogramType, _>(alloc, min(num_blocks, 64));
let mut max_num_pairs: usize = (64i32 * 64i32 / 2i32) as usize;
let pairs_capacity: usize = max_num_pairs.wrapping_add(1);
let mut pairs = allocate::<HistogramPair, _>(alloc, pairs_capacity);
let mut pos: usize = 0usize;
let mut clusters: <Alloc as Allocator<u32>>::AllocatedMemory;
static kInvalidIndex: u32 = u32::MAX;
let mut i: usize;
let mut sizes: [u32; 64] = [0; 64];
let mut new_clusters: [u32; 64] = [0; 64];
let mut symbols: [u32; 64] = [0; 64];
let mut remap: [u32; 64] = [0; 64];
{
let mut block_idx: usize = 0usize;
i = 0usize;
while i < length {
{
{
let _rhs = 1;
let _lhs = &mut block_lengths.slice_mut()[block_idx];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
if i.wrapping_add(1) == length
|| block_ids[i] as i32 != block_ids[i.wrapping_add(1)] as i32
{
block_idx = block_idx.wrapping_add(1);
}
}
i = i.wrapping_add(1);
}
}
i = 0usize;
while i < num_blocks {
{
let num_to_combine: usize = min(num_blocks.wrapping_sub(i), 64);
for j in 0usize..num_to_combine {
HistogramClear(&mut histograms.slice_mut()[j]);
for _k in 0usize..block_lengths.slice()[i.wrapping_add(j)] as usize {
HistogramAddItem(
&mut histograms.slice_mut()[j],
u64::from(data[pos].clone()) as usize,
);
pos = pos.wrapping_add(1);
}
let new_cost = BrotliPopulationCost(&histograms.slice()[j], scratch_space);
(histograms.slice_mut()[j]).set_bit_cost(new_cost);
new_clusters[j] = j as u32;
symbols[j] = j as u32;
sizes[j] = 1u32;
}
let num_new_clusters: usize = BrotliHistogramCombine(
histograms.slice_mut(),
&mut sizes[..],
&mut symbols[..],
&mut new_clusters[..],
pairs.slice_mut(),
num_to_combine,
num_to_combine,
64usize,
max_num_pairs,
scratch_space,
);
{
if all_histograms_capacity < all_histograms_size.wrapping_add(num_new_clusters) {
let mut _new_size: usize = if all_histograms_capacity == 0usize {
all_histograms_size.wrapping_add(num_new_clusters)
} else {
all_histograms_capacity
};
while _new_size < all_histograms_size.wrapping_add(num_new_clusters) {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<HistogramType, _>(alloc, _new_size);
new_array.slice_mut()[..all_histograms_capacity]
.clone_from_slice(&all_histograms.slice()[..all_histograms_capacity]);
<Alloc as Allocator<HistogramType>>::free_cell(
alloc,
core::mem::replace(&mut all_histograms, new_array),
);
all_histograms_capacity = _new_size;
}
}
{
if cluster_size_capacity < cluster_size_size.wrapping_add(num_new_clusters) {
let mut _new_size: usize = if cluster_size_capacity == 0usize {
cluster_size_size.wrapping_add(num_new_clusters)
} else {
cluster_size_capacity
};
while _new_size < cluster_size_size.wrapping_add(num_new_clusters) {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<u32, _>(alloc, _new_size);
new_array.slice_mut()[..cluster_size_capacity]
.clone_from_slice(&cluster_size.slice()[..cluster_size_capacity]);
<Alloc as Allocator<u32>>::free_cell(
alloc,
core::mem::replace(&mut cluster_size, new_array),
);
cluster_size_capacity = _new_size;
}
}
for j in 0usize..num_new_clusters {
all_histograms.slice_mut()[all_histograms_size] =
histograms.slice()[new_clusters[j] as usize].clone();
all_histograms_size = all_histograms_size.wrapping_add(1);
cluster_size.slice_mut()[cluster_size_size] = sizes[new_clusters[j] as usize];
cluster_size_size = cluster_size_size.wrapping_add(1);
remap[new_clusters[j] as usize] = j as u32;
}
for j in 0usize..num_to_combine {
histogram_symbols.slice_mut()[i.wrapping_add(j)] =
(num_clusters as u32).wrapping_add(remap[symbols[j] as usize]);
}
num_clusters = num_clusters.wrapping_add(num_new_clusters);
}
i = i.wrapping_add(64);
}
<Alloc as Allocator<HistogramType>>::free_cell(alloc, core::mem::take(&mut histograms));
max_num_pairs = min(
(64usize).wrapping_mul(num_clusters),
num_clusters.wrapping_div(2).wrapping_mul(num_clusters),
);
if pairs_capacity < max_num_pairs.wrapping_add(1) {
let new_cell = allocate::<HistogramPair, _>(alloc, max_num_pairs.wrapping_add(1));
<Alloc as Allocator<HistogramPair>>::free_cell(
alloc,
core::mem::replace(&mut pairs, new_cell),
);
}
clusters = allocate::<u32, _>(alloc, num_clusters);
i = 0usize;
for item in clusters.slice_mut()[..num_clusters].iter_mut() {
*item = i as u32;
i = i.wrapping_add(1);
}
let num_final_clusters: usize = BrotliHistogramCombine(
all_histograms.slice_mut(),
cluster_size.slice_mut(),
histogram_symbols.slice_mut(),
clusters.slice_mut(),
pairs.slice_mut(),
num_clusters,
num_blocks,
256usize,
max_num_pairs,
scratch_space,
);
<Alloc as Allocator<HistogramPair>>::free_cell(alloc, core::mem::take(&mut pairs));
<Alloc as Allocator<u32>>::free_cell(alloc, core::mem::take(&mut cluster_size));
let mut new_index = allocate::<u32, _>(alloc, num_clusters);
for item in new_index.slice_mut().iter_mut() {
*item = kInvalidIndex;
}
pos = 0usize;
{
let mut next_index: u32 = 0u32;
for i in 0usize..num_blocks {
let mut histo: HistogramType = HistogramType::default();
let mut best_out: u32;
let mut best_bits: floatX;
HistogramClear(&mut histo);
for _j in 0usize..block_lengths.slice()[i] as usize {
HistogramAddItem(&mut histo, u64::from(data[pos].clone()) as usize);
pos = pos.wrapping_add(1);
}
best_out = if i == 0usize {
histogram_symbols.slice()[0]
} else {
histogram_symbols.slice()[i.wrapping_sub(1)]
};
best_bits = BrotliHistogramBitCostDistance(
&mut histo,
&mut all_histograms.slice_mut()[(best_out as usize)],
scratch_space,
);
for j in 0usize..num_final_clusters {
let cur_bits: floatX = BrotliHistogramBitCostDistance(
&mut histo,
&mut all_histograms.slice_mut()[(clusters.slice()[j] as usize)],
scratch_space,
);
if cur_bits < best_bits {
best_bits = cur_bits;
best_out = clusters.slice()[j];
}
}
histogram_symbols.slice_mut()[i] = best_out;
if new_index.slice()[best_out as usize] == kInvalidIndex {
new_index.slice_mut()[best_out as usize] = next_index;
next_index = next_index.wrapping_add(1);
}
}
}
<Alloc as Allocator<u32>>::free_cell(alloc, core::mem::take(&mut clusters));
<Alloc as Allocator<HistogramType>>::free_cell(alloc, core::mem::take(&mut all_histograms));
{
if split.types_alloc_size() < num_blocks {
let mut _new_size: usize = if split.types_alloc_size() == 0usize {
num_blocks
} else {
split.types_alloc_size()
};
while _new_size < num_blocks {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<u8, _>(alloc, _new_size);
new_array.slice_mut()[..split.types_alloc_size()]
.clone_from_slice(&split.types.slice()[..split.types_alloc_size()]);
<Alloc as Allocator<u8>>::free_cell(
alloc,
core::mem::replace(&mut split.types, new_array),
);
}
}
{
if split.lengths_alloc_size() < num_blocks {
let mut _new_size: usize = if split.lengths_alloc_size() == 0usize {
num_blocks
} else {
split.lengths_alloc_size()
};
while _new_size < num_blocks {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<u32, _>(alloc, _new_size);
new_array.slice_mut()[..split.lengths_alloc_size()]
.clone_from_slice(split.lengths.slice());
<Alloc as Allocator<u32>>::free_cell(
alloc,
core::mem::replace(&mut split.lengths, new_array),
);
}
}
{
let mut cur_length: u32 = 0u32;
let mut block_idx: usize = 0usize;
let mut max_type: u8 = 0u8;
for i in 0usize..num_blocks {
cur_length = cur_length.wrapping_add(block_lengths.slice()[i]);
if i.wrapping_add(1) == num_blocks
|| histogram_symbols.slice()[i] != histogram_symbols.slice()[i.wrapping_add(1)]
{
let id: u8 = new_index.slice()[(histogram_symbols.slice()[i] as usize)] as u8;
split.types.slice_mut()[block_idx] = id;
split.lengths.slice_mut()[block_idx] = cur_length;
max_type = max(max_type, id);
cur_length = 0u32;
block_idx = block_idx.wrapping_add(1);
}
}
split.num_blocks = block_idx;
split.num_types = (max_type as usize).wrapping_add(1);
}
<Alloc as Allocator<u32>>::free_cell(alloc, new_index);
<Alloc as Allocator<u32>>::free_cell(alloc, block_lengths);
<Alloc as Allocator<u32>>::free_cell(alloc, histogram_symbols);
}
fn SplitByteVector<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors + core::default::Default + Clone,
Alloc: alloc::Allocator<u8>
+ alloc::Allocator<u16>
+ alloc::Allocator<u32>
+ alloc::Allocator<floatX>
+ alloc::Allocator<Mem256f>
+ alloc::Allocator<HistogramType>
+ alloc::Allocator<HistogramPair>,
IntegerType: Sized + Clone,
>(
alloc: &mut Alloc,
data: &[IntegerType],
length: usize,
literals_per_histogram: usize,
max_histograms: usize,
sampling_stride_length: usize,
block_switch_cost: floatX,
params: &BrotliEncoderParams,
scratch_space: &mut HistogramType::i32vec,
split: &mut BlockSplit<Alloc>,
) where
u64: core::convert::From<IntegerType>,
{
let data_size: usize = HistogramType::default().slice().len();
let mut num_histograms: usize = length.wrapping_div(literals_per_histogram).wrapping_add(1);
if num_histograms > max_histograms {
num_histograms = max_histograms;
}
if length == 0usize {
split.num_types = 1;
return;
} else if length < kMinLengthForBlockSplitting {
{
if split.types_alloc_size() < split.num_blocks.wrapping_add(1) {
let mut _new_size: usize = if split.types_alloc_size() == 0usize {
split.num_blocks.wrapping_add(1)
} else {
split.types_alloc_size()
};
while _new_size < split.num_blocks.wrapping_add(1) {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<u8, _>(alloc, _new_size);
new_array.slice_mut()[..split.types_alloc_size()]
.clone_from_slice(&split.types.slice()[..split.types_alloc_size()]);
<Alloc as Allocator<u8>>::free_cell(
alloc,
core::mem::replace(&mut split.types, new_array),
);
}
}
{
if split.lengths_alloc_size() < split.num_blocks.wrapping_add(1) {
let mut _new_size: usize = if split.lengths_alloc_size() == 0usize {
split.num_blocks.wrapping_add(1)
} else {
split.lengths_alloc_size()
};
while _new_size < split.num_blocks.wrapping_add(1) {
_new_size = _new_size.wrapping_mul(2);
}
let mut new_array = allocate::<u32, _>(alloc, _new_size);
new_array.slice_mut()[..split.lengths_alloc_size()]
.clone_from_slice(&split.lengths.slice()[..split.lengths_alloc_size()]);
<Alloc as Allocator<u32>>::free_cell(
alloc,
core::mem::replace(&mut split.lengths, new_array),
);
}
}
split.num_types = 1;
split.types.slice_mut()[split.num_blocks] = 0u8;
split.lengths.slice_mut()[split.num_blocks] = length as u32;
split.num_blocks = split.num_blocks.wrapping_add(1);
return;
}
let mut histograms = allocate::<HistogramType, _>(alloc, num_histograms);
InitialEntropyCodes(
data,
length,
sampling_stride_length,
num_histograms,
histograms.slice_mut(),
);
RefineEntropyCodes(
data,
length,
sampling_stride_length,
num_histograms,
histograms.slice_mut(),
);
{
let mut block_ids = allocate::<u8, _>(alloc, length);
let mut num_blocks: usize = 0usize;
let bitmaplen: usize = num_histograms.wrapping_add(7) >> 3;
let mut insert_cost = allocate::<floatX, _>(alloc, data_size.wrapping_mul(num_histograms));
let mut cost = allocate::<Mem256f, _>(alloc, ((num_histograms + 7) >> 3));
let mut switch_signal = allocate::<u8, _>(alloc, length.wrapping_mul(bitmaplen));
let mut new_id = allocate::<u16, _>(alloc, num_histograms);
let iters: usize = (if params.quality <= 11 { 3i32 } else { 10i32 }) as usize;
for _i in 0usize..iters {
num_blocks = FindBlocks(
data,
length,
block_switch_cost,
num_histograms,
histograms.slice_mut(),
insert_cost.slice_mut(),
cost.slice_mut(),
switch_signal.slice_mut(),
block_ids.slice_mut(),
);
num_histograms = RemapBlockIds(
block_ids.slice_mut(),
length,
new_id.slice_mut(),
num_histograms,
);
BuildBlockHistograms(
data,
length,
block_ids.slice(),
num_histograms,
histograms.slice_mut(),
);
}
<Alloc as Allocator<floatX>>::free_cell(alloc, insert_cost);
<Alloc as Allocator<Mem256f>>::free_cell(alloc, cost);
<Alloc as Allocator<u8>>::free_cell(alloc, switch_signal);
<Alloc as Allocator<u16>>::free_cell(alloc, new_id);
<Alloc as Allocator<HistogramType>>::free_cell(alloc, histograms);
ClusterBlocks::<HistogramType, Alloc, IntegerType>(
alloc,
data,
length,
num_blocks,
scratch_space,
block_ids.slice_mut(),
split,
);
<Alloc as Allocator<u8>>::free_cell(alloc, block_ids);
}
}
pub fn BrotliSplitBlock<
Alloc: alloc::Allocator<u8>
+ alloc::Allocator<u16>
+ alloc::Allocator<u32>
+ alloc::Allocator<floatX>
+ alloc::Allocator<Mem256f>
+ alloc::Allocator<HistogramLiteral>
+ alloc::Allocator<HistogramCommand>
+ alloc::Allocator<HistogramDistance>
+ alloc::Allocator<HistogramPair>,
>(
alloc: &mut Alloc,
cmds: &[Command],
num_commands: usize,
data: &[u8],
pos: usize,
mask: usize,
params: &BrotliEncoderParams,
lit_scratch_space: &mut <HistogramLiteral as CostAccessors>::i32vec,
cmd_scratch_space: &mut <HistogramCommand as CostAccessors>::i32vec,
dst_scratch_space: &mut <HistogramDistance as CostAccessors>::i32vec,
literal_split: &mut BlockSplit<Alloc>,
insert_and_copy_split: &mut BlockSplit<Alloc>,
dist_split: &mut BlockSplit<Alloc>,
) {
{
/*for (i, cmd) in cmds[..num_commands].iter().enumerate() {
println_stderr!("C {:} {:} {:} {:} {:} {:}",
i, cmd.insert_len_, cmd.copy_len_, cmd.dist_extra_, cmd.cmd_prefix_, cmd.dist_prefix_);
}*/
let literals_count: usize = CountLiterals(cmds, num_commands);
let mut literals = allocate::<u8, _>(alloc, literals_count);
CopyLiteralsToByteArray(cmds, num_commands, data, pos, mask, literals.slice_mut());
SplitByteVector::<HistogramLiteral, Alloc, u8>(
alloc,
literals.slice(),
literals_count,
kSymbolsPerLiteralHistogram,
kMaxLiteralHistograms,
kLiteralStrideLength,
kLiteralBlockSwitchCost,
params,
lit_scratch_space,
literal_split,
);
<Alloc as Allocator<u8>>::free_cell(alloc, literals);
}
{
let mut insert_and_copy_codes = allocate::<u16, _>(alloc, num_commands);
for i in 0..min(num_commands, cmds.len()) {
insert_and_copy_codes.slice_mut()[i] = (cmds[i]).cmd_prefix_;
}
SplitByteVector::<HistogramCommand, Alloc, u16>(
alloc,
insert_and_copy_codes.slice(),
num_commands,
kSymbolsPerCommandHistogram,
kMaxCommandHistograms,
kCommandStrideLength,
kCommandBlockSwitchCost,
params,
cmd_scratch_space,
insert_and_copy_split,
);
<Alloc as Allocator<u16>>::free_cell(alloc, insert_and_copy_codes);
}
{
let mut distance_prefixes = allocate::<u16, _>(alloc, num_commands);
let mut j: usize = 0usize;
for i in 0usize..num_commands {
let cmd = &cmds[i];
if cmd.copy_len() != 0 && cmd.cmd_prefix_ >= 128 {
distance_prefixes.slice_mut()[j] = cmd.dist_prefix_ & 0x03ff;
j = j.wrapping_add(1);
}
}
SplitByteVector::<HistogramDistance, Alloc, u16>(
alloc,
distance_prefixes.slice(),
j,
kSymbolsPerDistanceHistogram,
kMaxCommandHistograms,
kCommandStrideLength,
kDistanceBlockSwitchCost,
params,
dst_scratch_space,
dist_split,
);
<Alloc as Allocator<u16>>::free_cell(alloc, distance_prefixes);
}
}
File diff suppressed because it is too large Load Diff
+467
View File
@@ -0,0 +1,467 @@
use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core::cmp::min;
use {alloc, core};
use super::bit_cost::BrotliPopulationCost;
use super::histogram::{
CostAccessors, HistogramAddHistogram, HistogramClear, HistogramSelfAddHistogram,
};
use super::util::FastLog2;
use crate::enc::combined_alloc::{alloc_or_default, allocate};
#[derive(Clone, Copy)]
pub struct HistogramPair {
pub idx1: u32,
pub idx2: u32,
pub cost_combo: super::util::floatX,
pub cost_diff: super::util::floatX,
}
impl Default for HistogramPair {
#[inline(always)]
fn default() -> HistogramPair {
HistogramPair {
idx1: 0,
idx2: 0,
cost_combo: 0.0,
cost_diff: 0.0,
}
}
}
/* Returns entropy reduction of the context map when we combine two clusters. */
#[inline(always)]
fn ClusterCostDiff(size_a: usize, size_b: usize) -> super::util::floatX {
let size_c: usize = size_a.wrapping_add(size_b);
size_a as (super::util::floatX) * FastLog2(size_a as u64)
+ size_b as (super::util::floatX) * FastLog2(size_b as u64)
- size_c as (super::util::floatX) * FastLog2(size_c as u64)
}
#[inline(always)]
fn HistogramPairIsLess(p1: &HistogramPair, p2: &HistogramPair) -> bool {
if p1.cost_diff != p2.cost_diff {
p1.cost_diff > p2.cost_diff
} else {
p1.idx2.wrapping_sub(p1.idx1) > p2.idx2.wrapping_sub(p2.idx1)
}
}
/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if
it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */
fn BrotliCompareAndPushToQueue<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
>(
out: &[HistogramType],
cluster_size: &[u32],
mut idx1: u32,
mut idx2: u32,
max_num_pairs: usize,
scratch_space: &mut HistogramType::i32vec,
pairs: &mut [HistogramPair],
num_pairs: &mut usize,
) {
let mut is_good_pair = false;
let mut p: HistogramPair = HistogramPair {
idx1: 0,
idx2: 0,
cost_combo: 0.0,
cost_diff: 0.0,
};
if idx1 == idx2 {
} else {
if idx2 < idx1 {
core::mem::swap(&mut idx2, &mut idx1);
}
p.idx1 = idx1;
p.idx2 = idx2;
p.cost_diff = 0.5
* ClusterCostDiff(
cluster_size[idx1 as usize] as usize,
cluster_size[idx2 as usize] as usize,
);
p.cost_diff -= (out[idx1 as usize]).bit_cost();
p.cost_diff -= (out[idx2 as usize]).bit_cost();
if (out[idx1 as usize]).total_count() == 0usize {
p.cost_combo = (out[idx2 as usize]).bit_cost();
is_good_pair = true;
} else if (out[idx2 as usize]).total_count() == 0usize {
p.cost_combo = (out[idx1 as usize]).bit_cost();
is_good_pair = true;
} else {
let threshold = if *num_pairs == 0 {
1e38
} else {
pairs[0].cost_diff.max(0.0)
};
let mut combo: HistogramType = out[idx1 as usize].clone();
HistogramAddHistogram(&mut combo, &out[idx2 as usize]);
let cost_combo: super::util::floatX = BrotliPopulationCost(&combo, scratch_space);
if cost_combo < threshold - p.cost_diff {
p.cost_combo = cost_combo;
is_good_pair = true;
}
}
if is_good_pair {
p.cost_diff += p.cost_combo;
if *num_pairs > 0usize && HistogramPairIsLess(&pairs[0], &p) {
/* Replace the top of the queue if needed. */
if *num_pairs < max_num_pairs {
pairs[*num_pairs] = pairs[0];
*num_pairs = num_pairs.wrapping_add(1);
}
pairs[0] = p;
} else if *num_pairs < max_num_pairs {
pairs[*num_pairs] = p;
*num_pairs = num_pairs.wrapping_add(1);
}
}
}
}
pub fn BrotliHistogramCombine<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
>(
out: &mut [HistogramType],
cluster_size: &mut [u32],
symbols: &mut [u32],
clusters: &mut [u32],
pairs: &mut [HistogramPair],
mut num_clusters: usize,
symbols_size: usize,
max_clusters: usize,
max_num_pairs: usize,
scratch_space: &mut HistogramType::i32vec,
) -> usize {
let mut cost_diff_threshold: super::util::floatX = 0.0;
let mut min_cluster_size: usize = 1;
let mut num_pairs: usize = 0usize;
{
/* We maintain a vector of histogram pairs, with the property that the pair
with the maximum bit cost reduction is the first. */
for idx1 in 0..num_clusters {
for idx2 in idx1 + 1..num_clusters {
BrotliCompareAndPushToQueue(
out,
cluster_size,
clusters[idx1],
clusters[idx2],
max_num_pairs,
scratch_space,
pairs,
&mut num_pairs,
);
}
}
}
while num_clusters > min_cluster_size {
let mut i: usize;
if (pairs[0]).cost_diff >= cost_diff_threshold {
cost_diff_threshold = 1e38;
min_cluster_size = max_clusters;
{
continue;
}
}
/* Take the best pair from the top of heap. */
let best_idx1: u32 = (pairs[0]).idx1;
let best_idx2: u32 = (pairs[0]).idx2;
HistogramSelfAddHistogram(out, (best_idx1 as usize), (best_idx2 as usize));
(out[(best_idx1 as usize)]).set_bit_cost((pairs[0]).cost_combo);
{
let _rhs = cluster_size[(best_idx2 as usize)];
let _lhs = &mut cluster_size[(best_idx1 as usize)];
*_lhs = (*_lhs).wrapping_add(_rhs);
}
for i in 0usize..symbols_size {
if symbols[i] == best_idx2 {
symbols[i] = best_idx1;
}
}
i = 0usize;
'break9: while i < num_clusters {
{
if clusters[i] == best_idx2 {
for offset in 0..(num_clusters - i - 1) {
clusters[i + offset] = clusters[i + 1 + offset];
}
break 'break9;
}
}
i = i.wrapping_add(1);
}
num_clusters = num_clusters.wrapping_sub(1);
{
/* Remove pairs intersecting the just combined best pair. */
let mut copy_to_idx: usize = 0usize;
i = 0usize;
while i < num_pairs {
'continue12: loop {
{
let p: HistogramPair = pairs[i];
if (p).idx1 == best_idx1
|| (p).idx2 == best_idx1
|| (p).idx1 == best_idx2
|| (p).idx2 == best_idx2
{
/* Remove invalid pair from the queue. */
break 'continue12;
}
if HistogramPairIsLess(&pairs[0], &p) {
/* Replace the top of the queue if needed. */
let front: HistogramPair = pairs[0];
pairs[0] = p;
pairs[copy_to_idx] = front;
} else {
pairs[copy_to_idx] = p;
}
copy_to_idx = copy_to_idx.wrapping_add(1);
}
break;
}
i = i.wrapping_add(1);
}
num_pairs = copy_to_idx;
}
for i in 0usize..num_clusters {
BrotliCompareAndPushToQueue(
out,
cluster_size,
best_idx1,
clusters[i],
max_num_pairs,
scratch_space,
pairs,
&mut num_pairs,
);
}
}
num_clusters
}
/* What is the bit cost of moving histogram from cur_symbol to candidate. */
#[inline(always)]
pub fn BrotliHistogramBitCostDistance<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
>(
histogram: &HistogramType,
candidate: &HistogramType,
scratch_space: &mut HistogramType::i32vec,
) -> super::util::floatX {
if histogram.total_count() == 0usize {
0.0
} else {
let mut tmp: HistogramType = histogram.clone();
HistogramAddHistogram(&mut tmp, candidate);
BrotliPopulationCost(&tmp, scratch_space) - candidate.bit_cost()
}
}
/* Find the best 'out' histogram for each of the 'in' histograms.
When called, clusters[0..num_clusters) contains the unique values from
symbols[0..in_size), but this property is not preserved in this function.
Note: we assume that out[]->bit_cost_ is already up-to-date. */
pub fn BrotliHistogramRemap<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
>(
inp: &[HistogramType],
in_size: usize,
clusters: &[u32],
num_clusters: usize,
scratch_space: &mut HistogramType::i32vec,
out: &mut [HistogramType],
symbols: &mut [u32],
) {
for i in 0usize..in_size {
let mut best_out: u32 = if i == 0usize {
symbols[0]
} else {
symbols[i.wrapping_sub(1)]
};
let mut best_bits: super::util::floatX =
BrotliHistogramBitCostDistance(&inp[i], &mut out[(best_out as usize)], scratch_space);
for j in 0usize..num_clusters {
let cur_bits: super::util::floatX = BrotliHistogramBitCostDistance(
&inp[i],
&mut out[(clusters[j] as usize)],
scratch_space,
);
if cur_bits < best_bits {
best_bits = cur_bits;
best_out = clusters[j];
}
}
symbols[i] = best_out;
}
for i in 0usize..num_clusters {
HistogramClear(&mut out[(clusters[i] as usize)]);
}
for i in 0usize..in_size {
HistogramAddHistogram(&mut out[(symbols[i] as usize)], &inp[i]);
}
}
/* Reorders elements of the out[0..length) array and changes values in
symbols[0..length) array in the following way:
* when called, symbols[] contains indexes into out[], and has N unique
values (possibly N < length)
* on return, symbols'[i] = f(symbols[i]) and
out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length,
where f is a bijection between the range of symbols[] and [0..N), and
the first occurrences of values in symbols'[i] come in consecutive
increasing order.
Returns N, the number of unique values in symbols[]. */
pub fn BrotliHistogramReindex<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
Alloc: alloc::Allocator<u32> + alloc::Allocator<HistogramType>,
>(
alloc: &mut Alloc,
out: &mut [HistogramType],
symbols: &mut [u32],
length: usize,
) -> usize {
static kInvalidIndex: u32 = u32::MAX;
let mut new_index = alloc_or_default::<u32, _>(alloc, length);
let mut next_index: u32;
let mut tmp: <Alloc as Allocator<HistogramType>>::AllocatedMemory;
for i in 0usize..length {
new_index.slice_mut()[i] = kInvalidIndex;
}
next_index = 0u32;
for i in 0usize..length {
if new_index.slice()[(symbols[i] as usize)] == kInvalidIndex {
new_index.slice_mut()[(symbols[i] as usize)] = next_index;
next_index = next_index.wrapping_add(1);
}
}
tmp = alloc_or_default::<HistogramType, _>(alloc, next_index as usize);
next_index = 0u32;
for i in 0usize..length {
if new_index.slice()[(symbols[i] as usize)] == next_index {
tmp.slice_mut()[(next_index as usize)] = out[(symbols[i] as usize)].clone();
next_index = next_index.wrapping_add(1);
}
symbols[i] = new_index.slice()[(symbols[i] as usize)];
}
{
<Alloc as Allocator<u32>>::free_cell(alloc, new_index);
}
for i in 0usize..next_index as usize {
out[i] = tmp.slice()[i].clone();
}
{
<Alloc as Allocator<HistogramType>>::free_cell(alloc, tmp)
}
next_index as usize
}
pub fn BrotliClusterHistograms<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors + Clone,
Alloc: alloc::Allocator<u32> + alloc::Allocator<HistogramPair> + alloc::Allocator<HistogramType>,
>(
alloc: &mut Alloc,
inp: &[HistogramType],
in_size: usize,
max_histograms: usize,
scratch_space: &mut HistogramType::i32vec,
out: &mut [HistogramType],
out_size: &mut usize,
histogram_symbols: &mut [u32],
) {
let mut cluster_size = alloc_or_default::<u32, Alloc>(alloc, in_size);
let mut clusters = alloc_or_default::<u32, Alloc>(alloc, in_size);
let mut num_clusters: usize = 0usize;
let max_input_histograms: usize = 64usize;
let pairs_capacity: usize = max_input_histograms
.wrapping_mul(max_input_histograms)
.wrapping_div(2);
let mut pairs = allocate::<HistogramPair, _>(alloc, pairs_capacity.wrapping_add(1));
let mut i: usize;
for i in 0usize..in_size {
cluster_size.slice_mut()[i] = 1u32;
}
for i in 0usize..in_size {
out[i] = inp[i].clone();
(out[i]).set_bit_cost(BrotliPopulationCost(&inp[i], scratch_space));
histogram_symbols[i] = i as u32;
}
i = 0usize;
while i < in_size {
{
let num_to_combine: usize = min(in_size.wrapping_sub(i), max_input_histograms);
for j in 0usize..num_to_combine {
clusters.slice_mut()[num_clusters.wrapping_add(j)] = i.wrapping_add(j) as u32;
}
let num_new_clusters: usize = BrotliHistogramCombine(
out,
cluster_size.slice_mut(),
&mut histogram_symbols[i..],
&mut clusters.slice_mut()[num_clusters..],
pairs.slice_mut(),
num_to_combine,
num_to_combine,
max_histograms,
pairs_capacity,
scratch_space,
);
num_clusters = num_clusters.wrapping_add(num_new_clusters);
}
i = i.wrapping_add(max_input_histograms);
}
{
let max_num_pairs: usize = min(
(64usize).wrapping_mul(num_clusters),
num_clusters.wrapping_div(2).wrapping_mul(num_clusters),
);
{
if pairs_capacity < max_num_pairs.wrapping_add(1) {
let mut _new_size: usize = if pairs_capacity == 0usize {
max_num_pairs.wrapping_add(1)
} else {
pairs_capacity
};
let mut new_array: <Alloc as Allocator<HistogramPair>>::AllocatedMemory;
while _new_size < max_num_pairs.wrapping_add(1) {
_new_size = _new_size.wrapping_mul(2);
}
new_array = alloc_or_default::<HistogramPair, _>(alloc, _new_size);
new_array.slice_mut()[..pairs_capacity]
.clone_from_slice(&pairs.slice()[..pairs_capacity]);
<Alloc as Allocator<HistogramPair>>::free_cell(
alloc,
core::mem::replace(&mut pairs, new_array),
);
}
}
num_clusters = BrotliHistogramCombine(
out,
cluster_size.slice_mut(),
histogram_symbols,
clusters.slice_mut(),
pairs.slice_mut(),
num_clusters,
in_size,
max_histograms,
max_num_pairs,
scratch_space,
);
}
<Alloc as Allocator<HistogramPair>>::free_cell(alloc, pairs);
<Alloc as Allocator<u32>>::free_cell(alloc, cluster_size);
BrotliHistogramRemap(
inp,
in_size,
clusters.slice(),
num_clusters,
scratch_space,
out,
histogram_symbols,
);
<Alloc as Allocator<u32>>::free_cell(alloc, clusters);
*out_size = BrotliHistogramReindex(alloc, out, histogram_symbols, in_size);
}
/////////// DONE //////////////////////////
+552
View File
@@ -0,0 +1,552 @@
pub use alloc::Allocator;
#[cfg(feature = "std")]
use alloc_stdlib::StandardAlloc;
use super::cluster::HistogramPair;
use super::command::Command;
use super::entropy_encode::HuffmanTree;
use super::hash_to_binary_tree::ZopfliNode;
use super::histogram::{ContextType, HistogramCommand, HistogramDistance, HistogramLiteral};
use super::interface::StaticCommand;
use super::util::floatX;
use super::{s16, v8, PDF};
/*
struct CombiningAllocator<T1, T2, AllocT1:Allocator<T1>, AllocT2:Allocator<T2>>(AllocT1, AllocT2);
impl <T1, T2, AllocT1:Allocator<T1>, AllocT2:Allocator<T2>> CombiningAllocator<T1, T2, AllocT1, AllocT2> {
pub fn new(a: AllocT1, b: AllocT2) -> Self {
CombiningAllocator(a, b)
}
}
impl <T1, T2, AllocT1:Allocator<T1>, AllocT2:Allocator<T2>> Allocator<T1> for CombiningAllocator<T1, T2, AllocT1, AllocT2> {
}
impl <T1, T2, AllocT1:Allocator<T1>, AllocT2:Allocator<T2>> Allocator<T2> for CombiningAllocator<T1, T2, AllocT1, AllocT2> {
}
*/
pub trait BrotliAlloc:
Allocator<u8>
+ Allocator<u16>
+ Allocator<i32>
+ Allocator<u32>
+ Allocator<u64>
+ Allocator<Command>
+ Allocator<super::util::floatX>
+ Allocator<v8>
+ Allocator<s16>
+ Allocator<PDF>
+ Allocator<StaticCommand>
+ Allocator<HistogramLiteral>
+ Allocator<HistogramCommand>
+ Allocator<HistogramDistance>
+ Allocator<HistogramPair>
+ Allocator<ContextType>
+ Allocator<HuffmanTree>
+ Allocator<ZopfliNode>
{
}
#[cfg(feature = "std")]
impl BrotliAlloc for StandardAlloc {}
pub struct CombiningAllocator<
AllocU8: Allocator<u8>,
AllocU16: Allocator<u16>,
AllocI32: Allocator<i32>,
AllocU32: Allocator<u32>,
AllocU64: Allocator<u64>,
AllocCommand: Allocator<Command>,
AllocFloatX: Allocator<floatX>,
AllocV8: Allocator<v8>,
AllocS16: Allocator<s16>,
AllocPDF: Allocator<PDF>,
AllocStaticCommand: Allocator<StaticCommand>,
AllocHistogramLiteral: Allocator<HistogramLiteral>,
AllocHistogramCommand: Allocator<HistogramCommand>,
AllocHistogramDistance: Allocator<HistogramDistance>,
AllocHistogramPair: Allocator<HistogramPair>,
AllocContextType: Allocator<ContextType>,
AllocHuffmanTree: Allocator<HuffmanTree>,
AllocZopfliNode: Allocator<ZopfliNode>,
> {
alloc_u8: AllocU8,
alloc_u16: AllocU16,
alloc_i32: AllocI32,
alloc_u32: AllocU32,
alloc_u64: AllocU64,
alloc_c: AllocCommand,
alloc_f: AllocFloatX,
alloc_f32x8: AllocV8,
alloc_i16x16: AllocS16,
alloc_pdf: AllocPDF,
alloc_sc: AllocStaticCommand,
alloc_hl: AllocHistogramLiteral,
alloc_hc: AllocHistogramCommand,
alloc_hd: AllocHistogramDistance,
alloc_hp: AllocHistogramPair,
alloc_ct: AllocContextType,
alloc_ht: AllocHuffmanTree,
alloc_zn: AllocZopfliNode,
}
impl<
AllocU8: Allocator<u8>,
AllocU16: Allocator<u16>,
AllocI32: Allocator<i32>,
AllocU32: Allocator<u32>,
AllocU64: Allocator<u64>,
AllocCommand: Allocator<Command>,
AllocFloatX: Allocator<floatX>,
AllocV8: Allocator<v8>,
AllocS16: Allocator<s16>,
AllocPDF: Allocator<PDF>,
AllocStaticCommand: Allocator<StaticCommand>,
AllocHistogramLiteral: Allocator<HistogramLiteral>,
AllocHistogramCommand: Allocator<HistogramCommand>,
AllocHistogramDistance: Allocator<HistogramDistance>,
AllocHistogramPair: Allocator<HistogramPair>,
AllocContextType: Allocator<ContextType>,
AllocHuffmanTree: Allocator<HuffmanTree>,
AllocZopfliNode: Allocator<ZopfliNode>,
>
CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
pub fn new(
alloc_u8: AllocU8,
alloc_u16: AllocU16,
alloc_i32: AllocI32,
alloc_u32: AllocU32,
alloc_u64: AllocU64,
alloc_c: AllocCommand,
alloc_f: AllocFloatX,
alloc_f32x8: AllocV8,
alloc_i16x16: AllocS16,
alloc_pdf: AllocPDF,
alloc_sc: AllocStaticCommand,
alloc_hl: AllocHistogramLiteral,
alloc_hc: AllocHistogramCommand,
alloc_hd: AllocHistogramDistance,
alloc_hp: AllocHistogramPair,
alloc_ct: AllocContextType,
alloc_ht: AllocHuffmanTree,
alloc_zn: AllocZopfliNode,
) -> Self {
CombiningAllocator {
alloc_u8,
alloc_u16,
alloc_i32,
alloc_u32,
alloc_u64,
alloc_c,
alloc_f,
alloc_f32x8,
alloc_i16x16,
alloc_pdf,
alloc_sc,
alloc_hl,
alloc_hc,
alloc_hd,
alloc_hp,
alloc_ct,
alloc_ht,
alloc_zn,
}
}
}
impl<
AllocU8: Allocator<u8>,
AllocU16: Allocator<u16>,
AllocI32: Allocator<i32>,
AllocU32: Allocator<u32>,
AllocU64: Allocator<u64>,
AllocCommand: Allocator<Command>,
AllocFloatX: Allocator<floatX>,
AllocV8: Allocator<v8>,
AllocS16: Allocator<s16>,
AllocPDF: Allocator<PDF>,
AllocStaticCommand: Allocator<StaticCommand>,
AllocHistogramLiteral: Allocator<HistogramLiteral>,
AllocHistogramCommand: Allocator<HistogramCommand>,
AllocHistogramDistance: Allocator<HistogramDistance>,
AllocHistogramPair: Allocator<HistogramPair>,
AllocContextType: Allocator<ContextType>,
AllocHuffmanTree: Allocator<HuffmanTree>,
AllocZopfliNode: Allocator<ZopfliNode>,
> BrotliAlloc
for CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
}
impl<
AllocU8: Allocator<u8> + Default,
AllocU16: Allocator<u16> + Default,
AllocI32: Allocator<i32> + Default,
AllocU32: Allocator<u32> + Default,
AllocU64: Allocator<u64> + Default,
AllocCommand: Allocator<Command> + Default,
AllocFloatX: Allocator<floatX> + Default,
AllocV8: Allocator<v8> + Default,
AllocS16: Allocator<s16> + Default,
AllocPDF: Allocator<PDF> + Default,
AllocStaticCommand: Allocator<StaticCommand> + Default,
AllocHistogramLiteral: Allocator<HistogramLiteral> + Default,
AllocHistogramCommand: Allocator<HistogramCommand> + Default,
AllocHistogramDistance: Allocator<HistogramDistance> + Default,
AllocHistogramPair: Allocator<HistogramPair> + Default,
AllocContextType: Allocator<ContextType> + Default,
AllocHuffmanTree: Allocator<HuffmanTree> + Default,
AllocZopfliNode: Allocator<ZopfliNode> + Default,
> Default
for CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
fn default() -> Self {
CombiningAllocator {
alloc_u8: AllocU8::default(),
alloc_u16: AllocU16::default(),
alloc_i32: AllocI32::default(),
alloc_u32: AllocU32::default(),
alloc_u64: AllocU64::default(),
alloc_c: AllocCommand::default(),
alloc_f: AllocFloatX::default(),
alloc_f32x8: AllocV8::default(),
alloc_i16x16: AllocS16::default(),
alloc_pdf: AllocPDF::default(),
alloc_sc: AllocStaticCommand::default(),
alloc_hl: AllocHistogramLiteral::default(),
alloc_hc: AllocHistogramCommand::default(),
alloc_hd: AllocHistogramDistance::default(),
alloc_hp: AllocHistogramPair::default(),
alloc_ct: AllocContextType::default(),
alloc_ht: AllocHuffmanTree::default(),
alloc_zn: AllocZopfliNode::default(),
}
}
}
impl<
AllocU8: Allocator<u8> + Clone,
AllocU16: Allocator<u16> + Clone,
AllocI32: Allocator<i32> + Clone,
AllocU32: Allocator<u32> + Clone,
AllocU64: Allocator<u64> + Clone,
AllocCommand: Allocator<Command> + Clone,
AllocFloatX: Allocator<floatX> + Clone,
AllocV8: Allocator<v8> + Clone,
AllocS16: Allocator<s16> + Clone,
AllocPDF: Allocator<PDF> + Clone,
AllocStaticCommand: Allocator<StaticCommand> + Clone,
AllocHistogramLiteral: Allocator<HistogramLiteral> + Clone,
AllocHistogramCommand: Allocator<HistogramCommand> + Clone,
AllocHistogramDistance: Allocator<HistogramDistance> + Clone,
AllocHistogramPair: Allocator<HistogramPair> + Clone,
AllocContextType: Allocator<ContextType> + Clone,
AllocHuffmanTree: Allocator<HuffmanTree> + Clone,
AllocZopfliNode: Allocator<ZopfliNode> + Clone,
> Clone
for CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
fn clone(&self) -> Self {
CombiningAllocator {
alloc_u8: self.alloc_u8.clone(),
alloc_u16: self.alloc_u16.clone(),
alloc_i32: self.alloc_i32.clone(),
alloc_u32: self.alloc_u32.clone(),
alloc_u64: self.alloc_u64.clone(),
alloc_c: self.alloc_c.clone(),
alloc_f: self.alloc_f.clone(),
alloc_f32x8: self.alloc_f32x8.clone(),
alloc_i16x16: self.alloc_i16x16.clone(),
alloc_pdf: self.alloc_pdf.clone(),
alloc_sc: self.alloc_sc.clone(),
alloc_hl: self.alloc_hl.clone(),
alloc_hc: self.alloc_hc.clone(),
alloc_hd: self.alloc_hd.clone(),
alloc_hp: self.alloc_hp.clone(),
alloc_ct: self.alloc_ct.clone(),
alloc_ht: self.alloc_ht.clone(),
alloc_zn: self.alloc_zn.clone(),
}
}
}
impl<
AllocU8: Allocator<u8> + Copy,
AllocU16: Allocator<u16> + Copy,
AllocI32: Allocator<i32> + Copy,
AllocU32: Allocator<u32> + Copy,
AllocU64: Allocator<u64> + Copy,
AllocCommand: Allocator<Command> + Copy,
AllocFloatX: Allocator<floatX> + Copy,
AllocV8: Allocator<v8> + Copy,
AllocS16: Allocator<s16> + Copy,
AllocPDF: Allocator<PDF> + Copy,
AllocStaticCommand: Allocator<StaticCommand> + Copy,
AllocHistogramLiteral: Allocator<HistogramLiteral> + Copy,
AllocHistogramCommand: Allocator<HistogramCommand> + Copy,
AllocHistogramDistance: Allocator<HistogramDistance> + Copy,
AllocHistogramPair: Allocator<HistogramPair> + Copy,
AllocContextType: Allocator<ContextType> + Copy,
AllocHuffmanTree: Allocator<HuffmanTree> + Copy,
AllocZopfliNode: Allocator<ZopfliNode> + Copy,
> Copy
for CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
}
macro_rules! implement_allocator {
($bound_name: ty,
$type_name: ty,
$sub_type_name: ty,
$local_name: ident) => {
impl<
AllocU8: Allocator<u8>,
AllocU16: Allocator<u16>,
AllocI32: Allocator<i32>,
AllocU32: Allocator<u32>,
AllocU64: Allocator<u64>,
AllocCommand: Allocator<Command>,
AllocFloatX: Allocator<floatX>,
AllocV8: Allocator<v8>,
AllocS16: Allocator<s16>,
AllocPDF: Allocator<PDF>,
AllocStaticCommand: Allocator<StaticCommand>,
AllocHistogramLiteral: Allocator<HistogramLiteral>,
AllocHistogramCommand: Allocator<HistogramCommand>,
AllocHistogramDistance: Allocator<HistogramDistance>,
AllocHistogramPair: Allocator<HistogramPair>,
AllocContextType: Allocator<ContextType>,
AllocHuffmanTree: Allocator<HuffmanTree>,
AllocZopfliNode: Allocator<ZopfliNode>,
> Allocator<$type_name>
for CombiningAllocator<
AllocU8,
AllocU16,
AllocI32,
AllocU32,
AllocU64,
AllocCommand,
AllocFloatX,
AllocV8,
AllocS16,
AllocPDF,
AllocStaticCommand,
AllocHistogramLiteral,
AllocHistogramCommand,
AllocHistogramDistance,
AllocHistogramPair,
AllocContextType,
AllocHuffmanTree,
AllocZopfliNode,
>
{
type AllocatedMemory = $sub_type_name;
fn alloc_cell(
&mut self,
size: usize,
) -> <Self as Allocator<$type_name>>::AllocatedMemory {
self.$local_name.alloc_cell(size)
}
fn free_cell(&mut self, data: <Self as Allocator<$type_name>>::AllocatedMemory) {
self.$local_name.free_cell(data)
}
}
};
}
implement_allocator!(AllocU8, u8, AllocU8::AllocatedMemory, alloc_u8);
implement_allocator!(AllocU16, u16, AllocU16::AllocatedMemory, alloc_u16);
implement_allocator!(AllocI32, i32, AllocI32::AllocatedMemory, alloc_i32);
implement_allocator!(AllocU32, u32, AllocU32::AllocatedMemory, alloc_u32);
implement_allocator!(AllocU64, u64, AllocU64::AllocatedMemory, alloc_u64);
implement_allocator!(
AllocCommand,
Command,
AllocCommand::AllocatedMemory,
alloc_c
);
implement_allocator!(AllocFloatX, floatX, AllocFloatX::AllocatedMemory, alloc_f);
implement_allocator!(AllocV8, v8, AllocV8::AllocatedMemory, alloc_f32x8);
implement_allocator!(AllocS16, s16, AllocS16::AllocatedMemory, alloc_i16x16);
implement_allocator!(AllocPDF, PDF, AllocPDF::AllocatedMemory, alloc_pdf);
implement_allocator!(
AllocStaticCommand,
StaticCommand,
AllocStaticCommand::AllocatedMemory,
alloc_sc
);
implement_allocator!(
AllocHistogramLiteral,
HistogramLiteral,
AllocHistogramLiteral::AllocatedMemory,
alloc_hl
);
implement_allocator!(
AllocHistogramCommand,
HistogramCommand,
AllocHistogramCommand::AllocatedMemory,
alloc_hc
);
implement_allocator!(
AllocHistogramDistance,
HistogramDistance,
AllocHistogramDistance::AllocatedMemory,
alloc_hd
);
implement_allocator!(
AllocHistogramPair,
HistogramPair,
AllocHistogramPair::AllocatedMemory,
alloc_hp
);
implement_allocator!(
AllocContextType,
ContextType,
AllocContextType::AllocatedMemory,
alloc_ct
);
implement_allocator!(
AllocHuffmanTree,
HuffmanTree,
AllocHuffmanTree::AllocatedMemory,
alloc_ht
);
implement_allocator!(
AllocZopfliNode,
ZopfliNode,
AllocZopfliNode::AllocatedMemory,
alloc_zn
);
/// Helper function to allocate memory with the given allocator (may crash if the `len` is 0).
pub(crate) fn allocate<T, A: Allocator<T>>(alloc: &mut A, len: usize) -> A::AllocatedMemory {
A::alloc_cell(alloc, len)
}
/// Helper function to create an empty allocator object
pub(crate) fn alloc_default<T, A: Allocator<T>>() -> A::AllocatedMemory {
A::AllocatedMemory::default()
}
/// Helper function to allocate memory or return a default value if the condition is false
pub(crate) fn alloc_if<T, A: Allocator<T>>(
condition: bool,
alloc: &mut A,
len: usize,
) -> A::AllocatedMemory {
if condition {
allocate(alloc, len)
} else {
alloc_default::<T, A>()
}
}
/// Helper function to allocate memory or return a default value when the size is 0.
pub(crate) fn alloc_or_default<T, A: Allocator<T>>(
alloc: &mut A,
len: usize,
) -> A::AllocatedMemory {
alloc_if(len > 0, alloc, len)
}
+440
View File
@@ -0,0 +1,440 @@
use super::encode::BROTLI_NUM_DISTANCE_SHORT_CODES;
use super::util::Log2FloorNonZero;
#[derive(Copy, Clone, Debug)]
pub struct BrotliDistanceParams {
pub distance_postfix_bits: u32,
pub num_direct_distance_codes: u32,
pub alphabet_size: u32,
pub max_distance: usize,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Command {
// stores copy_len in low 25 bits and copy_code - copy_len in high 7 bit
pub insert_len_: u32,
pub copy_len_: u32,
//stores distance_extra bits
pub dist_extra_: u32,
pub cmd_prefix_: u16,
// stores distance code in low 10 bits and num extra bits in high 6 bits
pub dist_prefix_: u16,
}
impl Command {
pub fn copy_len(&self) -> u32 {
self.copy_len_ & 0x01ff_ffff
}
pub fn distance_context(&self) -> u32 {
let r: u32 = (self.cmd_prefix_ as i32 >> 6) as u32;
let c: u32 = (self.cmd_prefix_ as i32 & 7i32) as u32;
if (r == 0 || r == 2 || r == 4 || r == 7) && c <= 2 {
c
} else {
3
}
}
pub fn init_insert(&mut self, insertlen: usize) {
self.insert_len_ = insertlen as u32;
self.copy_len_ = (4i32 << 25) as u32;
self.dist_extra_ = 0u32;
self.dist_prefix_ = (1u16 << 10) | BROTLI_NUM_DISTANCE_SHORT_CODES as u16;
get_length_code(insertlen, 4usize, false, &mut self.cmd_prefix_);
}
}
#[inline(always)]
pub fn ComputeDistanceCode(distance: usize, max_distance: usize, dist_cache: &[i32]) -> usize {
if distance <= max_distance {
let distance_plus_3: usize = distance.wrapping_add(3);
let offset0: usize = distance_plus_3.wrapping_sub(dist_cache[0] as usize);
let offset1: usize = distance_plus_3.wrapping_sub(dist_cache[1] as usize);
if distance == dist_cache[0] as usize {
return 0usize;
} else if distance == dist_cache[1] as usize {
return 1;
} else if offset0 < 7usize {
return (0x0975_0468_i32 >> (4usize).wrapping_mul(offset0) & 0xfi32) as usize;
} else if offset1 < 7usize {
return (0x0fdb_1ace_i32 >> (4usize).wrapping_mul(offset1) & 0xfi32) as usize;
} else if distance == dist_cache[2] as usize {
return 2usize;
} else if distance == dist_cache[3] as usize {
return 3usize;
}
}
distance.wrapping_add(16).wrapping_sub(1)
}
#[inline(always)]
pub fn GetInsertLengthCode(insertlen: usize) -> u16 {
if insertlen < 6usize {
insertlen as u16
} else if insertlen < 130usize {
let nbits: u32 = Log2FloorNonZero(insertlen.wrapping_sub(2) as u64).wrapping_sub(1);
((nbits << 1) as usize)
.wrapping_add(insertlen.wrapping_sub(2) >> nbits)
.wrapping_add(2) as u16
} else if insertlen < 2114usize {
Log2FloorNonZero(insertlen.wrapping_sub(66) as u64).wrapping_add(10) as u16
} else if insertlen < 6210usize {
21u32 as u16
} else if insertlen < 22594usize {
22u32 as u16
} else {
23u32 as u16
}
}
#[inline(always)]
pub fn GetCopyLengthCode(copylen: usize) -> u16 {
if copylen < 10usize {
copylen.wrapping_sub(2) as u16
} else if copylen < 134usize {
let nbits: u32 = Log2FloorNonZero(copylen.wrapping_sub(6) as u64).wrapping_sub(1);
((nbits << 1) as usize)
.wrapping_add(copylen.wrapping_sub(6) >> nbits)
.wrapping_add(4) as u16
} else if copylen < 2118usize {
Log2FloorNonZero(copylen.wrapping_sub(70) as u64).wrapping_add(12) as u16
} else {
23u32 as u16
}
}
#[inline(always)]
pub(crate) fn combine_length_codes(inscode: u16, copycode: u16, use_last_distance: bool) -> u16 {
let bits64: u16 = (copycode as u32 & 0x7u32 | (inscode as u32 & 0x7u32) << 3) as u16;
if use_last_distance && inscode < 8 && copycode < 16 {
if (copycode as i32) < 8i32 {
bits64
} else {
let s64: u16 = 64u16;
(bits64 as i32 | s64 as i32) as u16
}
} else {
let sub_offset: i32 = 2i32 * ((copycode as i32 >> 3) + 3i32 * (inscode as i32 >> 3));
let offset = (sub_offset << 5) + 0x40i32 + (0x520d40i32 >> sub_offset & 0xc0i32);
(offset as u16 as i32 | bits64 as i32) as u16
}
}
#[inline(always)]
pub(crate) fn get_length_code(
insertlen: usize,
copylen: usize,
use_last_distance: bool,
code: &mut u16,
) {
let inscode: u16 = GetInsertLengthCode(insertlen);
let copycode: u16 = GetCopyLengthCode(copylen);
*code = combine_length_codes(inscode, copycode, use_last_distance);
}
pub fn PrefixEncodeCopyDistance(
distance_code: usize,
num_direct_codes: usize,
postfix_bits: u64,
code: &mut u16,
extra_bits: &mut u32,
) {
if distance_code < (BROTLI_NUM_DISTANCE_SHORT_CODES as usize).wrapping_add(num_direct_codes) {
*code = distance_code as u16;
*extra_bits = 0u32;
} else {
let dist: u64 = (1u64 << postfix_bits.wrapping_add(2u32 as (u64))).wrapping_add(
(distance_code as u64)
.wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
.wrapping_sub(num_direct_codes as u64),
);
let bucket: u64 = Log2FloorNonZero(dist).wrapping_sub(1) as (u64);
let postfix_mask: u64 = (1u32 << postfix_bits).wrapping_sub(1) as (u64);
let postfix: u64 = dist & postfix_mask;
let prefix: u64 = (dist >> bucket) & 1;
let offset: u64 = (2u64).wrapping_add(prefix) << bucket;
let nbits: u64 = bucket.wrapping_sub(postfix_bits);
*code = ((nbits << 10)
| ((BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
.wrapping_add(num_direct_codes as u64)
.wrapping_add(
2u64.wrapping_mul(nbits.wrapping_sub(1))
.wrapping_add(prefix)
<< postfix_bits,
)
.wrapping_add(postfix))) as u16;
*extra_bits = (dist.wrapping_sub(offset) >> postfix_bits) as u32;
/*(16u64)
.wrapping_add(num_direct_codes as u64)
.wrapping_add((2u64).wrapping_mul(nbits.wrapping_sub(1)).wrapping_add(prefix) <<
postfix_bits)
.wrapping_add(postfix) as u16;*/
//*extra_bits = (nbits << 24 | dist.wrapping_sub(offset) >> postfix_bits) as u32;
}
}
impl Command {
pub fn restore_distance_code(&self, dist: &BrotliDistanceParams) -> u32 {
if (self.dist_prefix_ as i32 & 0x3ff)
< BROTLI_NUM_DISTANCE_SHORT_CODES as i32 + dist.num_direct_distance_codes as i32
{
self.dist_prefix_ as u32 & 0x3ff
} else {
let dcode = self.dist_prefix_ as u32 & 0x3ff;
let nbits: u32 = u32::from(self.dist_prefix_ >> 10);
let extra: u32 = self.dist_extra_;
let postfix_mask = (1u32 << dist.distance_postfix_bits) - 1;
let hcode = dcode
.wrapping_sub(dist.num_direct_distance_codes)
.wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
>> dist.distance_postfix_bits;
let lcode = dcode
.wrapping_sub(dist.num_direct_distance_codes)
.wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
& postfix_mask;
let offset = (2u32.wrapping_add((hcode & 1)) << nbits).wrapping_sub(4);
(offset.wrapping_add(extra) << dist.distance_postfix_bits)
.wrapping_add(lcode)
.wrapping_add(dist.num_direct_distance_codes)
.wrapping_add(BROTLI_NUM_DISTANCE_SHORT_CODES)
}
}
// returns which distance code to use ( 0 means none, 1 means last, 2 means penultimate, 3 means the prior to penultimate
pub fn distance_index_and_offset(&self, dist: &BrotliDistanceParams) -> (usize, isize) {
let n_postfix = dist.distance_postfix_bits;
let n_direct = dist.num_direct_distance_codes;
let dextra = self.dist_extra_;
let dprefix = self.dist_prefix_ & 0x3ff;
let n_dist_bits = self.dist_prefix_ >> 10;
if u32::from(dprefix) < BROTLI_NUM_DISTANCE_SHORT_CODES {
let table: [(usize, isize); 16] = [
(1, 0),
(2, 0),
(3, 0),
(4, 0),
(1, -1),
(1, 1),
(1, -2),
(1, 2),
(1, -3),
(1, 3),
(2, -1),
(2, 1),
(2, -2),
(2, 2),
(2, -3),
(2, 3),
];
//eprint!("AA {:?} {:?} -> {:?}\n",*self, *dist, table[dprefix as usize]);
return table[dprefix as usize];
}
if (dprefix as usize) < BROTLI_NUM_DISTANCE_SHORT_CODES as usize + n_direct as usize {
let ret = dprefix as isize + 1 - BROTLI_NUM_DISTANCE_SHORT_CODES as isize;
//eprint!("BB {:?} {:?} -> {:?}\n",*self, *dist, ret);
return (0, ret);
}
let postfix_mask = (1 << n_postfix) - 1;
let dcode = dprefix as u32 - BROTLI_NUM_DISTANCE_SHORT_CODES - n_direct;
let hcode = dcode >> n_postfix;
let lcode = dcode & postfix_mask;
let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
let ret = (((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize;
//assert!(ret != 0);
(0, ret)
}
}
pub fn RecomputeDistancePrefixes(
cmds: &mut [Command],
num_commands: usize,
num_direct_distance_codes: u32,
distance_postfix_bits: u32,
dist: &BrotliDistanceParams,
) {
if num_direct_distance_codes == 0u32 && (distance_postfix_bits == 0u32) {
return;
}
for i in 0usize..num_commands {
let cmd: &mut Command = &mut cmds[i];
if cmd.copy_len() != 0 && cmd.cmd_prefix_ >= 128 {
PrefixEncodeCopyDistance(
cmd.restore_distance_code(dist) as usize,
num_direct_distance_codes as usize,
distance_postfix_bits as (u64),
&mut cmd.dist_prefix_,
&mut cmd.dist_extra_,
);
}
}
}
impl Command {
pub fn init(
&mut self,
dist: &BrotliDistanceParams,
insertlen: usize,
copylen: usize,
copylen_code: usize,
distance_code: usize,
) {
self.insert_len_ = insertlen as u32;
let copylen_code_delta = (copylen_code as i32 - copylen as i32) as i8;
self.copy_len_ = (copylen as u32 | (u32::from(copylen_code_delta as u8) << 25));
PrefixEncodeCopyDistance(
distance_code,
dist.num_direct_distance_codes as usize,
u64::from(dist.distance_postfix_bits),
&mut self.dist_prefix_,
&mut self.dist_extra_,
);
get_length_code(
insertlen,
copylen_code,
(self.dist_prefix_ & 0x3ff) == 0,
&mut self.cmd_prefix_,
);
}
pub fn new(
dist: &BrotliDistanceParams,
insertlen: usize,
copylen: usize,
copylen_code: usize,
distance_code: usize,
) -> Self {
let mut cmd = Command {
insert_len_: insertlen as u32,
copy_len_: (copylen | ((copylen_code ^ copylen) << 25)) as u32,
dist_extra_: 0,
cmd_prefix_: 0,
dist_prefix_: 0,
};
cmd.init(dist, insertlen, copylen, copylen_code, distance_code);
cmd
}
}
#[cfg(test)]
mod test {
// returns which distance code to use ( 0 means none, 1 means last, 2 means penultimate, 3 means the prior to penultimate
pub fn helperCommandDistanceIndexAndOffset(
cmd: &super::Command,
dist: &super::BrotliDistanceParams,
) -> (usize, isize) {
let n_postfix = dist.distance_postfix_bits;
let n_direct = dist.num_direct_distance_codes;
let dextra = cmd.dist_extra_;
let dist_prefix = cmd.dist_prefix_ & 0x3ff;
if dist_prefix < 16 {
let table: [(usize, isize); 16] = [
(1, 0),
(2, 0),
(3, 0),
(4, 0),
(1, -1),
(1, 1),
(1, -2),
(1, 2),
(1, -3),
(1, 3),
(2, -1),
(2, 1),
(2, -2),
(2, 2),
(2, -3),
(2, 3),
];
return table[cmd.dist_prefix_ as usize];
}
if (dist_prefix as usize) < 16 + n_direct as usize {
return (0, dist_prefix as isize + 1 - 16);
}
let postfix_mask = (1 << n_postfix) - 1;
let dcode = dist_prefix as u32 - 16 - n_direct;
let n_dist_bits = 1 + (dcode >> (n_postfix + 1));
let hcode = dcode >> n_postfix;
let lcode = dcode & postfix_mask;
let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
(
0,
(((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize,
)
}
#[test]
fn test_command_return_distance_index_offset() {
let param = super::BrotliDistanceParams {
distance_postfix_bits: 2,
num_direct_distance_codes: 16,
alphabet_size: 224,
max_distance: 268435456,
};
let mut cmd = super::Command::default();
cmd.insert_len_ = 63;
cmd.copy_len_ = 3;
cmd.dist_extra_ = 3;
cmd.cmd_prefix_ = 297;
cmd.dist_prefix_ = 2089;
assert_eq!(cmd.distance_index_and_offset(&param), (0, 46));
assert_eq!(
cmd.distance_index_and_offset(&param),
helperCommandDistanceIndexAndOffset(&cmd, &param)
);
cmd = super::Command {
insert_len_: 27,
copy_len_: 3,
dist_extra_: 0,
cmd_prefix_: 281,
dist_prefix_: 6,
};
assert_eq!(cmd.distance_index_and_offset(&param), (1, -2));
assert_eq!(
cmd.distance_index_and_offset(&param),
helperCommandDistanceIndexAndOffset(&cmd, &param)
);
cmd = super::Command {
insert_len_: 1,
copy_len_: 3,
dist_extra_: 0,
cmd_prefix_: 137,
dist_prefix_: 27,
};
assert_eq!(cmd.distance_index_and_offset(&param), (0, 12));
assert_eq!(
cmd.distance_index_and_offset(&param),
helperCommandDistanceIndexAndOffset(&cmd, &param)
);
cmd = super::Command {
insert_len_: 5,
copy_len_: 4,
dist_extra_: 297,
cmd_prefix_: 170,
dist_prefix_: 11377,
};
assert_eq!(cmd.distance_index_and_offset(&param), (0, 17574));
assert_eq!(
cmd.distance_index_and_offset(&param),
helperCommandDistanceIndexAndOffset(&cmd, &param)
);
cmd.init_insert(24);
assert_eq!(cmd.distance_index_and_offset(&param), (0, 1));
}
/*
#[test]
fn test_restore_distance_code() {
for dist_code in 0..50000 {
let mut cmd = super::Command::default();
let param =super::BrotliDistanceParams{
distance_postfix_bits:2,
num_direct_distance_codes:16,
alphabet_size:224,
max_distance:268435456,
};
super::InitCommand(&mut cmd, &param, 4, 4, 4, dist_code);
let exp_dist_code = super::CommandRestoreDistanceCode(&cmd, &param);
assert_eq!(exp_dist_code as u32, dist_code as u32);
}
}*/
}
+392
View File
@@ -0,0 +1,392 @@
#![cfg_attr(feature = "simd", allow(unused))]
use core::ops::{Add, AddAssign, BitAnd, Index, IndexMut, Mul, Shr, Sub};
#[derive(Default, Copy, Clone, Debug)]
pub struct Compat16x16([i16; 16]);
impl Compat16x16 {
#[inline(always)]
pub fn splat(a: i16) -> Compat16x16 {
Compat16x16([a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a])
}
#[inline(always)]
pub fn to_int(&self) -> Self {
*self
}
#[inline(always)]
pub fn simd_gt(&self, rhs: Compat16x16) -> Compat16x16 {
Self([
-((self[0] > rhs[0]) as i16),
-((self[1] > rhs[1]) as i16),
-((self[2] > rhs[2]) as i16),
-((self[3] > rhs[3]) as i16),
-((self[4] > rhs[4]) as i16),
-((self[5] > rhs[5]) as i16),
-((self[6] > rhs[6]) as i16),
-((self[7] > rhs[7]) as i16),
-((self[8] > rhs[8]) as i16),
-((self[9] > rhs[9]) as i16),
-((self[10] > rhs[10]) as i16),
-((self[11] > rhs[11]) as i16),
-((self[12] > rhs[12]) as i16),
-((self[13] > rhs[13]) as i16),
-((self[14] > rhs[14]) as i16),
-((self[15] > rhs[15]) as i16),
])
}
}
macro_rules! op16 {
($a: expr, $b: expr, $op: expr) => {
Compat16x16([
$op($a[0], $b[0]),
$op($a[1], $b[1]),
$op($a[2], $b[2]),
$op($a[3], $b[3]),
$op($a[4], $b[4]),
$op($a[5], $b[5]),
$op($a[6], $b[6]),
$op($a[7], $b[7]),
$op($a[8], $b[8]),
$op($a[9], $b[9]),
$op($a[10], $b[10]),
$op($a[11], $b[11]),
$op($a[12], $b[12]),
$op($a[13], $b[13]),
$op($a[14], $b[14]),
$op($a[15], $b[15]),
])
};
}
macro_rules! scalar_op16 {
($a: expr, $b: expr, $op: expr) => {
Compat16x16([
$op($a[0], $b),
$op($a[1], $b),
$op($a[2], $b),
$op($a[3], $b),
$op($a[4], $b),
$op($a[5], $b),
$op($a[6], $b),
$op($a[7], $b),
$op($a[8], $b),
$op($a[9], $b),
$op($a[10], $b),
$op($a[11], $b),
$op($a[12], $b),
$op($a[13], $b),
$op($a[14], $b),
$op($a[15], $b),
])
};
}
#[inline(always)]
fn wrapping_i16_add(a: i16, b: i16) -> i16 {
a.wrapping_add(b)
}
#[inline(always)]
fn wrapping_i16_sub(a: i16, b: i16) -> i16 {
a.wrapping_sub(b)
}
#[inline(always)]
fn i16_bitand(a: i16, b: i16) -> i16 {
a & b
}
#[inline(always)]
fn shift16<Scalar>(a: i16, b: Scalar) -> i16
where
i64: From<Scalar>,
{
a >> i64::from(b)
}
impl Add for Compat16x16 {
type Output = Compat16x16;
#[inline(always)]
fn add(self, other: Compat16x16) -> Compat16x16 {
op16!(self.0, other.0, wrapping_i16_add)
}
}
impl Sub for Compat16x16 {
type Output = Compat16x16;
#[inline(always)]
fn sub(self, other: Compat16x16) -> Compat16x16 {
op16!(self.0, other.0, wrapping_i16_sub)
}
}
impl BitAnd for Compat16x16 {
type Output = Compat16x16;
#[inline(always)]
fn bitand(self, other: Compat16x16) -> Compat16x16 {
op16!(self.0, other.0, i16_bitand)
}
}
impl From<[i16; 16]> for Compat16x16 {
fn from(value: [i16; 16]) -> Self {
Self(value)
}
}
impl<I> Index<I> for Compat16x16
where
I: core::slice::SliceIndex<[i16]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
impl<I> IndexMut<I> for Compat16x16
where
I: core::slice::SliceIndex<[i16]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.0[index]
}
}
impl<Scalar: Clone> Shr<Scalar> for Compat16x16
where
i64: From<Scalar>,
{
type Output = Compat16x16;
#[inline(always)]
fn shr(self, other: Scalar) -> Compat16x16 {
scalar_op16!(self.0, other.clone(), shift16)
}
}
#[derive(Default, Copy, Clone, Debug)]
pub struct Compat32x8([i32; 8]);
impl Compat32x8 {
#[inline(always)]
pub fn splat(a: i32) -> Compat32x8 {
Compat32x8([a, a, a, a, a, a, a, a])
}
#[inline(always)]
pub fn simd_gt(&self, rhs: Compat32x8) -> Compat32x8 {
Self([
-((self[0] > rhs[0]) as i32),
-((self[1] > rhs[1]) as i32),
-((self[2] > rhs[2]) as i32),
-((self[3] > rhs[3]) as i32),
-((self[4] > rhs[4]) as i32),
-((self[5] > rhs[5]) as i32),
-((self[6] > rhs[6]) as i32),
-((self[7] > rhs[7]) as i32),
])
}
#[inline(always)]
pub fn simd_ge(&self, rhs: Compat32x8) -> Compat32x8 {
Self([
-((self[0] >= rhs[0]) as i32),
-((self[1] >= rhs[1]) as i32),
-((self[2] >= rhs[2]) as i32),
-((self[3] >= rhs[3]) as i32),
-((self[4] >= rhs[4]) as i32),
-((self[5] >= rhs[5]) as i32),
-((self[6] >= rhs[6]) as i32),
-((self[7] >= rhs[7]) as i32),
])
}
pub fn to_int(&self) -> Self {
*self
}
}
#[inline(always)]
fn fmin(a: f32, b: f32) -> f32 {
if a < b {
a
} else {
b
}
}
#[derive(Default, Copy, Clone, Debug)]
pub struct CompatF8([f32; 8]);
impl CompatF8 {
#[inline(always)]
pub fn splat(a: f32) -> CompatF8 {
CompatF8([a, a, a, a, a, a, a, a])
}
#[inline(always)]
pub fn simd_ge(&self, rhs: CompatF8) -> Compat32x8 {
Compat32x8([
-((self[0] >= rhs[0]) as i32),
-((self[1] >= rhs[1]) as i32),
-((self[2] >= rhs[2]) as i32),
-((self[3] >= rhs[3]) as i32),
-((self[4] >= rhs[4]) as i32),
-((self[5] >= rhs[5]) as i32),
-((self[6] >= rhs[6]) as i32),
-((self[7] >= rhs[7]) as i32),
])
}
#[inline(always)]
pub fn simd_min(&self, rhs: CompatF8) -> CompatF8 {
Self([
fmin(self[0], rhs[0]),
fmin(self[1], rhs[1]),
fmin(self[2], rhs[2]),
fmin(self[3], rhs[3]),
fmin(self[4], rhs[4]),
fmin(self[5], rhs[5]),
fmin(self[6], rhs[6]),
fmin(self[7], rhs[7]),
])
}
}
impl Add for Compat32x8 {
type Output = Compat32x8;
#[inline(always)]
fn add(self, other: Compat32x8) -> Compat32x8 {
Compat32x8([
self.0[0].wrapping_add(other.0[0]),
self.0[1].wrapping_add(other.0[1]),
self.0[2].wrapping_add(other.0[2]),
self.0[3].wrapping_add(other.0[3]),
self.0[4].wrapping_add(other.0[4]),
self.0[5].wrapping_add(other.0[5]),
self.0[6].wrapping_add(other.0[6]),
self.0[7].wrapping_add(other.0[7]),
])
}
}
impl BitAnd for Compat32x8 {
type Output = Compat32x8;
#[inline(always)]
fn bitand(self, other: Compat32x8) -> Compat32x8 {
Compat32x8([
self.0[0] & other.0[0],
self.0[1] & other.0[1],
self.0[2] & other.0[2],
self.0[3] & other.0[3],
self.0[4] & other.0[4],
self.0[5] & other.0[5],
self.0[6] & other.0[6],
self.0[7] & other.0[7],
])
}
}
impl Mul for Compat32x8 {
type Output = Compat32x8;
#[inline(always)]
fn mul(self, other: Compat32x8) -> Compat32x8 {
Compat32x8([
self.0[0].wrapping_mul(other.0[0]),
self.0[1].wrapping_mul(other.0[1]),
self.0[2].wrapping_mul(other.0[2]),
self.0[3].wrapping_mul(other.0[3]),
self.0[4].wrapping_mul(other.0[4]),
self.0[5].wrapping_mul(other.0[5]),
self.0[6].wrapping_mul(other.0[6]),
self.0[7].wrapping_mul(other.0[7]),
])
}
}
impl From<[i32; 8]> for Compat32x8 {
fn from(value: [i32; 8]) -> Self {
Self(value)
}
}
impl<I> Index<I> for Compat32x8
where
I: core::slice::SliceIndex<[i32]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
impl<I> IndexMut<I> for Compat32x8
where
I: core::slice::SliceIndex<[i32]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.0[index]
}
}
impl Add for CompatF8 {
type Output = CompatF8;
#[inline(always)]
fn add(self, other: CompatF8) -> CompatF8 {
CompatF8([
self.0[0] + other.0[0],
self.0[1] + other.0[1],
self.0[2] + other.0[2],
self.0[3] + other.0[3],
self.0[4] + other.0[4],
self.0[5] + other.0[5],
self.0[6] + other.0[6],
self.0[7] + other.0[7],
])
}
}
impl Sub for CompatF8 {
type Output = CompatF8;
#[inline(always)]
fn sub(self, other: CompatF8) -> CompatF8 {
CompatF8([
self.0[0] - other.0[0],
self.0[1] - other.0[1],
self.0[2] - other.0[2],
self.0[3] - other.0[3],
self.0[4] - other.0[4],
self.0[5] - other.0[5],
self.0[6] - other.0[6],
self.0[7] - other.0[7],
])
}
}
impl Mul for CompatF8 {
type Output = CompatF8;
#[inline(always)]
fn mul(self, other: CompatF8) -> CompatF8 {
CompatF8([
self.0[0] * other.0[0],
self.0[1] * other.0[1],
self.0[2] * other.0[2],
self.0[3] * other.0[3],
self.0[4] * other.0[4],
self.0[5] * other.0[5],
self.0[6] * other.0[6],
self.0[7] * other.0[7],
])
}
}
impl AddAssign for CompatF8 {
#[inline(always)]
fn add_assign(&mut self, other: CompatF8) {
self.0[0] += other.0[0];
self.0[1] += other.0[1];
self.0[2] += other.0[2];
self.0[3] += other.0[3];
self.0[4] += other.0[4];
self.0[5] += other.0[5];
self.0[6] += other.0[6];
self.0[7] += other.0[7];
}
}
impl From<[f32; 8]> for CompatF8 {
fn from(value: [f32; 8]) -> Self {
Self(value)
}
}
impl<I> Index<I> for CompatF8
where
I: core::slice::SliceIndex<[f32]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.0[index]
}
}
impl<I> IndexMut<I> for CompatF8
where
I: core::slice::SliceIndex<[f32]>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
&mut self.0[index]
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,905 @@
use core;
use core::cmp::min;
use super::super::alloc;
use super::backward_references::kHashMul32;
use super::bit_cost::BitsEntropy;
use super::brotli_bit_stream::{BrotliBuildAndStoreHuffmanTreeFast, BrotliStoreHuffmanTree};
use super::entropy_encode::{
BrotliConvertBitDepthsToSymbols, BrotliCreateHuffmanTree, HuffmanTree,
};
use super::static_dict::{
FindMatchLengthWithLimit, BROTLI_UNALIGNED_LOAD32, BROTLI_UNALIGNED_LOAD64,
BROTLI_UNALIGNED_STORE64,
};
use super::util::{floatX, Log2FloorNonZero};
static kCompressFragmentTwoPassBlockSize: usize = (1i32 << 17) as usize;
// returns number of commands inserted
fn EmitInsertLen(insertlen: u32, commands: &mut &mut [u32]) -> usize {
if insertlen < 6u32 {
(*commands)[0] = insertlen;
} else if insertlen < 130u32 {
let tail: u32 = insertlen.wrapping_sub(2);
let nbits: u32 = Log2FloorNonZero(tail as (u64)).wrapping_sub(1);
let prefix: u32 = tail >> nbits;
let inscode: u32 = (nbits << 1).wrapping_add(prefix).wrapping_add(2);
let extra: u32 = tail.wrapping_sub(prefix << nbits);
(*commands)[0] = inscode | extra << 8;
} else if insertlen < 2114u32 {
let tail: u32 = insertlen.wrapping_sub(66);
let nbits: u32 = Log2FloorNonZero(tail as (u64));
let code: u32 = nbits.wrapping_add(10);
let extra: u32 = tail.wrapping_sub(1u32 << nbits);
(*commands)[0] = code | extra << 8;
} else if insertlen < 6210u32 {
let extra: u32 = insertlen.wrapping_sub(2114);
(*commands)[0] = 21u32 | extra << 8;
} else if insertlen < 22594u32 {
let extra: u32 = insertlen.wrapping_sub(6210);
(*commands)[0] = 22u32 | extra << 8;
} else {
let extra: u32 = insertlen.wrapping_sub(22594);
(*commands)[0] = 23u32 | extra << 8;
}
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
1
}
fn EmitDistance(distance: u32, commands: &mut &mut [u32]) -> usize {
let d: u32 = distance.wrapping_add(3);
let nbits: u32 = Log2FloorNonZero(d as (u64)).wrapping_sub(1);
let prefix: u32 = d >> nbits & 1u32;
let offset: u32 = (2u32).wrapping_add(prefix) << nbits;
let distcode: u32 = (2u32)
.wrapping_mul(nbits.wrapping_sub(1))
.wrapping_add(prefix)
.wrapping_add(80);
let extra: u32 = d.wrapping_sub(offset);
(*commands)[0] = distcode | extra << 8;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
1
}
fn EmitCopyLenLastDistance(copylen: usize, commands: &mut &mut [u32]) -> usize {
if copylen < 12usize {
(*commands)[0] = copylen.wrapping_add(20) as u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
1
} else if copylen < 72usize {
let tail: usize = copylen.wrapping_sub(8);
let nbits: usize = Log2FloorNonZero(tail as u64).wrapping_sub(1) as usize;
let prefix: usize = tail >> nbits;
let code: usize = (nbits << 1).wrapping_add(prefix).wrapping_add(28);
let extra: usize = tail.wrapping_sub(prefix << nbits);
(*commands)[0] = (code | extra << 8) as u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
1
} else if copylen < 136usize {
let tail: usize = copylen.wrapping_sub(8);
let code: usize = (tail >> 5).wrapping_add(54);
let extra: usize = tail & 31usize;
(*commands)[0] = (code | extra << 8) as u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
(*commands)[0] = 64u32;
let remainder2 = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder2[1..]);
2
} else if copylen < 2120usize {
let tail: usize = copylen.wrapping_sub(72);
let nbits: usize = Log2FloorNonZero(tail as u64) as usize;
let code: usize = nbits.wrapping_add(52);
let extra: usize = tail.wrapping_sub(1usize << nbits);
(*commands)[0] = (code | extra << 8) as u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
(*commands)[0] = 64u32;
let remainder2 = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder2[1..]);
2
} else {
let extra: usize = copylen.wrapping_sub(2120);
(*commands)[0] = (63usize | extra << 8) as u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
(*commands)[0] = 64u32;
let remainder2 = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder2[1..]);
2
}
}
fn HashBytesAtOffset(v: u64, offset: i32, shift: usize, length: usize) -> u32 {
let h: u64 = (v >> (8i32 * offset) << ((8 - length) * 8)).wrapping_mul(kHashMul32 as (u64));
(h >> shift) as u32
}
fn EmitCopyLen(copylen: usize, commands: &mut &mut [u32]) -> usize {
if copylen < 10usize {
(*commands)[0] = copylen.wrapping_add(38) as u32;
} else if copylen < 134usize {
let tail: usize = copylen.wrapping_sub(6);
let nbits: usize = Log2FloorNonZero(tail as u64).wrapping_sub(1) as usize;
let prefix: usize = tail >> nbits;
let code: usize = (nbits << 1).wrapping_add(prefix).wrapping_add(44);
let extra: usize = tail.wrapping_sub(prefix << nbits);
(*commands)[0] = (code | extra << 8) as u32;
} else if copylen < 2118usize {
let tail: usize = copylen.wrapping_sub(70);
let nbits: usize = Log2FloorNonZero(tail as u64) as usize;
let code: usize = nbits.wrapping_add(52);
let extra: usize = tail.wrapping_sub(1usize << nbits);
(*commands)[0] = (code | extra << 8) as u32;
} else {
let extra: usize = copylen.wrapping_sub(2118);
(*commands)[0] = (63usize | extra << 8) as u32;
}
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
1
}
fn Hash(p: &[u8], shift: usize, length: usize) -> u32 {
let h: u64 =
(BROTLI_UNALIGNED_LOAD64(p) << ((8 - length) * 8)).wrapping_mul(kHashMul32 as (u64));
(h >> shift) as u32
}
fn IsMatch(p1: &[u8], p2: &[u8], length: usize) -> bool {
BROTLI_UNALIGNED_LOAD32(p1) == BROTLI_UNALIGNED_LOAD32(p2)
&& (length == 4 || (p1[4] == p2[4] && p1[5] == p2[5]))
}
#[allow(unused_assignments)]
fn CreateCommands(
input_index: usize,
block_size: usize,
input_size: usize,
base_ip: &[u8],
table: &mut [i32],
table_bits: usize,
min_match: usize,
literals: &mut &mut [u8],
num_literals: &mut usize,
commands: &mut &mut [u32],
num_commands: &mut usize,
) {
let mut ip_index: usize = input_index;
let shift: usize = (64u32 as usize).wrapping_sub(table_bits);
let ip_end: usize = input_index.wrapping_add(block_size);
let mut next_emit: usize = input_index;
let mut last_distance: i32 = -1i32;
let kInputMarginBytes: usize = 16usize;
if block_size >= kInputMarginBytes {
let len_limit: usize = min(
block_size.wrapping_sub(min_match),
input_size.wrapping_sub(kInputMarginBytes),
);
let ip_limit: usize = input_index.wrapping_add(len_limit);
let mut next_hash: u32;
let mut goto_emit_remainder = false;
next_hash = Hash(
&base_ip[{
ip_index = ip_index.wrapping_add(1);
ip_index
}..],
shift,
min_match,
);
while !goto_emit_remainder {
let mut skip: u32 = 32u32;
let mut next_ip: usize = ip_index;
let mut candidate: usize = 0;
loop {
{
'break3: loop {
{
let hash: u32 = next_hash;
let bytes_between_hash_lookups: u32 = skip >> 5;
skip = skip.wrapping_add(1);
ip_index = next_ip;
next_ip = ip_index.wrapping_add(bytes_between_hash_lookups as usize);
if next_ip > ip_limit {
goto_emit_remainder = true;
{
break 'break3;
}
}
next_hash = Hash(&base_ip[next_ip..], shift, min_match);
candidate = ip_index.wrapping_sub(last_distance as usize);
if IsMatch(&base_ip[ip_index..], &base_ip[candidate..], min_match)
&& candidate < ip_index
{
table[(hash as usize)] = ip_index.wrapping_sub(0) as i32;
{
break 'break3;
}
}
candidate = table[(hash as usize)] as usize;
table[(hash as usize)] = ip_index.wrapping_sub(0) as i32;
}
if IsMatch(&base_ip[ip_index..], &base_ip[candidate..], min_match) {
break;
}
}
}
if !(ip_index.wrapping_sub(candidate)
> (1usize << 18).wrapping_sub(16) as isize as usize
&& !goto_emit_remainder)
{
break;
}
}
if goto_emit_remainder {
break;
}
{
let base: usize = ip_index;
let matched: usize = min_match.wrapping_add(FindMatchLengthWithLimit(
&base_ip[(candidate + min_match)..],
&base_ip[(ip_index + min_match)..],
ip_end.wrapping_sub(ip_index).wrapping_sub(min_match),
));
let distance: i32 = base.wrapping_sub(candidate) as i32;
let insert: i32 = base.wrapping_sub(next_emit) as i32;
ip_index = ip_index.wrapping_add(matched);
*num_commands += EmitInsertLen(insert as u32, commands);
(*literals)[..(insert as usize)]
.clone_from_slice(&base_ip[next_emit..(next_emit + insert as usize)]);
*num_literals += insert as usize;
let new_literals = core::mem::take(literals);
let _ = core::mem::replace(literals, &mut new_literals[(insert as usize)..]);
if distance == last_distance {
(*commands)[0] = 64u32;
let remainder = core::mem::take(commands);
let _ = core::mem::replace(commands, &mut remainder[1..]);
*num_commands += 1;
} else {
*num_commands += EmitDistance(distance as u32, commands);
last_distance = distance;
}
*num_commands += EmitCopyLenLastDistance(matched, commands);
next_emit = ip_index;
if ip_index >= ip_limit {
goto_emit_remainder = true;
{
break;
}
}
{
let mut input_bytes: u64;
let mut prev_hash: u32;
let cur_hash: u32;
if min_match == 4 {
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 3)..]);
cur_hash = HashBytesAtOffset(input_bytes, 3i32, shift, min_match);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(3) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(2) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(1) as i32;
} else {
assert!(ip_index >= 5);
// could this be off the end FIXME
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 5)..]);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(5) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(4) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 2i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(3) as i32;
assert!(ip_index >= 2);
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 2)..]);
cur_hash = HashBytesAtOffset(input_bytes, 2i32, shift, min_match);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(2) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(1) as i32;
}
candidate = table[(cur_hash as usize)] as usize;
table[(cur_hash as usize)] = ip_index as i32;
}
}
while ip_index.wrapping_sub(candidate)
<= (1usize << 18).wrapping_sub(16) as isize as usize
&& IsMatch(&base_ip[ip_index..], &base_ip[candidate..], min_match)
{
let base_index: usize = ip_index;
let matched: usize = min_match.wrapping_add(FindMatchLengthWithLimit(
&base_ip[(candidate + min_match)..],
&base_ip[(ip_index + min_match)..],
ip_end.wrapping_sub(ip_index).wrapping_sub(min_match),
));
ip_index = ip_index.wrapping_add(matched);
last_distance = base_index.wrapping_sub(candidate) as i32;
*num_commands += EmitCopyLen(matched, commands);
*num_commands += EmitDistance(last_distance as u32, commands);
next_emit = ip_index;
if ip_index >= ip_limit {
goto_emit_remainder = true;
{
break;
}
}
{
assert!(ip_index >= 5);
let mut input_bytes: u64;
let cur_hash: u32;
let mut prev_hash: u32;
if min_match == 4 {
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 3)..]);
cur_hash = HashBytesAtOffset(input_bytes, 3i32, shift, min_match);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(3) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(2) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 2i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(1) as i32;
} else {
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 5)..]);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(5) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(4) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 2i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(3) as i32;
assert!(ip_index >= 2);
input_bytes = BROTLI_UNALIGNED_LOAD64(&base_ip[(ip_index - 2)..]);
cur_hash = HashBytesAtOffset(input_bytes, 2i32, shift, min_match);
prev_hash = HashBytesAtOffset(input_bytes, 0i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(2) as i32;
prev_hash = HashBytesAtOffset(input_bytes, 1i32, shift, min_match);
table[(prev_hash as usize)] = ip_index.wrapping_sub(1) as i32;
}
candidate = table[(cur_hash as usize)] as usize;
table[(cur_hash as usize)] = ip_index as i32;
}
}
if !goto_emit_remainder {
next_hash = Hash(
&base_ip[{
ip_index = ip_index.wrapping_add(1);
ip_index
}..],
shift,
min_match,
);
}
}
}
if next_emit < ip_end {
let insert: u32 = ip_end.wrapping_sub(next_emit) as u32;
*num_commands += EmitInsertLen(insert, commands);
literals[..insert as usize]
.clone_from_slice(&base_ip[next_emit..(next_emit + insert as usize)]);
let mut xliterals = core::mem::take(literals);
*literals = &mut core::mem::take(&mut xliterals)[(insert as usize)..];
*num_literals += insert as usize;
}
}
fn ShouldCompress(input: &[u8], input_size: usize, num_literals: usize) -> bool {
let corpus_size = input_size as floatX;
if (num_literals as floatX) < 0.98 * corpus_size {
true
} else {
let mut literal_histo: [u32; 256] = [0; 256];
let max_total_bit_cost: floatX = corpus_size * 8.0 * 0.98 / 43.0;
let mut i: usize;
i = 0usize;
while i < input_size {
{
let _rhs = 1;
let _lhs = &mut literal_histo[input[i] as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
i = i.wrapping_add(43);
}
BitsEntropy(&mut literal_histo[..], 256) < max_total_bit_cost
}
}
pub fn BrotliWriteBits(n_bits: usize, bits: u64, pos: &mut usize, array: &mut [u8]) {
let p = &mut array[(*pos >> 3)..];
let mut v: u64 = p[0] as (u64);
v |= bits << (*pos & 7);
BROTLI_UNALIGNED_STORE64(p, v);
*pos = pos.wrapping_add(n_bits);
}
pub(crate) fn store_meta_block_header(
len: usize,
is_uncompressed: bool,
storage_ix: &mut usize,
storage: &mut [u8],
) {
let mut nibbles: u64 = 6;
BrotliWriteBits(1, 0, storage_ix, storage);
if len <= (1u32 << 16) as usize {
nibbles = 4;
} else if len <= (1u32 << 20) as usize {
nibbles = 5;
}
BrotliWriteBits(2, nibbles.wrapping_sub(4), storage_ix, storage);
BrotliWriteBits(
nibbles.wrapping_mul(4) as usize,
len.wrapping_sub(1) as u64,
storage_ix,
storage,
);
BrotliWriteBits(1, u64::from(is_uncompressed), storage_ix, storage);
}
pub fn memcpy<T: Sized + Clone>(
dst: &mut [T],
dst_offset: usize,
src: &[T],
src_offset: usize,
size_to_copy: usize,
) {
dst[dst_offset..(dst_offset + size_to_copy)]
.clone_from_slice(&src[src_offset..(src_offset + size_to_copy)]);
}
fn BuildAndStoreCommandPrefixCode(
histogram: &[u32],
depth: &mut [u8],
bits: &mut [u16],
storage_ix: &mut usize,
storage: &mut [u8],
) {
let mut tree = [HuffmanTree::new(0, 0, 0); 129];
let mut cmd_depth: [u8; 704] = [0; 704];
let mut cmd_bits: [u16; 64] = [0; 64];
BrotliCreateHuffmanTree(histogram, 64usize, 15i32, &mut tree[..], depth);
BrotliCreateHuffmanTree(
&histogram[64..],
64usize,
14i32,
&mut tree[..],
&mut depth[64..],
);
/* We have to jump through a few hoops here in order to compute
the command bits because the symbols are in a different order than in
the full alphabet. This looks complicated, but having the symbols
in this order in the command bits saves a few branches in the Emit*
functions. */
memcpy(&mut cmd_depth[..], 0, depth, 24, 24);
memcpy(&mut cmd_depth[..], 24, depth, 0, 8);
memcpy(&mut cmd_depth[..], 32usize, depth, (48usize), 8usize);
memcpy(&mut cmd_depth[..], 40usize, depth, (8usize), 8usize);
memcpy(&mut cmd_depth[..], 48usize, depth, (56usize), 8usize);
memcpy(&mut cmd_depth[..], 56usize, depth, (16usize), 8usize);
BrotliConvertBitDepthsToSymbols(&mut cmd_depth[..], 64usize, &mut cmd_bits[..]);
memcpy(bits, 0, &cmd_bits[..], 24usize, 16usize);
memcpy(bits, (8usize), &cmd_bits[..], 40usize, 8usize);
memcpy(bits, (16usize), &cmd_bits[..], 56usize, 8usize);
memcpy(bits, (24usize), &cmd_bits[..], 0, 48usize);
memcpy(bits, (48usize), &cmd_bits[..], 32usize, 8usize);
memcpy(bits, (56usize), &cmd_bits[..], 48usize, 8usize);
BrotliConvertBitDepthsToSymbols(&mut depth[64..], 64usize, &mut bits[64..]);
{
for item in cmd_depth[..64].iter_mut() {
*item = 0;
}
//memset(&mut cmd_depth[..], 0i32, 64usize);
memcpy(&mut cmd_depth[..], 0, depth, (24usize), 8usize);
memcpy(&mut cmd_depth[..], 64usize, depth, (32usize), 8usize);
memcpy(&mut cmd_depth[..], 128usize, depth, (40usize), 8usize);
memcpy(&mut cmd_depth[..], 192usize, depth, (48usize), 8usize);
memcpy(&mut cmd_depth[..], 384usize, depth, (56usize), 8usize);
for i in 0usize..8usize {
cmd_depth[(128usize).wrapping_add((8usize).wrapping_mul(i))] = depth[i];
cmd_depth[(256usize).wrapping_add((8usize).wrapping_mul(i))] = depth[i.wrapping_add(8)];
cmd_depth[(448usize).wrapping_add((8usize).wrapping_mul(i))] =
depth[i.wrapping_add(16)];
}
BrotliStoreHuffmanTree(
&mut cmd_depth[..],
704usize,
&mut tree[..],
storage_ix,
storage,
);
}
BrotliStoreHuffmanTree(
&mut depth[64..],
64usize,
&mut tree[..],
storage_ix,
storage,
);
}
fn StoreCommands<AllocHT: alloc::Allocator<HuffmanTree>>(
mht: &mut AllocHT,
mut literals: &[u8],
num_literals: usize,
commands: &[u32],
num_commands: usize,
storage_ix: &mut usize,
storage: &mut [u8],
) {
static kNumExtraBits: [u32; 128] = [
0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
7, 8, 9, 10, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17,
18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24,
];
static kInsertOffset: [u32; 24] = [
0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114,
6210, 22594,
];
let mut lit_depths: [u8; 256] = [0; 256];
let mut lit_bits: [u16; 256] = [0; 256]; // maybe return this instead
let mut lit_histo: [u32; 256] = [0; 256]; // maybe return this instead of init
let mut cmd_depths: [u8; 128] = [0; 128];
let mut cmd_bits: [u16; 128] = [0; 128];
let mut cmd_histo: [u32; 128] = [0; 128];
let mut i: usize;
for i in 0usize..num_literals {
let _rhs = 1;
let _lhs = &mut lit_histo[literals[i] as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
BrotliBuildAndStoreHuffmanTreeFast(
mht,
&lit_histo[..],
num_literals,
8usize,
&mut lit_depths[..],
&mut lit_bits[..],
storage_ix,
storage,
);
i = 0usize;
while i < num_commands {
{
let code: u32 = commands[i] & 0xffu32;
{
let _rhs = 1;
let _lhs = &mut cmd_histo[code as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
}
i = i.wrapping_add(1);
}
{
let _rhs = 1i32;
let _lhs = &mut cmd_histo[1];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
{
let _rhs = 1i32;
let _lhs = &mut cmd_histo[2];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
{
let _rhs = 1i32;
let _lhs = &mut cmd_histo[64];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
{
let _rhs = 1i32;
let _lhs = &mut cmd_histo[84];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
BuildAndStoreCommandPrefixCode(
&mut cmd_histo[..],
&mut cmd_depths[..],
&mut cmd_bits[..],
storage_ix,
storage,
);
for i in 0usize..num_commands {
let cmd: u32 = commands[i];
let code: u32 = cmd & 0xffu32;
let extra: u32 = cmd >> 8;
BrotliWriteBits(
cmd_depths[code as usize] as usize,
cmd_bits[code as usize] as (u64),
storage_ix,
storage,
);
BrotliWriteBits(
kNumExtraBits[code as usize] as usize,
extra as (u64),
storage_ix,
storage,
);
if code < 24u32 {
let insert: u32 = kInsertOffset[code as usize].wrapping_add(extra);
for literal in literals[..(insert as usize)].iter() {
let lit: u8 = *literal;
BrotliWriteBits(
lit_depths[lit as usize] as usize,
lit_bits[lit as usize] as (u64),
storage_ix,
storage,
);
}
literals = &literals[insert as usize..];
}
}
}
fn EmitUncompressedMetaBlock(
input: &[u8],
input_size: usize,
storage_ix: &mut usize,
storage: &mut [u8],
) {
store_meta_block_header(input_size, true, storage_ix, storage);
*storage_ix = storage_ix.wrapping_add(7u32 as usize) & !7u32 as usize;
memcpy(storage, (*storage_ix >> 3), input, 0, input_size);
*storage_ix = storage_ix.wrapping_add(input_size << 3);
storage[(*storage_ix >> 3)] = 0u8;
}
#[allow(unused_variables)]
#[inline(always)]
fn compress_fragment_two_pass_impl<AllocHT: alloc::Allocator<HuffmanTree>>(
m: &mut AllocHT,
base_ip: &[u8],
mut input_size: usize,
is_last: bool,
command_buf: &mut [u32],
literal_buf: &mut [u8],
table: &mut [i32],
table_bits: usize,
min_match: usize,
storage_ix: &mut usize,
storage: &mut [u8],
) {
let mut input_index: usize = 0usize;
while input_size > 0usize {
let block_size: usize = min(input_size, kCompressFragmentTwoPassBlockSize);
let mut num_literals: usize = 0;
let mut num_commands: usize = 0;
{
let mut literals = &mut literal_buf[..];
let mut commands = &mut command_buf[..];
CreateCommands(
input_index,
block_size,
input_size,
base_ip,
table,
table_bits,
min_match,
&mut literals,
&mut num_literals,
&mut commands,
&mut num_commands,
);
}
if ShouldCompress(&base_ip[input_index..], block_size, num_literals) {
store_meta_block_header(block_size, false, storage_ix, storage);
BrotliWriteBits(13usize, 0, storage_ix, storage);
StoreCommands(
m,
literal_buf,
num_literals,
command_buf,
num_commands,
storage_ix,
storage,
);
} else {
EmitUncompressedMetaBlock(&base_ip[input_index..], block_size, storage_ix, storage);
}
input_index = input_index.wrapping_add(block_size);
input_size = input_size.wrapping_sub(block_size);
}
}
macro_rules! compress_specialization {
($table_bits : expr, $fname: ident) => {
fn $fname<AllocHT: alloc::Allocator<HuffmanTree>>(
mht: &mut AllocHT,
input: &[u8],
input_size: usize,
is_last: bool,
command_buf: &mut [u32],
literal_buf: &mut [u8],
table: &mut [i32],
storage_ix: &mut usize,
storage: &mut [u8],
) {
let min_match = if $table_bits < 15 { 4 } else { 6 };
compress_fragment_two_pass_impl(
mht,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
$table_bits,
min_match,
storage_ix,
storage,
);
}
};
}
compress_specialization!(8, BrotliCompressFragmentTwoPassImpl8);
compress_specialization!(9, BrotliCompressFragmentTwoPassImpl9);
compress_specialization!(10, BrotliCompressFragmentTwoPassImpl10);
compress_specialization!(11, BrotliCompressFragmentTwoPassImpl11);
compress_specialization!(12, BrotliCompressFragmentTwoPassImpl12);
compress_specialization!(13, BrotliCompressFragmentTwoPassImpl13);
compress_specialization!(14, BrotliCompressFragmentTwoPassImpl14);
compress_specialization!(15, BrotliCompressFragmentTwoPassImpl15);
compress_specialization!(16, BrotliCompressFragmentTwoPassImpl16);
compress_specialization!(17, BrotliCompressFragmentTwoPassImpl17);
fn RewindBitPosition(new_storage_ix: usize, storage_ix: &mut usize, storage: &mut [u8]) {
let bitpos: usize = new_storage_ix & 7usize;
let mask: usize = (1u32 << bitpos).wrapping_sub(1) as usize;
{
let _rhs = mask as u8;
let _lhs = &mut storage[(new_storage_ix >> 3)];
*_lhs = (*_lhs as i32 & _rhs as i32) as u8;
}
*storage_ix = new_storage_ix;
}
pub(crate) fn compress_fragment_two_pass<AllocHT: alloc::Allocator<HuffmanTree>>(
m: &mut AllocHT,
input: &[u8],
input_size: usize,
is_last: bool,
command_buf: &mut [u32],
literal_buf: &mut [u8],
table: &mut [i32],
table_size: usize,
storage_ix: &mut usize,
storage: &mut [u8],
) {
let initial_storage_ix: usize = *storage_ix;
let table_bits: usize = Log2FloorNonZero(table_size as u64) as usize;
if table_bits == 8usize {
BrotliCompressFragmentTwoPassImpl8(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 9usize {
BrotliCompressFragmentTwoPassImpl9(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 10usize {
BrotliCompressFragmentTwoPassImpl10(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 11usize {
BrotliCompressFragmentTwoPassImpl11(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 12usize {
BrotliCompressFragmentTwoPassImpl12(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 13usize {
BrotliCompressFragmentTwoPassImpl13(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 14usize {
BrotliCompressFragmentTwoPassImpl14(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 15usize {
BrotliCompressFragmentTwoPassImpl15(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 16usize {
BrotliCompressFragmentTwoPassImpl16(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if table_bits == 17usize {
BrotliCompressFragmentTwoPassImpl17(
m,
input,
input_size,
is_last,
command_buf,
literal_buf,
table,
storage_ix,
storage,
);
}
if storage_ix.wrapping_sub(initial_storage_ix) > (31usize).wrapping_add(input_size << 3) {
RewindBitPosition(initial_storage_ix, storage_ix, storage);
EmitUncompressedMetaBlock(input, input_size, storage_ix, storage);
}
if is_last {
BrotliWriteBits(1, 1, storage_ix, storage);
BrotliWriteBits(1, 1, storage_ix, storage);
*storage_ix = storage_ix.wrapping_add(7u32 as usize) & !7u32 as usize;
}
}
+462
View File
@@ -0,0 +1,462 @@
pub const BROTLI_NUM_BLOCK_LEN_SYMBOLS: usize = 26;
pub static kInsBase: [u32; 24] = [
0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210,
22594,
];
pub static kInsExtra: [u32; 24] = [
0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24,
];
pub static kCopyBase: [u32; 24] = [
2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 18, 22, 30, 38, 54, 70, 102, 134, 198, 326, 582, 1094, 2118,
];
pub static kCopyExtra: [u32; 24] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24,
];
/* Common context lookup table for all context modes. */
static kContextLookup: [u8; 2048] = [
/* CONTEXT_LSB6, last byte. */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, /* CONTEXT_LSB6, second last byte, */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* CONTEXT_MSB6, last byte. */
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14,
14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20,
20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26,
26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32,
32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38,
38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44,
44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50,
50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56,
56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62,
62, 62, 63, 63, 63, 63, /* CONTEXT_MSB6, second last byte, */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* CONTEXT_UTF8, last byte. */
/* ASCII range. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 32, 32, 24, 40, 28, 12, 12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12, 12, 56, 60, 60, 60, 56, 60, 60,
60, 56, 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
/* UTF8 continuation byte range. */
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
/* UTF8 lead byte range. */
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
/* CONTEXT_UTF8 second last byte. */
/* ASCII range. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0,
/* UTF8 continuation byte range. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* UTF8 lead byte range. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */
0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 48, 48, 48, 48,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56, /* CONTEXT_SIGNED, second last byte. */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
];
pub const BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS: usize = 544;
pub const BROTLI_NUM_LITERAL_SYMBOLS: usize = 256;
pub const BROTLI_NUM_COMMAND_SYMBOLS: usize = 704;
pub const BROTLI_WINDOW_GAP: usize = 16;
pub const BROTLI_MAX_NPOSTFIX: usize = 3;
pub const BROTLI_MAX_NDIRECT: usize = 120;
#[inline(always)]
pub fn BROTLI_CONTEXT_LUT(mode: super::histogram::ContextType) -> &'static [u8] {
&kContextLookup[((mode as usize) << 9)..]
}
pub fn BROTLI_CONTEXT(P1: u8, P2: u8, LUT: &[u8]) -> u8 {
(LUT)[P1 as usize] | ((LUT)[256 + P2 as usize])
}
pub static kZeroRepsBits: [usize; 704] = [
0x0, 0x0, 0x0, 0x7, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x770, 0xb87, 0x1387, 0x1b87,
0x2387, 0x2b87, 0x3387, 0x3b87, 0x397, 0xb97, 0x1397, 0x1b97, 0x2397, 0x2b97, 0x3397, 0x3b97,
0x3a7, 0xba7, 0x13a7, 0x1ba7, 0x23a7, 0x2ba7, 0x33a7, 0x3ba7, 0x3b7, 0xbb7, 0x13b7, 0x1bb7,
0x23b7, 0x2bb7, 0x33b7, 0x3bb7, 0x3c7, 0xbc7, 0x13c7, 0x1bc7, 0x23c7, 0x2bc7, 0x33c7, 0x3bc7,
0x3d7, 0xbd7, 0x13d7, 0x1bd7, 0x23d7, 0x2bd7, 0x33d7, 0x3bd7, 0x3e7, 0xbe7, 0x13e7, 0x1be7,
0x23e7, 0x2be7, 0x33e7, 0x3be7, 0x3f7, 0xbf7, 0x13f7, 0x1bf7, 0x23f7, 0x2bf7, 0x33f7, 0x3bf7,
0x1c387, 0x5c387, 0x9c387, 0xdc387, 0x11c387, 0x15c387, 0x19c387, 0x1dc387, 0x1cb87, 0x5cb87,
0x9cb87, 0xdcb87, 0x11cb87, 0x15cb87, 0x19cb87, 0x1dcb87, 0x1d387, 0x5d387, 0x9d387, 0xdd387,
0x11d387, 0x15d387, 0x19d387, 0x1dd387, 0x1db87, 0x5db87, 0x9db87, 0xddb87, 0x11db87, 0x15db87,
0x19db87, 0x1ddb87, 0x1e387, 0x5e387, 0x9e387, 0xde387, 0x11e387, 0x15e387, 0x19e387, 0x1de387,
0x1eb87, 0x5eb87, 0x9eb87, 0xdeb87, 0x11eb87, 0x15eb87, 0x19eb87, 0x1deb87, 0x1f387, 0x5f387,
0x9f387, 0xdf387, 0x11f387, 0x15f387, 0x19f387, 0x1df387, 0x1fb87, 0x5fb87, 0x9fb87, 0xdfb87,
0x11fb87, 0x15fb87, 0x19fb87, 0x1dfb87, 0x1c397, 0x5c397, 0x9c397, 0xdc397, 0x11c397, 0x15c397,
0x19c397, 0x1dc397, 0x1cb97, 0x5cb97, 0x9cb97, 0xdcb97, 0x11cb97, 0x15cb97, 0x19cb97, 0x1dcb97,
0x1d397, 0x5d397, 0x9d397, 0xdd397, 0x11d397, 0x15d397, 0x19d397, 0x1dd397, 0x1db97, 0x5db97,
0x9db97, 0xddb97, 0x11db97, 0x15db97, 0x19db97, 0x1ddb97, 0x1e397, 0x5e397, 0x9e397, 0xde397,
0x11e397, 0x15e397, 0x19e397, 0x1de397, 0x1eb97, 0x5eb97, 0x9eb97, 0xdeb97, 0x11eb97, 0x15eb97,
0x19eb97, 0x1deb97, 0x1f397, 0x5f397, 0x9f397, 0xdf397, 0x11f397, 0x15f397, 0x19f397, 0x1df397,
0x1fb97, 0x5fb97, 0x9fb97, 0xdfb97, 0x11fb97, 0x15fb97, 0x19fb97, 0x1dfb97, 0x1c3a7, 0x5c3a7,
0x9c3a7, 0xdc3a7, 0x11c3a7, 0x15c3a7, 0x19c3a7, 0x1dc3a7, 0x1cba7, 0x5cba7, 0x9cba7, 0xdcba7,
0x11cba7, 0x15cba7, 0x19cba7, 0x1dcba7, 0x1d3a7, 0x5d3a7, 0x9d3a7, 0xdd3a7, 0x11d3a7, 0x15d3a7,
0x19d3a7, 0x1dd3a7, 0x1dba7, 0x5dba7, 0x9dba7, 0xddba7, 0x11dba7, 0x15dba7, 0x19dba7, 0x1ddba7,
0x1e3a7, 0x5e3a7, 0x9e3a7, 0xde3a7, 0x11e3a7, 0x15e3a7, 0x19e3a7, 0x1de3a7, 0x1eba7, 0x5eba7,
0x9eba7, 0xdeba7, 0x11eba7, 0x15eba7, 0x19eba7, 0x1deba7, 0x1f3a7, 0x5f3a7, 0x9f3a7, 0xdf3a7,
0x11f3a7, 0x15f3a7, 0x19f3a7, 0x1df3a7, 0x1fba7, 0x5fba7, 0x9fba7, 0xdfba7, 0x11fba7, 0x15fba7,
0x19fba7, 0x1dfba7, 0x1c3b7, 0x5c3b7, 0x9c3b7, 0xdc3b7, 0x11c3b7, 0x15c3b7, 0x19c3b7, 0x1dc3b7,
0x1cbb7, 0x5cbb7, 0x9cbb7, 0xdcbb7, 0x11cbb7, 0x15cbb7, 0x19cbb7, 0x1dcbb7, 0x1d3b7, 0x5d3b7,
0x9d3b7, 0xdd3b7, 0x11d3b7, 0x15d3b7, 0x19d3b7, 0x1dd3b7, 0x1dbb7, 0x5dbb7, 0x9dbb7, 0xddbb7,
0x11dbb7, 0x15dbb7, 0x19dbb7, 0x1ddbb7, 0x1e3b7, 0x5e3b7, 0x9e3b7, 0xde3b7, 0x11e3b7, 0x15e3b7,
0x19e3b7, 0x1de3b7, 0x1ebb7, 0x5ebb7, 0x9ebb7, 0xdebb7, 0x11ebb7, 0x15ebb7, 0x19ebb7, 0x1debb7,
0x1f3b7, 0x5f3b7, 0x9f3b7, 0xdf3b7, 0x11f3b7, 0x15f3b7, 0x19f3b7, 0x1df3b7, 0x1fbb7, 0x5fbb7,
0x9fbb7, 0xdfbb7, 0x11fbb7, 0x15fbb7, 0x19fbb7, 0x1dfbb7, 0x1c3c7, 0x5c3c7, 0x9c3c7, 0xdc3c7,
0x11c3c7, 0x15c3c7, 0x19c3c7, 0x1dc3c7, 0x1cbc7, 0x5cbc7, 0x9cbc7, 0xdcbc7, 0x11cbc7, 0x15cbc7,
0x19cbc7, 0x1dcbc7, 0x1d3c7, 0x5d3c7, 0x9d3c7, 0xdd3c7, 0x11d3c7, 0x15d3c7, 0x19d3c7, 0x1dd3c7,
0x1dbc7, 0x5dbc7, 0x9dbc7, 0xddbc7, 0x11dbc7, 0x15dbc7, 0x19dbc7, 0x1ddbc7, 0x1e3c7, 0x5e3c7,
0x9e3c7, 0xde3c7, 0x11e3c7, 0x15e3c7, 0x19e3c7, 0x1de3c7, 0x1ebc7, 0x5ebc7, 0x9ebc7, 0xdebc7,
0x11ebc7, 0x15ebc7, 0x19ebc7, 0x1debc7, 0x1f3c7, 0x5f3c7, 0x9f3c7, 0xdf3c7, 0x11f3c7, 0x15f3c7,
0x19f3c7, 0x1df3c7, 0x1fbc7, 0x5fbc7, 0x9fbc7, 0xdfbc7, 0x11fbc7, 0x15fbc7, 0x19fbc7, 0x1dfbc7,
0x1c3d7, 0x5c3d7, 0x9c3d7, 0xdc3d7, 0x11c3d7, 0x15c3d7, 0x19c3d7, 0x1dc3d7, 0x1cbd7, 0x5cbd7,
0x9cbd7, 0xdcbd7, 0x11cbd7, 0x15cbd7, 0x19cbd7, 0x1dcbd7, 0x1d3d7, 0x5d3d7, 0x9d3d7, 0xdd3d7,
0x11d3d7, 0x15d3d7, 0x19d3d7, 0x1dd3d7, 0x1dbd7, 0x5dbd7, 0x9dbd7, 0xddbd7, 0x11dbd7, 0x15dbd7,
0x19dbd7, 0x1ddbd7, 0x1e3d7, 0x5e3d7, 0x9e3d7, 0xde3d7, 0x11e3d7, 0x15e3d7, 0x19e3d7, 0x1de3d7,
0x1ebd7, 0x5ebd7, 0x9ebd7, 0xdebd7, 0x11ebd7, 0x15ebd7, 0x19ebd7, 0x1debd7, 0x1f3d7, 0x5f3d7,
0x9f3d7, 0xdf3d7, 0x11f3d7, 0x15f3d7, 0x19f3d7, 0x1df3d7, 0x1fbd7, 0x5fbd7, 0x9fbd7, 0xdfbd7,
0x11fbd7, 0x15fbd7, 0x19fbd7, 0x1dfbd7, 0x1c3e7, 0x5c3e7, 0x9c3e7, 0xdc3e7, 0x11c3e7, 0x15c3e7,
0x19c3e7, 0x1dc3e7, 0x1cbe7, 0x5cbe7, 0x9cbe7, 0xdcbe7, 0x11cbe7, 0x15cbe7, 0x19cbe7, 0x1dcbe7,
0x1d3e7, 0x5d3e7, 0x9d3e7, 0xdd3e7, 0x11d3e7, 0x15d3e7, 0x19d3e7, 0x1dd3e7, 0x1dbe7, 0x5dbe7,
0x9dbe7, 0xddbe7, 0x11dbe7, 0x15dbe7, 0x19dbe7, 0x1ddbe7, 0x1e3e7, 0x5e3e7, 0x9e3e7, 0xde3e7,
0x11e3e7, 0x15e3e7, 0x19e3e7, 0x1de3e7, 0x1ebe7, 0x5ebe7, 0x9ebe7, 0xdebe7, 0x11ebe7, 0x15ebe7,
0x19ebe7, 0x1debe7, 0x1f3e7, 0x5f3e7, 0x9f3e7, 0xdf3e7, 0x11f3e7, 0x15f3e7, 0x19f3e7, 0x1df3e7,
0x1fbe7, 0x5fbe7, 0x9fbe7, 0xdfbe7, 0x11fbe7, 0x15fbe7, 0x19fbe7, 0x1dfbe7, 0x1c3f7, 0x5c3f7,
0x9c3f7, 0xdc3f7, 0x11c3f7, 0x15c3f7, 0x19c3f7, 0x1dc3f7, 0x1cbf7, 0x5cbf7, 0x9cbf7, 0xdcbf7,
0x11cbf7, 0x15cbf7, 0x19cbf7, 0x1dcbf7, 0x1d3f7, 0x5d3f7, 0x9d3f7, 0xdd3f7, 0x11d3f7, 0x15d3f7,
0x19d3f7, 0x1dd3f7, 0x1dbf7, 0x5dbf7, 0x9dbf7, 0xddbf7, 0x11dbf7, 0x15dbf7, 0x19dbf7, 0x1ddbf7,
0x1e3f7, 0x5e3f7, 0x9e3f7, 0xde3f7, 0x11e3f7, 0x15e3f7, 0x19e3f7, 0x1de3f7, 0x1ebf7, 0x5ebf7,
0x9ebf7, 0xdebf7, 0x11ebf7, 0x15ebf7, 0x19ebf7, 0x1debf7, 0x1f3f7, 0x5f3f7, 0x9f3f7, 0xdf3f7,
0x11f3f7, 0x15f3f7, 0x19f3f7, 0x1df3f7, 0x1fbf7, 0x5fbf7, 0x9fbf7, 0xdfbf7, 0x11fbf7, 0x15fbf7,
0x19fbf7, 0x1dfbf7, 0xe1c387, 0x2e1c387, 0x4e1c387, 0x6e1c387, 0x8e1c387, 0xae1c387, 0xce1c387,
0xee1c387, 0xe5c387, 0x2e5c387, 0x4e5c387, 0x6e5c387, 0x8e5c387, 0xae5c387, 0xce5c387,
0xee5c387, 0xe9c387, 0x2e9c387, 0x4e9c387, 0x6e9c387, 0x8e9c387, 0xae9c387, 0xce9c387,
0xee9c387, 0xedc387, 0x2edc387, 0x4edc387, 0x6edc387, 0x8edc387, 0xaedc387, 0xcedc387,
0xeedc387, 0xf1c387, 0x2f1c387, 0x4f1c387, 0x6f1c387, 0x8f1c387, 0xaf1c387, 0xcf1c387,
0xef1c387, 0xf5c387, 0x2f5c387, 0x4f5c387, 0x6f5c387, 0x8f5c387, 0xaf5c387, 0xcf5c387,
0xef5c387, 0xf9c387, 0x2f9c387, 0x4f9c387, 0x6f9c387, 0x8f9c387, 0xaf9c387, 0xcf9c387,
0xef9c387, 0xfdc387, 0x2fdc387, 0x4fdc387, 0x6fdc387, 0x8fdc387, 0xafdc387, 0xcfdc387,
0xefdc387, 0xe1cb87, 0x2e1cb87, 0x4e1cb87, 0x6e1cb87, 0x8e1cb87, 0xae1cb87, 0xce1cb87,
0xee1cb87, 0xe5cb87, 0x2e5cb87, 0x4e5cb87, 0x6e5cb87, 0x8e5cb87, 0xae5cb87, 0xce5cb87,
0xee5cb87, 0xe9cb87, 0x2e9cb87, 0x4e9cb87, 0x6e9cb87, 0x8e9cb87, 0xae9cb87, 0xce9cb87,
0xee9cb87, 0xedcb87, 0x2edcb87, 0x4edcb87, 0x6edcb87, 0x8edcb87, 0xaedcb87, 0xcedcb87,
0xeedcb87, 0xf1cb87, 0x2f1cb87, 0x4f1cb87, 0x6f1cb87, 0x8f1cb87, 0xaf1cb87, 0xcf1cb87,
0xef1cb87, 0xf5cb87, 0x2f5cb87, 0x4f5cb87, 0x6f5cb87, 0x8f5cb87, 0xaf5cb87, 0xcf5cb87,
0xef5cb87, 0xf9cb87, 0x2f9cb87, 0x4f9cb87, 0x6f9cb87, 0x8f9cb87,
];
pub static kZeroRepsDepth: [u32; 704] = [
0, 4, 8, 7, 7, 7, 7, 7, 7, 7, 7, 11, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28,
];
pub static kUTF8ContextLookup: [u8; 512] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12, 44, 44, 44, 44, 44, 44, 44, 44,
44, 44, 32, 32, 24, 40, 28, 12, 12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12, 12, 56, 60, 60, 60, 56, 60, 60,
60, 56, 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
];
pub static kSigned3BitContextLookup: [u8; 256] = [
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
];
pub static kBrotliMinWindowBits: i32 = 10i32;
pub static kBrotliMaxWindowBits: i32 = 24i32;
pub static kCodeLengthDepth: [u8; 18] = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 0, 4, 4];
pub static kStaticDistanceCodeDepth: [u8; 64] = [
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
];
pub static kCodeLengthBits: [u32; 18] =
[0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 15, 31, 0, 11, 7];
pub static kNonZeroRepsBits: [usize; 704] = [
0xb, 0x1b, 0x2b, 0x3b, 0x2cb, 0x6cb, 0xacb, 0xecb, 0x2db, 0x6db, 0xadb, 0xedb, 0x2eb, 0x6eb,
0xaeb, 0xeeb, 0x2fb, 0x6fb, 0xafb, 0xefb, 0xb2cb, 0x1b2cb, 0x2b2cb, 0x3b2cb, 0xb6cb, 0x1b6cb,
0x2b6cb, 0x3b6cb, 0xbacb, 0x1bacb, 0x2bacb, 0x3bacb, 0xbecb, 0x1becb, 0x2becb, 0x3becb, 0xb2db,
0x1b2db, 0x2b2db, 0x3b2db, 0xb6db, 0x1b6db, 0x2b6db, 0x3b6db, 0xbadb, 0x1badb, 0x2badb,
0x3badb, 0xbedb, 0x1bedb, 0x2bedb, 0x3bedb, 0xb2eb, 0x1b2eb, 0x2b2eb, 0x3b2eb, 0xb6eb, 0x1b6eb,
0x2b6eb, 0x3b6eb, 0xbaeb, 0x1baeb, 0x2baeb, 0x3baeb, 0xbeeb, 0x1beeb, 0x2beeb, 0x3beeb, 0xb2fb,
0x1b2fb, 0x2b2fb, 0x3b2fb, 0xb6fb, 0x1b6fb, 0x2b6fb, 0x3b6fb, 0xbafb, 0x1bafb, 0x2bafb,
0x3bafb, 0xbefb, 0x1befb, 0x2befb, 0x3befb, 0x2cb2cb, 0x6cb2cb, 0xacb2cb, 0xecb2cb, 0x2db2cb,
0x6db2cb, 0xadb2cb, 0xedb2cb, 0x2eb2cb, 0x6eb2cb, 0xaeb2cb, 0xeeb2cb, 0x2fb2cb, 0x6fb2cb,
0xafb2cb, 0xefb2cb, 0x2cb6cb, 0x6cb6cb, 0xacb6cb, 0xecb6cb, 0x2db6cb, 0x6db6cb, 0xadb6cb,
0xedb6cb, 0x2eb6cb, 0x6eb6cb, 0xaeb6cb, 0xeeb6cb, 0x2fb6cb, 0x6fb6cb, 0xafb6cb, 0xefb6cb,
0x2cbacb, 0x6cbacb, 0xacbacb, 0xecbacb, 0x2dbacb, 0x6dbacb, 0xadbacb, 0xedbacb, 0x2ebacb,
0x6ebacb, 0xaebacb, 0xeebacb, 0x2fbacb, 0x6fbacb, 0xafbacb, 0xefbacb, 0x2cbecb, 0x6cbecb,
0xacbecb, 0xecbecb, 0x2dbecb, 0x6dbecb, 0xadbecb, 0xedbecb, 0x2ebecb, 0x6ebecb, 0xaebecb,
0xeebecb, 0x2fbecb, 0x6fbecb, 0xafbecb, 0xefbecb, 0x2cb2db, 0x6cb2db, 0xacb2db, 0xecb2db,
0x2db2db, 0x6db2db, 0xadb2db, 0xedb2db, 0x2eb2db, 0x6eb2db, 0xaeb2db, 0xeeb2db, 0x2fb2db,
0x6fb2db, 0xafb2db, 0xefb2db, 0x2cb6db, 0x6cb6db, 0xacb6db, 0xecb6db, 0x2db6db, 0x6db6db,
0xadb6db, 0xedb6db, 0x2eb6db, 0x6eb6db, 0xaeb6db, 0xeeb6db, 0x2fb6db, 0x6fb6db, 0xafb6db,
0xefb6db, 0x2cbadb, 0x6cbadb, 0xacbadb, 0xecbadb, 0x2dbadb, 0x6dbadb, 0xadbadb, 0xedbadb,
0x2ebadb, 0x6ebadb, 0xaebadb, 0xeebadb, 0x2fbadb, 0x6fbadb, 0xafbadb, 0xefbadb, 0x2cbedb,
0x6cbedb, 0xacbedb, 0xecbedb, 0x2dbedb, 0x6dbedb, 0xadbedb, 0xedbedb, 0x2ebedb, 0x6ebedb,
0xaebedb, 0xeebedb, 0x2fbedb, 0x6fbedb, 0xafbedb, 0xefbedb, 0x2cb2eb, 0x6cb2eb, 0xacb2eb,
0xecb2eb, 0x2db2eb, 0x6db2eb, 0xadb2eb, 0xedb2eb, 0x2eb2eb, 0x6eb2eb, 0xaeb2eb, 0xeeb2eb,
0x2fb2eb, 0x6fb2eb, 0xafb2eb, 0xefb2eb, 0x2cb6eb, 0x6cb6eb, 0xacb6eb, 0xecb6eb, 0x2db6eb,
0x6db6eb, 0xadb6eb, 0xedb6eb, 0x2eb6eb, 0x6eb6eb, 0xaeb6eb, 0xeeb6eb, 0x2fb6eb, 0x6fb6eb,
0xafb6eb, 0xefb6eb, 0x2cbaeb, 0x6cbaeb, 0xacbaeb, 0xecbaeb, 0x2dbaeb, 0x6dbaeb, 0xadbaeb,
0xedbaeb, 0x2ebaeb, 0x6ebaeb, 0xaebaeb, 0xeebaeb, 0x2fbaeb, 0x6fbaeb, 0xafbaeb, 0xefbaeb,
0x2cbeeb, 0x6cbeeb, 0xacbeeb, 0xecbeeb, 0x2dbeeb, 0x6dbeeb, 0xadbeeb, 0xedbeeb, 0x2ebeeb,
0x6ebeeb, 0xaebeeb, 0xeebeeb, 0x2fbeeb, 0x6fbeeb, 0xafbeeb, 0xefbeeb, 0x2cb2fb, 0x6cb2fb,
0xacb2fb, 0xecb2fb, 0x2db2fb, 0x6db2fb, 0xadb2fb, 0xedb2fb, 0x2eb2fb, 0x6eb2fb, 0xaeb2fb,
0xeeb2fb, 0x2fb2fb, 0x6fb2fb, 0xafb2fb, 0xefb2fb, 0x2cb6fb, 0x6cb6fb, 0xacb6fb, 0xecb6fb,
0x2db6fb, 0x6db6fb, 0xadb6fb, 0xedb6fb, 0x2eb6fb, 0x6eb6fb, 0xaeb6fb, 0xeeb6fb, 0x2fb6fb,
0x6fb6fb, 0xafb6fb, 0xefb6fb, 0x2cbafb, 0x6cbafb, 0xacbafb, 0xecbafb, 0x2dbafb, 0x6dbafb,
0xadbafb, 0xedbafb, 0x2ebafb, 0x6ebafb, 0xaebafb, 0xeebafb, 0x2fbafb, 0x6fbafb, 0xafbafb,
0xefbafb, 0x2cbefb, 0x6cbefb, 0xacbefb, 0xecbefb, 0x2dbefb, 0x6dbefb, 0xadbefb, 0xedbefb,
0x2ebefb, 0x6ebefb, 0xaebefb, 0xeebefb, 0x2fbefb, 0x6fbefb, 0xafbefb, 0xefbefb, 0xb2cb2cb,
0x1b2cb2cb, 0x2b2cb2cb, 0x3b2cb2cb, 0xb6cb2cb, 0x1b6cb2cb, 0x2b6cb2cb, 0x3b6cb2cb, 0xbacb2cb,
0x1bacb2cb, 0x2bacb2cb, 0x3bacb2cb, 0xbecb2cb, 0x1becb2cb, 0x2becb2cb, 0x3becb2cb, 0xb2db2cb,
0x1b2db2cb, 0x2b2db2cb, 0x3b2db2cb, 0xb6db2cb, 0x1b6db2cb, 0x2b6db2cb, 0x3b6db2cb, 0xbadb2cb,
0x1badb2cb, 0x2badb2cb, 0x3badb2cb, 0xbedb2cb, 0x1bedb2cb, 0x2bedb2cb, 0x3bedb2cb, 0xb2eb2cb,
0x1b2eb2cb, 0x2b2eb2cb, 0x3b2eb2cb, 0xb6eb2cb, 0x1b6eb2cb, 0x2b6eb2cb, 0x3b6eb2cb, 0xbaeb2cb,
0x1baeb2cb, 0x2baeb2cb, 0x3baeb2cb, 0xbeeb2cb, 0x1beeb2cb, 0x2beeb2cb, 0x3beeb2cb, 0xb2fb2cb,
0x1b2fb2cb, 0x2b2fb2cb, 0x3b2fb2cb, 0xb6fb2cb, 0x1b6fb2cb, 0x2b6fb2cb, 0x3b6fb2cb, 0xbafb2cb,
0x1bafb2cb, 0x2bafb2cb, 0x3bafb2cb, 0xbefb2cb, 0x1befb2cb, 0x2befb2cb, 0x3befb2cb, 0xb2cb6cb,
0x1b2cb6cb, 0x2b2cb6cb, 0x3b2cb6cb, 0xb6cb6cb, 0x1b6cb6cb, 0x2b6cb6cb, 0x3b6cb6cb, 0xbacb6cb,
0x1bacb6cb, 0x2bacb6cb, 0x3bacb6cb, 0xbecb6cb, 0x1becb6cb, 0x2becb6cb, 0x3becb6cb, 0xb2db6cb,
0x1b2db6cb, 0x2b2db6cb, 0x3b2db6cb, 0xb6db6cb, 0x1b6db6cb, 0x2b6db6cb, 0x3b6db6cb, 0xbadb6cb,
0x1badb6cb, 0x2badb6cb, 0x3badb6cb, 0xbedb6cb, 0x1bedb6cb, 0x2bedb6cb, 0x3bedb6cb, 0xb2eb6cb,
0x1b2eb6cb, 0x2b2eb6cb, 0x3b2eb6cb, 0xb6eb6cb, 0x1b6eb6cb, 0x2b6eb6cb, 0x3b6eb6cb, 0xbaeb6cb,
0x1baeb6cb, 0x2baeb6cb, 0x3baeb6cb, 0xbeeb6cb, 0x1beeb6cb, 0x2beeb6cb, 0x3beeb6cb, 0xb2fb6cb,
0x1b2fb6cb, 0x2b2fb6cb, 0x3b2fb6cb, 0xb6fb6cb, 0x1b6fb6cb, 0x2b6fb6cb, 0x3b6fb6cb, 0xbafb6cb,
0x1bafb6cb, 0x2bafb6cb, 0x3bafb6cb, 0xbefb6cb, 0x1befb6cb, 0x2befb6cb, 0x3befb6cb, 0xb2cbacb,
0x1b2cbacb, 0x2b2cbacb, 0x3b2cbacb, 0xb6cbacb, 0x1b6cbacb, 0x2b6cbacb, 0x3b6cbacb, 0xbacbacb,
0x1bacbacb, 0x2bacbacb, 0x3bacbacb, 0xbecbacb, 0x1becbacb, 0x2becbacb, 0x3becbacb, 0xb2dbacb,
0x1b2dbacb, 0x2b2dbacb, 0x3b2dbacb, 0xb6dbacb, 0x1b6dbacb, 0x2b6dbacb, 0x3b6dbacb, 0xbadbacb,
0x1badbacb, 0x2badbacb, 0x3badbacb, 0xbedbacb, 0x1bedbacb, 0x2bedbacb, 0x3bedbacb, 0xb2ebacb,
0x1b2ebacb, 0x2b2ebacb, 0x3b2ebacb, 0xb6ebacb, 0x1b6ebacb, 0x2b6ebacb, 0x3b6ebacb, 0xbaebacb,
0x1baebacb, 0x2baebacb, 0x3baebacb, 0xbeebacb, 0x1beebacb, 0x2beebacb, 0x3beebacb, 0xb2fbacb,
0x1b2fbacb, 0x2b2fbacb, 0x3b2fbacb, 0xb6fbacb, 0x1b6fbacb, 0x2b6fbacb, 0x3b6fbacb, 0xbafbacb,
0x1bafbacb, 0x2bafbacb, 0x3bafbacb, 0xbefbacb, 0x1befbacb, 0x2befbacb, 0x3befbacb, 0xb2cbecb,
0x1b2cbecb, 0x2b2cbecb, 0x3b2cbecb, 0xb6cbecb, 0x1b6cbecb, 0x2b6cbecb, 0x3b6cbecb, 0xbacbecb,
0x1bacbecb, 0x2bacbecb, 0x3bacbecb, 0xbecbecb, 0x1becbecb, 0x2becbecb, 0x3becbecb, 0xb2dbecb,
0x1b2dbecb, 0x2b2dbecb, 0x3b2dbecb, 0xb6dbecb, 0x1b6dbecb, 0x2b6dbecb, 0x3b6dbecb, 0xbadbecb,
0x1badbecb, 0x2badbecb, 0x3badbecb, 0xbedbecb, 0x1bedbecb, 0x2bedbecb, 0x3bedbecb, 0xb2ebecb,
0x1b2ebecb, 0x2b2ebecb, 0x3b2ebecb, 0xb6ebecb, 0x1b6ebecb, 0x2b6ebecb, 0x3b6ebecb, 0xbaebecb,
0x1baebecb, 0x2baebecb, 0x3baebecb, 0xbeebecb, 0x1beebecb, 0x2beebecb, 0x3beebecb, 0xb2fbecb,
0x1b2fbecb, 0x2b2fbecb, 0x3b2fbecb, 0xb6fbecb, 0x1b6fbecb, 0x2b6fbecb, 0x3b6fbecb, 0xbafbecb,
0x1bafbecb, 0x2bafbecb, 0x3bafbecb, 0xbefbecb, 0x1befbecb, 0x2befbecb, 0x3befbecb, 0xb2cb2db,
0x1b2cb2db, 0x2b2cb2db, 0x3b2cb2db, 0xb6cb2db, 0x1b6cb2db, 0x2b6cb2db, 0x3b6cb2db, 0xbacb2db,
0x1bacb2db, 0x2bacb2db, 0x3bacb2db, 0xbecb2db, 0x1becb2db, 0x2becb2db, 0x3becb2db, 0xb2db2db,
0x1b2db2db, 0x2b2db2db, 0x3b2db2db, 0xb6db2db, 0x1b6db2db, 0x2b6db2db, 0x3b6db2db, 0xbadb2db,
0x1badb2db, 0x2badb2db, 0x3badb2db, 0xbedb2db, 0x1bedb2db, 0x2bedb2db, 0x3bedb2db, 0xb2eb2db,
0x1b2eb2db, 0x2b2eb2db, 0x3b2eb2db, 0xb6eb2db, 0x1b6eb2db, 0x2b6eb2db, 0x3b6eb2db, 0xbaeb2db,
0x1baeb2db, 0x2baeb2db, 0x3baeb2db, 0xbeeb2db, 0x1beeb2db, 0x2beeb2db, 0x3beeb2db, 0xb2fb2db,
0x1b2fb2db, 0x2b2fb2db, 0x3b2fb2db, 0xb6fb2db, 0x1b6fb2db, 0x2b6fb2db, 0x3b6fb2db, 0xbafb2db,
0x1bafb2db, 0x2bafb2db, 0x3bafb2db, 0xbefb2db, 0x1befb2db, 0x2befb2db, 0x3befb2db, 0xb2cb6db,
0x1b2cb6db, 0x2b2cb6db, 0x3b2cb6db, 0xb6cb6db, 0x1b6cb6db, 0x2b6cb6db, 0x3b6cb6db, 0xbacb6db,
0x1bacb6db, 0x2bacb6db, 0x3bacb6db, 0xbecb6db, 0x1becb6db, 0x2becb6db, 0x3becb6db, 0xb2db6db,
0x1b2db6db, 0x2b2db6db, 0x3b2db6db, 0xb6db6db, 0x1b6db6db, 0x2b6db6db, 0x3b6db6db, 0xbadb6db,
0x1badb6db, 0x2badb6db, 0x3badb6db, 0xbedb6db, 0x1bedb6db, 0x2bedb6db, 0x3bedb6db, 0xb2eb6db,
0x1b2eb6db, 0x2b2eb6db, 0x3b2eb6db, 0xb6eb6db, 0x1b6eb6db, 0x2b6eb6db, 0x3b6eb6db, 0xbaeb6db,
0x1baeb6db, 0x2baeb6db, 0x3baeb6db,
];
pub static kNonZeroRepsDepth: [u32; 704] = [
6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30,
];
pub static kStaticCommandCodeDepth: [u8; 704] = [
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
];
pub static kStaticDistanceCodeBits: [u16; 64] = [
0, 32, 16, 48, 8, 40, 24, 56, 4, 36, 20, 52, 12, 44, 28, 60, 2, 34, 18, 50, 10, 42, 26, 58, 6,
38, 22, 54, 14, 46, 30, 62, 1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61, 3, 35,
19, 51, 11, 43, 27, 59, 7, 39, 23, 55, 15, 47, 31, 63,
];
pub static kStaticCommandCodeBits: [u16; 704] = [
0, 256, 128, 384, 64, 320, 192, 448, 32, 288, 160, 416, 96, 352, 224, 480, 16, 272, 144, 400,
80, 336, 208, 464, 48, 304, 176, 432, 112, 368, 240, 496, 8, 264, 136, 392, 72, 328, 200, 456,
40, 296, 168, 424, 104, 360, 232, 488, 24, 280, 152, 408, 88, 344, 216, 472, 56, 312, 184, 440,
120, 376, 248, 504, 4, 260, 132, 388, 68, 324, 196, 452, 36, 292, 164, 420, 100, 356, 228, 484,
20, 276, 148, 404, 84, 340, 212, 468, 52, 308, 180, 436, 116, 372, 244, 500, 12, 268, 140, 396,
76, 332, 204, 460, 44, 300, 172, 428, 108, 364, 236, 492, 28, 284, 156, 412, 92, 348, 220, 476,
60, 316, 188, 444, 124, 380, 252, 508, 2, 258, 130, 386, 66, 322, 194, 450, 34, 290, 162, 418,
98, 354, 226, 482, 18, 274, 146, 402, 82, 338, 210, 466, 50, 306, 178, 434, 114, 370, 242, 498,
10, 266, 138, 394, 74, 330, 202, 458, 42, 298, 170, 426, 106, 362, 234, 490, 26, 282, 154, 410,
90, 346, 218, 474, 58, 314, 186, 442, 122, 378, 250, 506, 6, 262, 134, 390, 70, 326, 198, 454,
38, 294, 166, 422, 102, 358, 230, 486, 22, 278, 150, 406, 86, 342, 214, 470, 54, 310, 182, 438,
118, 374, 246, 502, 14, 270, 142, 398, 78, 334, 206, 462, 46, 302, 174, 430, 110, 366, 238,
494, 30, 286, 158, 414, 94, 350, 222, 478, 62, 318, 190, 446, 126, 382, 254, 510, 1, 257, 129,
385, 65, 321, 193, 449, 33, 289, 161, 417, 97, 353, 225, 481, 17, 273, 145, 401, 81, 337, 209,
465, 49, 305, 177, 433, 113, 369, 241, 497, 9, 265, 137, 393, 73, 329, 201, 457, 41, 297, 169,
425, 105, 361, 233, 489, 25, 281, 153, 409, 89, 345, 217, 473, 57, 313, 185, 441, 121, 377,
249, 505, 5, 261, 133, 389, 69, 325, 197, 453, 37, 293, 165, 421, 101, 357, 229, 485, 21, 277,
149, 405, 85, 341, 213, 469, 53, 309, 181, 437, 117, 373, 245, 501, 13, 269, 141, 397, 77, 333,
205, 461, 45, 301, 173, 429, 109, 365, 237, 493, 29, 285, 157, 413, 93, 349, 221, 477, 61, 317,
189, 445, 125, 381, 253, 509, 3, 259, 131, 387, 67, 323, 195, 451, 35, 291, 163, 419, 99, 355,
227, 483, 19, 275, 147, 403, 83, 339, 211, 467, 51, 307, 179, 435, 115, 371, 243, 499, 11, 267,
139, 395, 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491, 27, 283, 155, 411, 91, 347,
219, 475, 59, 315, 187, 443, 123, 379, 251, 507, 7, 1031, 519, 1543, 263, 1287, 775, 1799, 135,
1159, 647, 1671, 391, 1415, 903, 1927, 71, 1095, 583, 1607, 327, 1351, 839, 1863, 199, 1223,
711, 1735, 455, 1479, 967, 1991, 39, 1063, 551, 1575, 295, 1319, 807, 1831, 167, 1191, 679,
1703, 423, 1447, 935, 1959, 103, 1127, 615, 1639, 359, 1383, 871, 1895, 231, 1255, 743, 1767,
487, 1511, 999, 2023, 23, 1047, 535, 1559, 279, 1303, 791, 1815, 151, 1175, 663, 1687, 407,
1431, 919, 1943, 87, 1111, 599, 1623, 343, 1367, 855, 1879, 215, 1239, 727, 1751, 471, 1495,
983, 2007, 55, 1079, 567, 1591, 311, 1335, 823, 1847, 183, 1207, 695, 1719, 439, 1463, 951,
1975, 119, 1143, 631, 1655, 375, 1399, 887, 1911, 247, 1271, 759, 1783, 503, 1527, 1015, 2039,
15, 1039, 527, 1551, 271, 1295, 783, 1807, 143, 1167, 655, 1679, 399, 1423, 911, 1935, 79,
1103, 591, 1615, 335, 1359, 847, 1871, 207, 1231, 719, 1743, 463, 1487, 975, 1999, 47, 1071,
559, 1583, 303, 1327, 815, 1839, 175, 1199, 687, 1711, 431, 1455, 943, 1967, 111, 1135, 623,
1647, 367, 1391, 879, 1903, 239, 1263, 751, 1775, 495, 1519, 1007, 2031, 31, 1055, 543, 1567,
287, 1311, 799, 1823, 159, 1183, 671, 1695, 415, 1439, 927, 1951, 95, 1119, 607, 1631, 351,
1375, 863, 1887, 223, 1247, 735, 1759, 479, 1503, 991, 2015, 63, 1087, 575, 1599, 319, 1343,
831, 1855, 191, 1215, 703, 1727, 447, 1471, 959, 1983, 127, 1151, 639, 1663, 383, 1407, 895,
1919, 255, 1279, 767, 1791, 511, 1535, 1023, 2047,
];
@@ -0,0 +1,554 @@
use core;
use super::super::alloc;
use super::super::alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use super::input_pair::{InputPair, InputReference, InputReferenceMut};
pub use super::ir_interpret::{push_base, Context, IRInterpreter};
use super::util::{floatX, FastLog2u16};
use super::weights::{Weights, BLEND_FIXED_POINT_PRECISION};
use super::{find_stride, interface};
use crate::enc::combined_alloc::alloc_if;
const DEFAULT_CM_SPEED_INDEX: usize = 8;
const NUM_SPEEDS_TO_TRY: usize = 16;
const SPEEDS_TO_SEARCH: [u16; NUM_SPEEDS_TO_TRY] = [
0, 1, 1, 1, 2, 4, 8, 16, 16, 32, 64, 128, 128, 512, 1664, 1664,
];
const MAXES_TO_SEARCH: [u16; NUM_SPEEDS_TO_TRY] = [
32, 32, 128, 16384, 1024, 1024, 8192, 48, 8192, 4096, 16384, 256, 16384, 16384, 16384, 16384,
];
const NIBBLE_PRIOR_SIZE: usize = 16 * NUM_SPEEDS_TO_TRY;
// the high nibble, followed by the low nibbles
const CONTEXT_MAP_PRIOR_SIZE: usize = 256 * NIBBLE_PRIOR_SIZE * 17;
const STRIDE_PRIOR_SIZE: usize = 256 * 256 * NIBBLE_PRIOR_SIZE * 2;
#[derive(Clone, Copy, Debug)]
pub struct SpeedAndMax(pub u16, pub u16);
pub fn speed_to_tuple(inp: [SpeedAndMax; 2]) -> [(u16, u16); 2] {
[(inp[0].0, inp[0].1), (inp[1].0, inp[1].1)]
}
fn get_stride_cdf_low(
data: &mut [u16],
stride_prior: u8,
cm_prior: usize,
high_nibble: u8,
) -> &mut [u16] {
let index: usize =
1 + 2 * (cm_prior | ((stride_prior as usize & 0xf) << 8) | ((high_nibble as usize) << 12));
data.split_at_mut((NUM_SPEEDS_TO_TRY * index) << 4)
.1
.split_at_mut(16 * NUM_SPEEDS_TO_TRY)
.0
}
fn get_stride_cdf_high(data: &mut [u16], stride_prior: u8, cm_prior: usize) -> &mut [u16] {
let index: usize = 2 * (cm_prior | ((stride_prior as usize) << 8));
data.split_at_mut((NUM_SPEEDS_TO_TRY * index) << 4)
.1
.split_at_mut(16 * NUM_SPEEDS_TO_TRY)
.0
}
fn get_cm_cdf_low(data: &mut [u16], cm_prior: usize, high_nibble: u8) -> &mut [u16] {
let index: usize = (high_nibble as usize + 1) + 17 * cm_prior;
data.split_at_mut((NUM_SPEEDS_TO_TRY * index) << 4)
.1
.split_at_mut(16 * NUM_SPEEDS_TO_TRY)
.0
}
fn get_cm_cdf_high(data: &mut [u16], cm_prior: usize) -> &mut [u16] {
let index: usize = 17 * cm_prior;
data.split_at_mut((NUM_SPEEDS_TO_TRY * index) << 4)
.1
.split_at_mut(16 * NUM_SPEEDS_TO_TRY)
.0
}
fn init_cdfs(cdfs: &mut [u16]) {
assert_eq!(cdfs.len() % (16 * NUM_SPEEDS_TO_TRY), 0);
let mut total_index = 0usize;
let len = cdfs.len();
loop {
for cdf_index in 0..16 {
let vec = cdfs
.split_at_mut(total_index)
.1
.split_at_mut(NUM_SPEEDS_TO_TRY)
.0;
for item in vec {
*item = 4 + 4 * cdf_index as u16;
}
total_index += NUM_SPEEDS_TO_TRY;
}
if total_index == len {
break;
}
}
}
fn compute_combined_cost(
singleton_cost: &mut [floatX; NUM_SPEEDS_TO_TRY],
cdfs: &[u16],
mixing_cdf: [u16; 16],
nibble_u8: u8,
_weights: &mut [Weights; NUM_SPEEDS_TO_TRY],
) {
assert_eq!(cdfs.len(), 16 * NUM_SPEEDS_TO_TRY);
let nibble = nibble_u8 as usize & 0xf;
let mut stride_pdf = [0u16; NUM_SPEEDS_TO_TRY];
stride_pdf.clone_from_slice(
cdfs.split_at(NUM_SPEEDS_TO_TRY * nibble)
.1
.split_at(NUM_SPEEDS_TO_TRY)
.0,
);
let mut cm_pdf: u16 = mixing_cdf[nibble];
if nibble_u8 != 0 {
let mut tmp = [0u16; NUM_SPEEDS_TO_TRY];
tmp.clone_from_slice(
cdfs.split_at(NUM_SPEEDS_TO_TRY * (nibble - 1))
.1
.split_at(NUM_SPEEDS_TO_TRY)
.0,
);
for i in 0..NUM_SPEEDS_TO_TRY {
stride_pdf[i] -= tmp[i];
}
cm_pdf -= mixing_cdf[nibble - 1]
}
let mut stride_max = [0u16; NUM_SPEEDS_TO_TRY];
stride_max.clone_from_slice(cdfs.split_at(NUM_SPEEDS_TO_TRY * 15).1);
let cm_max = mixing_cdf[15];
for i in 0..NUM_SPEEDS_TO_TRY {
if stride_pdf[i] == 0 {
assert_ne!(stride_pdf[i], 0);
}
if stride_max[i] == 0 {
assert_ne!(stride_max[i], 0);
}
let w = (1 << (BLEND_FIXED_POINT_PRECISION - 2)); // a quarter of weight to stride
let combined_pdf = w * u32::from(stride_pdf[i])
+ ((1 << BLEND_FIXED_POINT_PRECISION) - w) * u32::from(cm_pdf);
let combined_max = w * u32::from(stride_max[i])
+ ((1 << BLEND_FIXED_POINT_PRECISION) - w) * u32::from(cm_max);
let del = FastLog2u16((combined_pdf >> BLEND_FIXED_POINT_PRECISION) as u16)
- FastLog2u16((combined_max >> BLEND_FIXED_POINT_PRECISION) as u16);
singleton_cost[i] -= del;
}
}
fn compute_cost(singleton_cost: &mut [floatX; NUM_SPEEDS_TO_TRY], cdfs: &[u16], nibble_u8: u8) {
assert_eq!(cdfs.len(), 16 * NUM_SPEEDS_TO_TRY);
let nibble = nibble_u8 as usize & 0xf;
let mut pdf = [0u16; NUM_SPEEDS_TO_TRY];
pdf.clone_from_slice(
cdfs.split_at(NUM_SPEEDS_TO_TRY * nibble)
.1
.split_at(NUM_SPEEDS_TO_TRY)
.0,
);
if nibble_u8 != 0 {
let mut tmp = [0u16; NUM_SPEEDS_TO_TRY];
tmp.clone_from_slice(
cdfs.split_at(NUM_SPEEDS_TO_TRY * (nibble - 1))
.1
.split_at(NUM_SPEEDS_TO_TRY)
.0,
);
for i in 0..NUM_SPEEDS_TO_TRY {
pdf[i] -= tmp[i];
}
}
let mut max = [0u16; NUM_SPEEDS_TO_TRY];
max.clone_from_slice(cdfs.split_at(NUM_SPEEDS_TO_TRY * 15).1);
for i in 0..NUM_SPEEDS_TO_TRY {
if pdf[i] == 0 {
assert_ne!(pdf[i], 0);
}
if max[i] == 0 {
assert_ne!(max[i], 0);
}
let del = FastLog2u16(pdf[i]) - FastLog2u16(max[i]);
singleton_cost[i] -= del;
}
}
fn update_cdf(cdfs: &mut [u16], nibble_u8: u8) {
assert_eq!(cdfs.len(), 16 * NUM_SPEEDS_TO_TRY);
let mut overall_index = nibble_u8 as usize * NUM_SPEEDS_TO_TRY;
for _nibble in (nibble_u8 as usize & 0xf)..16 {
for speed_index in 0..NUM_SPEEDS_TO_TRY {
cdfs[overall_index + speed_index] += SPEEDS_TO_SEARCH[speed_index];
}
overall_index += NUM_SPEEDS_TO_TRY;
}
overall_index = 0;
for nibble in 0..16 {
for speed_index in 0..NUM_SPEEDS_TO_TRY {
if nibble == 0 {
assert_ne!(cdfs[overall_index + speed_index], 0);
} else {
assert_ne!(
cdfs[overall_index + speed_index]
- cdfs[overall_index + speed_index - NUM_SPEEDS_TO_TRY],
0
);
}
}
overall_index += NUM_SPEEDS_TO_TRY;
}
for max_index in 0..NUM_SPEEDS_TO_TRY {
if cdfs[15 * NUM_SPEEDS_TO_TRY + max_index] >= MAXES_TO_SEARCH[max_index] {
const CDF_BIAS: [u16; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
for nibble_index in 0..16 {
let tmp = &mut cdfs[nibble_index * NUM_SPEEDS_TO_TRY + max_index];
*tmp = (tmp.wrapping_add(CDF_BIAS[nibble_index]))
.wrapping_sub(tmp.wrapping_add(CDF_BIAS[nibble_index]) >> 2);
}
}
}
overall_index = 0;
for nibble in 0..16 {
for speed_index in 0..NUM_SPEEDS_TO_TRY {
if nibble == 0 {
assert_ne!(cdfs[overall_index + speed_index], 0);
} else {
assert_ne!(
cdfs[overall_index + speed_index]
- cdfs[overall_index + speed_index - NUM_SPEEDS_TO_TRY],
0
);
}
}
overall_index += NUM_SPEEDS_TO_TRY;
}
}
fn extract_single_cdf(cdf_bundle: &[u16], index: usize) -> [u16; 16] {
assert_eq!(cdf_bundle.len(), 16 * NUM_SPEEDS_TO_TRY);
assert!(index < NUM_SPEEDS_TO_TRY);
#[allow(clippy::identity_op)]
[
cdf_bundle[index + 0 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 1 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 2 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 3 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 4 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 5 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 6 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 7 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 8 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 9 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 10 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 11 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 12 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 13 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 14 * NUM_SPEEDS_TO_TRY],
cdf_bundle[index + 15 * NUM_SPEEDS_TO_TRY],
]
}
fn min_cost_index_for_speed(cost: &[floatX]) -> usize {
assert_eq!(cost.len(), NUM_SPEEDS_TO_TRY);
let mut min_cost = cost[0];
let mut best_choice = 0;
for i in 1..NUM_SPEEDS_TO_TRY {
if cost[i] < min_cost {
best_choice = i;
min_cost = cost[i];
}
}
best_choice
}
fn min_cost_speed_max(cost: &[floatX]) -> SpeedAndMax {
let best_choice = min_cost_index_for_speed(cost);
SpeedAndMax(SPEEDS_TO_SEARCH[best_choice], MAXES_TO_SEARCH[best_choice])
}
fn min_cost_value(cost: &[floatX]) -> floatX {
let best_choice = min_cost_index_for_speed(cost);
cost[best_choice]
}
const SINGLETON_COMBINED_STRATEGY: usize = 2;
const SINGLETON_STRIDE_STRATEGY: usize = 1;
const SINGLETON_CM_STRATEGY: usize = 0;
pub struct ContextMapEntropy<
'a,
Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>,
> {
input: InputPair<'a>,
context_map: interface::PredictionModeContextMap<InputReferenceMut<'a>>,
block_type: u8,
cur_stride: u8,
local_byte_offset: usize,
weight: [[Weights; NUM_SPEEDS_TO_TRY]; 2],
cm_priors: <Alloc as Allocator<u16>>::AllocatedMemory,
stride_priors: <Alloc as Allocator<u16>>::AllocatedMemory,
_stride_pyramid_leaves: [u8; find_stride::NUM_LEAF_NODES],
singleton_costs: [[[floatX; NUM_SPEEDS_TO_TRY]; 2]; 3],
}
impl<'a, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>>
ContextMapEntropy<'a, Alloc>
{
pub fn new(
m16: &mut Alloc,
input: InputPair<'a>,
stride: [u8; find_stride::NUM_LEAF_NODES],
prediction_mode: interface::PredictionModeContextMap<InputReferenceMut<'a>>,
cdf_detection_quality: u8,
) -> Self {
let cdf_detect = cdf_detection_quality != 0;
let mut ret = ContextMapEntropy::<Alloc> {
input,
context_map: prediction_mode,
block_type: 0,
cur_stride: 1,
local_byte_offset: 0,
cm_priors: alloc_if::<u16, _>(cdf_detect, m16, CONTEXT_MAP_PRIOR_SIZE),
stride_priors: alloc_if::<u16, _>(cdf_detect, m16, STRIDE_PRIOR_SIZE),
_stride_pyramid_leaves: stride,
weight: [
[Weights::new(); NUM_SPEEDS_TO_TRY],
[Weights::new(); NUM_SPEEDS_TO_TRY],
],
singleton_costs: [[[0.0; NUM_SPEEDS_TO_TRY]; 2]; 3],
};
if cdf_detect {
init_cdfs(ret.cm_priors.slice_mut());
init_cdfs(ret.stride_priors.slice_mut());
}
ret
}
pub fn take_prediction_mode(
&mut self,
) -> interface::PredictionModeContextMap<InputReferenceMut<'a>> {
core::mem::replace(
&mut self.context_map,
interface::PredictionModeContextMap::<InputReferenceMut<'a>> {
literal_context_map: InputReferenceMut::default(),
predmode_speed_and_distance_context_map: InputReferenceMut::default(),
},
)
}
pub fn prediction_mode_mut(
&mut self,
) -> &mut interface::PredictionModeContextMap<InputReferenceMut<'a>> {
&mut self.context_map
}
pub fn best_singleton_speeds(
&self,
cm: bool,
combined: bool,
) -> ([SpeedAndMax; 2], [floatX; 2]) {
let cost_type_index = if combined {
2usize
} else if cm {
0usize
} else {
1
};
let mut ret_cost = [
self.singleton_costs[cost_type_index][0][0],
self.singleton_costs[cost_type_index][1][0],
];
let mut best_indexes = [0, 0];
for speed_index in 1..NUM_SPEEDS_TO_TRY {
for highness in 0..2 {
let cur_cost = self.singleton_costs[cost_type_index][highness][speed_index];
if cur_cost < ret_cost[highness] {
best_indexes[highness] = speed_index;
ret_cost[highness] = cur_cost;
}
}
}
let ret_speed = [
SpeedAndMax(
SPEEDS_TO_SEARCH[best_indexes[0]],
MAXES_TO_SEARCH[best_indexes[0]],
),
SpeedAndMax(
SPEEDS_TO_SEARCH[best_indexes[1]],
MAXES_TO_SEARCH[best_indexes[1]],
),
];
(ret_speed, ret_cost)
}
pub fn best_speeds(
&mut self, // mut due to helpers
cm: bool,
combined: bool,
) -> [SpeedAndMax; 2] {
let mut ret = [SpeedAndMax(SPEEDS_TO_SEARCH[0], MAXES_TO_SEARCH[0]); 2];
let cost_type_index = if combined {
2usize
} else if cm {
0usize
} else {
1
};
for high in 0..2 {
/*eprintln!("TRIAL {} {}", cm, combined);
for i in 0..NUM_SPEEDS_TO_TRY {
eprintln!("{},{} costs {:?}", SPEEDS_TO_SEARCH[i], MAXES_TO_SEARCH[i], self.singleton_costs[cost_type_index][high][i]);
}*/
ret[high] = min_cost_speed_max(&self.singleton_costs[cost_type_index][high][..]);
}
ret
}
pub fn best_speeds_costs(
&mut self, // mut due to helpers
cm: bool,
combined: bool,
) -> [floatX; 2] {
let cost_type_index = if combined {
2usize
} else if cm {
0usize
} else {
1
};
let mut ret = [0.0; 2];
for high in 0..2 {
ret[high] = min_cost_value(&self.singleton_costs[cost_type_index][high][..]);
}
ret
}
pub fn free(&mut self, alloc: &mut Alloc) {
<Alloc as Allocator<u16>>::free_cell(alloc, core::mem::take(&mut self.cm_priors));
<Alloc as Allocator<u16>>::free_cell(alloc, core::mem::take(&mut self.stride_priors));
}
fn update_cost_base(
&mut self,
stride_prior: u8,
_selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
let upper_nibble = (literal >> 4);
let lower_nibble = literal & 0xf;
let provisional_cm_high_cdf: [u16; 16];
let provisional_cm_low_cdf: [u16; 16];
{
let cm_cdf_high = get_cm_cdf_high(self.cm_priors.slice_mut(), cm_prior);
compute_cost(
&mut self.singleton_costs[SINGLETON_CM_STRATEGY][1],
cm_cdf_high,
upper_nibble,
);
// choose a fairly reasonable cm speed rather than a selected one
let best_cm_index = DEFAULT_CM_SPEED_INDEX; // = min_cost_index_for_speed(&self.singleton_costs[SINGLETON_CM_STRATEGY][1]);
provisional_cm_high_cdf = extract_single_cdf(cm_cdf_high, best_cm_index);
}
{
let cm_cdf_low = get_cm_cdf_low(self.cm_priors.slice_mut(), cm_prior, upper_nibble);
compute_cost(
&mut self.singleton_costs[SINGLETON_CM_STRATEGY][0],
cm_cdf_low,
lower_nibble,
);
// choose a fairly reasonable cm speed rather than a selected one
let best_cm_index = DEFAULT_CM_SPEED_INDEX; //min_cost_index_for_speed(&self.singleton_costs[SINGLETON_CM_STRATEGY][0]);
provisional_cm_low_cdf = extract_single_cdf(cm_cdf_low, best_cm_index);
}
{
let stride_cdf_high =
get_stride_cdf_high(self.stride_priors.slice_mut(), stride_prior, cm_prior);
compute_combined_cost(
&mut self.singleton_costs[SINGLETON_COMBINED_STRATEGY][1],
stride_cdf_high,
provisional_cm_high_cdf,
upper_nibble,
&mut self.weight[1],
);
compute_cost(
&mut self.singleton_costs[SINGLETON_STRIDE_STRATEGY][1],
stride_cdf_high,
upper_nibble,
);
update_cdf(stride_cdf_high, upper_nibble);
}
{
let stride_cdf_low = get_stride_cdf_low(
self.stride_priors.slice_mut(),
stride_prior,
cm_prior,
upper_nibble,
);
compute_combined_cost(
&mut self.singleton_costs[SINGLETON_COMBINED_STRATEGY][0],
stride_cdf_low,
provisional_cm_low_cdf,
lower_nibble,
&mut self.weight[0],
);
compute_cost(
&mut self.singleton_costs[SINGLETON_STRIDE_STRATEGY][0],
stride_cdf_low,
lower_nibble,
);
update_cdf(stride_cdf_low, lower_nibble);
}
{
let cm_cdf_high = get_cm_cdf_high(self.cm_priors.slice_mut(), cm_prior);
update_cdf(cm_cdf_high, upper_nibble);
}
{
let cm_cdf_low = get_cm_cdf_low(self.cm_priors.slice_mut(), cm_prior, upper_nibble);
update_cdf(cm_cdf_low, lower_nibble);
}
}
}
impl<'a, 'b, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>>
interface::CommandProcessor<'b> for ContextMapEntropy<'a, Alloc>
{
fn push(&mut self, val: interface::Command<InputReference<'b>>) {
push_base(self, val)
}
}
impl<'a, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>>
IRInterpreter for ContextMapEntropy<'a, Alloc>
{
fn inc_local_byte_offset(&mut self, inc: usize) {
self.local_byte_offset += inc;
}
fn local_byte_offset(&self) -> usize {
self.local_byte_offset
}
fn update_block_type(&mut self, new_type: u8, stride: u8) {
self.block_type = new_type;
self.cur_stride = stride;
}
fn block_type(&self) -> u8 {
self.block_type
}
fn literal_data_at_offset(&self, index: usize) -> u8 {
self.input[index]
}
fn literal_context_map(&self) -> &[u8] {
self.context_map.literal_context_map.slice()
}
fn prediction_mode(&self) -> crate::interface::LiteralPredictionModeNibble {
self.context_map.literal_prediction_mode()
}
fn update_cost(
&mut self,
stride_prior: [u8; 8],
stride_prior_offset: usize,
selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
let stride = self.cur_stride as usize;
self.update_cost_base(
stride_prior[stride_prior_offset.wrapping_sub(stride) & 7],
selected_bits,
cm_prior,
literal,
)
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+575
View File
@@ -0,0 +1,575 @@
/* Copyright 2010 Google Inc. All Rights Reserved.
Distributed under MIT license.
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
/* Entropy encoding (Huffman) utilities. */
use core::cmp::max;
#[derive(Clone, Copy, Default)]
pub struct HuffmanTree {
pub total_count_: u32,
pub index_left_: i16,
pub index_right_or_value_: i16,
}
impl HuffmanTree {
pub fn new(count: u32, left: i16, right: i16) -> Self {
Self {
total_count_: count,
index_left_: left,
index_right_or_value_: right,
}
}
}
pub fn BrotliSetDepth(p0: i32, pool: &mut [HuffmanTree], depth: &mut [u8], max_depth: i32) -> bool {
let mut stack: [i32; 16] = [0; 16];
let mut level: i32 = 0i32;
let mut p: i32 = p0;
stack[0] = -1i32;
loop {
if (pool[(p as usize)]).index_left_ as i32 >= 0i32 {
level += 1;
if level > max_depth {
return false;
}
stack[level as usize] = (pool[(p as usize)]).index_right_or_value_ as i32;
p = (pool[(p as usize)]).index_left_ as i32;
{
continue;
}
} else {
let pp = pool[(p as usize)];
depth[((pp).index_right_or_value_ as usize)] = level as u8;
}
while level >= 0i32 && (stack[level as usize] == -1i32) {
level -= 1;
}
if level < 0i32 {
return true;
}
p = stack[level as usize];
stack[level as usize] = -1i32;
}
}
pub trait HuffmanComparator {
fn Cmp(&self, a: &HuffmanTree, b: &HuffmanTree) -> bool;
}
pub struct SortHuffmanTree {}
impl HuffmanComparator for SortHuffmanTree {
fn Cmp(&self, v0: &HuffmanTree, v1: &HuffmanTree) -> bool {
if v0.total_count_ != v1.total_count_ {
v0.total_count_ < v1.total_count_
} else {
v0.index_right_or_value_ > v1.index_right_or_value_
}
}
}
pub fn SortHuffmanTreeItems<Comparator: HuffmanComparator>(
items: &mut [HuffmanTree],
n: usize,
comparator: Comparator,
) {
static gaps: [usize; 6] = [132, 57, 23, 10, 4, 1];
if n < 13 {
for i in 1..n {
let mut tmp: HuffmanTree = items[i];
let mut k: usize = i;
let mut j: usize = i.wrapping_sub(1);
while comparator.Cmp(&mut tmp, &mut items[j]) {
items[k] = items[j];
k = j;
if {
let _old = j;
j = j.wrapping_sub(1);
_old
} == 0
{
break;
}
}
items[k] = tmp;
}
} else {
let mut g: i32 = if n < 57usize { 2i32 } else { 0i32 };
while g < 6i32 {
{
let gap: usize = gaps[g as usize];
for i in gap..n {
let mut j: usize = i;
let mut tmp: HuffmanTree = items[i];
while j >= gap && (comparator.Cmp(&mut tmp, &mut items[j.wrapping_sub(gap)])) {
{
items[j] = items[j.wrapping_sub(gap)];
}
j = j.wrapping_sub(gap);
}
items[j] = tmp;
}
}
g += 1;
}
}
}
/* This function will create a Huffman tree.
The catch here is that the tree cannot be arbitrarily deep.
Brotli specifies a maximum depth of 15 bits for "code trees"
and 7 bits for "code length code trees."
count_limit is the value that is to be faked as the minimum value
and this minimum value is raised until the tree matches the
maximum length requirement.
This algorithm is not of excellent performance for very long data blocks,
especially when population counts are longer than 2**tree_limit, but
we are not planning to use this with extremely long blocks.
See https://en.wikipedia.org/wiki/Huffman_coding */
pub fn BrotliCreateHuffmanTree(
data: &[u32],
length: usize,
tree_limit: i32,
tree: &mut [HuffmanTree],
depth: &mut [u8],
) {
let sentinel = HuffmanTree::new(u32::MAX, -1, -1);
let mut count_limit = 1u32;
'break1: loop {
{
let mut n: usize = 0usize;
let mut i: usize;
let mut j: usize;
let mut k: usize;
i = length;
while i != 0usize {
i = i.wrapping_sub(1);
if data[i] != 0 {
let count: u32 = max(data[i], count_limit);
tree[n] = HuffmanTree::new(count, -1, i as i16);
n = n.wrapping_add(1);
}
}
if n == 1 {
depth[((tree[0]).index_right_or_value_ as usize)] = 1u8;
{
break 'break1;
}
}
SortHuffmanTreeItems(tree, n, SortHuffmanTree {});
tree[n] = sentinel;
tree[n.wrapping_add(1)] = sentinel;
i = 0usize;
j = n.wrapping_add(1);
k = n.wrapping_sub(1);
while k != 0usize {
{
let left: usize;
let right: usize;
if (tree[i]).total_count_ <= (tree[j]).total_count_ {
left = i;
i = i.wrapping_add(1);
} else {
left = j;
j = j.wrapping_add(1);
}
if (tree[i]).total_count_ <= (tree[j]).total_count_ {
right = i;
i = i.wrapping_add(1);
} else {
right = j;
j = j.wrapping_add(1);
}
{
let j_end: usize = (2usize).wrapping_mul(n).wrapping_sub(k);
(tree[j_end]).total_count_ = (tree[left])
.total_count_
.wrapping_add((tree[right]).total_count_);
(tree[j_end]).index_left_ = left as i16;
(tree[j_end]).index_right_or_value_ = right as i16;
tree[j_end.wrapping_add(1)] = sentinel;
}
}
k = k.wrapping_sub(1);
}
if BrotliSetDepth(
(2usize).wrapping_mul(n).wrapping_sub(1) as i32,
tree,
depth,
tree_limit,
) {
break 'break1;
}
}
count_limit = count_limit.wrapping_mul(2);
}
}
pub fn BrotliOptimizeHuffmanCountsForRle(
mut length: usize,
counts: &mut [u32],
good_for_rle: &mut [u8],
) {
let mut nonzero_count: usize = 0usize;
let mut stride: usize;
let mut limit: usize;
let mut sum: usize;
let streak_limit: usize = 1240usize;
for i in 0usize..length {
if counts[i] != 0 {
nonzero_count = nonzero_count.wrapping_add(1);
}
}
if nonzero_count < 16usize {
return;
}
while length != 0usize && (counts[length.wrapping_sub(1)] == 0u32) {
length = length.wrapping_sub(1);
}
if length == 0usize {
return;
}
{
let mut nonzeros: usize = 0usize;
let mut smallest_nonzero: u32 = (1i32 << 30) as u32;
for i in 0usize..length {
if counts[i] != 0u32 {
nonzeros = nonzeros.wrapping_add(1);
if smallest_nonzero > counts[i] {
smallest_nonzero = counts[i];
}
}
}
if nonzeros < 5usize {
return;
}
if smallest_nonzero < 4u32 {
let zeros: usize = length.wrapping_sub(nonzeros);
if zeros < 6 {
for i in 1..length.wrapping_sub(1) {
if counts[i - 1] != 0 && counts[i] == 0 && counts[i + 1] != 0 {
counts[i] = 1;
}
}
}
}
if nonzeros < 28usize {
return;
}
}
for rle_item in good_for_rle.iter_mut() {
*rle_item = 0;
}
{
let mut symbol: u32 = counts[0];
let mut step: usize = 0usize;
for i in 0..=length {
if i == length || counts[i] != symbol {
if symbol == 0u32 && (step >= 5usize) || symbol != 0u32 && (step >= 7usize) {
for k in 0usize..step {
good_for_rle[i.wrapping_sub(k).wrapping_sub(1)] = 1u8;
}
}
step = 1;
if i != length {
symbol = counts[i];
}
} else {
step = step.wrapping_add(1);
}
}
}
stride = 0usize;
limit = (256u32)
.wrapping_mul((counts[0]).wrapping_add(counts[1]).wrapping_add(counts[2]))
.wrapping_div(3)
.wrapping_add(420) as usize;
sum = 0usize;
for i in 0..=length {
if i == length
|| good_for_rle[i] != 0
|| i != 0usize && (good_for_rle[i.wrapping_sub(1)] != 0)
|| ((256u32).wrapping_mul(counts[i]) as usize)
.wrapping_sub(limit)
.wrapping_add(streak_limit)
>= (2usize).wrapping_mul(streak_limit)
{
if stride >= 4usize || stride >= 3usize && (sum == 0usize) {
let mut count: usize = sum
.wrapping_add(stride.wrapping_div(2))
.wrapping_div(stride);
if count == 0usize {
count = 1;
}
if sum == 0usize {
count = 0usize;
}
for k in 0usize..stride {
counts[i.wrapping_sub(k).wrapping_sub(1)] = count as u32;
}
}
stride = 0usize;
sum = 0usize;
if i < length.wrapping_sub(2) {
limit = (256u32)
.wrapping_mul(
(counts[i])
.wrapping_add(counts[i.wrapping_add(1)])
.wrapping_add(counts[i.wrapping_add(2)]),
)
.wrapping_div(3)
.wrapping_add(420) as usize;
} else if i < length {
limit = (256u32).wrapping_mul(counts[i]) as usize;
} else {
limit = 0usize;
}
}
stride = stride.wrapping_add(1);
if i != length {
sum = sum.wrapping_add(counts[i] as usize);
if stride >= 4usize {
limit = (256usize)
.wrapping_mul(sum)
.wrapping_add(stride.wrapping_div(2))
.wrapping_div(stride);
}
if stride == 4usize {
limit = limit.wrapping_add(120);
}
}
}
}
pub(crate) fn decide_over_rle_use(depth: &[u8], length: usize) -> (bool, bool) {
let mut total_reps_zero: usize = 0usize;
let mut total_reps_non_zero: usize = 0usize;
let mut count_reps_zero: usize = 1;
let mut count_reps_non_zero: usize = 1;
let mut i: usize;
i = 0usize;
while i < length {
let value: u8 = depth[i];
let mut reps: usize = 1;
let mut k: usize;
k = i.wrapping_add(1);
while k < length && (depth[k] as i32 == value as i32) {
{
reps = reps.wrapping_add(1);
}
k = k.wrapping_add(1);
}
if reps >= 3usize && (value as i32 == 0i32) {
total_reps_zero = total_reps_zero.wrapping_add(reps);
count_reps_zero = count_reps_zero.wrapping_add(1);
}
if reps >= 4usize && (value as i32 != 0i32) {
total_reps_non_zero = total_reps_non_zero.wrapping_add(reps);
count_reps_non_zero = count_reps_non_zero.wrapping_add(1);
}
i = i.wrapping_add(reps);
}
let use_rle_for_non_zero = total_reps_non_zero > count_reps_non_zero.wrapping_mul(2);
let use_rle_for_zero = total_reps_zero > count_reps_zero.wrapping_mul(2);
(use_rle_for_non_zero, use_rle_for_zero)
}
fn Reverse(v: &mut [u8], mut start: usize, mut end: usize) {
end = end.wrapping_sub(1);
while start < end {
v.swap(start, end);
start = start.wrapping_add(1);
end = end.wrapping_sub(1);
}
}
fn BrotliWriteHuffmanTreeRepetitions(
previous_value: u8,
value: u8,
mut repetitions: usize,
tree_size: &mut usize,
tree: &mut [u8],
extra_bits_data: &mut [u8],
) {
if previous_value as i32 != value as i32 {
tree[*tree_size] = value;
extra_bits_data[*tree_size] = 0u8;
*tree_size = tree_size.wrapping_add(1);
repetitions = repetitions.wrapping_sub(1);
}
if repetitions == 7usize {
tree[*tree_size] = value;
extra_bits_data[*tree_size] = 0u8;
*tree_size = tree_size.wrapping_add(1);
repetitions = repetitions.wrapping_sub(1);
}
if repetitions < 3usize {
for _i in 0usize..repetitions {
tree[*tree_size] = value;
extra_bits_data[*tree_size] = 0u8;
*tree_size = tree_size.wrapping_add(1);
}
} else {
let start: usize = *tree_size;
repetitions = repetitions.wrapping_sub(3);
loop {
tree[*tree_size] = 16u8;
extra_bits_data[*tree_size] = (repetitions & 0x03) as u8;
*tree_size = tree_size.wrapping_add(1);
repetitions >>= 2i32;
if repetitions == 0usize {
break;
}
repetitions = repetitions.wrapping_sub(1);
}
Reverse(tree, start, *tree_size);
Reverse(extra_bits_data, start, *tree_size);
}
}
fn BrotliWriteHuffmanTreeRepetitionsZeros(
mut repetitions: usize,
tree_size: &mut usize,
tree: &mut [u8],
extra_bits_data: &mut [u8],
) {
if repetitions == 11 {
tree[*tree_size] = 0u8;
extra_bits_data[*tree_size] = 0u8;
*tree_size = tree_size.wrapping_add(1);
repetitions = repetitions.wrapping_sub(1);
}
if repetitions < 3usize {
for _i in 0usize..repetitions {
tree[*tree_size] = 0u8;
extra_bits_data[*tree_size] = 0u8;
*tree_size = tree_size.wrapping_add(1);
}
} else {
let start: usize = *tree_size;
repetitions = repetitions.wrapping_sub(3);
loop {
tree[*tree_size] = 17u8;
extra_bits_data[*tree_size] = (repetitions & 0x7usize) as u8;
*tree_size = tree_size.wrapping_add(1);
repetitions >>= 3i32;
if repetitions == 0usize {
break;
}
repetitions = repetitions.wrapping_sub(1);
}
Reverse(tree, start, *tree_size);
Reverse(extra_bits_data, start, *tree_size);
}
}
pub fn BrotliWriteHuffmanTree(
depth: &[u8],
length: usize,
tree_size: &mut usize,
tree: &mut [u8],
extra_bits_data: &mut [u8],
) {
let mut previous_value: u8 = 8u8;
let mut i: usize;
let mut use_rle_for_non_zero = false;
let mut use_rle_for_zero = false;
let mut new_length: usize = length;
i = 0usize;
'break27: while i < length {
{
if depth[length.wrapping_sub(i).wrapping_sub(1)] as i32 == 0i32 {
new_length = new_length.wrapping_sub(1);
} else {
break 'break27;
}
}
i = i.wrapping_add(1);
}
if length > 50 {
(use_rle_for_non_zero, use_rle_for_zero) = decide_over_rle_use(depth, new_length);
}
i = 0usize;
while i < new_length {
let value: u8 = depth[i];
let mut reps: usize = 1;
if value != 0 && use_rle_for_non_zero || value == 0 && use_rle_for_zero {
let mut k: usize;
k = i.wrapping_add(1);
while k < new_length && (depth[k] as i32 == value as i32) {
{
reps = reps.wrapping_add(1);
}
k = k.wrapping_add(1);
}
}
if value as i32 == 0i32 {
BrotliWriteHuffmanTreeRepetitionsZeros(reps, tree_size, tree, extra_bits_data);
} else {
BrotliWriteHuffmanTreeRepetitions(
previous_value,
value,
reps,
tree_size,
tree,
extra_bits_data,
);
previous_value = value;
}
i = i.wrapping_add(reps);
}
}
fn BrotliReverseBits(num_bits: usize, mut bits: u16) -> u16 {
static kLut: [usize; 16] = [
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf,
];
let mut retval: usize = kLut[(bits as i32 & 0xfi32) as usize];
let mut i: usize;
i = 4usize;
while i < num_bits {
{
retval <<= 4i32;
bits = (bits as i32 >> 4) as u16;
retval |= kLut[(bits as i32 & 0xfi32) as usize];
}
i = i.wrapping_add(4);
}
retval >>= (0usize.wrapping_sub(num_bits) & 0x3usize);
retval as u16
}
const MAX_HUFFMAN_BITS: usize = 16;
pub fn BrotliConvertBitDepthsToSymbols(depth: &[u8], len: usize, bits: &mut [u16]) {
/* In Brotli, all bit depths are [1..15]
0 bit depth means that the symbol does not exist. */
let mut bl_count: [u16; MAX_HUFFMAN_BITS] = [0; MAX_HUFFMAN_BITS];
let mut next_code: [u16; MAX_HUFFMAN_BITS] = [0; MAX_HUFFMAN_BITS];
let mut code: i32 = 0i32;
for i in 0usize..len {
let _rhs = 1;
let _lhs = &mut bl_count[depth[i] as usize];
*_lhs = (*_lhs as i32 + _rhs) as u16;
}
bl_count[0] = 0u16;
next_code[0] = 0u16;
for i in 1..MAX_HUFFMAN_BITS {
code = (code + bl_count[i - 1] as i32) << 1;
next_code[i] = code as u16;
}
for i in 0usize..len {
if depth[i] != 0 {
bits[i] = BrotliReverseBits(depth[i] as usize, {
let _rhs = 1;
let _lhs = &mut next_code[depth[i] as usize];
let _old = *_lhs;
*_lhs = (*_lhs as i32 + _rhs) as u16;
_old
});
}
}
}
+860
View File
@@ -0,0 +1,860 @@
use core::cmp::{max, min};
use core::ops::{Index, IndexMut, Range};
use super::super::alloc;
use super::super::alloc::{SliceWrapper, SliceWrapperMut};
use super::input_pair::{InputPair, InputReference};
use super::interface;
use super::util::FastLog2;
use crate::enc::combined_alloc::alloc_if;
// float32 doesn't have enough resolution for blocks of data more than 3.5 megs
pub type floatY = f64;
// the cost of storing a particular population of data including the approx
// cost of a huffman table to describe the frequencies of each symbol
pub fn HuffmanCost(population: &[u32]) -> floatY {
assert_eq!(population.len(), 256 * 256);
let mut cost: floatY = 0.0 as floatY;
let mut sum: floatY = 0.0 as floatY;
let mut buckets: floatY = 0.0 as floatY;
for pop in population.iter() {
if *pop == 0 {
continue;
}
cost -= *pop as floatY * FastLog2(*pop as u64) as floatY;
sum += *pop as floatY;
buckets += 1.0 as floatY;
}
//println!("Observed {} nonzero buckets with a sum of {}, hc={}", buckets, sum, cost);
16.0 as floatY * buckets + cost + sum * FastLog2(sum as u64) as floatY
}
// this holds a population of data assuming 1 byte of prior for that data
// bucket_populations is therefore a 65536-long dynamically allocated buffer
pub struct EntropyBucketPopulation<AllocU32: alloc::Allocator<u32>> {
pub bucket_populations: AllocU32::AllocatedMemory,
pub cached_bit_entropy: floatY,
}
impl<AllocU32: alloc::Allocator<u32>> EntropyBucketPopulation<AllocU32> {
pub fn new(m32: &mut AllocU32) -> Self {
let size = 256 * 256;
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
}
}
pub fn free(&mut self, m32: &mut AllocU32) {
m32.free_cell(core::mem::take(&mut self.bucket_populations));
}
fn clone_from(&mut self, other: &EntropyBucketPopulation<AllocU32>) {
self.bucket_populations
.slice_mut()
.clone_from_slice(other.bucket_populations.slice());
}
fn add_assign(&mut self, other: &EntropyBucketPopulation<AllocU32>) {
assert_eq!(
self.bucket_populations.slice().len(),
other.bucket_populations.slice().len()
);
for (item, other_item) in self
.bucket_populations
.slice_mut()
.iter_mut()
.zip(other.bucket_populations.slice().iter())
{
*item += *other_item;
}
self.cached_bit_entropy = HuffmanCost(self.bucket_populations.slice());
}
// clear the allocated memory and reset literal population to zero
fn bzero(&mut self) {
self.cached_bit_entropy = 0.0;
for bp in self.bucket_populations.slice_mut().iter_mut() {
*bp = 0;
}
}
// setup population to the sum of an array of populations where the stride of that row matches. Additionally allow another optional
fn initiate_from(
&mut self,
rows: [&[Self]; 2],
rows_stride: [&[u8]; 2],
stride: u8,
do_clear: bool,
) {
self.cached_bit_entropy = 0.0;
let mut found_any = false;
for (sub_row, sub_stride) in rows.iter().zip(rows_stride.iter()) {
for (item, istride) in sub_row.iter().zip(sub_stride.iter()) {
if *istride != stride {
continue; // if we chain, then optional was already filtered by stride
}
if do_clear && !found_any {
self.bucket_populations
.slice_mut()
.clone_from_slice(item.bucket_populations.slice());
found_any = true;
} else {
for (dst, src) in self
.bucket_populations
.slice_mut()
.iter_mut()
.zip(item.bucket_populations.slice().iter())
{
*dst += *src;
}
}
}
}
if do_clear && !found_any {
self.bzero();
} else {
self.cached_bit_entropy = HuffmanCost(self.bucket_populations.slice());
}
}
fn bit_cost_of_data_subset(
&mut self,
data0: &[u8],
mut stride: u8,
mut prev_bytes: [u8; NUM_STRIDES],
scratch: &mut EntropyBucketPopulation<AllocU32>,
) -> floatY {
prev_bytes.reverse();
stride = max(1, stride); // we return stride=1 to mean 1 away
scratch
.bucket_populations
.slice_mut()
.clone_from_slice(self.bucket_populations.slice());
scratch.bucket_populations.slice_mut()[65535] += 1; // to demonstrate that we have
scratch.bucket_populations.slice_mut()[65535] -= 1; // to demonstrate that we have write capability
let mut stray_count = 0.0 as floatY;
assert_eq!((NUM_STRIDES - 1) & NUM_STRIDES, 0); // must be power of two
for (index, val) in data0.iter().enumerate() {
let prior_byte =
prev_bytes[(index + (NUM_STRIDES - stride as usize)) & (NUM_STRIDES - 1)];
let loc = &mut scratch.bucket_populations.slice_mut()
[prior_byte as usize * 256 + *val as usize];
if *loc == 0 {
stray_count += 1.0;
} else {
*loc -= 1;
}
prev_bytes[index & (NUM_STRIDES - 1)] = *val;
}
if self.cached_bit_entropy == 0.0 as floatY {
self.cached_bit_entropy = HuffmanCost(self.bucket_populations.slice());
}
debug_assert_eq!(
HuffmanCost(self.bucket_populations.slice()),
self.cached_bit_entropy
);
scratch.cached_bit_entropy = HuffmanCost(scratch.bucket_populations.slice());
self.cached_bit_entropy - scratch.cached_bit_entropy + stray_count * 8.0
}
}
const NUM_STRIDES: usize = 8;
#[derive(Copy, Clone)]
pub struct BucketPopIndex {
pub val: u8,
pub six_bits: u8,
pub stride: u8,
}
impl<AllocU32: alloc::Allocator<u32>> Index<BucketPopIndex> for EntropyBucketPopulation<AllocU32> {
type Output = u32;
fn index(&self, index: BucketPopIndex) -> &u32 {
&self.bucket_populations.slice()
[index.val as usize + index.six_bits as usize * 256 + index.stride as usize * 256 * 64]
}
}
impl<AllocU32: alloc::Allocator<u32>> IndexMut<BucketPopIndex>
for EntropyBucketPopulation<AllocU32>
{
fn index_mut(&mut self, index: BucketPopIndex) -> &mut u32 {
&mut self.bucket_populations.slice_mut()
[index.val as usize + index.six_bits as usize * 256 + index.stride as usize * 256 * 64]
}
}
pub struct EntropyTally<AllocU32: alloc::Allocator<u32>> {
pop: [EntropyBucketPopulation<AllocU32>; NUM_STRIDES],
}
const NUM_LEVELS: usize = 4;
const NUM_NODES: usize = (1 << (NUM_LEVELS)) - 1;
pub const NUM_LEAF_NODES: usize = (NUM_NODES + 1) >> 1;
pub struct EntropyPyramid<AllocU32: alloc::Allocator<u32>> {
pop: [EntropyBucketPopulation<AllocU32>; NUM_NODES],
stride: [u8; NUM_NODES],
}
impl<AllocU32: alloc::Allocator<u32>> EntropyPyramid<AllocU32> {
pub fn last_level_range(&self) -> Range<usize> {
(NUM_NODES - (1 << (NUM_LEVELS - 1)))..NUM_NODES
}
pub fn byte_index_to_pyramid_index(&self, byte_index: usize, metablock_size: usize) -> usize {
let range = self.last_level_range();
min(
range.start + (range.end - range.start) * byte_index / metablock_size,
range.end - 1,
) // since we tally after the end of the literal block, it could be after the pyramid
}
pub fn reset_scratch_to_deepest_level(&self, output: &mut EntropyTally<AllocU32>) {
let mut has_modified = [false; NUM_STRIDES];
//println!("Last level range {:?}", self.last_level_range());
for index in self.last_level_range() {
if has_modified[self.stride[index] as usize] {
output.pop[self.stride[index] as usize].add_assign(&self.pop[index]);
} else {
output.pop[self.stride[index] as usize].clone_from(&self.pop[index]);
has_modified[self.stride[index] as usize] = true;
}
}
for stride in 0..NUM_STRIDES {
if !has_modified[stride] {
output.pop[stride].bzero();
output.pop[stride].cached_bit_entropy = 0.0;
} else {
output.pop[stride].cached_bit_entropy =
HuffmanCost(output.pop[stride].bucket_populations.slice());
}
//println!("BASE PYRAMID {} = {}", stride,output.pop[stride].cached_bit_entropy);
}
}
pub fn stride_last_level_range(&self) -> [u8; NUM_LEAF_NODES] {
let mut ret = [0u8; NUM_LEAF_NODES];
ret.clone_from_slice(self.stride.split_at(self.stride.len() - NUM_LEAF_NODES).1);
ret
}
pub fn free(&mut self, m32: &mut AllocU32) {
for item in self.pop.iter_mut() {
item.free(m32);
}
}
pub fn disabled_placeholder(_m32: &mut AllocU32) -> Self {
EntropyPyramid::<AllocU32> {
pop: [
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: AllocU32::AllocatedMemory::default(),
},
],
stride: [0; NUM_NODES],
}
}
pub fn new(m32: &mut AllocU32) -> Self {
let size = 256 * 256;
EntropyPyramid::<AllocU32> {
pop: [
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
EntropyBucketPopulation::<AllocU32> {
cached_bit_entropy: 0.0,
bucket_populations: m32.alloc_cell(size),
},
],
stride: [0; NUM_NODES],
}
}
pub fn bit_cost_of_literals(
&mut self,
data0: &[u8],
start_index: u32,
metablock_len: usize,
stride: u8,
previous_bytes: [u8; NUM_STRIDES],
scratch: &mut EntropyTally<AllocU32>,
) -> floatY {
assert!(stride as usize <= NUM_STRIDES);
self.pop[self.byte_index_to_pyramid_index(start_index as usize, metablock_len)]
.bit_cost_of_data_subset(data0, stride, previous_bytes, &mut scratch.pop[0])
}
fn populate_entry_stride1(&mut self, input: InputPair, index: u32) {
let mut prev_val = 0;
let pyr_item = &mut self.pop[index as usize];
pyr_item.bzero();
assert_eq!(pyr_item.bucket_populations.slice()[65535], 0);
for val in input.0.slice().iter().chain(input.1.slice().iter()) {
pyr_item.bucket_populations.slice_mut()[prev_val as usize * 256 + *val as usize] += 1;
prev_val = *val;
}
pyr_item.cached_bit_entropy = HuffmanCost(pyr_item.bucket_populations.slice());
self.stride[index as usize] = 0;
}
fn populate_entry(
&mut self,
input: InputPair,
scratch: &mut EntropyTally<AllocU32>,
index: u32,
mirror_range: Option<Range<usize>>,
prev_range: Option<Range<usize>>,
) {
let mut initial_entropies = [0.0 as floatY; NUM_STRIDES];
let nothing: &[EntropyBucketPopulation<AllocU32>] = &[];
let nothing_u8: &[u8] = &[];
{
let pop_ranges = [
match mirror_range {
None => nothing,
Some(ref ir) => &self.pop[ir.clone()],
},
match prev_range {
None => nothing,
Some(ref pr) => &self.pop[pr.clone()],
},
];
let stride_ranges = [
match mirror_range {
None => nothing_u8,
Some(ref ir) => &self.stride[ir.clone()],
},
match prev_range {
None => nothing_u8,
Some(ref pr) => &self.stride[pr.clone()],
},
];
for stride in 0..NUM_STRIDES {
scratch.pop[stride].initiate_from(pop_ranges, stride_ranges, stride as u8, true);
initial_entropies[stride] = scratch.pop[stride].cached_bit_entropy;
}
}
scratch.observe_input_stream(input.0.slice(), input.1.slice());
let mut best_entropy_index = 0;
let mut min_entropy_value = (scratch.pop[0].cached_bit_entropy - initial_entropies[0]);
//println!("{} OLD ENTROPY {:} NEW_ENTROPY {:}", best_entropy_index, scratch.pop[0].cached_bit_entropy, initial_entropies[0]);
for stride in 1..NUM_STRIDES {
let entropy_value = scratch.pop[stride].cached_bit_entropy - initial_entropies[stride];
//println!("{} OLD ENTROPY {:} NEW_ENTROPY {:}", stride, scratch.pop[stride].cached_bit_entropy, initial_entropies[stride]);
if entropy_value < min_entropy_value {
best_entropy_index = stride;
min_entropy_value = entropy_value;
}
}
self.pop[index as usize].clone_from(&scratch.pop[best_entropy_index]);
self.stride[index as usize] = best_entropy_index as u8;
}
pub fn populate_stride1(&mut self, input0: &[u8], input1: &[u8]) {
let input = InputPair(
InputReference {
data: input0,
orig_offset: 0,
},
InputReference {
data: input1,
orig_offset: input0.len(),
},
);
for i in 0..2 {
let first_range = if i == 0 {
input.split_at(input.len() >> 1).0
} else {
input.split_at(input.len() >> 1).1
};
for j in 0..2 {
let second_range = if j == 0 {
first_range.split_at(input.len() >> 2).0
} else {
first_range.split_at(input.len() >> 2).1
};
if NUM_LEVELS == 4 {
for k in 0..2 {
let third_range = if j == 0 {
second_range.split_at(input.len() >> 3).0
} else {
second_range.split_at(input.len() >> 3).1
};
self.populate_entry_stride1(third_range, 7 + ((i << 2) + (j << 1) + k));
}
} else {
assert_eq!(NUM_LEVELS, 3); // we hard coded the 3 levels for now... we can add more later or make this into some kind of recursion
self.populate_entry_stride1(second_range, 3 + ((i << 1) + j));
}
}
}
}
pub fn populate(&mut self, input0: &[u8], input1: &[u8], scratch: &mut EntropyTally<AllocU32>) {
let input = InputPair(
InputReference {
data: input0,
orig_offset: 0,
},
InputReference {
data: input1,
orig_offset: input0.len(),
},
);
self.populate_entry(input, scratch, 0, None, None); // BASE
// LEVEL 1
self.populate_entry(
input.split_at(input.len() >> 1).0,
scratch,
1,
Some(0..1),
None,
);
self.populate_entry(
input.split_at(input.len() >> 1).1,
scratch,
2,
None,
Some(1..2),
); // should we use the range from 0..1??
// LEVEL 2
self.populate_entry(
input.split_at(input.len() >> 2).0,
scratch,
3,
Some(1..3),
None,
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.0
.split_at(input.len() >> 2)
.1,
scratch,
4,
Some(2..3),
Some(3..4),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.0,
scratch,
5,
Some(3..5),
None,
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.1,
scratch,
6,
Some(3..6),
None,
);
if NUM_LEVELS == 4 {
// level 4
self.populate_entry(
input
.split_at(input.len() >> 1)
.0
.split_at(input.len() >> 2)
.0
.split_at(input.len() >> 3)
.0,
scratch,
7,
Some(4..7),
None,
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.0
.split_at(input.len() >> 2)
.0
.split_at(input.len() >> 3)
.1,
scratch,
8,
Some(4..7),
Some(7..8),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.0
.split_at(input.len() >> 2)
.1
.split_at(input.len() >> 3)
.0,
scratch,
9,
Some(5..7),
Some(7..9),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.0
.split_at(input.len() >> 2)
.1
.split_at(input.len() >> 3)
.1,
scratch,
0x0a,
Some(5..7),
Some(7..0xa),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.0
.split_at(input.len() >> 3)
.0,
scratch,
0xb,
Some(6..7),
Some(7..0xb),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.0
.split_at(input.len() >> 3)
.1,
scratch,
0xc,
Some(6..7),
Some(7..0xc),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.1
.split_at(input.len() >> 3)
.0,
scratch,
0xd,
None,
Some(7..0xd),
);
self.populate_entry(
input
.split_at(input.len() >> 1)
.1
.split_at(input.len() >> 2)
.1
.split_at(input.len() >> 3)
.1,
scratch,
0xe,
None,
Some(7..0xe),
);
} else {
assert_eq!(NUM_LEVELS, 3); // we hard coded the 3 levels for now... we can add more later or make this into some kind of recursion
}
}
}
impl<AllocU32: alloc::Allocator<u32>> EntropyTally<AllocU32> {
pub fn new(m32: &mut AllocU32, max_stride_arg: Option<u8>) -> EntropyTally<AllocU32> {
let size = 256 * 256;
let max_stride = max_stride_arg.unwrap_or(NUM_STRIDES as u8);
EntropyTally::<AllocU32> {
pop: [
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 0, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 1, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 2, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 3, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 4, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 5, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 6, m32, size),
},
EntropyBucketPopulation {
cached_bit_entropy: 0.0,
bucket_populations: alloc_if(max_stride > 7, m32, size),
},
],
}
}
pub fn disabled_placeholder(m32: &mut AllocU32) -> EntropyTally<AllocU32> {
Self::new(m32, Some(0))
}
fn observe_input_stream(&mut self, input0: &[u8], input1: &[u8]) {
let mut priors = [0u8; NUM_STRIDES];
for val in input0.iter().chain(input1.iter()) {
for stride in 0..NUM_STRIDES {
self.pop[stride].bucket_populations.slice_mut()
[priors[stride] as usize * 256 + (*val as usize)] += 1;
}
{
let mut tmp = [0u8; NUM_STRIDES - 1];
tmp.clone_from_slice(&priors[..(NUM_STRIDES - 1)]);
priors[1..].clone_from_slice(&tmp[..]);
priors[0] = *val;
}
}
for stride in 0..NUM_STRIDES {
self.pop[stride].cached_bit_entropy =
HuffmanCost(self.pop[stride].bucket_populations.slice());
}
}
fn identify_best_population_and_update_cache(&mut self) -> u8 {
let mut old_bit_entropy: [floatY; NUM_STRIDES] = [0.0; NUM_STRIDES];
for (obe, be) in old_bit_entropy.iter_mut().zip(self.pop.iter_mut()) {
*obe = be.cached_bit_entropy;
if *obe != 0.0 {
be.cached_bit_entropy = HuffmanCost(be.bucket_populations.slice());
}
}
let mut best_stride = 0u8;
let mut best_entropy = self.pop[0].cached_bit_entropy - old_bit_entropy[0];
//println!("Weighing {} as {}", best_stride, best_entropy);
for index in 1..NUM_STRIDES {
let cur = self.pop[index].cached_bit_entropy - old_bit_entropy[index];
//println!("Weighing {} as {} = [{} - {}]", index, cur, self.pop[index].cached_bit_entropy, old_bit_entropy[index]);
if (best_entropy == 0.0 || cur < best_entropy) && old_bit_entropy[index] > 0.0 {
best_stride = index as u8;
best_entropy = cur;
}
}
best_stride
}
pub fn peek(&mut self) -> &mut EntropyBucketPopulation<AllocU32> {
&mut self.pop[0]
}
pub fn get_previous_bytes(
&self,
input0: &[u8],
input1: &[u8],
bytes_processed: usize,
) -> [u8; NUM_STRIDES] {
let mut retval = [0u8; NUM_STRIDES];
for index in 0..NUM_STRIDES {
let bp_offset = index + 1;
if bp_offset <= bytes_processed {
let offset = bytes_processed - bp_offset;
if offset >= input0.len() {
retval[index] = input1[offset - input0.len()];
} else {
retval[index] = input0[offset];
}
}
}
retval
}
pub fn pick_best_stride<InputReference: SliceWrapper<u8>>(
&mut self,
commands: &[interface::Command<InputReference>],
input0: &[u8],
input1: &[u8],
bytes_processed: &mut usize,
entropy_pyramid: &EntropyPyramid<AllocU32>,
stride_detection_quality: u8,
) -> u8 {
if stride_detection_quality == 0 {
return 0;
}
//println!("ENTROPY PYRAMID {:?}", entropy_pyramid.stride);
if stride_detection_quality > 1 {
entropy_pyramid.reset_scratch_to_deepest_level(self);
}
let mut pyramid_byte_index: usize = 0;
for cmd in commands.iter() {
match *cmd {
interface::Command::Copy(ref copy) => {
*bytes_processed += copy.num_bytes as usize;
}
interface::Command::Dict(ref dict) => {
*bytes_processed += dict.final_size as usize;
}
interface::Command::Literal(ref lit) => {
if stride_detection_quality > 1 {
let mut priors = self.get_previous_bytes(input0, input1, *bytes_processed);
for (lindex, val) in lit.data.slice().iter().enumerate() {
if lindex == NUM_STRIDES {
let vpriors = self.get_previous_bytes(
input0,
input1,
NUM_STRIDES + *bytes_processed,
);
assert_eq!(vpriors, priors);
}
for (index, prior) in priors.iter().enumerate() {
self.pop[index].bucket_populations.slice_mut()
[256 * (*prior as usize) + *val as usize] += 1;
// increment the population value of this literal
// for the respective prior for the stride index
}
{
//reset prior values for the next item
let mut tmp = [0u8; 7];
tmp.clone_from_slice(&priors[..7]);
priors[1..].clone_from_slice(&tmp[..]);
priors[0] = *val;
}
}
}
*bytes_processed += lit.data.slice().len();
pyramid_byte_index = *bytes_processed;
}
interface::Command::BlockSwitchCommand(_)
| interface::Command::BlockSwitchLiteral(_)
| interface::Command::BlockSwitchDistance(_)
| interface::Command::PredictionMode(_) => {}
}
}
//println!("ENTROPY PYRAMID {:?} selected {}", entropy_pyramid.stride, best_stride);
if stride_detection_quality > 1 {
self.identify_best_population_and_update_cache() + 1
} else {
entropy_pyramid.stride[entropy_pyramid
.byte_index_to_pyramid_index(pyramid_byte_index, input0.len() + input1.len())]
+ 1
}
}
pub fn free(&mut self, m32: &mut AllocU32) {
for item in self.pop.iter_mut() {
m32.free_cell(core::mem::take(&mut item.bucket_populations))
}
}
pub fn is_free(&mut self) -> bool {
self.pop[0].bucket_populations.slice().is_empty()
}
}
+71
View File
@@ -0,0 +1,71 @@
pub const MAX_THREADS: usize = 16;
pub struct FixedQueue<T: Sized> {
data: [Option<T>; MAX_THREADS],
size: usize,
start: usize,
}
impl<T: Sized> Default for FixedQueue<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Sized> FixedQueue<T> {
pub fn new() -> Self {
FixedQueue {
data: [
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None,
],
size: 0,
start: 0,
}
}
pub fn can_push(&self) -> bool {
self.size < self.data.len()
}
pub fn size(&self) -> usize {
self.size
}
pub fn push(&mut self, item: T) -> Result<(), ()> {
if self.size == self.data.len() {
return Err(());
}
let index = (self.start + self.size) % self.data.len();
self.data[index] = Some(item);
self.size += 1;
Ok(())
}
pub fn pop(&mut self) -> Option<T> {
if self.size == 0 {
return None;
}
let index = self.start % self.data.len();
let ret = self.data[index].take();
self.start += 1;
self.size -= 1;
ret
}
pub fn how_much_free_space(&self) -> usize {
self.data.len() - self.size
}
pub fn remove<F: Fn(&Option<T>) -> bool>(&mut self, f: F) -> Option<T> {
if self.size == 0 {
return None;
}
for index in 0..self.size {
if f(&self.data[(self.start + index) % self.data.len()]) {
let start_index = self.start % self.data.len();
let target_index = (self.start + index) % self.data.len();
let ret = self.data[target_index].take();
let replace = self.data[start_index].take();
let is_none = core::mem::replace(&mut self.data[target_index], replace);
assert!(is_none.is_none());
self.start += 1;
self.size -= 1;
return ret;
}
}
None
}
}
+534
View File
@@ -0,0 +1,534 @@
use core;
use core::cmp::min;
use super::super::alloc;
use super::super::alloc::{SliceWrapper, SliceWrapperMut};
use super::block_split::BlockSplit;
use super::command::Command;
use super::constants::{kSigned3BitContextLookup, kUTF8ContextLookup};
use super::util::floatX;
use super::vectorization::Mem256i;
//#[derive(Clone)] clone is broken for arrays > 32
pub struct HistogramLiteral {
pub data_: [u32; 256],
pub total_count_: usize,
pub bit_cost_: floatX,
}
impl Clone for HistogramLiteral {
#[inline(always)]
fn clone(&self) -> HistogramLiteral {
HistogramLiteral {
data_: self.data_,
total_count_: self.total_count_,
bit_cost_: self.bit_cost_,
}
}
}
impl Default for HistogramLiteral {
#[inline(always)]
fn default() -> HistogramLiteral {
HistogramLiteral {
data_: [0; 256],
total_count_: 0,
bit_cost_: 3.402e+38,
}
}
}
//#[derive(Clone)] clone is broken for arrays > 32
pub struct HistogramCommand {
pub data_: [u32; 704],
pub total_count_: usize,
pub bit_cost_: floatX,
}
impl Clone for HistogramCommand {
#[inline(always)]
fn clone(&self) -> HistogramCommand {
HistogramCommand {
data_: self.data_,
total_count_: self.total_count_,
bit_cost_: self.bit_cost_,
}
}
}
impl Default for HistogramCommand {
#[inline(always)]
fn default() -> HistogramCommand {
HistogramCommand {
data_: [0; 704],
total_count_: 0,
bit_cost_: 3.402e+38,
}
}
}
//#[derive(Clone)] // #derive is broken for arrays > 32
#[cfg(not(feature = "disallow_large_window_size"))]
const BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS: usize = 544;
#[cfg(feature = "disallow_large_window_size")]
const BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS: usize = 520;
pub struct HistogramDistance {
pub data_: [u32; BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS],
pub total_count_: usize,
pub bit_cost_: floatX,
}
impl Clone for HistogramDistance {
fn clone(&self) -> HistogramDistance {
HistogramDistance {
data_: self.data_,
total_count_: self.total_count_,
bit_cost_: self.bit_cost_,
}
}
}
impl Default for HistogramDistance {
fn default() -> HistogramDistance {
HistogramDistance {
data_: [0; BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS],
total_count_: 0,
bit_cost_: 3.402e+38,
}
}
}
pub trait CostAccessors {
type i32vec: Sized + SliceWrapper<Mem256i> + SliceWrapperMut<Mem256i>;
fn make_nnz_storage() -> Self::i32vec;
fn total_count(&self) -> usize;
fn bit_cost(&self) -> floatX;
fn set_bit_cost(&mut self, cost: floatX);
fn set_total_count(&mut self, count: usize);
}
impl SliceWrapper<u32> for HistogramLiteral {
#[inline(always)]
fn slice(&self) -> &[u32] {
&self.data_[..]
}
}
impl SliceWrapperMut<u32> for HistogramLiteral {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [u32] {
&mut self.data_[..]
}
}
pub struct Array264i([Mem256i; 33]);
impl SliceWrapperMut<Mem256i> for Array264i {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [Mem256i] {
&mut self.0[..]
}
}
impl SliceWrapper<Mem256i> for Array264i {
#[inline(always)]
fn slice(&self) -> &[Mem256i] {
&self.0[..]
}
}
impl Default for Array264i {
#[inline(always)]
fn default() -> Array264i {
Array264i([Mem256i::default(); 33])
}
}
pub struct Array528i([Mem256i; 66]);
impl SliceWrapperMut<Mem256i> for Array528i {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [Mem256i] {
&mut self.0[..]
}
}
impl SliceWrapper<Mem256i> for Array528i {
#[inline(always)]
fn slice(&self) -> &[Mem256i] {
&self.0[..]
}
}
impl Default for Array528i {
#[inline(always)]
fn default() -> Array528i {
Array528i([Mem256i::default(); 66])
}
}
pub struct Array712i([Mem256i; 89]);
impl SliceWrapperMut<Mem256i> for Array712i {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [Mem256i] {
&mut self.0[..]
}
}
impl SliceWrapper<Mem256i> for Array712i {
#[inline(always)]
fn slice(&self) -> &[Mem256i] {
&self.0[..]
}
}
impl Default for Array712i {
#[inline(always)]
fn default() -> Array712i {
Array712i([Mem256i::default(); 89])
}
}
pub struct EmptyIVec {}
impl SliceWrapperMut<Mem256i> for EmptyIVec {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [Mem256i] {
&mut []
}
}
impl SliceWrapper<Mem256i> for EmptyIVec {
#[inline(always)]
fn slice(&self) -> &[Mem256i] {
&[]
}
}
impl Default for EmptyIVec {
#[inline(always)]
fn default() -> EmptyIVec {
EmptyIVec {}
}
}
#[cfg(feature = "vector_scratch_space")]
pub type HistogramLiteralScratch = Array264i;
#[cfg(not(feature = "vector_scratch_space"))]
pub type HistogramLiteralScratch = EmptyIVec;
impl CostAccessors for HistogramLiteral {
type i32vec = HistogramLiteralScratch;
fn make_nnz_storage() -> Self::i32vec {
HistogramLiteralScratch::default()
}
#[inline(always)]
fn total_count(&self) -> usize {
self.total_count_
}
#[inline(always)]
fn bit_cost(&self) -> floatX {
self.bit_cost_
}
#[inline(always)]
fn set_bit_cost(&mut self, data: floatX) {
self.bit_cost_ = data;
}
#[inline(always)]
fn set_total_count(&mut self, data: usize) {
self.total_count_ = data;
}
}
impl SliceWrapper<u32> for HistogramCommand {
#[inline(always)]
fn slice(&self) -> &[u32] {
&self.data_[..]
}
}
impl SliceWrapperMut<u32> for HistogramCommand {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [u32] {
&mut self.data_[..]
}
}
#[cfg(feature = "vector_scratch_space")]
pub type HistogramCommandScratch = Array712i;
#[cfg(not(feature = "vector_scratch_space"))]
pub type HistogramCommandScratch = EmptyIVec;
impl CostAccessors for HistogramCommand {
type i32vec = HistogramCommandScratch;
fn make_nnz_storage() -> Self::i32vec {
HistogramCommandScratch::default()
}
#[inline(always)]
fn total_count(&self) -> usize {
self.total_count_
}
#[inline(always)]
fn bit_cost(&self) -> floatX {
self.bit_cost_
}
#[inline(always)]
fn set_bit_cost(&mut self, data: floatX) {
self.bit_cost_ = data;
}
#[inline(always)]
fn set_total_count(&mut self, data: usize) {
self.total_count_ = data;
}
}
impl SliceWrapper<u32> for HistogramDistance {
#[inline(always)]
fn slice(&self) -> &[u32] {
&self.data_[..]
}
}
impl SliceWrapperMut<u32> for HistogramDistance {
#[inline(always)]
fn slice_mut(&mut self) -> &mut [u32] {
&mut self.data_[..]
}
}
#[cfg(feature = "vector_scratch_space")]
pub type HistogramDistanceScratch = Array528i;
#[cfg(not(feature = "vector_scratch_space"))]
pub type HistogramDistanceScratch = EmptyIVec;
impl CostAccessors for HistogramDistance {
type i32vec = HistogramDistanceScratch;
fn make_nnz_storage() -> Self::i32vec {
HistogramDistanceScratch::default()
}
#[inline(always)]
fn total_count(&self) -> usize {
self.total_count_
}
#[inline(always)]
fn bit_cost(&self) -> floatX {
self.bit_cost_
}
#[inline(always)]
fn set_bit_cost(&mut self, data: floatX) {
self.bit_cost_ = data;
}
#[inline(always)]
fn set_total_count(&mut self, data: usize) {
self.total_count_ = data;
}
}
#[derive(Copy, Clone)]
pub enum ContextType {
CONTEXT_LSB6 = 0,
CONTEXT_MSB6 = 1,
CONTEXT_UTF8 = 2,
CONTEXT_SIGNED = 3,
}
impl Default for ContextType {
#[inline(always)]
fn default() -> ContextType {
ContextType::CONTEXT_LSB6
}
}
pub struct BlockSplitIterator<'a, Alloc: alloc::Allocator<u8> + 'a + alloc::Allocator<u32> + 'a> {
pub split_: &'a BlockSplit<Alloc>,
pub idx_: usize,
pub type_: usize,
pub length_: usize,
}
impl<'a, Alloc: alloc::Allocator<u8> + alloc::Allocator<u32> + 'a> BlockSplitIterator<'a, Alloc> {
fn new(split: &'a BlockSplit<Alloc>) -> Self {
Self {
split_: split,
idx_: 0,
type_: 0,
length_: if !split.lengths.slice().is_empty() {
split.lengths.slice()[0] as usize
} else {
0
},
}
}
fn next(&mut self) {
if self.length_ == 0 {
self.idx_ = self.idx_.wrapping_add(1);
self.type_ = self.split_.types.slice()[self.idx_] as usize;
self.length_ = self.split_.lengths.slice()[self.idx_] as usize;
}
self.length_ = self.length_.wrapping_sub(1);
}
}
pub fn HistogramAddItem<HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors>(
xself: &mut HistogramType,
val: usize,
) {
{
let _rhs = 1;
let _lhs = &mut xself.slice_mut()[val];
let val = (*_lhs).wrapping_add(_rhs as u32);
*_lhs = val;
}
let new_count = xself.total_count().wrapping_add(1);
xself.set_total_count(new_count);
}
pub fn HistogramAddVector<
HistogramType: SliceWrapper<u32> + SliceWrapperMut<u32> + CostAccessors,
IntegerType: Sized + Clone,
>(
xself: &mut HistogramType,
p: &[IntegerType],
n: usize,
) where
u64: core::convert::From<IntegerType>,
{
let new_tc = xself.total_count().wrapping_add(n);
xself.set_total_count(new_tc);
for p_item in p[..n].iter() {
let _rhs = 1;
let index: usize = u64::from(p_item.clone()) as usize;
let _lhs = &mut xself.slice_mut()[index];
*_lhs = (*_lhs).wrapping_add(_rhs as u32);
}
}
#[inline(always)]
pub fn HistogramClear<HistogramType: SliceWrapperMut<u32> + CostAccessors>(
xself: &mut HistogramType,
) {
for data_elem in xself.slice_mut().iter_mut() {
*data_elem = 0;
}
xself.set_total_count(0);
xself.set_bit_cost(3.402e+38);
}
pub fn ClearHistograms<HistogramType: SliceWrapperMut<u32> + CostAccessors>(
array: &mut [HistogramType],
length: usize,
) {
for item in array[..length].iter_mut() {
HistogramClear(item)
}
}
#[inline(always)]
pub fn HistogramAddHistogram<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors,
>(
xself: &mut HistogramType,
v: &HistogramType,
) {
let old_total_count = xself.total_count();
xself.set_total_count(old_total_count + (*v).total_count());
let h0 = xself.slice_mut();
let h1 = v.slice();
let n = min(h0.len(), h1.len());
for i in 0..n {
let h0val = &mut h0[i];
let val = h0val.wrapping_add(h1[i]);
*h0val = val;
}
}
pub fn HistogramSelfAddHistogram<
HistogramType: SliceWrapperMut<u32> + SliceWrapper<u32> + CostAccessors,
>(
xself: &mut [HistogramType],
i0: usize,
i1: usize,
) {
let tc_new = xself[i1].total_count();
let tc_old = xself[i0].total_count();
xself[i0].set_total_count(tc_old.wrapping_add(tc_new));
let h0 = xself[i0].slice().len();
let h0a = xself[i0].slice().len();
let h1 = xself[i1].slice().len();
let n = min(h0, min(h0a, h1));
for h_index in 0..n {
let val = xself[i0].slice()[h_index].wrapping_add(xself[i1].slice()[h_index]);
xself[i0].slice_mut()[h_index] = val;
}
}
#[inline(always)]
pub fn Context(p1: u8, p2: u8, mode: ContextType) -> u8 {
match mode {
ContextType::CONTEXT_SIGNED => {
(((kSigned3BitContextLookup[p1 as usize] as i32) << 3)
+ kSigned3BitContextLookup[p2 as usize] as i32) as u8
}
ContextType::CONTEXT_UTF8 => {
(kUTF8ContextLookup[p1 as usize] as i32
| kUTF8ContextLookup[(p2 as i32 + 256i32) as usize] as i32) as u8
}
ContextType::CONTEXT_MSB6 => (p1 as i32 >> 2) as u8,
ContextType::CONTEXT_LSB6 => (p1 as i32 & 0x3fi32) as u8, /* else {
0u8
}*/
}
}
pub fn BrotliBuildHistogramsWithContext<'a, Alloc: alloc::Allocator<u8> + alloc::Allocator<u32>>(
cmds: &[Command],
num_commands: usize,
literal_split: &BlockSplit<Alloc>,
insert_and_copy_split: &BlockSplit<Alloc>,
dist_split: &BlockSplit<Alloc>,
ringbuffer: &[u8],
start_pos: usize,
mask: usize,
mut prev_byte: u8,
mut prev_byte2: u8,
context_modes: &[ContextType],
literal_histograms: &mut [HistogramLiteral],
insert_and_copy_histograms: &mut [HistogramCommand],
copy_dist_histograms: &mut [HistogramDistance],
) {
let mut pos: usize = start_pos;
let mut literal_it: BlockSplitIterator<Alloc>;
let mut insert_and_copy_it: BlockSplitIterator<Alloc>;
let mut dist_it: BlockSplitIterator<Alloc>;
literal_it = BlockSplitIterator::new(literal_split);
insert_and_copy_it = BlockSplitIterator::new(insert_and_copy_split);
dist_it = BlockSplitIterator::new(dist_split);
for i in 0usize..num_commands {
let cmd = &cmds[i];
let mut j: usize;
insert_and_copy_it.next();
HistogramAddItem(
&mut insert_and_copy_histograms[insert_and_copy_it.type_],
cmd.cmd_prefix_ as usize,
);
j = cmd.insert_len_ as usize;
while j != 0usize {
{
literal_it.next();
let context: usize = if !context_modes.is_empty() {
(literal_it.type_ << 6).wrapping_add(Context(
prev_byte,
prev_byte2,
context_modes[literal_it.type_],
) as usize)
} else {
literal_it.type_
};
HistogramAddItem(
&mut literal_histograms[(context as usize)],
ringbuffer[(pos & mask)] as usize,
);
prev_byte2 = prev_byte;
prev_byte = ringbuffer[(pos & mask)];
pos = pos.wrapping_add(1);
}
j = j.wrapping_sub(1);
}
pos = pos.wrapping_add(cmd.copy_len() as usize);
if cmd.copy_len() != 0 {
prev_byte2 = ringbuffer[(pos.wrapping_sub(2) & mask)];
prev_byte = ringbuffer[(pos.wrapping_sub(1) & mask)];
if cmd.cmd_prefix_ as i32 >= 128i32 {
dist_it.next();
let context: usize =
(dist_it.type_ << 2).wrapping_add(cmd.distance_context() as usize);
HistogramAddItem(
&mut copy_dist_histograms[(context as usize)],
cmd.dist_prefix_ as usize & 0x3ff,
);
}
}
}
}
+146
View File
@@ -0,0 +1,146 @@
use core;
use core::cmp::min;
use super::super::alloc::{SliceWrapper, SliceWrapperMut};
use super::interface::Freezable;
#[derive(Copy, Clone, Default, Debug)]
pub struct InputReference<'a> {
pub data: &'a [u8],
pub orig_offset: usize, // offset into the original slice of data
}
impl<'a> SliceWrapper<u8> for InputReference<'a> {
fn slice(&self) -> &[u8] {
self.data
}
}
impl<'a> Freezable for InputReference<'a> {
fn freeze(&self) -> super::interface::SliceOffset {
debug_assert!(self.data.len() <= 0xffff_ffff);
super::interface::SliceOffset(self.orig_offset, self.data.len() as u32)
}
}
#[derive(Default)]
pub struct InputReferenceMut<'a> {
pub data: &'a mut [u8],
pub orig_offset: usize, // offset into the original slice of data
}
impl<'a> SliceWrapper<u8> for InputReferenceMut<'a> {
fn slice(&self) -> &[u8] {
self.data
}
}
impl<'a> SliceWrapperMut<u8> for InputReferenceMut<'a> {
fn slice_mut(&mut self) -> &mut [u8] {
self.data
}
}
impl<'a> From<InputReferenceMut<'a>> for InputReference<'a> {
fn from(val: InputReferenceMut<'a>) -> InputReference<'a> {
InputReference {
data: val.data,
orig_offset: val.orig_offset,
}
}
}
impl<'a> From<&'a InputReferenceMut<'a>> for InputReference<'a> {
fn from(val: &'a InputReferenceMut<'a>) -> InputReference<'a> {
InputReference {
data: val.data,
orig_offset: val.orig_offset,
}
}
}
#[derive(Clone, Debug, Copy)]
pub struct InputPair<'a>(pub InputReference<'a>, pub InputReference<'a>);
impl<'a> PartialEq for InputPair<'a> {
fn eq(&self, other: &InputPair<'_>) -> bool {
if self.0.len() + self.1.len() != other.0.len() + other.1.len() {
return false;
}
for (a_iter, b_iter) in self
.0
.data
.iter()
.chain(self.1.data.iter())
.zip(other.0.data.iter().chain(other.1.data.iter()))
{
if *a_iter != *b_iter {
return false;
}
}
true
}
}
impl<'a> core::ops::Index<usize> for InputPair<'a> {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
if index >= self.0.len() {
&self.1.data[index - self.0.len()]
} else {
&self.0.data[index]
}
}
}
impl<'a> core::fmt::LowerHex for InputPair<'a> {
fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
for item in self.0.data {
fmtr.write_fmt(format_args!("{:02x}", item))?
}
for item in self.1.data {
fmtr.write_fmt(format_args!("{:02x}", item))?
}
Ok(())
}
}
impl<'a> InputPair<'a> {
pub fn split_at(&self, loc: usize) -> (InputPair<'a>, InputPair<'a>) {
if loc >= self.0.len() {
let offset_from_self_1 = loc - self.0.len();
let (first, second) = self.1.data.split_at(min(offset_from_self_1, self.1.len()));
return (
InputPair::<'a>(
self.0,
InputReference::<'a> {
data: first,
orig_offset: self.1.orig_offset,
},
),
InputPair::<'a>(
InputReference::<'a>::default(),
InputReference::<'a> {
data: second,
orig_offset: offset_from_self_1 + self.1.orig_offset,
},
),
);
}
let (first, second) = self.0.data.split_at(min(loc, self.0.len()));
(
InputPair::<'a>(
InputReference::<'a> {
data: first,
orig_offset: self.0.orig_offset,
},
InputReference::<'a>::default(),
),
InputPair::<'a>(
InputReference::<'a> {
data: second,
orig_offset: self.0.orig_offset + loc,
},
self.1,
),
)
}
pub fn len(&self) -> usize {
self.0.len() + self.1.len()
}
}
+795
View File
@@ -0,0 +1,795 @@
use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core;
use super::histogram;
pub use super::input_pair::{InputPair, InputReference, InputReferenceMut};
#[derive(Debug, Copy, Clone, Default)]
pub struct BlockSwitch(pub u8);
/// Commands that can instantiate as a no-op should implement this.
pub trait Nop<T> {
fn nop() -> T;
}
impl BlockSwitch {
#[inline(always)]
pub fn new(block_type: u8) -> Self {
BlockSwitch(block_type)
}
#[inline(always)]
pub fn block_type(&self) -> u8 {
self.0
}
}
#[derive(Debug, Copy, Clone, Default)]
pub struct LiteralBlockSwitch(pub BlockSwitch, pub u8);
impl LiteralBlockSwitch {
pub fn new(block_type: u8, stride: u8) -> Self {
LiteralBlockSwitch(BlockSwitch::new(block_type), stride)
}
#[inline(always)]
pub fn block_type(&self) -> u8 {
self.0.block_type()
}
#[inline(always)]
pub fn stride(&self) -> u8 {
self.1
}
#[inline(always)]
pub fn update_stride(&mut self, new_stride: u8) {
self.1 = new_stride;
}
}
pub const LITERAL_PREDICTION_MODE_SIGN: u8 = 3;
pub const LITERAL_PREDICTION_MODE_UTF8: u8 = 2;
pub const LITERAL_PREDICTION_MODE_MSB6: u8 = 1;
pub const LITERAL_PREDICTION_MODE_LSB6: u8 = 0;
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct LiteralPredictionModeNibble(pub u8);
impl LiteralPredictionModeNibble {
#[inline(always)]
pub fn new(prediction_mode: u8) -> Result<Self, ()> {
if prediction_mode < 16 {
return Ok(LiteralPredictionModeNibble(prediction_mode));
}
Err(())
}
#[inline(always)]
pub fn prediction_mode(&self) -> u8 {
self.0
}
#[inline(always)]
pub fn signed() -> Self {
LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_SIGN)
}
#[inline(always)]
pub fn utf8() -> Self {
LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_UTF8)
}
#[inline(always)]
pub fn msb6() -> Self {
LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_MSB6)
}
#[inline(always)]
pub fn lsb6() -> Self {
LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_LSB6)
}
#[inline(always)]
pub fn to_context_enum(&self) -> Result<histogram::ContextType, ()> {
match self.0 {
LITERAL_PREDICTION_MODE_LSB6 => Ok(histogram::ContextType::CONTEXT_LSB6),
LITERAL_PREDICTION_MODE_MSB6 => Ok(histogram::ContextType::CONTEXT_MSB6),
LITERAL_PREDICTION_MODE_UTF8 => Ok(histogram::ContextType::CONTEXT_UTF8),
LITERAL_PREDICTION_MODE_SIGN => Ok(histogram::ContextType::CONTEXT_SIGNED),
_ => Err(()),
}
}
}
pub const NUM_SPEED_VALUES: usize = 12;
pub const NUM_MIXING_VALUES: usize = 16 * 256 + 16 * 256;
pub const NUM_PREDMODE_SETUP_VALUES: usize = 4;
pub const RESERVED_OFFSET: usize = 3;
pub const ADV_CONTEXT_MAP_OFFSET: usize = 2;
pub const MIXING_MATH_OFFSET: usize = 1;
pub const PREDMODE_OFFSET: usize = 0;
pub const MIXING_OFFSET: usize = NUM_PREDMODE_SETUP_VALUES + PREDMODE_OFFSET;
pub const SPEED_OFFSET: usize = MIXING_OFFSET + NUM_MIXING_VALUES;
pub const DISTANCE_CONTEXT_MAP_OFFSET: usize = SPEED_OFFSET + NUM_SPEED_VALUES;
pub const MAX_PREDMODE_SPEED_AND_DISTANCE_CONTEXT_MAP_SIZE: usize =
DISTANCE_CONTEXT_MAP_OFFSET + 256 * 4;
pub const MAX_LITERAL_CONTEXT_MAP_SIZE: usize = 256 * 64;
pub const MAX_ADV_LITERAL_CONTEXT_MAP_SIZE: usize = 256 * 64 * 2;
#[derive(Debug)]
pub struct PredictionModeContextMap<SliceType: SliceWrapper<u8>> {
pub literal_context_map: SliceType,
pub predmode_speed_and_distance_context_map: SliceType,
}
impl<SliceType: SliceWrapper<u8> + SliceWrapperMut<u8>> PredictionModeContextMap<SliceType> {
#[inline]
pub fn distance_context_map_mut(&mut self) -> &mut [u8] {
self.predmode_speed_and_distance_context_map
.slice_mut()
.split_at_mut(DISTANCE_CONTEXT_MAP_OFFSET)
.1
}
#[inline]
pub fn set_stride_context_speed(&mut self, speed_max: [(u16, u16); 2]) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
for high in 0..2 {
cm_slice[Self::stride_context_speed_offset() + high] =
Self::u16_to_f8(speed_max[high].0);
cm_slice[Self::stride_context_speed_max_offset() + high] =
Self::u16_to_f8(speed_max[high].1);
}
}
#[inline]
pub fn set_context_map_speed(&mut self, speed_max: [(u16, u16); 2]) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
for high in 0..2 {
cm_slice[Self::context_map_speed_offset() + high] = Self::u16_to_f8(speed_max[high].0);
cm_slice[Self::context_map_speed_max_offset() + high] =
Self::u16_to_f8(speed_max[high].1);
}
}
pub fn set_mixing_math(&mut self, math_enum: u8) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
cm_slice[MIXING_MATH_OFFSET] = math_enum;
}
pub fn set_adv_context_map(&mut self, is_adv: u8) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
cm_slice[ADV_CONTEXT_MAP_OFFSET] = is_adv;
}
#[inline]
pub fn set_mixing_values(&mut self, mixing_mask: &[u8; NUM_MIXING_VALUES]) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)]
.clone_from_slice(&mixing_mask[..]);
}
#[inline]
pub fn get_mixing_values_mut(&mut self) -> &mut [u8] {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
&mut cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)]
}
#[inline]
pub fn set_combined_stride_context_speed(&mut self, speed_max: [(u16, u16); 2]) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
for high in 0..2 {
cm_slice[Self::combined_stride_context_speed_offset() + high] =
Self::u16_to_f8(speed_max[high].0);
cm_slice[Self::combined_stride_context_speed_max_offset() + high] =
Self::u16_to_f8(speed_max[high].1);
}
}
pub fn set_literal_prediction_mode(&mut self, val: LiteralPredictionModeNibble) {
let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut();
cm_slice[PREDMODE_OFFSET] = val.0;
}
}
impl<SliceType: SliceWrapper<u8>> PredictionModeContextMap<SliceType> {
#[inline]
pub fn from_mut<Other: SliceWrapper<u8>>(
other: PredictionModeContextMap<Other>,
) -> PredictionModeContextMap<SliceType>
where
SliceType: From<Other>,
{
PredictionModeContextMap::<SliceType> {
literal_context_map: SliceType::from(other.literal_context_map),
predmode_speed_and_distance_context_map: SliceType::from(
other.predmode_speed_and_distance_context_map,
),
}
}
#[inline]
pub fn get_mixing_values(&self) -> &[u8] {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
&cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)]
}
#[inline]
pub fn get_mixing_math(&self) -> u8 {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
if cm_slice.len() <= MIXING_MATH_OFFSET {
return 1;
}
cm_slice[MIXING_MATH_OFFSET]
}
#[inline]
pub fn get_is_adv_context_map(&self) -> u8 {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
if cm_slice.len() <= ADV_CONTEXT_MAP_OFFSET {
return 0;
}
cm_slice[ADV_CONTEXT_MAP_OFFSET]
}
#[inline]
pub fn has_context_speeds(&self) -> bool {
self.predmode_speed_and_distance_context_map.slice().len() >= DISTANCE_CONTEXT_MAP_OFFSET
}
#[inline]
pub fn size_of_combined_array(distance_context_map_size: usize) -> usize {
distance_context_map_size + DISTANCE_CONTEXT_MAP_OFFSET
}
#[inline]
pub fn context_speeds_standard_len(&self) -> usize {
NUM_SPEED_VALUES
}
#[inline]
pub fn context_speeds_f8(&self) -> &[u8] {
&self.predmode_speed_and_distance_context_map.slice()
[SPEED_OFFSET..DISTANCE_CONTEXT_MAP_OFFSET]
}
#[inline]
pub fn distance_context_map(&self) -> &[u8] {
self.predmode_speed_and_distance_context_map
.slice()
.split_at(DISTANCE_CONTEXT_MAP_OFFSET)
.1
}
#[inline]
pub fn f8_to_u16(data: u8) -> u16 {
self::u8_to_speed(data)
}
#[inline]
pub fn u16_to_f8(data: u16) -> u8 {
self::speed_to_u8(data)
}
#[inline]
pub fn stride_context_speed_offset() -> usize {
SPEED_OFFSET
}
#[inline]
pub fn stride_context_speed_max_offset() -> usize {
SPEED_OFFSET + 2
}
#[inline]
pub fn context_map_speed_offset() -> usize {
SPEED_OFFSET + 4
}
#[inline]
pub fn context_map_speed_max_offset() -> usize {
SPEED_OFFSET + 6
}
#[inline]
pub fn combined_stride_context_speed_offset() -> usize {
SPEED_OFFSET + 8
}
#[inline]
pub fn combined_stride_context_speed_max_offset() -> usize {
SPEED_OFFSET + 10
}
#[inline]
pub fn literal_prediction_mode(&self) -> LiteralPredictionModeNibble {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
if PREDMODE_OFFSET < cm_slice.len() {
LiteralPredictionModeNibble(cm_slice[PREDMODE_OFFSET])
} else {
LiteralPredictionModeNibble::default()
}
}
pub fn stride_context_speed(&self) -> [(u16, u16); 2] {
let v = self.stride_context_speed_f8();
[
(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)),
(self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1)),
]
}
pub fn context_map_speed(&self) -> [(u16, u16); 2] {
let v = self.context_map_speed_f8();
[
(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)),
(self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1)),
]
}
pub fn combined_stride_context_speed(&self) -> [(u16, u16); 2] {
let v = self.combined_stride_context_speed_f8();
[
(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)),
(self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1)),
]
}
#[inline]
pub fn stride_context_speed_f8(&self) -> [(u8, u8); 2] {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
let low_speed = cm_slice[Self::stride_context_speed_offset()];
let high_speed = cm_slice[Self::stride_context_speed_offset() + 1];
let low_max = cm_slice[Self::stride_context_speed_max_offset()];
let high_max = cm_slice[Self::stride_context_speed_max_offset() + 1];
[(low_speed, low_max), (high_speed, high_max)]
}
#[inline]
pub fn combined_stride_context_speed_f8(&self) -> [(u8, u8); 2] {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
let low_speed = cm_slice[Self::combined_stride_context_speed_offset()];
let high_speed = cm_slice[Self::combined_stride_context_speed_offset() + 1];
let low_max = cm_slice[Self::combined_stride_context_speed_max_offset()];
let high_max = cm_slice[Self::combined_stride_context_speed_max_offset() + 1];
[(low_speed, low_max), (high_speed, high_max)]
}
#[inline]
pub fn context_map_speed_f8(&self) -> [(u8, u8); 2] {
let cm_slice = self.predmode_speed_and_distance_context_map.slice();
let low_speed = cm_slice[Self::context_map_speed_offset()];
let high_speed = cm_slice[Self::context_map_speed_offset() + 1];
let low_max = cm_slice[Self::context_map_speed_max_offset()];
let high_max = cm_slice[Self::context_map_speed_max_offset() + 1];
[(low_speed, low_max), (high_speed, high_max)]
}
}
impl<SliceType: SliceWrapper<u8> + Clone> Clone for PredictionModeContextMap<SliceType> {
#[inline(always)]
fn clone(&self) -> Self {
PredictionModeContextMap::<SliceType> {
literal_context_map: self.literal_context_map.clone(),
predmode_speed_and_distance_context_map: self
.predmode_speed_and_distance_context_map
.clone(),
}
}
}
impl<SliceType: SliceWrapper<u8> + Clone + Copy> Copy for PredictionModeContextMap<SliceType> {}
#[derive(Debug, Clone, Copy)]
pub struct CopyCommand {
pub distance: u32,
pub num_bytes: u32,
}
impl Nop<CopyCommand> for CopyCommand {
#[inline(always)]
fn nop() -> Self {
CopyCommand {
distance: 1,
num_bytes: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DictCommand {
pub word_size: u8,
pub transform: u8,
pub final_size: u8,
pub empty: u8,
pub word_id: u32,
}
impl Nop<DictCommand> for DictCommand {
#[inline(always)]
fn nop() -> Self {
DictCommand {
word_size: 0,
transform: 0,
final_size: 0,
empty: 1,
word_id: 0,
}
}
}
#[derive(Debug)]
#[cfg(not(feature = "external-literal-probability"))]
pub struct FeatureFlagSliceType<SliceType: SliceWrapper<u8>>(core::marker::PhantomData<SliceType>);
#[cfg(not(feature = "external-literal-probability"))]
impl<SliceType: SliceWrapper<u8>> SliceWrapper<u8> for FeatureFlagSliceType<SliceType> {
fn slice(&self) -> &[u8] {
&[]
}
}
#[cfg(not(feature = "external-literal-probability"))]
impl<SliceType: SliceWrapper<u8> + Default> Default for FeatureFlagSliceType<SliceType> {
fn default() -> Self {
FeatureFlagSliceType::<SliceType>(core::marker::PhantomData::<SliceType>)
}
}
#[derive(Debug)]
#[cfg(feature = "external-literal-probability")]
pub struct FeatureFlagSliceType<SliceType: SliceWrapper<u8>>(pub SliceType);
#[cfg(feature = "external-literal-probability")]
impl<SliceType: SliceWrapper<u8>> SliceWrapper<u8> for FeatureFlagSliceType<SliceType> {
#[inline(always)]
fn slice(&self) -> &[u8] {
self.0.slice()
}
}
#[cfg(feature = "external-literal-probability")]
impl<SliceType: SliceWrapper<u8> + Default> Default for FeatureFlagSliceType<SliceType> {
#[inline(always)]
fn default() -> Self {
FeatureFlagSliceType::<SliceType>(SliceType::default())
}
}
impl<SliceType: SliceWrapper<u8> + Clone> Clone for FeatureFlagSliceType<SliceType> {
#[inline(always)]
fn clone(&self) -> Self {
FeatureFlagSliceType::<SliceType>(self.0)
}
}
impl<SliceType: SliceWrapper<u8> + Clone + Copy> Copy for FeatureFlagSliceType<SliceType> {}
#[derive(Debug)]
pub struct LiteralCommand<SliceType: SliceWrapper<u8>> {
pub data: SliceType,
pub prob: FeatureFlagSliceType<SliceType>,
pub high_entropy: bool, // this block of bytes is high entropy with a few patterns never seen again; adapt slower
}
impl<SliceType: SliceWrapper<u8>> SliceWrapper<u8> for LiteralCommand<SliceType> {
#[inline(always)]
fn slice(&self) -> &[u8] {
self.data.slice()
}
}
impl<SliceType: SliceWrapper<u8> + SliceWrapperMut<u8>> SliceWrapperMut<u8>
for LiteralCommand<SliceType>
{
#[inline(always)]
fn slice_mut(&mut self) -> &mut [u8] {
self.data.slice_mut()
}
}
impl<SliceType: SliceWrapper<u8> + Default> Nop<LiteralCommand<SliceType>>
for LiteralCommand<SliceType>
{
#[inline(always)]
fn nop() -> Self {
LiteralCommand {
data: SliceType::default(),
prob: FeatureFlagSliceType::<SliceType>::default(),
high_entropy: false,
}
}
}
impl<SliceType: SliceWrapper<u8> + Clone> Clone for LiteralCommand<SliceType> {
#[inline(always)]
fn clone(&self) -> LiteralCommand<SliceType> {
LiteralCommand::<SliceType> {
data: self.data.clone(),
prob: self.prob.clone(),
high_entropy: self.high_entropy,
}
}
}
impl<SliceType: SliceWrapper<u8> + Clone + Copy> Copy for LiteralCommand<SliceType> {}
#[derive(Debug)]
pub enum Command<SliceType: SliceWrapper<u8>> {
Copy(CopyCommand),
Dict(DictCommand),
Literal(LiteralCommand<SliceType>),
BlockSwitchCommand(BlockSwitch),
BlockSwitchLiteral(LiteralBlockSwitch),
BlockSwitchDistance(BlockSwitch),
PredictionMode(PredictionModeContextMap<SliceType>),
}
impl<SliceType: SliceWrapper<u8> + Default> Command<SliceType> {
#[inline]
pub fn free_array<F>(&mut self, apply_func: &mut F)
where
F: FnMut(SliceType),
{
match self {
Command::Literal(ref mut lit) => apply_func(core::mem::take(&mut lit.data)),
Command::PredictionMode(ref mut pm) => {
apply_func(core::mem::take(&mut pm.literal_context_map));
apply_func(core::mem::take(
&mut pm.predmode_speed_and_distance_context_map,
));
}
_ => {}
}
}
}
impl<SliceType: SliceWrapper<u8>> Default for Command<SliceType> {
#[inline(always)]
fn default() -> Self {
Command::<SliceType>::nop()
}
}
impl<SliceType: SliceWrapper<u8>> Nop<Command<SliceType>> for Command<SliceType> {
#[inline(always)]
fn nop() -> Command<SliceType> {
Command::Copy(CopyCommand::nop())
}
}
impl<SliceType: SliceWrapper<u8> + Clone> Clone for Command<SliceType> {
#[inline]
fn clone(&self) -> Command<SliceType> {
match self {
Command::Copy(copy) => Command::Copy(*copy),
Command::Dict(dict) => Command::Dict(*dict),
Command::Literal(literal) => Command::Literal(literal.clone()),
Command::BlockSwitchCommand(switch) => Command::BlockSwitchCommand(*switch),
Command::BlockSwitchLiteral(switch) => Command::BlockSwitchLiteral(*switch),
Command::BlockSwitchDistance(switch) => Command::BlockSwitchDistance(*switch),
Command::PredictionMode(pm) => Command::PredictionMode(pm.clone()),
}
}
}
impl<SliceType: SliceWrapper<u8> + Clone + Copy> Copy for Command<SliceType> {}
#[inline(always)]
pub fn free_cmd_inline<SliceTypeAllocator: Allocator<u8>>(
xself: &mut Command<SliceTypeAllocator::AllocatedMemory>,
m8: &mut SliceTypeAllocator,
) {
match *xself {
Command::Literal(ref mut lit) => m8.free_cell(core::mem::take(&mut lit.data)),
Command::PredictionMode(ref mut pm) => {
m8.free_cell(core::mem::take(&mut pm.literal_context_map));
m8.free_cell(core::mem::take(
&mut pm.predmode_speed_and_distance_context_map,
));
}
Command::Dict(_)
| Command::Copy(_)
| Command::BlockSwitchCommand(_)
| Command::BlockSwitchLiteral(_)
| Command::BlockSwitchDistance(_) => {}
}
}
#[inline(never)]
pub fn free_cmd<SliceTypeAllocator: Allocator<u8>>(
xself: &mut Command<SliceTypeAllocator::AllocatedMemory>,
m8: &mut SliceTypeAllocator,
) {
free_cmd_inline(xself, m8)
}
#[derive(Clone, Copy, Default, Debug)]
pub struct SliceOffset(pub usize, pub u32);
impl SliceWrapper<u8> for SliceOffset {
fn slice(&self) -> &[u8] {
// not perfect--shouldn't be calling this without thawing the wrapper
&[]
}
}
pub trait Freezable {
fn freeze(&self) -> SliceOffset;
}
pub trait Unfreezable {
fn thaw<'a>(&self, data: &'a [u8]) -> InputReference<'a>;
fn thaw_mut<'a>(&self, data: &'a mut [u8]) -> InputReferenceMut<'a>;
fn thaw_pair<'a>(&self, pair: &InputPair<'a>) -> Result<InputReference<'a>, ()>;
}
impl<'a> From<InputReference<'a>> for SliceOffset {
fn from(f: InputReference<'a>) -> Self {
debug_assert!(f.data.len() <= 0xffff_ffff);
SliceOffset(f.orig_offset, f.data.len() as u32)
}
}
impl Unfreezable for SliceOffset {
fn thaw<'a>(&self, data: &'a [u8]) -> InputReference<'a> {
InputReference {
data: data.split_at(self.0).1.split_at(self.1 as usize).0,
orig_offset: self.0,
}
}
fn thaw_mut<'a>(&self, data: &'a mut [u8]) -> InputReferenceMut<'a> {
InputReferenceMut {
data: data.split_at_mut(self.0).1.split_at_mut(self.1 as usize).0,
orig_offset: self.0,
}
}
fn thaw_pair<'a>(&self, pair: &InputPair<'a>) -> Result<InputReference<'a>, ()> {
if self.0 >= pair.1.orig_offset {
return Ok(InputReference {
data: pair
.1
.data
.split_at(self.0 - pair.1.orig_offset)
.1
.split_at(self.1 as usize)
.0,
orig_offset: self.0,
});
}
let offset = self.0 - pair.0.orig_offset;
if offset + self.1 as usize <= pair.0.data.len() {
// overlap
Ok(InputReference {
data: pair.0.data.split_at(offset).1.split_at(self.1 as usize).0,
orig_offset: self.0,
})
} else {
Err(())
}
}
}
impl SliceOffset {
pub fn offset(&self) -> usize {
self.0
}
pub fn len(&self) -> usize {
self.1 as usize
}
pub fn len32(&self) -> u32 {
self.1
}
}
pub type StaticCommand = Command<SliceOffset>;
pub trait CommandProcessor<'a> {
fn push(&mut self, val: Command<InputReference<'a>>);
fn push_literals(&mut self, data: &InputPair<'a>) {
if data.0.len() != 0 {
self.push(Command::Literal(LiteralCommand {
data: data.0,
prob: FeatureFlagSliceType::<InputReference>::default(),
high_entropy: false,
}));
}
if data.1.len() != 0 {
self.push(Command::Literal(LiteralCommand {
data: data.1,
prob: FeatureFlagSliceType::<InputReference>::default(),
high_entropy: false,
}));
}
}
fn push_rand_literals(&mut self, data: &InputPair<'a>) {
if data.0.len() != 0 {
self.push(Command::Literal(LiteralCommand {
data: data.0,
prob: FeatureFlagSliceType::<InputReference>::default(),
high_entropy: true,
}));
}
if data.1.len() != 0 {
self.push(Command::Literal(LiteralCommand {
data: data.1,
prob: FeatureFlagSliceType::<InputReference>::default(),
high_entropy: true,
}));
}
}
fn push_block_switch_literal(&mut self, block_type: u8) {
self.push(Command::BlockSwitchLiteral(LiteralBlockSwitch::new(
block_type, 0,
)))
}
}
impl<SliceType: Unfreezable + SliceWrapper<u8>> Command<SliceType> {
pub fn thaw_pair<'a>(&self, data: &InputPair<'a>) -> Command<InputReference<'a>> {
match self {
Command::Literal(ref lit) => Command::Literal(LiteralCommand {
data: lit.data.thaw_pair(data).unwrap(),
prob: FeatureFlagSliceType::default(),
high_entropy: lit.high_entropy,
}),
Command::PredictionMode(ref pm) => Command::PredictionMode(PredictionModeContextMap {
literal_context_map: pm.literal_context_map.thaw_pair(data).unwrap(),
predmode_speed_and_distance_context_map: pm
.predmode_speed_and_distance_context_map
.thaw_pair(data)
.unwrap(),
}),
Command::Dict(ref d) => Command::Dict(*d),
Command::Copy(ref c) => Command::Copy(*c),
Command::BlockSwitchCommand(ref c) => Command::BlockSwitchCommand(*c),
Command::BlockSwitchLiteral(ref c) => Command::BlockSwitchLiteral(*c),
Command::BlockSwitchDistance(ref c) => Command::BlockSwitchDistance(*c),
}
}
pub fn thaw<'a>(&self, data: &'a [u8]) -> Command<InputReference<'a>> {
match self {
Command::Literal(ref lit) => Command::Literal(LiteralCommand {
data: lit.data.thaw(data),
prob: FeatureFlagSliceType::default(),
high_entropy: lit.high_entropy,
}),
Command::PredictionMode(ref pm) => Command::PredictionMode(PredictionModeContextMap {
literal_context_map: pm.literal_context_map.thaw(data),
predmode_speed_and_distance_context_map: pm
.predmode_speed_and_distance_context_map
.thaw(data),
}),
Command::Dict(ref d) => Command::Dict(*d),
Command::Copy(ref c) => Command::Copy(*c),
Command::BlockSwitchCommand(ref c) => Command::BlockSwitchCommand(*c),
Command::BlockSwitchLiteral(ref c) => Command::BlockSwitchLiteral(*c),
Command::BlockSwitchDistance(ref c) => Command::BlockSwitchDistance(*c),
}
}
}
impl<SliceType: SliceWrapper<u8> + Freezable> Command<SliceType> {
pub fn freeze(&self) -> Command<SliceOffset> {
match self {
Command::Literal(ref lit) => Command::Literal(LiteralCommand {
data: lit.data.freeze(),
prob: FeatureFlagSliceType::default(),
high_entropy: lit.high_entropy,
}),
Command::PredictionMode(ref pm) => Command::PredictionMode(PredictionModeContextMap {
literal_context_map: pm.literal_context_map.freeze(),
predmode_speed_and_distance_context_map: pm
.predmode_speed_and_distance_context_map
.freeze(),
}),
Command::Dict(ref d) => Command::Dict(*d),
Command::Copy(ref c) => Command::Copy(*c),
Command::BlockSwitchCommand(ref c) => Command::BlockSwitchCommand(*c),
Command::BlockSwitchLiteral(ref c) => Command::BlockSwitchLiteral(*c),
Command::BlockSwitchDistance(ref c) => Command::BlockSwitchDistance(*c),
}
}
}
#[inline(always)]
pub fn speed_to_u8(data: u16) -> u8 {
let length = 16 - data.leading_zeros() as u8;
let mantissa = if data != 0 {
let rem = data - (1 << (length - 1));
(rem << 3) >> (length - 1)
} else {
0
};
(length << 3) | mantissa as u8
}
#[inline(always)]
pub fn u8_to_speed(data: u8) -> u16 {
if data < 8 {
0
} else {
let log_val = (data >> 3) - 1;
let rem = (u16::from(data) & 0x07) << log_val;
(1u16 << log_val) | (rem >> 3)
}
}
#[cfg(test)]
mod test {
use super::{speed_to_u8, u8_to_speed};
fn tst_u8_to_speed(data: u16) {
assert_eq!(u8_to_speed(speed_to_u8(data)), data);
}
#[test]
fn test_u8_to_speed() {
tst_u8_to_speed(0);
tst_u8_to_speed(1);
tst_u8_to_speed(2);
tst_u8_to_speed(3);
tst_u8_to_speed(4);
tst_u8_to_speed(5);
tst_u8_to_speed(6);
tst_u8_to_speed(7);
tst_u8_to_speed(8);
tst_u8_to_speed(10);
tst_u8_to_speed(12);
tst_u8_to_speed(16);
tst_u8_to_speed(24);
tst_u8_to_speed(32);
tst_u8_to_speed(48);
tst_u8_to_speed(64);
tst_u8_to_speed(96);
tst_u8_to_speed(768);
tst_u8_to_speed(1280);
tst_u8_to_speed(1536);
tst_u8_to_speed(1664);
}
}
+121
View File
@@ -0,0 +1,121 @@
use super::super::alloc::SliceWrapper;
use super::constants::{kSigned3BitContextLookup, kUTF8ContextLookup};
use super::histogram::ContextType;
use super::input_pair::InputReference;
use super::interface;
use super::interface::LiteralPredictionModeNibble;
pub trait IRInterpreter {
fn inc_local_byte_offset(&mut self, inc: usize);
fn local_byte_offset(&self) -> usize;
fn update_block_type(&mut self, new_type: u8, new_stride: u8);
fn block_type(&self) -> u8;
fn literal_data_at_offset(&self, index: usize) -> u8;
fn literal_context_map(&self) -> &[u8];
fn prediction_mode(&self) -> crate::interface::LiteralPredictionModeNibble;
fn update_cost(
&mut self,
stride_prior: [u8; 8],
stride_byte_offset: usize,
selected_bits: u8,
cm_prior: usize,
literal: u8,
);
}
pub fn push_base<Interpreter: IRInterpreter>(
xself: &mut Interpreter,
val: interface::Command<InputReference<'_>>,
) {
match val {
interface::Command::BlockSwitchCommand(_)
| interface::Command::BlockSwitchDistance(_)
| interface::Command::PredictionMode(_) => {}
interface::Command::Copy(ref copy) => {
xself.inc_local_byte_offset(copy.num_bytes as usize);
}
interface::Command::Dict(ref dict) => {
xself.inc_local_byte_offset(dict.final_size as usize);
}
interface::Command::BlockSwitchLiteral(block_type) => {
xself.update_block_type(block_type.block_type(), block_type.stride())
}
interface::Command::Literal(ref lit) => {
//let stride = xself.get_stride(xself.local_byte_offset()) as usize;
let mut priors = [0u8; 8];
for poffset in 0..8 {
if xself.local_byte_offset() > poffset {
let input_offset = xself.local_byte_offset() - poffset - 1;
priors[7 - poffset] = xself.literal_data_at_offset(input_offset);
}
}
let mut cur = 0usize;
for literal in lit.data.slice().iter() {
let (huffman_table_index, selected_bits) =
compute_huffman_table_index_for_context_map(
priors[(cur + 7) & 7],
priors[(cur + 6) & 7],
xself.literal_context_map(),
xself.prediction_mode(),
xself.block_type(),
);
xself.update_cost(
priors,
(cur + 7) & 7,
selected_bits,
huffman_table_index,
*literal,
);
priors[cur & 7] = *literal;
cur += 1;
cur &= 7;
}
xself.inc_local_byte_offset(lit.data.slice().len());
}
}
}
// not sure why this fails
//impl<'a> interface::CommandProcessor<'a> for IRInterpreter {
// fn push<Cb: FnMut(&[interface::Command<InputReference>])>(&mut self,
// val: interface::Command<InputReference<'a>>,
// callback: &mut Cb) {
// push_base(self, val, callback)
// }
//}
fn compute_huffman_table_index_for_context_map(
prev_byte: u8,
prev_prev_byte: u8,
literal_context_map: &[u8], //interface::PredictionModeContextMap<SliceType>,
prediction_mode: LiteralPredictionModeNibble,
block_type: u8,
) -> (usize, u8) {
let prior = Context(
prev_byte,
prev_prev_byte,
prediction_mode.to_context_enum().unwrap(),
);
assert!(prior < 64);
let context_map_index = ((block_type as usize) << 6) | prior as usize;
if context_map_index < literal_context_map.len() {
(literal_context_map[context_map_index] as usize, prior)
} else {
(prior as usize, prior)
}
}
pub fn Context(p1: u8, p2: u8, mode: ContextType) -> u8 {
match mode {
ContextType::CONTEXT_LSB6 => p1 & 0x3f,
ContextType::CONTEXT_MSB6 => (p1 as i32 >> 2) as u8,
ContextType::CONTEXT_UTF8 => {
(kUTF8ContextLookup[p1 as usize] as i32
| kUTF8ContextLookup[(p2 as i32 + 256i32) as usize] as i32) as u8
}
ContextType::CONTEXT_SIGNED => {
(((kSigned3BitContextLookup[p1 as usize] as i32) << 3)
+ kSigned3BitContextLookup[p2 as usize] as i32) as u8
}
}
// 0u8
}
+239
View File
@@ -0,0 +1,239 @@
use core::cmp::min;
use super::util::{floatX, FastLog2f64};
use crate::enc::utf8_util::is_mostly_utf8;
static kMinUTF8Ratio: floatX = 0.75;
fn UTF8Position(last: usize, c: usize, clamp: usize) -> usize {
if c < 128usize {
0usize
} else if c >= 192usize {
min(1usize, clamp)
} else if last < 0xe0usize {
0usize
} else {
min(2usize, clamp)
}
}
fn DecideMultiByteStatsLevel(pos: usize, len: usize, mask: usize, data: &[u8]) -> usize {
let mut counts = [0usize; 3];
let mut max_utf8: usize = 1;
let mut last_c: usize = 0usize;
let mut i: usize;
i = 0usize;
while i < len {
{
let c: usize = data[(pos.wrapping_add(i) & mask)] as usize;
{
let _rhs = 1;
let _lhs = &mut counts[UTF8Position(last_c, c, 2usize)];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
last_c = c;
}
i = i.wrapping_add(1);
}
if counts[2] < 500usize {
max_utf8 = 1;
}
if counts[1].wrapping_add(counts[2]) < 25usize {
max_utf8 = 0usize;
}
max_utf8
}
fn EstimateBitCostsForLiteralsUTF8(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
cost: &mut [floatX],
) {
let max_utf8: usize = DecideMultiByteStatsLevel(pos, len, mask, data);
let mut histogram = [[0usize; 256]; 3];
let window_half: usize = 495usize;
let in_window: usize = min(window_half, len);
let mut in_window_utf8 = [0usize; 3];
let mut i: usize;
{
let mut last_c: usize = 0usize;
let mut utf8_pos: usize = 0usize;
i = 0usize;
while i < in_window {
{
let c: usize = data[(pos.wrapping_add(i) & mask)] as usize;
{
let _rhs = 1;
let _lhs = &mut histogram[utf8_pos][c];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
{
let _rhs = 1;
let _lhs = &mut in_window_utf8[utf8_pos];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
utf8_pos = UTF8Position(last_c, c, max_utf8);
last_c = c;
}
i = i.wrapping_add(1);
}
}
i = 0usize;
while i < len {
{
if i >= window_half {
let c: usize = (if i < window_half.wrapping_add(1) {
0i32
} else {
data[(pos
.wrapping_add(i)
.wrapping_sub(window_half)
.wrapping_sub(1)
& mask)] as i32
}) as usize;
let last_c: usize = (if i < window_half.wrapping_add(2) {
0i32
} else {
data[(pos
.wrapping_add(i)
.wrapping_sub(window_half)
.wrapping_sub(2)
& mask)] as i32
}) as usize;
let utf8_pos2: usize = UTF8Position(last_c, c, max_utf8);
{
let _rhs = 1;
let _lhs = &mut histogram[utf8_pos2]
[data[(pos.wrapping_add(i).wrapping_sub(window_half) & mask)] as usize];
*_lhs = (*_lhs).wrapping_sub(_rhs as usize);
}
{
let _rhs = 1;
let _lhs = &mut in_window_utf8[utf8_pos2];
*_lhs = (*_lhs).wrapping_sub(_rhs as usize);
}
}
if i.wrapping_add(window_half) < len {
let c: usize = data[(pos
.wrapping_add(i)
.wrapping_add(window_half)
.wrapping_sub(1)
& mask)] as usize;
let last_c: usize = data[(pos
.wrapping_add(i)
.wrapping_add(window_half)
.wrapping_sub(2)
& mask)] as usize;
let utf8_pos2: usize = UTF8Position(last_c, c, max_utf8);
{
let _rhs = 1;
let _lhs = &mut histogram[utf8_pos2]
[data[(pos.wrapping_add(i).wrapping_add(window_half) & mask)] as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
{
let _rhs = 1;
let _lhs = &mut in_window_utf8[utf8_pos2];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
}
{
let c: usize = (if i < 1 {
0i32
} else {
data[(pos.wrapping_add(i).wrapping_sub(1) & mask)] as i32
}) as usize;
let last_c: usize = (if i < 2usize {
0i32
} else {
data[(pos.wrapping_add(i).wrapping_sub(2) & mask)] as i32
}) as usize;
let utf8_pos: usize = UTF8Position(last_c, c, max_utf8);
let masked_pos: usize = pos.wrapping_add(i) & mask;
let mut histo: usize = histogram[utf8_pos][data[masked_pos] as usize];
//precision is vital here: lets keep double precision
let mut lit_cost: f64;
if histo == 0usize {
histo = 1;
}
lit_cost = FastLog2f64(in_window_utf8[utf8_pos] as u64) as f64
- FastLog2f64(histo as u64) as f64;
lit_cost += 0.02905;
if lit_cost < 1.0 {
lit_cost *= 0.5;
lit_cost += 0.5;
}
if i < 2000usize {
lit_cost += (0.7 - (2000usize).wrapping_sub(i) as (f64) / 2000.0 * 0.35);
}
cost[i] = lit_cost as floatX;
}
}
i = i.wrapping_add(1);
}
}
pub fn BrotliEstimateBitCostsForLiterals(
pos: usize,
len: usize,
mask: usize,
data: &[u8],
cost: &mut [floatX],
) {
if is_mostly_utf8(data, pos, mask, len, kMinUTF8Ratio) {
EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, cost);
} else {
let mut histogram: [usize; 256] = [0; 256];
let window_half: usize = 2000usize;
let mut in_window: usize = min(window_half, len);
let mut i: usize;
for i in 0usize..in_window {
let _rhs = 1;
let _lhs = &mut histogram[data[(pos.wrapping_add(i) & mask)] as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
i = 0usize;
while i < len {
{
let mut histo: usize;
if i >= window_half {
{
let _rhs = 1;
let _lhs = &mut histogram
[data[(pos.wrapping_add(i).wrapping_sub(window_half) & mask)] as usize];
*_lhs = (*_lhs).wrapping_sub(_rhs as usize);
}
in_window = in_window.wrapping_sub(1);
}
if i.wrapping_add(window_half) < len {
{
let _rhs = 1;
let _lhs = &mut histogram
[data[(pos.wrapping_add(i).wrapping_add(window_half) & mask)] as usize];
*_lhs = (*_lhs).wrapping_add(_rhs as usize);
}
in_window = in_window.wrapping_add(1);
}
histo = histogram[data[(pos.wrapping_add(i) & mask)] as usize];
if histo == 0usize {
histo = 1;
}
{
//precision is vital here: lets keep double precision
let mut lit_cost: f64 =
FastLog2f64(in_window as u64) as f64 - FastLog2f64(histo as u64) as f64;
lit_cost += 0.029;
if lit_cost < 1.0 {
lit_cost *= 0.5;
lit_cost += 0.5;
}
cost[i] = lit_cost as floatX;
}
}
i = i.wrapping_add(1);
}
}
}
File diff suppressed because it is too large Load Diff
+261
View File
@@ -0,0 +1,261 @@
use crate::enc::floatX;
#[allow(clippy::excessive_precision)]
pub static logs_8: [floatX; 256] = [
0.0000000000000000,
0.0000000000000000,
1.0000000000000000,
1.5849625007211563,
2.0000000000000000,
2.3219280948873622,
2.5849625007211561,
2.8073549220576042,
3.0000000000000000,
3.1699250014423126,
3.3219280948873626,
3.4594316186372978,
3.5849625007211565,
3.7004397181410922,
3.8073549220576037,
3.9068905956085187,
4.0000000000000000,
4.0874628412503400,
4.1699250014423122,
4.2479275134435852,
4.3219280948873626,
4.3923174227787607,
4.4594316186372973,
4.5235619560570131,
4.5849625007211570,
4.6438561897747244,
4.7004397181410926,
4.7548875021634691,
4.8073549220576037,
4.8579809951275728,
4.9068905956085187,
4.9541963103868758,
5.0000000000000000,
5.0443941193584534,
5.0874628412503400,
5.1292830169449664,
5.1699250014423122,
5.2094533656289501,
5.2479275134435852,
5.2854022188622487,
5.3219280948873626,
5.3575520046180838,
5.3923174227787607,
5.4262647547020979,
5.4594316186372973,
5.4918530963296748,
5.5235619560570131,
5.5545888516776376,
5.5849625007211570,
5.6147098441152083,
5.6438561897747244,
5.6724253419714961,
5.7004397181410926,
5.7279204545631996,
5.7548875021634691,
5.7813597135246599,
5.8073549220576046,
5.8328900141647422,
5.8579809951275719,
5.8826430493618416,
5.9068905956085187,
5.9307373375628867,
5.9541963103868758,
5.9772799234999168,
6.0000000000000000,
6.0223678130284544,
6.0443941193584534,
6.0660891904577721,
6.0874628412503400,
6.1085244567781700,
6.1292830169449672,
6.1497471195046822,
6.1699250014423122,
6.1898245588800176,
6.2094533656289510,
6.2288186904958804,
6.2479275134435861,
6.2667865406949019,
6.2854022188622487,
6.3037807481771031,
6.3219280948873617,
6.3398500028846252,
6.3575520046180847,
6.3750394313469254,
6.3923174227787598,
6.4093909361377026,
6.4262647547020979,
6.4429434958487288,
6.4594316186372982,
6.4757334309663976,
6.4918530963296748,
6.5077946401986964,
6.5235619560570131,
6.5391588111080319,
6.5545888516776376,
6.5698556083309478,
6.5849625007211561,
6.5999128421871278,
6.6147098441152092,
6.6293566200796095,
6.6438561897747253,
6.6582114827517955,
6.6724253419714952,
6.6865005271832185,
6.7004397181410917,
6.7142455176661224,
6.7279204545631988,
6.7414669864011465,
6.7548875021634691,
6.7681843247769260,
6.7813597135246599,
6.7944158663501062,
6.8073549220576037,
6.8201789624151887,
6.8328900141647422,
6.8454900509443757,
6.8579809951275719,
6.8703647195834048,
6.8826430493618416,
6.8948177633079437,
6.9068905956085187,
6.9188632372745955,
6.9307373375628867,
6.9425145053392399,
6.9541963103868758,
6.9657842846620879,
6.9772799234999168,
6.9886846867721664,
7.0000000000000000,
7.0112272554232540,
7.0223678130284544,
7.0334230015374501,
7.0443941193584534,
7.0552824355011898,
7.0660891904577721,
7.0768155970508317,
7.0874628412503400,
7.0980320829605272,
7.1085244567781700,
7.1189410727235076,
7.1292830169449664,
7.1395513523987937,
7.1497471195046822,
7.1598713367783891,
7.1699250014423130,
7.1799090900149345,
7.1898245588800176,
7.1996723448363644,
7.2094533656289492,
7.2191685204621621,
7.2288186904958804,
7.2384047393250794,
7.2479275134435861,
7.2573878426926521,
7.2667865406949019,
7.2761244052742384,
7.2854022188622487,
7.2946207488916270,
7.3037807481771031,
7.3128829552843557,
7.3219280948873617,
7.3309168781146177,
7.3398500028846243,
7.3487281542310781,
7.3575520046180847,
7.3663222142458151,
7.3750394313469254,
7.3837042924740528,
7.3923174227787607,
7.4008794362821844,
7.4093909361377026,
7.4178525148858991,
7.4262647547020979,
7.4346282276367255,
7.4429434958487288,
7.4512111118323299,
7.4594316186372973,
7.4676055500829976,
7.4757334309663976,
7.4838157772642564,
7.4918530963296748,
7.4998458870832057,
7.5077946401986964,
7.5156998382840436,
7.5235619560570131,
7.5313814605163119,
7.5391588111080319,
7.5468944598876373,
7.5545888516776376,
7.5622424242210728,
7.5698556083309478,
7.5774288280357487,
7.5849625007211561,
7.5924570372680806,
7.5999128421871278,
7.6073303137496113,
7.6147098441152075,
7.6220518194563764,
7.6293566200796095,
7.6366246205436488,
7.6438561897747244,
7.6510516911789290,
7.6582114827517955,
7.6653359171851765,
7.6724253419714952,
7.6794800995054464,
7.6865005271832185,
7.6934869574993252,
7.7004397181410926,
7.7073591320808825,
7.7142455176661224,
7.7210991887071856,
7.7279204545631996,
7.7347096202258392,
7.7414669864011465,
7.7481928495894596,
7.7548875021634691,
7.7615512324444795,
7.7681843247769260,
7.7747870596011737,
7.7813597135246608,
7.7879025593914317,
7.7944158663501062,
7.8008998999203047,
7.8073549220576037,
7.8137811912170374,
7.8201789624151887,
7.8265484872909159,
7.8328900141647422,
7.8392037880969445,
7.8454900509443757,
7.8517490414160571,
7.8579809951275719,
7.8641861446542798,
7.8703647195834048,
7.8765169465650002,
7.8826430493618425,
7.8887432488982601,
7.8948177633079446,
7.9008668079807496,
7.9068905956085187,
7.9128893362299619,
7.9188632372745955,
7.9248125036057813,
7.9307373375628867,
7.9366379390025719,
7.9425145053392399,
7.9483672315846778,
7.9541963103868758,
7.9600019320680806,
7.9657842846620870,
7.9715435539507720,
7.9772799234999168,
7.9829935746943104,
7.9886846867721664,
7.9943534368588578,
];
File diff suppressed because it is too large Load Diff
+344
View File
@@ -0,0 +1,344 @@
#[macro_use]
pub mod vectorization;
pub mod backward_references;
pub mod bit_cost;
pub mod block_split;
pub mod block_splitter;
pub mod brotli_bit_stream;
pub mod cluster;
pub mod combined_alloc;
pub mod command;
mod compat;
pub mod compress_fragment;
pub mod compress_fragment_two_pass;
pub mod constants;
pub mod context_map_entropy;
pub mod dictionary_hash;
pub mod encode;
pub mod entropy_encode;
pub mod find_stride;
pub mod fixed_queue;
pub mod histogram;
pub mod input_pair;
pub mod interface;
pub mod ir_interpret;
pub mod literal_cost;
mod log_table_16;
mod log_table_8;
pub mod metablock;
pub mod multithreading;
mod parameters;
pub mod pdf;
pub mod prior_eval;
pub mod reader;
pub mod singlethreading;
pub mod static_dict;
pub mod static_dict_lut;
pub mod stride_eval;
mod test;
pub mod threading;
pub mod utf8_util;
pub mod util;
mod weights;
pub mod worker_pool;
pub mod writer;
pub use alloc::{AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut, StackAllocator};
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "std")]
use std::io::{Error, ErrorKind, Read, Write};
#[cfg(feature = "std")]
pub use alloc_stdlib::StandardAlloc;
use brotli_decompressor::{CustomRead, CustomWrite};
#[cfg(feature = "std")]
pub use brotli_decompressor::{IntoIoReader, IoReaderWrapper, IoWriterWrapper};
pub use interface::{InputPair, InputReference, InputReferenceMut};
pub use self::backward_references::{
hash_to_binary_tree, hq as backward_references_hq, BrotliEncoderParams, UnionHasher,
};
pub use self::combined_alloc::{BrotliAlloc, CombiningAllocator};
use self::encode::{BrotliEncoderDestroyInstance, BrotliEncoderOperation};
pub use self::encode::{
BrotliEncoderInitParams, BrotliEncoderMaxCompressedSize, BrotliEncoderMaxCompressedSizeMulti,
};
pub use self::hash_to_binary_tree::ZopfliNode;
pub use self::interface::StaticCommand;
pub use self::pdf::PDF;
#[cfg(not(feature = "std"))]
pub use self::singlethreading::{compress_worker_pool, new_work_pool, WorkerPool};
pub use self::threading::{
BatchSpawnableLite, BrotliEncoderThreadError, CompressionThreadResult, Owned, SendAlloc,
};
pub use self::util::floatX;
pub use self::vectorization::{v256, v256i, Mem256f};
#[cfg(feature = "std")]
pub use self::worker_pool::{compress_worker_pool, new_work_pool, WorkerPool};
use crate::enc::encode::BrotliEncoderStateStruct;
#[cfg(feature = "simd")]
pub type s16 = core::simd::i16x16;
#[cfg(feature = "simd")]
pub type v8 = core::simd::f32x8;
#[cfg(feature = "simd")]
pub type s8 = core::simd::i32x8;
#[cfg(not(feature = "simd"))]
pub type s16 = compat::Compat16x16;
#[cfg(not(feature = "simd"))]
pub type v8 = compat::CompatF8;
#[cfg(not(feature = "simd"))]
pub type s8 = compat::Compat32x8;
#[cfg(feature = "std")]
pub fn compress_multi<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
backward_references::UnionHasher<Alloc>,
Alloc,
<WorkerPool<
CompressionThreadResult<Alloc>,
backward_references::UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
> as BatchSpawnableLite<
CompressionThreadResult<Alloc>,
backward_references::UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>>::JoinHandle,
>],
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
let mut work_pool = self::worker_pool::new_work_pool(alloc_per_thread.len() - 1);
compress_worker_pool(
params,
owned_input,
output,
alloc_per_thread,
&mut work_pool,
)
}
#[cfg(feature = "std")]
pub use self::multithreading::compress_multi as compress_multi_no_threadpool;
#[cfg(not(feature = "std"))]
pub use self::singlethreading::compress_multi;
#[cfg(not(feature = "std"))]
pub use self::singlethreading::compress_multi as compress_multi_no_threadpool;
#[cfg(feature = "std")]
pub fn BrotliCompress<InputType, OutputType>(
r: &mut InputType,
w: &mut OutputType,
params: &BrotliEncoderParams,
) -> Result<usize, io::Error>
where
InputType: Read,
OutputType: Write,
{
let mut input_buffer: [u8; 4096] = [0; 4096];
let mut output_buffer: [u8; 4096] = [0; 4096];
BrotliCompressCustomAlloc(
r,
w,
&mut input_buffer[..],
&mut output_buffer[..],
params,
StandardAlloc::default(),
)
}
#[cfg(feature = "std")]
pub fn BrotliCompressCustomAlloc<InputType, OutputType, Alloc: BrotliAlloc>(
r: &mut InputType,
w: &mut OutputType,
input_buffer: &mut [u8],
output_buffer: &mut [u8],
params: &BrotliEncoderParams,
alloc: Alloc,
) -> Result<usize, io::Error>
where
InputType: Read,
OutputType: Write,
{
let mut nop_callback = |_data: &mut interface::PredictionModeContextMap<InputReferenceMut>,
_cmds: &mut [interface::StaticCommand],
_mb: interface::InputPair,
_m: &mut Alloc| ();
BrotliCompressCustomIo(
&mut IoReaderWrapper::<InputType>(r),
&mut IoWriterWrapper::<OutputType>(w),
input_buffer,
output_buffer,
params,
alloc,
&mut nop_callback,
Error::new(ErrorKind::UnexpectedEof, "Unexpected EOF"),
)
}
pub fn BrotliCompressCustomIo<
ErrType,
InputType,
OutputType,
Alloc: BrotliAlloc,
MetablockCallback: FnMut(
&mut interface::PredictionModeContextMap<InputReferenceMut>,
&mut [interface::StaticCommand],
interface::InputPair,
&mut Alloc,
),
>(
r: &mut InputType,
w: &mut OutputType,
input_buffer: &mut [u8],
output_buffer: &mut [u8],
params: &BrotliEncoderParams,
alloc: Alloc,
metablock_callback: &mut MetablockCallback,
unexpected_eof_error_constant: ErrType,
) -> Result<usize, ErrType>
where
InputType: CustomRead<ErrType>,
OutputType: CustomWrite<ErrType>,
{
BrotliCompressCustomIoCustomDict(
r,
w,
input_buffer,
output_buffer,
params,
alloc,
metablock_callback,
&[],
unexpected_eof_error_constant,
)
}
pub fn BrotliCompressCustomIoCustomDict<
ErrType,
InputType,
OutputType,
Alloc: BrotliAlloc,
MetablockCallback: FnMut(
&mut interface::PredictionModeContextMap<InputReferenceMut>,
&mut [interface::StaticCommand],
interface::InputPair,
&mut Alloc,
),
>(
r: &mut InputType,
w: &mut OutputType,
input_buffer: &mut [u8],
output_buffer: &mut [u8],
params: &BrotliEncoderParams,
alloc: Alloc,
metablock_callback: &mut MetablockCallback,
dict: &[u8],
unexpected_eof_error_constant: ErrType,
) -> Result<usize, ErrType>
where
InputType: CustomRead<ErrType>,
OutputType: CustomWrite<ErrType>,
{
assert!(!input_buffer.is_empty());
assert!(!output_buffer.is_empty());
let mut s_orig = BrotliEncoderStateStruct::new(alloc);
s_orig.params = params.clone();
if !dict.is_empty() {
s_orig.set_custom_dictionary(dict.len(), dict);
}
let mut next_in_offset: usize = 0;
let mut next_out_offset: usize = 0;
let mut total_out = Some(0);
let mut read_err: Result<(), ErrType> = Ok(());
{
let s = &mut s_orig;
//BrotliEncoderSetParameter(s, BrotliEncoderParameter::BROTLI_PARAM_MODE, 0 as u32); // gen, text, font
//BrotliEncoderSetParameter(s,
// BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
// input.len() as u32);
let mut available_in: usize = 0;
let mut available_out: usize = output_buffer.len();
let mut eof = false;
loop {
if available_in == 0 && !eof {
next_in_offset = 0;
match r.read(input_buffer) {
Err(e) => {
read_err = Err(e);
available_in = 0;
eof = true;
}
Ok(size) => {
if size == 0 {
eof = true;
}
available_in = size;
}
}
}
let op: BrotliEncoderOperation;
if available_in == 0 {
op = BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
} else {
op = BrotliEncoderOperation::BROTLI_OPERATION_PROCESS;
}
let result = s.compress_stream(
op,
&mut available_in,
input_buffer,
&mut next_in_offset,
&mut available_out,
output_buffer,
&mut next_out_offset,
&mut total_out,
metablock_callback,
);
let fin = s.is_finished();
if available_out == 0 || fin {
let lim = output_buffer.len() - available_out;
assert_eq!(next_out_offset, lim);
next_out_offset = 0;
while next_out_offset < lim {
match w.write(&mut output_buffer[next_out_offset..lim]) {
Err(e) => {
BrotliEncoderDestroyInstance(s);
read_err?;
return Err(e);
}
Ok(size) => {
next_out_offset += size;
}
}
}
available_out = output_buffer.len();
next_out_offset = 0;
}
if !result {
if read_err.is_ok() {
read_err = Err(unexpected_eof_error_constant);
}
break;
}
if fin {
break;
}
}
BrotliEncoderDestroyInstance(s);
}
read_err?;
Ok(total_out.unwrap())
}
+194
View File
@@ -0,0 +1,194 @@
#![cfg(feature = "std")]
use alloc::{Allocator, SliceWrapper};
use core::marker::PhantomData;
use core::mem;
use std;
// in-place thread create
use std::sync::RwLock;
use std::thread::JoinHandle;
use crate::enc::backward_references::UnionHasher;
use crate::enc::threading::{
AnyBoxConstructor, BatchSpawnable, BatchSpawnableLite, BrotliEncoderThreadError, CompressMulti,
CompressionThreadResult, InternalOwned, InternalSendAlloc, Joinable, Owned, OwnedRetriever,
PoisonedThreadError, SendAlloc,
};
use crate::enc::{BrotliAlloc, BrotliEncoderParams};
pub struct MultiThreadedJoinable<T: Send + 'static, U: Send + 'static>(
Option<JoinHandle<T>>,
PhantomData<U>,
);
impl<T: Send + 'static, U: Send + 'static + AnyBoxConstructor> Joinable<T, U>
for MultiThreadedJoinable<T, U>
{
fn join(mut self) -> Result<T, U> {
match self.0.take().unwrap().join() {
Ok(t) => Ok(t),
Err(e) => Err(<U as AnyBoxConstructor>::new(e)),
}
}
}
impl<T: Send + 'static, U: Send + 'static> Drop for MultiThreadedJoinable<T, U> {
fn drop(&mut self) {
if let Some(join_handle) = self.0.take() {
let _ = join_handle.join();
}
}
}
pub struct MultiThreadedOwnedRetriever<U: Send + 'static>(RwLock<U>);
impl<U: Send + 'static> OwnedRetriever<U> for MultiThreadedOwnedRetriever<U> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError> {
match self.0.read() {
Ok(u) => Ok(f(&*u)),
Err(_) => Err(PoisonedThreadError::default()),
}
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
match self.0.into_inner() {
Ok(u) => Ok(u),
Err(_) => Err(PoisonedThreadError::default()),
}
}
}
#[derive(Default)]
pub struct MultiThreadedSpawner {}
fn spawn_work<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
F: Fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue + Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
>(
extra_input: ExtraInput,
index: usize,
num_threads: usize,
locked_input: std::sync::Arc<RwLock<U>>,
alloc: Alloc,
f: F,
) -> std::thread::JoinHandle<ReturnValue>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
std::thread::spawn(move || {
let t: ReturnValue = locked_input
.view(move |guard: &U| -> ReturnValue {
f(extra_input, index, num_threads, guard, alloc)
})
.unwrap();
t
})
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnable<ReturnValue, ExtraInput, Alloc, U> for MultiThreadedSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle = MultiThreadedJoinable<ReturnValue, BrotliEncoderThreadError>;
type FinalJoinHandle = std::sync::Arc<RwLock<U>>;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
std::sync::Arc::<RwLock<U>>::new(RwLock::new(
mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap(),
))
}
fn spawn<F: Fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue + Send + 'static + Copy>(
&mut self,
input: &mut Self::FinalJoinHandle,
work: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: F,
) {
let (alloc, extra_input) = work.replace_with_default();
let ret = spawn_work(extra_input, index, num_threads, input.clone(), alloc, f);
*work = SendAlloc(InternalSendAlloc::Join(MultiThreadedJoinable(
Some(ret),
PhantomData,
)));
}
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnableLite<ReturnValue, ExtraInput, Alloc, U> for MultiThreadedSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
type JoinHandle =
<MultiThreadedSpawner as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::JoinHandle;
type FinalJoinHandle = <MultiThreadedSpawner as BatchSpawnable<
ReturnValue,
ExtraInput,
Alloc,
U,
>>::FinalJoinHandle;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
<Self as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::make_spawner(self, input)
}
fn spawn(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
) {
<Self as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::spawn(
self,
handle,
alloc_per_thread,
index,
num_threads,
f,
)
}
}
pub fn compress_multi<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
<MultiThreadedSpawner as BatchSpawnable<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
SliceW,
>>::JoinHandle,
>],
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
CompressMulti(
params,
owned_input,
output,
alloc_per_thread,
&mut MultiThreadedSpawner::default(),
)
}
+259
View File
@@ -0,0 +1,259 @@
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[repr(C)]
pub enum BrotliEncoderParameter {
BROTLI_PARAM_MODE = 0,
BROTLI_PARAM_QUALITY = 1,
BROTLI_PARAM_LGWIN = 2,
BROTLI_PARAM_LGBLOCK = 3,
BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4,
BROTLI_PARAM_SIZE_HINT = 5,
BROTLI_PARAM_LARGE_WINDOW = 6,
BROTLI_PARAM_Q9_5 = 150,
BROTLI_METABLOCK_CALLBACK = 151,
BROTLI_PARAM_STRIDE_DETECTION_QUALITY = 152,
BROTLI_PARAM_HIGH_ENTROPY_DETECTION_QUALITY = 153,
BROTLI_PARAM_LITERAL_BYTE_SCORE = 154,
BROTLI_PARAM_CDF_ADAPTATION_DETECTION = 155,
BROTLI_PARAM_PRIOR_BITMASK_DETECTION = 156,
BROTLI_PARAM_SPEED = 157,
BROTLI_PARAM_SPEED_MAX = 158,
BROTLI_PARAM_CM_SPEED = 159,
BROTLI_PARAM_CM_SPEED_MAX = 160,
BROTLI_PARAM_SPEED_LOW = 161,
BROTLI_PARAM_SPEED_LOW_MAX = 162,
BROTLI_PARAM_CM_SPEED_LOW = 164,
BROTLI_PARAM_CM_SPEED_LOW_MAX = 165,
BROTLI_PARAM_AVOID_DISTANCE_PREFIX_SEARCH = 166,
BROTLI_PARAM_CATABLE = 167,
BROTLI_PARAM_APPENDABLE = 168,
BROTLI_PARAM_MAGIC_NUMBER = 169,
BROTLI_PARAM_NO_DICTIONARY = 170,
BROTLI_PARAM_FAVOR_EFFICIENCY = 171,
BROTLI_PARAM_BYTE_ALIGN = 172,
BROTLI_PARAM_BARE_STREAM = 173,
UNUSED7 = 7,
UNUSED8 = 8,
UNUSED9 = 9,
UNUSED10 = 10,
UNUSED11 = 11,
UNUSED12 = 12,
UNUSED13 = 13,
UNUSED14 = 14,
UNUSED15 = 15,
UNUSED16 = 16,
UNUSED17 = 17,
UNUSED18 = 18,
UNUSED19 = 19,
UNUSED20 = 20,
UNUSED21 = 21,
UNUSED22 = 22,
UNUSED23 = 23,
UNUSED24 = 24,
UNUSED25 = 25,
UNUSED26 = 26,
UNUSED27 = 27,
UNUSED28 = 28,
UNUSED29 = 29,
UNUSED30 = 30,
UNUSED31 = 31,
UNUSED32 = 32,
UNUSED33 = 33,
UNUSED34 = 34,
UNUSED35 = 35,
UNUSED36 = 36,
UNUSED37 = 37,
UNUSED38 = 38,
UNUSED39 = 39,
UNUSED40 = 40,
UNUSED41 = 41,
UNUSED42 = 42,
UNUSED43 = 43,
UNUSED44 = 44,
UNUSED45 = 45,
UNUSED46 = 46,
UNUSED47 = 47,
UNUSED48 = 48,
UNUSED49 = 49,
UNUSED50 = 50,
UNUSED51 = 51,
UNUSED52 = 52,
UNUSED53 = 53,
UNUSED54 = 54,
UNUSED55 = 55,
UNUSED56 = 56,
UNUSED57 = 57,
UNUSED58 = 58,
UNUSED59 = 59,
UNUSED60 = 60,
UNUSED61 = 61,
UNUSED62 = 62,
UNUSED63 = 63,
UNUSED64 = 64,
UNUSED65 = 65,
UNUSED66 = 66,
UNUSED67 = 67,
UNUSED68 = 68,
UNUSED69 = 69,
UNUSED70 = 70,
UNUSED71 = 71,
UNUSED72 = 72,
UNUSED73 = 73,
UNUSED74 = 74,
UNUSED75 = 75,
UNUSED76 = 76,
UNUSED77 = 77,
UNUSED78 = 78,
UNUSED79 = 79,
UNUSED80 = 80,
UNUSED81 = 81,
UNUSED82 = 82,
UNUSED83 = 83,
UNUSED84 = 84,
UNUSED85 = 85,
UNUSED86 = 86,
UNUSED87 = 87,
UNUSED88 = 88,
UNUSED89 = 89,
UNUSED90 = 90,
UNUSED91 = 91,
UNUSED92 = 92,
UNUSED93 = 93,
UNUSED94 = 94,
UNUSED95 = 95,
UNUSED96 = 96,
UNUSED97 = 97,
UNUSED98 = 98,
UNUSED99 = 99,
UNUSED100 = 100,
UNUSED101 = 101,
UNUSED102 = 102,
UNUSED103 = 103,
UNUSED104 = 104,
UNUSED105 = 105,
UNUSED106 = 106,
UNUSED107 = 107,
UNUSED108 = 108,
UNUSED109 = 109,
UNUSED110 = 110,
UNUSED111 = 111,
UNUSED112 = 112,
UNUSED113 = 113,
UNUSED114 = 114,
UNUSED115 = 115,
UNUSED116 = 116,
UNUSED117 = 117,
UNUSED118 = 118,
UNUSED119 = 119,
UNUSED120 = 120,
UNUSED121 = 121,
UNUSED122 = 122,
UNUSED123 = 123,
UNUSED124 = 124,
UNUSED125 = 125,
UNUSED126 = 126,
UNUSED127 = 127,
UNUSED128 = 128,
UNUSED129 = 129,
UNUSED130 = 130,
UNUSED131 = 131,
UNUSED132 = 132,
UNUSED133 = 133,
UNUSED134 = 134,
UNUSED135 = 135,
UNUSED136 = 136,
UNUSED137 = 137,
UNUSED138 = 138,
UNUSED139 = 139,
UNUSED140 = 140,
UNUSED141 = 141,
UNUSED142 = 142,
UNUSED143 = 143,
UNUSED144 = 144,
UNUSED145 = 145,
UNUSED146 = 146,
UNUSED147 = 147,
UNUSED148 = 148,
UNUSED149 = 149,
UNUSED174 = 174,
UNUSED175 = 175,
UNUSED176 = 176,
UNUSED177 = 177,
UNUSED178 = 178,
UNUSED179 = 179,
UNUSED180 = 180,
UNUSED181 = 181,
UNUSED182 = 182,
UNUSED183 = 183,
UNUSED184 = 184,
UNUSED185 = 185,
UNUSED186 = 186,
UNUSED187 = 187,
UNUSED188 = 188,
UNUSED189 = 189,
UNUSED190 = 190,
UNUSED191 = 191,
UNUSED192 = 192,
UNUSED193 = 193,
UNUSED194 = 194,
UNUSED195 = 195,
UNUSED196 = 196,
UNUSED197 = 197,
UNUSED198 = 198,
UNUSED199 = 199,
UNUSED200 = 200,
UNUSED201 = 201,
UNUSED202 = 202,
UNUSED203 = 203,
UNUSED204 = 204,
UNUSED205 = 205,
UNUSED206 = 206,
UNUSED207 = 207,
UNUSED208 = 208,
UNUSED209 = 209,
UNUSED210 = 210,
UNUSED211 = 211,
UNUSED212 = 212,
UNUSED213 = 213,
UNUSED214 = 214,
UNUSED215 = 215,
UNUSED216 = 216,
UNUSED217 = 217,
UNUSED218 = 218,
UNUSED219 = 219,
UNUSED220 = 220,
UNUSED221 = 221,
UNUSED222 = 222,
UNUSED223 = 223,
UNUSED224 = 224,
UNUSED225 = 225,
UNUSED226 = 226,
UNUSED227 = 227,
UNUSED228 = 228,
UNUSED229 = 229,
UNUSED230 = 230,
UNUSED231 = 231,
UNUSED232 = 232,
UNUSED233 = 233,
UNUSED234 = 234,
UNUSED235 = 235,
UNUSED236 = 236,
UNUSED237 = 237,
UNUSED238 = 238,
UNUSED239 = 239,
UNUSED240 = 240,
UNUSED241 = 241,
UNUSED242 = 242,
UNUSED243 = 243,
UNUSED244 = 244,
UNUSED245 = 245,
UNUSED246 = 246,
UNUSED247 = 247,
UNUSED248 = 248,
UNUSED249 = 249,
UNUSED250 = 250,
UNUSED251 = 251,
UNUSED252 = 252,
UNUSED253 = 253,
UNUSED254 = 254,
UNUSED255 = 255,
}
+6
View File
@@ -0,0 +1,6 @@
//TODO: replace with builtin SIMD type
// FIXME!!!
#[allow(dead_code)]
#[derive(Copy, Clone, Default, Debug)]
pub struct PDF([i16; 16]);
+854
View File
@@ -0,0 +1,854 @@
use core;
use core::cmp::min;
#[cfg(feature = "simd")]
use core::simd::prelude::SimdPartialOrd;
use super::super::alloc;
use super::super::alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use super::backward_references::BrotliEncoderParams;
use super::input_pair::{InputPair, InputReference, InputReferenceMut};
use super::ir_interpret::{push_base, IRInterpreter};
use super::util::{floatX, FastLog2u16};
use super::{find_stride, interface, s16, v8};
use crate::enc::combined_alloc::{alloc_default, alloc_if};
// the high nibble, followed by the low nibbles
pub const CONTEXT_MAP_PRIOR_SIZE: usize = 256 * 17;
pub const STRIDE_PRIOR_SIZE: usize = 256 * 256 * 2;
pub const ADV_PRIOR_SIZE: usize = 65536 + (20 << 16);
pub const DEFAULT_SPEED: (u16, u16) = (8, 8192);
pub enum WhichPrior {
CM = 0,
ADV = 1,
SLOW_CM = 2,
FAST_CM = 3,
STRIDE1 = 4,
STRIDE2 = 5,
STRIDE3 = 6,
STRIDE4 = 7,
// STRIDE8 = 8,
NUM_PRIORS = 8,
// future ideas
}
pub trait Prior {
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize;
#[inline(always)]
fn lookup_mut(
data: &mut [s16],
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> CDF<'_> {
let index = Self::lookup_lin(stride_byte, selected_context, actual_context, high_nibble);
CDF::from(&mut data[index])
}
#[inline(always)]
fn lookup(
data: &[s16],
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> &s16 {
let index = Self::lookup_lin(stride_byte, selected_context, actual_context, high_nibble);
&data[index]
}
#[allow(unused_variables)]
#[inline(always)]
fn score_index(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
let which = Self::which();
assert!(which < WhichPrior::NUM_PRIORS as usize);
assert!(actual_context < 256);
if let Some(nibble) = high_nibble {
WhichPrior::NUM_PRIORS as usize * (actual_context + 4096 + 256 * nibble as usize)
+ which
} else {
WhichPrior::NUM_PRIORS as usize * (actual_context + 256 * (stride_byte >> 4) as usize)
+ which
}
}
fn which() -> usize;
}
#[inline(always)]
fn upper_score_index(stride_byte: u8, _selected_context: u8, actual_context: usize) -> usize {
actual_context + 256 * (stride_byte >> 4) as usize
}
#[inline(always)]
fn lower_score_index(
_stride_byte: u8,
_selected_context: u8,
actual_context: usize,
high_nibble: u8,
) -> usize {
debug_assert!(actual_context < 256);
debug_assert!(high_nibble < 16);
actual_context + 4096 + 256 * high_nibble as usize
}
#[allow(unused_variables)]
#[inline(always)]
fn stride_lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
1 + 2 * (actual_context | ((stride_byte as usize & 0x0f) << 8) | ((nibble as usize) << 12))
} else {
2 * (actual_context | ((stride_byte as usize) << 8))
}
}
pub struct Stride1Prior {}
impl Stride1Prior {
#[inline(always)]
pub fn offset() -> usize {
0
}
}
impl Prior for Stride1Prior {
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
#[inline(always)]
fn which() -> usize {
WhichPrior::STRIDE1 as usize
}
}
/*impl StridePrior for Stride1Prior {
const STRIDE_OFFSET:usize = 0;
}*/
pub struct Stride2Prior {}
impl Stride2Prior {
#[inline(always)]
pub fn offset() -> usize {
1
}
}
impl Prior for Stride2Prior {
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
#[inline]
fn which() -> usize {
WhichPrior::STRIDE2 as usize
}
}
/*impl StridePrior for Stride2Prior {
const STRIDE_OFFSET:usize = 1;
}*/
pub struct Stride3Prior {}
impl Stride3Prior {
#[inline(always)]
pub fn offset() -> usize {
2
}
}
impl Prior for Stride3Prior {
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
#[inline(always)]
fn which() -> usize {
WhichPrior::STRIDE3 as usize
}
}
/*impl StridePrior for Stride3Prior {
const STRIDE_OFFSET:usize = 2;
}*/
pub struct Stride4Prior {}
impl Stride4Prior {
#[inline(always)]
pub fn offset() -> usize {
3
}
}
impl Prior for Stride4Prior {
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
#[inline]
fn which() -> usize {
WhichPrior::STRIDE4 as usize
}
}
/*impl StridePrior for Stride4Prior {
const STRIDE_OFFSET:usize = 3;
}*/
/*pub struct Stride8Prior{
}
impl StridePrior for Stride8Prior {
const STRIDE_OFFSET:usize = 7;
}
impl Stride8Prior {
#[inline(always)]
pub fn offset() -> usize{
7
}
}
impl Prior for Stride8Prior {
fn lookup_lin(stride_byte:u8, selected_context:u8, actual_context:usize, high_nibble: Option<u8>) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
#[inline]
fn which() -> usize {
WhichPrior::STRIDE8 as usize
}
}
*/
pub struct CMPrior {}
impl Prior for CMPrior {
#[allow(unused_variables)]
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
(nibble as usize + 1) + 17 * actual_context
} else {
17 * actual_context
}
}
#[inline(always)]
fn which() -> usize {
WhichPrior::CM as usize
}
}
pub struct FastCMPrior {}
impl Prior for FastCMPrior {
#[allow(unused_variables)]
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
2 * actual_context
} else {
2 * actual_context + 1
}
}
#[inline(always)]
fn which() -> usize {
WhichPrior::FAST_CM as usize
}
}
pub struct SlowCMPrior {}
impl Prior for SlowCMPrior {
#[allow(unused_variables)]
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
(nibble as usize + 1) + 17 * actual_context
} else {
17 * actual_context
}
}
#[inline]
fn which() -> usize {
WhichPrior::SLOW_CM as usize
}
}
pub struct AdvPrior {}
impl Prior for AdvPrior {
#[allow(unused_variables)]
#[inline(always)]
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
65536
+ (actual_context | ((stride_byte as usize) << 8) | ((nibble as usize & 0xf) << 16))
} else {
actual_context | ((stride_byte as usize & 0xf0) << 8)
}
}
#[inline(always)]
fn which() -> usize {
WhichPrior::ADV as usize
}
}
pub struct CDF<'a> {
cdf: &'a mut s16,
}
impl<'a> CDF<'a> {
#[inline(always)]
pub fn cost(&self, nibble_u8: u8) -> floatX {
let nibble = nibble_u8 as usize & 0xf;
let mut pdf = self.cdf[nibble];
if nibble_u8 != 0 {
pdf -= self.cdf[(nibble - 1)];
}
FastLog2u16(self.cdf[15] as u16) - FastLog2u16(pdf as u16)
}
#[inline(always)]
pub fn update(&mut self, nibble_u8: u8, speed: (u16, u16)) {
let mut cdf = *self.cdf;
let increment_v = s16::splat(speed.0 as i16);
let one_to_16 = s16::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
let mask_v: s16 = one_to_16.simd_gt(s16::splat(i16::from(nibble_u8))).to_int();
cdf = cdf + (increment_v & mask_v);
if cdf[15] >= speed.1 as i16 {
let cdf_bias = one_to_16;
cdf = cdf + cdf_bias - ((cdf + cdf_bias) >> 2);
}
*self.cdf = cdf;
}
}
impl<'a> From<&'a mut s16> for CDF<'a> {
#[inline(always)]
fn from(cdf: &'a mut s16) -> CDF<'a> {
CDF { cdf }
}
}
pub fn init_cdfs(cdfs: &mut [s16]) {
for item in cdfs.iter_mut() {
*item = s16::from([4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64]);
}
}
pub struct PriorEval<
'a,
Alloc: alloc::Allocator<s16> + alloc::Allocator<u32> + alloc::Allocator<v8>,
> {
input: InputPair<'a>,
context_map: interface::PredictionModeContextMap<InputReferenceMut<'a>>,
block_type: u8,
local_byte_offset: usize,
_nop: <Alloc as Allocator<u32>>::AllocatedMemory,
cm_priors: <Alloc as Allocator<s16>>::AllocatedMemory,
slow_cm_priors: <Alloc as Allocator<s16>>::AllocatedMemory,
fast_cm_priors: <Alloc as Allocator<s16>>::AllocatedMemory,
stride_priors: [<Alloc as Allocator<s16>>::AllocatedMemory; 4],
adv_priors: <Alloc as Allocator<s16>>::AllocatedMemory,
_stride_pyramid_leaves: [u8; find_stride::NUM_LEAF_NODES],
score: <Alloc as Allocator<v8>>::AllocatedMemory,
cm_speed: [(u16, u16); 2],
stride_speed: [(u16, u16); 2],
cur_stride: u8,
}
impl<'a, Alloc: alloc::Allocator<s16> + alloc::Allocator<u32> + alloc::Allocator<v8>>
PriorEval<'a, Alloc>
{
pub fn new(
alloc: &mut Alloc,
input: InputPair<'a>,
stride: [u8; find_stride::NUM_LEAF_NODES],
prediction_mode: interface::PredictionModeContextMap<InputReferenceMut<'a>>,
params: &BrotliEncoderParams,
) -> Self {
let do_alloc = params.prior_bitmask_detection != 0;
let mut cm_speed = prediction_mode.context_map_speed();
let mut stride_speed = prediction_mode.stride_context_speed();
if cm_speed[0] == (0, 0) {
cm_speed[0] = params.literal_adaptation[2]
}
if cm_speed[0] == (0, 0) {
cm_speed[0] = DEFAULT_SPEED;
}
if cm_speed[1] == (0, 0) {
cm_speed[1] = params.literal_adaptation[3]
}
if cm_speed[1] == (0, 0) {
cm_speed[1] = cm_speed[0];
}
if stride_speed[0] == (0, 0) {
stride_speed[0] = params.literal_adaptation[0]
}
if stride_speed[0] == (0, 0) {
stride_speed[0] = DEFAULT_SPEED;
}
if stride_speed[1] == (0, 0) {
stride_speed[1] = params.literal_adaptation[1]
}
if stride_speed[1] == (0, 0) {
stride_speed[1] = stride_speed[0];
}
let mut ret = PriorEval::<Alloc> {
input,
context_map: prediction_mode,
block_type: 0,
cur_stride: 1,
local_byte_offset: 0,
_nop: alloc_default::<u32, Alloc>(),
cm_priors: alloc_if::<s16, _>(do_alloc, alloc, CONTEXT_MAP_PRIOR_SIZE),
slow_cm_priors: alloc_if::<s16, _>(do_alloc, alloc, CONTEXT_MAP_PRIOR_SIZE),
fast_cm_priors: alloc_if::<s16, _>(do_alloc, alloc, CONTEXT_MAP_PRIOR_SIZE),
stride_priors: [
alloc_if::<s16, _>(do_alloc, alloc, STRIDE_PRIOR_SIZE),
alloc_if::<s16, _>(do_alloc, alloc, STRIDE_PRIOR_SIZE),
alloc_if::<s16, _>(do_alloc, alloc, STRIDE_PRIOR_SIZE),
alloc_if::<s16, _>(do_alloc, alloc, STRIDE_PRIOR_SIZE),
/*if do_alloc {m16x16.alloc_cell(STRIDE_PRIOR_SIZE)} else {
Alloc16x16::AllocatedMemory::default()},*/
],
adv_priors: alloc_if::<s16, _>(do_alloc, alloc, ADV_PRIOR_SIZE),
_stride_pyramid_leaves: stride,
score: alloc_if::<v8, _>(do_alloc, alloc, 8192),
cm_speed,
stride_speed,
};
init_cdfs(ret.cm_priors.slice_mut());
init_cdfs(ret.slow_cm_priors.slice_mut());
init_cdfs(ret.fast_cm_priors.slice_mut());
init_cdfs(ret.stride_priors[0].slice_mut());
init_cdfs(ret.stride_priors[1].slice_mut());
init_cdfs(ret.stride_priors[2].slice_mut());
init_cdfs(ret.stride_priors[3].slice_mut());
//init_cdfs(ret.stride_priors[4].slice_mut());
init_cdfs(ret.adv_priors.slice_mut());
ret
}
pub fn choose_bitmask(&mut self) {
let epsilon = 6.0;
let mut max_popularity = 0u32;
let mut max_popularity_index = 0u8;
assert_eq!(WhichPrior::NUM_PRIORS as usize, 8);
let mut popularity = [0u32; 8];
let mut bitmask = [0u8; super::interface::NUM_MIXING_VALUES];
for (i, score) in self.score.slice().iter().enumerate() {
let cm_score = score[WhichPrior::CM as usize];
let slow_cm_score = score[WhichPrior::SLOW_CM as usize];
let fast_cm_score = score[WhichPrior::FAST_CM as usize] + 16.0;
let stride1_score = score[WhichPrior::STRIDE1 as usize];
let stride2_score = score[WhichPrior::STRIDE2 as usize];
let stride3_score = score[WhichPrior::STRIDE3 as usize] + 16.0;
let stride4_score = score[WhichPrior::STRIDE4 as usize];
//let stride8_score = score[WhichPrior::STRIDE8] * 1.125 + 16.0;
let stride8_score = stride4_score + 1.0; // FIXME: never lowest -- ignore stride 8
let stride_score = min(
stride1_score as u64,
min(
stride2_score as u64,
min(
stride3_score as u64,
min(stride4_score as u64, stride8_score as u64),
),
),
);
let adv_score = score[WhichPrior::ADV as usize];
if adv_score + epsilon < (stride_score as floatX)
&& adv_score + epsilon < cm_score
&& adv_score + epsilon < slow_cm_score
&& adv_score + epsilon < fast_cm_score
{
bitmask[i] = 1;
} else if slow_cm_score + epsilon < (stride_score as floatX)
&& slow_cm_score + epsilon < cm_score
&& slow_cm_score + epsilon < fast_cm_score
{
bitmask[i] = 2;
} else if fast_cm_score + epsilon < (stride_score as floatX)
&& fast_cm_score + epsilon < cm_score
{
bitmask[i] = 3;
} else if epsilon + (stride_score as floatX) < cm_score {
bitmask[i] = WhichPrior::STRIDE1 as u8;
if stride_score == stride8_score as u64 {
//bitmask[i] = WhichPrior::STRIDE8 as u8;
}
if stride_score == stride4_score as u64 {
bitmask[i] = WhichPrior::STRIDE4 as u8;
}
if stride_score == stride3_score as u64 {
bitmask[i] = WhichPrior::STRIDE3 as u8;
}
if stride_score == stride2_score as u64 {
bitmask[i] = WhichPrior::STRIDE2 as u8;
}
if stride_score == stride1_score as u64 {
bitmask[i] = WhichPrior::STRIDE1 as u8;
}
} else {
bitmask[i] = 0;
}
if stride_score == 0 {
bitmask[i] = max_popularity_index;
//eprintln!("Miss {}[{}] ~ {}", bitmask[i], i, max_popularity_index);
} else {
popularity[bitmask[i] as usize] += 1;
if popularity[bitmask[i] as usize] > max_popularity {
max_popularity = popularity[bitmask[i] as usize];
max_popularity_index = bitmask[i];
}
//eprintln!("Score {} {} {} {} {}: {}[{}] max={},{}", cm_score, adv_score, slow_cm_score, fast_cm_score, stride_score, bitmask[i], i, max_popularity, max_popularity_index);
}
}
self.context_map.set_mixing_values(&bitmask);
}
pub fn free(&mut self, alloc: &mut Alloc) {
<Alloc as Allocator<v8>>::free_cell(alloc, core::mem::take(&mut self.score));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.cm_priors));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.slow_cm_priors));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.fast_cm_priors));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.stride_priors[0]));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.stride_priors[1]));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.stride_priors[2]));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.stride_priors[3]));
//<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::replace(&mut self.stride_priors[4], alloc_default::<s16, Alloc>()));
<Alloc as Allocator<s16>>::free_cell(alloc, core::mem::take(&mut self.adv_priors));
}
pub fn take_prediction_mode(
&mut self,
) -> interface::PredictionModeContextMap<InputReferenceMut<'a>> {
core::mem::replace(
&mut self.context_map,
interface::PredictionModeContextMap::<InputReferenceMut<'a>> {
literal_context_map: InputReferenceMut::default(),
predmode_speed_and_distance_context_map: InputReferenceMut::default(),
},
)
}
fn update_cost_base(
&mut self,
stride_prior: [u8; 8],
stride_prior_offset: usize,
selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
let mut l_score = v8::splat(0.0);
let mut h_score = v8::splat(0.0);
let base_stride_prior =
stride_prior[stride_prior_offset.wrapping_sub(self.cur_stride as usize) & 7];
let hscore_index = upper_score_index(base_stride_prior, selected_bits, cm_prior);
let lscore_index =
lower_score_index(base_stride_prior, selected_bits, cm_prior, literal >> 4);
{
type CurPrior = CMPrior;
let mut cdf = CurPrior::lookup_mut(
self.cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.cm_speed[1]);
}
{
type CurPrior = CMPrior;
let mut cdf = CurPrior::lookup_mut(
self.cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.cm_speed[0]);
}
{
type CurPrior = SlowCMPrior;
let mut cdf = CurPrior::lookup_mut(
self.slow_cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, (0, 1024));
}
{
type CurPrior = SlowCMPrior;
let mut cdf = CurPrior::lookup_mut(
self.slow_cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, (0, 1024));
}
{
type CurPrior = FastCMPrior;
let mut cdf = CurPrior::lookup_mut(
self.fast_cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.cm_speed[0]);
}
{
type CurPrior = FastCMPrior;
let mut cdf = CurPrior::lookup_mut(
self.fast_cm_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.cm_speed[0]);
}
{
type CurPrior = Stride1Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[0].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
type CurPrior = Stride1Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[0].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
{
type CurPrior = Stride2Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[1].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
type CurPrior = Stride2Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[1].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
{
type CurPrior = Stride3Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[2].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
type CurPrior = Stride3Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[2].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
{
type CurPrior = Stride4Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[3].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
type CurPrior = Stride4Prior;
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[3].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
/* {
type CurPrior = Stride8Prior;
let mut cdf = CurPrior::lookup_mut(self.stride_priors[4].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset())&7], selected_bits, cm_prior, None);
h_score[CurPrior::which()] = cdf.cost(literal>>4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
type CurPrior = Stride8Prior;
let mut cdf = CurPrior::lookup_mut(self.stride_priors[4].slice_mut(),
stride_prior[stride_prior_offset.wrapping_sub(CurPrior::offset()) & 7],
selected_bits,
cm_prior,
Some(literal >> 4));
l_score[CurPrior::which()] = cdf.cost(literal&0xf);
cdf.update(literal&0xf, self.stride_speed[0]);
}
*/
type CurPrior = AdvPrior;
{
let mut cdf = CurPrior::lookup_mut(
self.adv_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
None,
);
h_score[CurPrior::which()] = cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
{
let mut cdf = CurPrior::lookup_mut(
self.adv_priors.slice_mut(),
base_stride_prior,
selected_bits,
cm_prior,
Some(literal >> 4),
);
l_score[CurPrior::which()] = cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
self.score.slice_mut()[lscore_index] += l_score;
self.score.slice_mut()[hscore_index] += h_score;
}
}
impl<'a, Alloc: alloc::Allocator<s16> + alloc::Allocator<u32> + alloc::Allocator<v8>> IRInterpreter
for PriorEval<'a, Alloc>
{
#[inline]
fn inc_local_byte_offset(&mut self, inc: usize) {
self.local_byte_offset += inc;
}
#[inline]
fn local_byte_offset(&self) -> usize {
self.local_byte_offset
}
#[inline]
fn update_block_type(&mut self, new_type: u8, stride: u8) {
self.block_type = new_type;
self.cur_stride = stride;
}
#[inline]
fn block_type(&self) -> u8 {
self.block_type
}
#[inline]
fn literal_data_at_offset(&self, index: usize) -> u8 {
self.input[index]
}
#[inline]
fn literal_context_map(&self) -> &[u8] {
self.context_map.literal_context_map.slice()
}
#[inline]
fn prediction_mode(&self) -> crate::interface::LiteralPredictionModeNibble {
self.context_map.literal_prediction_mode()
}
#[inline]
fn update_cost(
&mut self,
stride_prior: [u8; 8],
stride_prior_offset: usize,
selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
//let stride = self.cur_stride as usize;
self.update_cost_base(
stride_prior,
stride_prior_offset,
selected_bits,
cm_prior,
literal,
)
}
}
impl<'a, 'b, Alloc: alloc::Allocator<s16> + alloc::Allocator<u32> + alloc::Allocator<v8>>
interface::CommandProcessor<'b> for PriorEval<'a, Alloc>
{
#[inline]
fn push(&mut self, val: interface::Command<InputReference<'b>>) {
push_base(self, val)
}
}
+251
View File
@@ -0,0 +1,251 @@
use alloc::{Allocator, SliceWrapperMut};
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "std")]
use std::io::{Error, ErrorKind, Read};
#[cfg(feature = "std")]
pub use alloc_stdlib::StandardAlloc;
use brotli_decompressor::CustomRead;
#[cfg(feature = "std")]
pub use brotli_decompressor::{IntoIoReader, IoReaderWrapper, IoWriterWrapper};
use super::backward_references::BrotliEncoderParams;
use super::combined_alloc::BrotliAlloc;
use super::encode::{
BrotliEncoderDestroyInstance, BrotliEncoderOperation, BrotliEncoderParameter,
BrotliEncoderStateStruct,
};
use super::interface;
use crate::enc::combined_alloc::allocate;
#[cfg(feature = "std")]
pub struct CompressorReaderCustomAlloc<R: Read, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>(
CompressorReaderCustomIo<io::Error, IntoIoReader<R>, BufferType, Alloc>,
);
#[cfg(feature = "std")]
impl<R: Read, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CompressorReaderCustomAlloc<R, BufferType, Alloc>
{
pub fn new(r: R, buffer: BufferType, alloc: Alloc, q: u32, lgwin: u32) -> Self {
CompressorReaderCustomAlloc::<R, BufferType, Alloc>(CompressorReaderCustomIo::<
Error,
IntoIoReader<R>,
BufferType,
Alloc,
>::new(
IntoIoReader::<R>(r),
buffer,
alloc,
Error::new(ErrorKind::InvalidData, "Invalid Data"),
q,
lgwin,
))
}
pub fn get_ref(&self) -> &R {
&self.0.get_ref().0
}
pub fn into_inner(self) -> R {
self.0.into_inner().0
}
}
#[cfg(feature = "std")]
impl<R: Read, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc> Read
for CompressorReaderCustomAlloc<R, BufferType, Alloc>
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.0.read(buf)
}
}
#[cfg(feature = "std")]
pub struct CompressorReader<R: Read>(
CompressorReaderCustomAlloc<
R,
<StandardAlloc as Allocator<u8>>::AllocatedMemory,
StandardAlloc,
>,
);
#[cfg(feature = "std")]
impl<R: Read> CompressorReader<R> {
pub fn new(r: R, buffer_size: usize, q: u32, lgwin: u32) -> Self {
let mut alloc = StandardAlloc::default();
let buffer = allocate::<u8, _>(
&mut alloc,
if buffer_size == 0 { 4096 } else { buffer_size },
);
CompressorReader::<R>(CompressorReaderCustomAlloc::new(r, buffer, alloc, q, lgwin))
}
pub fn with_params(r: R, buffer_size: usize, params: &BrotliEncoderParams) -> Self {
let mut reader = Self::new(r, buffer_size, params.quality as u32, params.lgwin as u32);
(reader.0).0.state.0.params = params.clone();
reader
}
pub fn get_ref(&self) -> &R {
self.0.get_ref()
}
pub fn into_inner(self) -> R {
self.0.into_inner()
}
}
#[cfg(feature = "std")]
impl<R: Read> Read for CompressorReader<R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.0.read(buf)
}
}
pub struct CompressorReaderCustomIo<
ErrType,
R: CustomRead<ErrType>,
BufferType: SliceWrapperMut<u8>,
Alloc: BrotliAlloc,
> {
input_buffer: BufferType,
total_out: Option<usize>,
input_offset: usize,
input_len: usize,
input: R,
input_eof: bool,
error_if_invalid_data: Option<ErrType>,
state: StateWrapper<Alloc>,
}
struct StateWrapper<Alloc: BrotliAlloc>(BrotliEncoderStateStruct<Alloc>);
impl<Alloc: BrotliAlloc> Drop for StateWrapper<Alloc> {
fn drop(&mut self) {
BrotliEncoderDestroyInstance(&mut self.0);
}
}
impl<ErrType, R: CustomRead<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CompressorReaderCustomIo<ErrType, R, BufferType, Alloc>
{
pub fn new(
r: R,
buffer: BufferType,
alloc: Alloc,
invalid_data_error_type: ErrType,
q: u32,
lgwin: u32,
) -> Self {
let mut ret = CompressorReaderCustomIo {
input_buffer: buffer,
total_out: Some(0),
input_offset: 0,
input_len: 0,
input_eof: false,
input: r,
state: StateWrapper(BrotliEncoderStateStruct::new(alloc)),
error_if_invalid_data: Some(invalid_data_error_type),
};
ret.state
.0
.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, q);
ret.state
.0
.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin);
ret
}
pub fn copy_to_front(&mut self) {
let avail_in = self.input_len - self.input_offset;
if self.input_offset == self.input_buffer.slice_mut().len() {
self.input_offset = 0;
self.input_len = 0;
} else if self.input_offset + 256 > self.input_buffer.slice_mut().len()
&& avail_in < self.input_offset
{
let (first, second) = self
.input_buffer
.slice_mut()
.split_at_mut(self.input_offset);
first[0..avail_in].clone_from_slice(&second[0..avail_in]);
self.input_len -= self.input_offset;
self.input_offset = 0;
}
}
pub fn into_inner(self) -> R {
match self {
CompressorReaderCustomIo {
input_buffer: _ib,
total_out: _to,
input_offset: _io,
input_len: _len,
input,
input_eof: _ieof,
error_if_invalid_data: _eiid,
state: _state,
} => input,
}
}
pub fn get_ref(&self) -> &R {
&self.input
}
}
impl<ErrType, R: CustomRead<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CustomRead<ErrType> for CompressorReaderCustomIo<ErrType, R, BufferType, Alloc>
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ErrType> {
let mut nop_callback =
|_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
_cmds: &mut [interface::StaticCommand],
_mb: interface::InputPair,
_mfv: &mut Alloc| ();
let mut output_offset: usize = 0;
let mut avail_out = buf.len();
let mut avail_in = self.input_len - self.input_offset;
while output_offset == 0 {
if self.input_len < self.input_buffer.slice_mut().len() && !self.input_eof {
match self
.input
.read(&mut self.input_buffer.slice_mut()[self.input_len..])
{
Err(e) => return Err(e),
Ok(size) => {
if size == 0 {
self.input_eof = true;
} else {
self.input_len += size;
avail_in = self.input_len - self.input_offset;
}
}
}
}
let op: BrotliEncoderOperation;
if avail_in == 0 {
op = BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
} else {
op = BrotliEncoderOperation::BROTLI_OPERATION_PROCESS;
}
let ret = self.state.0.compress_stream(
op,
&mut avail_in,
self.input_buffer.slice_mut(),
&mut self.input_offset,
&mut avail_out,
buf,
&mut output_offset,
&mut self.total_out,
&mut nop_callback,
);
if avail_in == 0 {
self.copy_to_front();
}
if !ret {
return Err(self.error_if_invalid_data.take().unwrap());
}
if self.state.0.is_finished() {
break;
}
}
Ok(output_offset)
}
}
+213
View File
@@ -0,0 +1,213 @@
use alloc::{Allocator, SliceWrapper};
use core::marker::PhantomData;
use core::mem;
#[cfg(feature = "std")]
use std;
use super::backward_references::UnionHasher;
use crate::enc::threading::{
BatchSpawnable, BatchSpawnableLite, BrotliEncoderThreadError, CompressMulti,
CompressionThreadResult, InternalOwned, InternalSendAlloc, Joinable, Owned, OwnedRetriever,
PoisonedThreadError, SendAlloc,
};
use crate::enc::{BrotliAlloc, BrotliEncoderParams};
pub struct SingleThreadedJoinable<T: Send + 'static, U: Send + 'static> {
result: Result<T, U>,
}
impl<T: Send + 'static, U: Send + 'static> Joinable<T, U> for SingleThreadedJoinable<T, U> {
fn join(self) -> Result<T, U> {
self.result
}
}
#[cfg(feature = "std")]
pub struct SingleThreadedOwnedRetriever<U: Send + 'static>(std::sync::RwLock<U>);
#[cfg(feature = "std")]
impl<U: Send + 'static> OwnedRetriever<U> for SingleThreadedOwnedRetriever<U> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError> {
Ok(f(&*self.0.read().unwrap()))
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
Ok(self.0.into_inner().unwrap())
}
}
#[cfg(feature = "std")]
impl<U: Send + 'static> SingleThreadedOwnedRetriever<U> {
fn new(u: U) -> Self {
SingleThreadedOwnedRetriever(std::sync::RwLock::new(u))
}
}
#[cfg(not(feature = "std"))]
pub struct SingleThreadedOwnedRetriever<U: Send + 'static>(U);
#[cfg(not(feature = "std"))]
impl<U: Send + 'static> SingleThreadedOwnedRetriever<U> {
fn new(u: U) -> Self {
SingleThreadedOwnedRetriever(u)
}
}
#[cfg(not(feature = "std"))]
impl<U: Send + 'static> OwnedRetriever<U> for SingleThreadedOwnedRetriever<U> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError> {
Ok(f(&self.0))
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
Ok(self.0)
}
}
#[derive(Default)]
pub struct SingleThreadedSpawner {}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnable<ReturnValue, ExtraInput, Alloc, U> for SingleThreadedSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle = SingleThreadedJoinable<ReturnValue, BrotliEncoderThreadError>;
type FinalJoinHandle = SingleThreadedOwnedRetriever<U>;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
SingleThreadedOwnedRetriever::<U>::new(
mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap(),
)
}
fn spawn<F: Fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue + Send + 'static + Copy>(
&mut self,
handle: &mut Self::FinalJoinHandle,
work: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: F,
) {
let (alloc, extra_input) = work.replace_with_default();
let ret = handle.view(|sub_view| f(extra_input, index, num_threads, sub_view, alloc));
*work = SendAlloc(InternalSendAlloc::Join(SingleThreadedJoinable {
result: Ok(ret.unwrap()),
}));
}
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnableLite<ReturnValue, ExtraInput, Alloc, U> for SingleThreadedSpawner
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle =
<SingleThreadedSpawner as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::JoinHandle;
type FinalJoinHandle = <SingleThreadedSpawner as BatchSpawnable<
ReturnValue,
ExtraInput,
Alloc,
U,
>>::FinalJoinHandle;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
<Self as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::make_spawner(self, input)
}
fn spawn(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
) {
<Self as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::spawn(
self,
handle,
alloc_per_thread,
index,
num_threads,
f,
)
}
}
pub fn compress_multi<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
<SingleThreadedSpawner as BatchSpawnable<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
SliceW,
>>::JoinHandle,
>],
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send,
<Alloc as Allocator<u32>>::AllocatedMemory: Send,
{
CompressMulti(
params,
owned_input,
output,
alloc_per_thread,
&mut SingleThreadedSpawner::default(),
)
}
pub struct WorkerPool<A, B, C, D> {
a: PhantomData<A>,
b: PhantomData<B>,
c: PhantomData<C>,
d: PhantomData<D>,
}
pub fn new_work_pool<A, B, C, D>(_num_threads: usize) -> WorkerPool<A, B, C, D> {
WorkerPool::<A, B, C, D> {
a: PhantomData,
b: PhantomData,
c: PhantomData,
d: PhantomData,
}
}
pub fn compress_worker_pool<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
<SingleThreadedSpawner as BatchSpawnable<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
SliceW,
>>::JoinHandle,
>],
_worker_pool: &mut WorkerPool<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>,
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send,
<Alloc as Allocator<u32>>::AllocatedMemory: Send,
{
compress_multi(params, owned_input, output, alloc_per_thread)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+328
View File
@@ -0,0 +1,328 @@
use core;
use super::super::alloc;
use super::super::alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use super::backward_references::BrotliEncoderParams;
use super::input_pair::{InputPair, InputReference, InputReferenceMut};
use super::interface;
use super::ir_interpret::{push_base, IRInterpreter};
use super::prior_eval::DEFAULT_SPEED;
use super::util::{floatX, FastLog2u16};
use crate::enc::combined_alloc::{alloc_default, allocate};
const NIBBLE_PRIOR_SIZE: usize = 16;
pub const STRIDE_PRIOR_SIZE: usize = 256 * 256 * NIBBLE_PRIOR_SIZE * 2;
pub fn local_init_cdfs(cdfs: &mut [u16]) {
for (index, item) in cdfs.iter_mut().enumerate() {
*item = 4 + 4 * (index as u16 & 0x0f);
}
}
#[allow(unused_variables)]
fn stride_lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
if let Some(nibble) = high_nibble {
1 + 2 * (actual_context | ((stride_byte as usize & 0xf) << 8) | ((nibble as usize) << 12))
} else {
2 * (actual_context | ((stride_byte as usize) << 8))
}
}
struct CDF<'a> {
cdf: &'a mut [u16],
}
struct Stride1Prior {}
impl Stride1Prior {
fn lookup_lin(
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> usize {
stride_lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
}
fn lookup_mut(
data: &mut [u16],
stride_byte: u8,
selected_context: u8,
actual_context: usize,
high_nibble: Option<u8>,
) -> CDF<'_> {
let index = Self::lookup_lin(stride_byte, selected_context, actual_context, high_nibble)
* NIBBLE_PRIOR_SIZE;
CDF::from(data.split_at_mut(index).1.split_at_mut(16).0)
}
}
impl<'a> CDF<'a> {
pub fn cost(&self, nibble_u8: u8) -> floatX {
assert_eq!(self.cdf.len(), 16);
let nibble = nibble_u8 as usize & 0xf;
let mut pdf = self.cdf[nibble];
if nibble_u8 != 0 {
pdf -= self.cdf[nibble - 1];
}
FastLog2u16(self.cdf[15]) - FastLog2u16(pdf)
}
pub fn update(&mut self, nibble_u8: u8, speed: (u16, u16)) {
assert_eq!(self.cdf.len(), 16);
for nib_range in (nibble_u8 as usize & 0xf)..16 {
self.cdf[nib_range] += speed.0;
}
if self.cdf[15] >= speed.1 {
const CDF_BIAS: [u16; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
for nibble_index in 0..16 {
let tmp = &mut self.cdf[nibble_index];
*tmp = (tmp.wrapping_add(CDF_BIAS[nibble_index]))
.wrapping_sub(tmp.wrapping_add(CDF_BIAS[nibble_index]) >> 2);
}
}
}
}
impl<'a> From<&'a mut [u16]> for CDF<'a> {
fn from(cdf: &'a mut [u16]) -> CDF<'a> {
assert_eq!(cdf.len(), 16);
CDF { cdf }
}
}
pub struct StrideEval<
'a,
Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX> + 'a,
> {
input: InputPair<'a>,
alloc: &'a mut Alloc,
context_map: &'a interface::PredictionModeContextMap<InputReferenceMut<'a>>,
block_type: u8,
local_byte_offset: usize,
stride_priors: [<Alloc as Allocator<u16>>::AllocatedMemory; 8],
score: <Alloc as Allocator<floatX>>::AllocatedMemory,
cur_score_epoch: usize,
stride_speed: [(u16, u16); 2],
cur_stride: u8,
}
impl<'a, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX> + 'a>
StrideEval<'a, Alloc>
{
pub fn new(
alloc: &'a mut Alloc,
input: InputPair<'a>,
prediction_mode: &'a interface::PredictionModeContextMap<InputReferenceMut<'a>>,
params: &BrotliEncoderParams,
) -> Self {
let do_alloc = true;
let mut stride_speed = prediction_mode.stride_context_speed();
if stride_speed[0] == (0, 0) {
stride_speed[0] = params.literal_adaptation[0]
}
if stride_speed[0] == (0, 0) {
stride_speed[0] = DEFAULT_SPEED;
}
if stride_speed[1] == (0, 0) {
stride_speed[1] = params.literal_adaptation[1]
}
if stride_speed[1] == (0, 0) {
stride_speed[1] = stride_speed[0];
}
let score = if do_alloc {
allocate::<floatX, _>(alloc, 8 * 4) // FIXME make this bigger than just 4
} else {
alloc_default::<floatX, Alloc>()
};
let stride_priors = if do_alloc {
[
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
allocate::<u16, _>(alloc, STRIDE_PRIOR_SIZE),
]
} else {
[
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
alloc_default::<u16, Alloc>(),
]
};
let mut ret = StrideEval::<Alloc> {
input,
context_map: prediction_mode,
block_type: 0,
alloc,
cur_stride: 1,
cur_score_epoch: 0,
local_byte_offset: 0,
stride_priors,
score,
stride_speed,
};
for stride_prior in ret.stride_priors.iter_mut() {
local_init_cdfs(stride_prior.slice_mut());
}
ret
}
pub fn alloc(&mut self) -> &mut Alloc {
self.alloc
}
pub fn choose_stride(&self, stride_data: &mut [u8]) {
assert_eq!(stride_data.len(), self.cur_score_epoch);
assert!(self.score.slice().len() > stride_data.len());
assert!(self.score.slice().len() > (stride_data.len() << 3) + 7 + 8);
for (index, choice) in stride_data.iter_mut().enumerate() {
let choices = self
.score
.slice()
.split_at((1 + index) << 3)
.1
.split_at(8)
.0;
let mut best_choice: u8 = 0;
let mut best_score = choices[0];
for (cur_index, cur_score) in choices.iter().enumerate() {
if *cur_score + 2.0 < best_score {
// needs to be 2 bits better to be worth the type switch
best_score = *cur_score;
best_choice = cur_index as u8;
}
}
*choice = best_choice;
}
}
pub fn num_types(&self) -> usize {
self.cur_score_epoch
}
fn update_cost_base(
&mut self,
stride_prior: [u8; 8],
selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
type CurPrior = Stride1Prior;
{
for i in 0..8 {
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[i].slice_mut(),
stride_prior[i],
selected_bits,
cm_prior,
None,
);
self.score.slice_mut()[self.cur_score_epoch * 8 + i] += cdf.cost(literal >> 4);
cdf.update(literal >> 4, self.stride_speed[1]);
}
}
{
for i in 0..8 {
let mut cdf = CurPrior::lookup_mut(
self.stride_priors[i].slice_mut(),
stride_prior[i],
selected_bits,
cm_prior,
Some(literal >> 4),
);
self.score.slice_mut()[self.cur_score_epoch * 8 + i] += cdf.cost(literal & 0xf);
cdf.update(literal & 0xf, self.stride_speed[0]);
}
}
}
}
impl<'a, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>> Drop
for StrideEval<'a, Alloc>
{
fn drop(&mut self) {
<Alloc as Allocator<floatX>>::free_cell(self.alloc, core::mem::take(&mut self.score));
for i in 0..8 {
<Alloc as Allocator<u16>>::free_cell(
self.alloc,
core::mem::take(&mut self.stride_priors[i]),
);
}
}
}
impl<'a, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>>
IRInterpreter for StrideEval<'a, Alloc>
{
fn inc_local_byte_offset(&mut self, inc: usize) {
self.local_byte_offset += inc;
}
fn local_byte_offset(&self) -> usize {
self.local_byte_offset
}
fn update_block_type(&mut self, new_type: u8, stride: u8) {
self.block_type = new_type;
self.cur_stride = stride;
self.cur_score_epoch += 1;
if self.cur_score_epoch * 8 + 7 >= self.score.slice().len() {
let new_len = self.score.slice().len() * 2;
let mut new_score = allocate::<floatX, _>(self.alloc, new_len);
for (src, dst) in self.score.slice().iter().zip(
new_score
.slice_mut()
.split_at_mut(self.score.slice().len())
.0
.iter_mut(),
) {
*dst = *src;
}
<Alloc as Allocator<floatX>>::free_cell(
self.alloc,
core::mem::replace(&mut self.score, new_score),
);
}
}
fn block_type(&self) -> u8 {
self.block_type
}
fn literal_data_at_offset(&self, index: usize) -> u8 {
self.input[index]
}
fn literal_context_map(&self) -> &[u8] {
self.context_map.literal_context_map.slice()
}
fn prediction_mode(&self) -> crate::interface::LiteralPredictionModeNibble {
self.context_map.literal_prediction_mode()
}
fn update_cost(
&mut self,
stride_prior: [u8; 8],
stride_prior_offset: usize,
selected_bits: u8,
cm_prior: usize,
literal: u8,
) {
let reversed_stride_priors = [
stride_prior[stride_prior_offset & 7],
stride_prior[stride_prior_offset.wrapping_sub(1) & 7],
stride_prior[stride_prior_offset.wrapping_sub(2) & 7],
stride_prior[stride_prior_offset.wrapping_sub(3) & 7],
stride_prior[stride_prior_offset.wrapping_sub(4) & 7],
stride_prior[stride_prior_offset.wrapping_sub(5) & 7],
stride_prior[stride_prior_offset.wrapping_sub(6) & 7],
stride_prior[stride_prior_offset.wrapping_sub(7) & 7],
];
self.update_cost_base(reversed_stride_priors, selected_bits, cm_prior, literal)
}
}
impl<'a, 'b, Alloc: alloc::Allocator<u16> + alloc::Allocator<u32> + alloc::Allocator<floatX>>
interface::CommandProcessor<'b> for StrideEval<'a, Alloc>
{
fn push(&mut self, val: interface::Command<InputReference<'b>>) {
push_base(self, val)
}
}
+649
View File
@@ -0,0 +1,649 @@
#![cfg(test)]
extern crate alloc_no_stdlib;
extern crate brotli_decompressor;
extern "C" {
fn calloc(n_elem: usize, el_size: usize) -> *mut u8;
}
extern "C" {
fn free(ptr: *mut u8);
}
// FIXME: Remove this after https://github.com/dropbox/rust-alloc-no-stdlib/issues/19 is fixed
use alloc::{
declare_stack_allocator_struct, define_allocator_memory_pool, define_stack_allocator_traits,
static_array,
};
use core;
use core::cmp::min;
use core::ops;
use brotli_decompressor::HuffmanCode;
use super::super::alloc::{
bzero, AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut, StackAllocator,
};
pub use super::super::{BrotliDecompressStream, BrotliResult, BrotliState};
use super::cluster::HistogramPair;
use super::combined_alloc::CombiningAllocator;
use super::command::Command;
use super::encode::{BrotliEncoderOperation, BrotliEncoderParameter};
use super::entropy_encode::HuffmanTree;
use super::histogram::{ContextType, HistogramCommand, HistogramDistance, HistogramLiteral};
use super::pdf::PDF;
use super::{interface, s16, v8, StaticCommand, ZopfliNode};
use crate::enc::encode::BrotliEncoderStateStruct;
declare_stack_allocator_struct!(MemPool, 128, stack);
declare_stack_allocator_struct!(CallocatedFreelist4096, 128, calloc);
declare_stack_allocator_struct!(CallocatedFreelist2048, 64, calloc);
declare_stack_allocator_struct!(CallocatedFreelist1024, 32, calloc);
declare_stack_allocator_struct!(StackAllocatedFreelist64, 64, stack);
fn oneshot_compress(
input: &[u8],
output: &mut [u8],
quality: u32,
lgwin: u32,
magic: bool,
in_batch_size: usize,
out_batch_size: usize,
) -> (bool, usize) {
let stack_u8_buffer =
unsafe { define_allocator_memory_pool!(96, u8, [0; 24 * 1024 * 1024], calloc) };
let stack_u16_buffer =
unsafe { define_allocator_memory_pool!(96, u16, [0; 128 * 1024], calloc) };
let stack_i32_buffer =
unsafe { define_allocator_memory_pool!(96, i32, [0; 128 * 1024], calloc) };
let stack_u32_buffer =
unsafe { define_allocator_memory_pool!(96, u32, [0; 32 * 1024 * 1024], calloc) };
let stack_u64_buffer =
unsafe { define_allocator_memory_pool!(96, u64, [0; 32 * 1024], calloc) };
let stack_f64_buffer =
unsafe { define_allocator_memory_pool!(48, super::util::floatX, [0; 128 * 1024], calloc) };
let mut stack_global_buffer_v8 =
define_allocator_memory_pool!(64, v8, [v8::default(); 1024 * 16], stack);
let mf8 = StackAllocatedFreelist64::<v8>::new_allocator(&mut stack_global_buffer_v8, bzero);
let mut stack_16x16_buffer =
define_allocator_memory_pool!(64, s16, [s16::default(); 1024 * 16], stack);
let m16x16 = StackAllocatedFreelist64::<s16>::new_allocator(&mut stack_16x16_buffer, bzero);
let stack_hl_buffer =
unsafe { define_allocator_memory_pool!(48, HistogramLiteral, [0; 128 * 1024], calloc) };
let stack_hc_buffer =
unsafe { define_allocator_memory_pool!(48, HistogramCommand, [0; 128 * 1024], calloc) };
let stack_hd_buffer =
unsafe { define_allocator_memory_pool!(48, HistogramDistance, [0; 128 * 1024], calloc) };
let stack_hp_buffer =
unsafe { define_allocator_memory_pool!(48, HistogramPair, [0; 128 * 1024], calloc) };
let stack_ct_buffer =
unsafe { define_allocator_memory_pool!(48, ContextType, [0; 128 * 1024], calloc) };
let stack_ht_buffer =
unsafe { define_allocator_memory_pool!(48, HuffmanTree, [0; 128 * 1024], calloc) };
let stack_zn_buffer =
unsafe { define_allocator_memory_pool!(48, ZopfliNode, [0; 1024], calloc) };
let stack_mc_buffer =
unsafe { define_allocator_memory_pool!(48, Command, [0; 128 * 1024], calloc) };
let stack_pdf_buffer = unsafe { define_allocator_memory_pool!(48, PDF, [0; 1], calloc) };
let stack_sc_buffer =
unsafe { define_allocator_memory_pool!(48, StaticCommand, [0; 100], calloc) };
let stack_u8_allocator =
CallocatedFreelist4096::<u8>::new_allocator(stack_u8_buffer.data, bzero);
let stack_u16_allocator =
CallocatedFreelist4096::<u16>::new_allocator(stack_u16_buffer.data, bzero);
let stack_i32_allocator =
CallocatedFreelist1024::<i32>::new_allocator(stack_i32_buffer.data, bzero);
let stack_u32_allocator =
CallocatedFreelist4096::<u32>::new_allocator(stack_u32_buffer.data, bzero);
let stack_u64_allocator =
CallocatedFreelist1024::<u64>::new_allocator(stack_u64_buffer.data, bzero);
let stack_zn_allocator =
CallocatedFreelist1024::<ZopfliNode>::new_allocator(stack_zn_buffer.data, bzero);
let mf64 =
CallocatedFreelist2048::<super::util::floatX>::new_allocator(stack_f64_buffer.data, bzero);
let mpdf = CallocatedFreelist2048::<PDF>::new_allocator(stack_pdf_buffer.data, bzero);
let msc = CallocatedFreelist2048::<StaticCommand>::new_allocator(stack_sc_buffer.data, bzero);
let stack_mc_allocator =
CallocatedFreelist2048::<Command>::new_allocator(stack_mc_buffer.data, bzero);
let mhl =
CallocatedFreelist2048::<HistogramLiteral>::new_allocator(stack_hl_buffer.data, bzero);
let mhc =
CallocatedFreelist2048::<HistogramCommand>::new_allocator(stack_hc_buffer.data, bzero);
let mhd =
CallocatedFreelist2048::<HistogramDistance>::new_allocator(stack_hd_buffer.data, bzero);
let mhp = CallocatedFreelist2048::<HistogramPair>::new_allocator(stack_hp_buffer.data, bzero);
let mct = CallocatedFreelist2048::<ContextType>::new_allocator(stack_ct_buffer.data, bzero);
let mht = CallocatedFreelist2048::<HuffmanTree>::new_allocator(stack_ht_buffer.data, bzero);
let mut s_orig = BrotliEncoderStateStruct::new(CombiningAllocator::new(
stack_u8_allocator,
stack_u16_allocator,
stack_i32_allocator,
stack_u32_allocator,
stack_u64_allocator,
stack_mc_allocator,
mf64,
mf8,
m16x16,
mpdf,
msc,
mhl,
mhc,
mhd,
mhp,
mct,
mht,
stack_zn_allocator,
));
let mut next_in_offset: usize = 0;
let mut next_out_offset: usize = 0;
{
let s = &mut s_orig;
s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, quality);
if magic {
s.set_parameter(
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
magic as u32,
);
}
if quality >= 10 {
s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_Q9_5, 1);
}
s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin);
s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_MODE, 0); // gen, text, font
s.set_parameter(
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
input.len() as u32,
);
loop {
let mut available_in: usize = min(input.len() - next_in_offset, in_batch_size);
let mut available_out: usize = min(output.len() - next_out_offset, out_batch_size);
if available_out == 0 {
panic!("No output buffer space");
}
let mut total_out = Some(0);
let op: BrotliEncoderOperation;
if available_in == input.len() - next_in_offset {
op = BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
} else {
op = BrotliEncoderOperation::BROTLI_OPERATION_PROCESS;
}
let mut nop_callback =
|_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
_cmds: &mut [interface::StaticCommand],
_mb: interface::InputPair,
_mfv: &mut CombiningAllocator<
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
_,
>| ();
let result = s.compress_stream(
op,
&mut available_in,
input,
&mut next_in_offset,
&mut available_out,
output,
&mut next_out_offset,
&mut total_out,
&mut nop_callback,
);
if !result {
return (result, next_out_offset);
}
if s.is_finished() {
break;
}
}
}
(true, next_out_offset)
}
#[cfg(target_pointer_width = "32")]
static lock32: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
/// 32bit systems do not have sufficient memory to compress multiple items
/// at the same time with the current limits and defaults. So we instead spin
/// until a process has completed compression. We cannot use proper locks
/// in nostd, so we fall back to this simple spin lock.
#[cfg(target_pointer_width = "32")]
fn lock_if_32bit() {
use core::sync::atomic::Ordering;
loop {
let cur = lock32.fetch_add(1, Ordering::SeqCst);
if cur == 0 {
return;
}
lock32.fetch_sub(1, Ordering::SeqCst);
}
}
#[cfg(target_pointer_width = "32")]
fn unlock_if_32bit() {
use core::sync::atomic::Ordering;
lock32.fetch_sub(1, Ordering::SeqCst);
}
#[cfg(not(target_pointer_width = "32"))]
fn lock_if_32bit() {}
#[cfg(not(target_pointer_width = "32"))]
fn unlock_if_32bit() {}
pub(crate) fn oneshot_decompress(
compressed: &[u8],
output: &mut [u8],
) -> (BrotliResult, usize, usize) {
let mut available_in: usize = compressed.len();
let mut available_out: usize = output.len();
let mut stack_u8_buffer = define_allocator_memory_pool!(128, u8, [0; 100 * 1024], stack);
let mut stack_u32_buffer = define_allocator_memory_pool!(128, u32, [0; 36 * 1024], stack);
let mut stack_hc_buffer = define_allocator_memory_pool!(
128,
HuffmanCode,
[HuffmanCode::default(); 116 * 1024],
stack
);
let stack_u8_allocator = MemPool::<u8>::new_allocator(&mut stack_u8_buffer, bzero);
let stack_u32_allocator = MemPool::<u32>::new_allocator(&mut stack_u32_buffer, bzero);
let stack_hc_allocator = MemPool::<HuffmanCode>::new_allocator(&mut stack_hc_buffer, bzero);
let mut input_offset: usize = 0;
let mut output_offset: usize = 0;
let mut written: usize = 0;
let mut brotli_state =
BrotliState::new(stack_u8_allocator, stack_u32_allocator, stack_hc_allocator);
let result = BrotliDecompressStream(
&mut available_in,
&mut input_offset,
compressed,
&mut available_out,
&mut output_offset,
output,
&mut written,
&mut brotli_state,
);
(result, input_offset, output_offset)
}
fn oneshot(
input: &[u8],
compressed: &mut [u8],
output: &mut [u8],
q: u32,
lg: u32,
magic: bool,
in_buffer_size: usize,
out_buffer_size: usize,
) -> (BrotliResult, usize, usize) {
lock_if_32bit();
let (success, mut available_in) = oneshot_compress(
input,
compressed,
q,
lg,
magic,
in_buffer_size,
out_buffer_size,
);
if !success {
//return (BrotliResult::ResultFailure, 0, 0);
available_in = compressed.len();
}
let ret = oneshot_decompress(&mut compressed[..available_in], output);
unlock_if_32bit();
ret
}
#[test]
fn test_roundtrip_10x10y() {
const BUFFER_SIZE: usize = 128;
let mut compressed: [u8; 13] = [0; 13];
let mut output = [0u8; BUFFER_SIZE];
let mut input = [
'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8, 'x' as u8,
'x' as u8, 'x' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8,
'y' as u8, 'y' as u8, 'y' as u8, 'y' as u8,
];
let (result, compressed_offset, output_offset) = oneshot(
&mut input[..],
&mut compressed,
&mut output[..],
9,
10,
false,
1,
1,
);
match result {
BrotliResult::ResultSuccess => {}
_ => assert!(false),
}
let mut i: usize = 0;
while i < 10 {
assert_eq!(output[i], 'x' as u8);
assert_eq!(output[i + 10], 'y' as u8);
i += 1;
}
assert_eq!(output_offset, 20);
assert_eq!(compressed_offset, compressed.len());
}
macro_rules! test_roundtrip_file {
($filedata : expr, $bufsize: expr, $quality: expr, $lgwin: expr, $magic: expr, $in_buf:expr, $out_buf:expr) => {{
let stack_u8_buffer = unsafe {
alloc::define_allocator_memory_pool!(4096, u8, [0; 18 * 1024 * 1024], calloc)
};
let mut stack_u8_allocator =
CallocatedFreelist4096::<u8>::new_allocator(stack_u8_buffer.data, bzero);
let mut compressed = stack_u8_allocator.alloc_cell($bufsize);
let inp = $filedata;
let mut output = stack_u8_allocator.alloc_cell(inp.len() + 16);
let (result, compressed_offset, output_offset) = oneshot(
&inp[..],
compressed.slice_mut(),
output.slice_mut(),
$quality,
$lgwin,
$magic,
$in_buf,
$out_buf,
);
match result {
BrotliResult::ResultSuccess => {}
_ => assert!(false),
}
for i in 0..inp.len() {
if inp[i] != output[i] {
assert_eq!((i, inp[i]), (i, output[i]));
}
assert_eq!(inp[i], output[i]);
}
assert!(compressed_offset <= compressed.slice().len());
assert_eq!(output_offset, inp.len());
stack_u8_allocator.free_cell(output);
stack_u8_allocator.free_cell(compressed);
}};
}
#[test]
fn test_roundtrip_64x() {
test_roundtrip_file!(include_bytes!("../../testdata/64x"), 72, 9, 10, false, 3, 2);
}
#[test]
fn test_roundtrip_ukkonooa() {
test_roundtrip_file!(
include_bytes!("../../testdata/ukkonooa"),
82,
9,
10,
true,
3,
2
);
}
#[test]
fn test_roundtrip_backward65536() {
test_roundtrip_file!(
include_bytes!("../../testdata/backward65536"),
72000,
9,
10,
false,
3,
2
);
}
#[test]
fn test_roundtrip_aaabaaaa() {
test_roundtrip_file!(
include_bytes!("../../testdata/aaabaaaa"),
72000,
9,
10,
true,
3,
2
);
}
#[test]
fn test_roundtrip_monkey() {
test_roundtrip_file!(
include_bytes!("../../testdata/monkey"),
72000,
9,
10,
false,
16,
15
);
}
#[test]
fn test_roundtrip_quickfox_repeated() {
test_roundtrip_file!(
include_bytes!("../../testdata/quickfox_repeated"),
16384,
9,
10,
true,
257,
255
);
}
#[test]
fn test_roundtrip_asyoulik() {
test_roundtrip_file!(
include_bytes!("../../testdata/asyoulik.txt"),
64384,
9,
15,
false,
513,
511
);
}
#[test]
fn test_roundtrip_asyoulik9_5() {
test_roundtrip_file!(
include_bytes!("../../testdata/asyoulik.txt"),
62384,
10,
15,
true,
513,
511
);
}
#[test]
fn test_roundtrip_compressed() {
test_roundtrip_file!(
include_bytes!("../../testdata/compressed_file"),
50400,
9,
10,
false,
1025,
1024
);
}
#[test]
fn test_roundtrip_compressed_repeated() {
test_roundtrip_file!(
include_bytes!("../../testdata/compressed_repeated"),
120000,
9,
16,
false,
2049,
2047
);
}
#[test]
fn test_roundtrip_first_58_bytes_alice() {
test_roundtrip_file!(
&include_bytes!("../../testdata/alice29.txt")[..58],
50400,
2,
10,
true,
1,
2
);
}
#[test]
fn test_roundtrip_first_2_bytes_alice() {
test_roundtrip_file!(
&include_bytes!("../../testdata/alice29.txt")[..2],
50400,
2,
10,
true,
1,
2
);
}
#[test]
fn test_roundtrip_quickfox() {
test_roundtrip_file!(
include_bytes!("../../testdata/quickfox"),
256,
9,
10,
false,
1,
2
);
}
#[test]
fn test_roundtrip_x() {
const BUFFER_SIZE: usize = 16384;
let mut compressed: [u8; 6] = [0x0b, 0x00, 0x80, 0x58, 0x03, 0];
let mut output = [0u8; BUFFER_SIZE];
let mut input = ['X' as u8];
let (result, compressed_offset, output_offset) = oneshot(
&mut input[..],
&mut compressed[..],
&mut output[..],
9,
10,
false,
1,
2,
);
match result {
BrotliResult::ResultSuccess => {}
_ => assert!(false),
}
assert_eq!(output[0], 'X' as u8);
assert_eq!(output_offset, 1);
assert_eq!(compressed_offset, compressed.len());
}
#[test]
fn test_roundtrip_empty() {
let mut compressed: [u8; 2] = [0x06, 0];
let mut output = [0u8; 1];
let (result, compressed_offset, output_offset) = oneshot(
&mut [],
&mut compressed[..],
&mut output[..],
9,
10,
false,
2,
3,
);
match result {
BrotliResult::ResultSuccess => {}
_ => assert!(false),
}
assert_eq!(output_offset, 0);
assert_eq!(compressed_offset, compressed.len());
}
#[cfg(feature = "std")]
#[test]
fn test_compress_into_short_buffer() {
use std::io::{Cursor, ErrorKind, Write};
// this plaintext should compress to 11 bytes
let plaintext = [0u8; 2048];
// but we only provide space for 10
let mut output_buffer = [0u8; 10];
let mut output_cursor = Cursor::new(&mut output_buffer[..]);
let mut w = crate::CompressorWriter::new(&mut output_cursor, 4096, 4, 22);
assert_eq!(w.write(&plaintext).unwrap(), 2048);
assert_eq!(w.flush().unwrap_err().kind(), ErrorKind::WriteZero);
w.into_inner();
println!("{output_buffer:?}");
}
/*
#[cfg(feature="std")]
struct Buffer {
data: Vec<u8>,
read_offset: usize,
}
#[cfg(feature="std")]
impl Buffer {
pub fn new(buf: &[u8]) -> Buffer {
let mut ret = Buffer {
data: Vec::<u8>::new(),
read_offset: 0,
};
ret.data.extend(buf);
return ret;
}
}
#[cfg(feature="std")]
impl io::Read for Buffer {
fn read(self: &mut Self, buf: &mut [u8]) -> io::Result<usize> {
let bytes_to_read = min(buf.len(), self.data.len() - self.read_offset);
if bytes_to_read > 0 {
buf[0..bytes_to_read]
.clone_from_slice(&self.data[self.read_offset..self.read_offset + bytes_to_read]);
}
self.read_offset += bytes_to_read;
return Ok(bytes_to_read);
}
}
#[cfg(feature="std")]
impl io::Write for Buffer {
fn write(self: &mut Self, buf: &[u8]) -> io::Result<usize> {
self.data.extend(buf);
return Ok(buf.len());
}
fn flush(self: &mut Self) -> io::Result<()> {
return Ok(());
}
}
*/
+663
View File
@@ -0,0 +1,663 @@
use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use core::marker::PhantomData;
use core::ops::Range;
use core::{any, mem};
#[cfg(feature = "std")]
use std;
use super::backward_references::{AnyHasher, BrotliEncoderParams, CloneWithAlloc, UnionHasher};
use super::encode::{
hasher_setup, BrotliEncoderDestroyInstance, BrotliEncoderMaxCompressedSize,
BrotliEncoderOperation, SanitizeParams,
};
use super::BrotliAlloc;
use crate::concat::{BroCatli, BroCatliResult};
use crate::enc::combined_alloc::{alloc_default, allocate};
use crate::enc::encode::BrotliEncoderStateStruct;
pub type PoisonedThreadError = ();
#[cfg(feature = "std")]
pub type LowLevelThreadError = std::boxed::Box<dyn any::Any + Send + 'static>;
#[cfg(not(feature = "std"))]
pub type LowLevelThreadError = ();
pub trait AnyBoxConstructor {
fn new(data: LowLevelThreadError) -> Self;
}
pub trait Joinable<T: Send + 'static, U: Send + 'static>: Sized {
fn join(self) -> Result<T, U>;
}
#[derive(Debug)]
pub enum BrotliEncoderThreadError {
InsufficientOutputSpace,
ConcatenationDidNotProcessFullFile,
ConcatenationError(BroCatliResult),
ConcatenationFinalizationError(BroCatliResult),
OtherThreadPanic,
ThreadExecError(LowLevelThreadError),
}
impl AnyBoxConstructor for BrotliEncoderThreadError {
fn new(data: LowLevelThreadError) -> Self {
BrotliEncoderThreadError::ThreadExecError(data)
}
}
fn set_pending_error(
pending_error: &mut Option<BrotliEncoderThreadError>,
error: BrotliEncoderThreadError,
) {
if pending_error.is_none() {
*pending_error = Some(error);
}
}
pub struct CompressedFileChunk<Alloc: BrotliAlloc + Send + 'static>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
{
data_backing: <Alloc as Allocator<u8>>::AllocatedMemory,
data_size: usize,
}
pub struct CompressionThreadResult<Alloc: BrotliAlloc + Send + 'static>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
{
compressed: Result<CompressedFileChunk<Alloc>, BrotliEncoderThreadError>,
alloc: Alloc,
}
pub enum InternalSendAlloc<
ReturnVal: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
Join: Joinable<ReturnVal, BrotliEncoderThreadError>,
> where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
{
A(Alloc, ExtraInput),
Join(Join),
SpawningOrJoining(PhantomData<ReturnVal>),
}
impl<
ReturnVal: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
Join: Joinable<ReturnVal, BrotliEncoderThreadError>,
> InternalSendAlloc<ReturnVal, ExtraInput, Alloc, Join>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
{
fn unwrap_input(&mut self) -> (&mut Alloc, &mut ExtraInput) {
match *self {
InternalSendAlloc::A(ref mut alloc, ref mut extra) => (alloc, extra),
_ => panic!("Bad state for allocator"),
}
}
}
pub struct SendAlloc<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
Join: Joinable<ReturnValue, BrotliEncoderThreadError>,
>(pub InternalSendAlloc<ReturnValue, ExtraInput, Alloc, Join>)
//FIXME pub
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send;
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
Join: Joinable<ReturnValue, BrotliEncoderThreadError>,
> SendAlloc<ReturnValue, ExtraInput, Alloc, Join>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
{
pub fn new(alloc: Alloc, extra_input: ExtraInput) -> Self {
SendAlloc::<ReturnValue, ExtraInput, Alloc, Join>(InternalSendAlloc::A(alloc, extra_input))
}
pub fn unwrap_or(self, other: Alloc, other_extra: ExtraInput) -> (Alloc, ExtraInput) {
match self.0 {
InternalSendAlloc::A(alloc, extra_input) => (alloc, extra_input),
InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
(other, other_extra)
}
}
}
fn unwrap_view_mut(&mut self) -> (&mut Alloc, &mut ExtraInput) {
match self.0 {
InternalSendAlloc::A(ref mut alloc, ref mut extra_input) => (alloc, extra_input),
InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
panic!("Item permanently borrowed/leaked")
}
}
}
pub fn unwrap(self) -> (Alloc, ExtraInput) {
match self.0 {
InternalSendAlloc::A(alloc, extra_input) => (alloc, extra_input),
InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
panic!("Item permanently borrowed/leaked")
}
}
}
pub fn replace_with_default(&mut self) -> (Alloc, ExtraInput) {
match mem::replace(
&mut self.0,
InternalSendAlloc::SpawningOrJoining(PhantomData),
) {
InternalSendAlloc::A(alloc, extra_input) => (alloc, extra_input),
InternalSendAlloc::SpawningOrJoining(_) | InternalSendAlloc::Join(_) => {
panic!("Item permanently borrowed/leaked")
}
}
}
}
pub enum InternalOwned<T> {
// FIXME pub
Item(T),
Borrowed,
}
pub struct Owned<T>(pub InternalOwned<T>); // FIXME pub
impl<T> Owned<T> {
pub fn new(data: T) -> Self {
Owned::<T>(InternalOwned::Item(data))
}
pub fn unwrap_or(self, other: T) -> T {
if let InternalOwned::Item(x) = self.0 {
x
} else {
other
}
}
pub fn unwrap(self) -> T {
if let InternalOwned::Item(x) = self.0 {
x
} else {
panic!("Item permanently borrowed")
}
}
pub fn view(&self) -> &T {
if let InternalOwned::Item(ref x) = self.0 {
x
} else {
panic!("Item permanently borrowed")
}
}
}
pub trait OwnedRetriever<U: Send + 'static> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError>;
fn unwrap(self) -> Result<U, PoisonedThreadError>;
}
#[cfg(feature = "std")]
impl<U: Send + 'static> OwnedRetriever<U> for std::sync::Arc<std::sync::RwLock<U>> {
fn view<T, F: FnOnce(&U) -> T>(&self, f: F) -> Result<T, PoisonedThreadError> {
match self.read() {
Ok(ref u) => Ok(f(u)),
Err(_) => Err(PoisonedThreadError::default()),
}
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
match std::sync::Arc::try_unwrap(self) {
Ok(rwlock) => match rwlock.into_inner() {
Ok(u) => Ok(u),
Err(_) => Err(PoisonedThreadError::default()),
},
Err(_) => Err(PoisonedThreadError::default()),
}
}
}
pub trait BatchSpawnable<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle: Joinable<ReturnValue, BrotliEncoderThreadError>;
type FinalJoinHandle: OwnedRetriever<U>;
// this function takes in an input slice
// a SendAlloc per thread and converts them all into JoinHandle
// the input is borrowed until the joins complete
// owned is set to borrowed
// the final join handle is a r/w lock which will return the SliceW to the owner
// the FinalJoinHandle is only to be called when each individual JoinHandle has been examined
// the function is called with the thread_index, the num_threads, a reference to the slice under a read lock,
// and an allocator from the alloc_per_thread
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle;
fn spawn<F: Fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue + Send + 'static + Copy>(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: F,
);
}
pub trait BatchSpawnableLite<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
type JoinHandle: Joinable<ReturnValue, BrotliEncoderThreadError>;
type FinalJoinHandle: OwnedRetriever<U>;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle;
fn spawn(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
);
}
/*
impl<ReturnValue:Send+'static,
ExtraInput:Send+'static,
Alloc:BrotliAlloc+Send+'static,
U:Send+'static+Sync>
BatchSpawnableLite<T, Alloc, U> for BatchSpawnable<T, Alloc, U> {
type JoinHandle = <Self as BatchSpawnable<T, Alloc, U>>::JoinHandle;
type FinalJoinHandle = <Self as BatchSpawnable<T, Alloc, U>>::FinalJoinHandle;
fn batch_spawn(
&mut self,
input: &mut Owned<U>,
alloc_per_thread:&mut [SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>],
f: fn(usize, usize, &U, Alloc) -> T,
) -> Self::FinalJoinHandle {
<Self as BatchSpawnable<ReturnValue, ExtraInput, Alloc, U>>::batch_spawn(self, input, alloc_per_thread, f)
}
}*/
pub fn CompressMultiSlice<
Alloc: BrotliAlloc + Send + 'static,
Spawner: BatchSpawnableLite<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(
<Alloc as Allocator<u8>>::AllocatedMemory,
BrotliEncoderParams,
),
>,
>(
params: &BrotliEncoderParams,
input_slice: &[u8],
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
Spawner::JoinHandle,
>],
thread_spawner: &mut Spawner,
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
let input = if let InternalSendAlloc::A(ref mut alloc, ref _extra) = alloc_per_thread[0].0 {
let mut input = allocate::<u8, _>(alloc, input_slice.len());
input.slice_mut().clone_from_slice(input_slice);
input
} else {
alloc_default::<u8, Alloc>()
};
let mut owned_input = Owned::new(input);
let ret = CompressMulti(
params,
&mut owned_input,
output,
alloc_per_thread,
thread_spawner,
);
if let InternalSendAlloc::A(ref mut alloc, ref _extra) = alloc_per_thread[0].0 {
<Alloc as Allocator<u8>>::free_cell(alloc, owned_input.unwrap());
}
ret
}
fn get_range(thread_index: usize, num_threads: usize, file_size: usize) -> Range<usize> {
((thread_index * file_size) / num_threads)..(((thread_index + 1) * file_size) / num_threads)
}
fn compress_part<Alloc: BrotliAlloc + Send + 'static, SliceW: SliceWrapper<u8>>(
hasher: UnionHasher<Alloc>,
thread_index: usize,
num_threads: usize,
input_and_params: &(SliceW, BrotliEncoderParams),
mut alloc: Alloc,
) -> CompressionThreadResult<Alloc>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
{
let mut range = get_range(thread_index, num_threads, input_and_params.0.len());
let mut mem = allocate::<u8, _>(
&mut alloc,
BrotliEncoderMaxCompressedSize(range.end - range.start),
);
let mut state = BrotliEncoderStateStruct::new(alloc);
state.params = input_and_params.1.clone();
if thread_index != 0 {
state.params.catable = true; // make sure we can concatenate this to the other work results
state.params.magic_number = false; // no reason to pepper this around
}
state.params.appendable = true; // make sure we are at least appendable, so that future items can be catted in
if thread_index != 0 {
state.set_custom_dictionary_with_optional_precomputed_hasher(
range.start,
&input_and_params.0.slice()[..range.start],
hasher,
true,
);
}
let mut out_offset = 0usize;
let compression_result;
let mut available_out = mem.len();
loop {
let mut next_in_offset = 0usize;
let mut available_in = range.end - range.start;
let result = state.compress_stream(
BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
&mut available_in,
&input_and_params.0.slice()[range.clone()],
&mut next_in_offset,
&mut available_out,
mem.slice_mut(),
&mut out_offset,
&mut None,
&mut |_a, _b, _c, _d| (),
);
let new_range = range.start + next_in_offset..range.end;
range = new_range;
if result {
compression_result = Ok(out_offset);
break;
} else if available_out == 0 {
compression_result = Err(BrotliEncoderThreadError::InsufficientOutputSpace); // mark no space??
break;
}
}
BrotliEncoderDestroyInstance(&mut state);
match compression_result {
Ok(size) => CompressionThreadResult::<Alloc> {
compressed: Ok(CompressedFileChunk {
data_backing: mem,
data_size: size,
}),
alloc: state.m8,
},
Err(e) => {
<Alloc as Allocator<u8>>::free_cell(&mut state.m8, mem);
CompressionThreadResult::<Alloc> {
compressed: Err(e),
alloc: state.m8,
}
}
}
}
pub fn CompressMulti<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
Spawner: BatchSpawnableLite<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
Spawner::JoinHandle,
>],
thread_spawner: &mut Spawner,
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send,
<Alloc as Allocator<u32>>::AllocatedMemory: Send,
{
let num_threads = alloc_per_thread.len();
let actually_owned_mem = mem::replace(owned_input, Owned(InternalOwned::Borrowed));
let mut owned_input_pair = Owned::new((actually_owned_mem.unwrap(), params.clone()));
// start thread spawner
let mut spawner_and_input = thread_spawner.make_spawner(&mut owned_input_pair);
if num_threads > 1 {
// spawn first thread without "custom dictionary" while we compute the custom dictionary for other work items
thread_spawner.spawn(
&mut spawner_and_input,
&mut alloc_per_thread[0],
0,
num_threads,
compress_part,
);
}
// populate all hashers at once, cloning them one by one
let mut compression_last_thread_result;
if num_threads > 1 && params.favor_cpu_efficiency {
let mut local_params = params.clone();
SanitizeParams(&mut local_params);
let mut hasher = UnionHasher::Uninit;
hasher_setup(
alloc_per_thread[num_threads - 1].0.unwrap_input().0,
&mut hasher,
&mut local_params,
None, // No unwrappable custom dict used here.
&[],
0,
0,
false,
);
let mut setup_error = false;
for thread_index in 1..num_threads {
let res = spawner_and_input.view(|input_and_params: &(SliceW, BrotliEncoderParams)| {
let range = get_range(thread_index - 1, num_threads, input_and_params.0.len());
let overlap = hasher.StoreLookahead().wrapping_sub(1);
if range.end - range.start > overlap {
hasher.BulkStoreRange(
input_and_params.0.slice(),
usize::MAX,
if range.start > overlap {
range.start - overlap
} else {
0
},
range.end - overlap,
);
}
});
if let Err(_e) = res {
setup_error = true;
break;
}
if thread_index + 1 != num_threads {
{
let (alloc, out_hasher) = alloc_per_thread[thread_index].unwrap_view_mut();
*out_hasher = hasher.clone_with_alloc(alloc);
}
thread_spawner.spawn(
&mut spawner_and_input,
&mut alloc_per_thread[thread_index],
thread_index,
num_threads,
compress_part,
);
}
}
if setup_error {
let mut setup_result = Err(BrotliEncoderThreadError::OtherThreadPanic);
for thread in alloc_per_thread.iter_mut() {
match mem::replace(
&mut thread.0,
InternalSendAlloc::SpawningOrJoining(PhantomData),
) {
InternalSendAlloc::Join(join) => match join.join() {
Ok(mut thread_result) => {
if let Ok(compressed_out) = thread_result.compressed {
<Alloc as Allocator<u8>>::free_cell(
&mut thread_result.alloc,
compressed_out.data_backing,
);
}
thread.0 =
InternalSendAlloc::A(thread_result.alloc, UnionHasher::Uninit);
}
Err(join_error) => setup_result = Err(join_error),
},
other => thread.0 = other,
}
}
if let Ok(retrieved_owned_input) = spawner_and_input.unwrap() {
*owned_input = Owned::new(retrieved_owned_input.0);
}
return setup_result;
}
let (alloc, _extra) = alloc_per_thread[num_threads - 1].replace_with_default();
compression_last_thread_result = spawner_and_input.view(move |input_and_params:&(SliceW, BrotliEncoderParams)| -> CompressionThreadResult<Alloc> {
compress_part(hasher,
num_threads - 1,
num_threads,
input_and_params,
alloc,
)
});
} else {
if num_threads > 1 {
for thread_index in 1..num_threads - 1 {
thread_spawner.spawn(
&mut spawner_and_input,
&mut alloc_per_thread[thread_index],
thread_index,
num_threads,
compress_part,
);
}
}
let (alloc, _extra) = alloc_per_thread[num_threads - 1].replace_with_default();
compression_last_thread_result = spawner_and_input.view(move |input_and_params:&(SliceW, BrotliEncoderParams)| -> CompressionThreadResult<Alloc> {
compress_part(UnionHasher::Uninit,
num_threads - 1,
num_threads,
input_and_params,
alloc,
)
});
}
let mut compression_result = Ok(0usize);
let mut pending_error = None;
let mut out_file_size = 0usize;
let mut bro_cat_li = BroCatli::new();
for (index, thread) in alloc_per_thread.iter_mut().enumerate() {
let cur_result = if index + 1 == num_threads {
match mem::replace(&mut compression_last_thread_result, Err(())) {
Ok(result) => Some(result),
Err(_err) => {
set_pending_error(
&mut pending_error,
BrotliEncoderThreadError::OtherThreadPanic,
);
None
}
}
} else {
match mem::replace(
&mut thread.0,
InternalSendAlloc::SpawningOrJoining(PhantomData),
) {
InternalSendAlloc::A(_, _) | InternalSendAlloc::SpawningOrJoining(_) => {
panic!("Thread not properly spawned")
}
InternalSendAlloc::Join(join) => match join.join() {
Ok(result) => Some(result),
Err(err) => {
set_pending_error(&mut pending_error, err);
None
}
},
}
};
if let Some(mut cur_result) = cur_result {
match cur_result.compressed {
Ok(compressed_out) => {
if pending_error.is_none() {
bro_cat_li.new_brotli_file();
let mut in_offset = 0usize;
let cat_result = bro_cat_li.stream(
&compressed_out.data_backing.slice()[..compressed_out.data_size],
&mut in_offset,
output,
&mut out_file_size,
);
match cat_result {
BroCatliResult::Success | BroCatliResult::NeedsMoreInput => {
compression_result = Ok(out_file_size);
}
BroCatliResult::NeedsMoreOutput => {
set_pending_error(
&mut pending_error,
BrotliEncoderThreadError::InsufficientOutputSpace,
);
// not enough space
}
err => {
set_pending_error(
&mut pending_error,
BrotliEncoderThreadError::ConcatenationError(err),
);
// misc error
}
}
}
<Alloc as Allocator<u8>>::free_cell(
&mut cur_result.alloc,
compressed_out.data_backing,
);
}
Err(e) => {
set_pending_error(&mut pending_error, e);
}
}
thread.0 = InternalSendAlloc::A(cur_result.alloc, UnionHasher::Uninit);
}
}
if let Some(error) = pending_error {
compression_result = Err(error);
}
if compression_result.is_ok() {
match bro_cat_li.finish(output, &mut out_file_size) {
BroCatliResult::Success => compression_result = Ok(out_file_size),
err => {
compression_result = Err(BrotliEncoderThreadError::ConcatenationFinalizationError(
err,
))
}
}
}
if let Ok(retrieved_owned_input) = spawner_and_input.unwrap() {
*owned_input = Owned::new(retrieved_owned_input.0); // return the input to its rightful owner before returning
} else if compression_result.is_ok() {
compression_result = Err(BrotliEncoderThreadError::OtherThreadPanic);
}
compression_result
}
mod test;
+309
View File
@@ -0,0 +1,309 @@
#![cfg(test)]
#![cfg(feature = "std")]
// Unit tests for the parent `threading` module. These exercise CompressMulti's
// error-draining behavior
use super::*;
use alloc::SliceWrapper;
use alloc_stdlib::StandardAlloc;
use core::sync::atomic::{AtomicUsize, Ordering};
static JOINED_AFTER_JOIN_ERROR: AtomicUsize = AtomicUsize::new(0);
static JOINED_AFTER_SETUP_ERROR: AtomicUsize = AtomicUsize::new(0);
static DRAINED_AFTER_STREAM_PHASE: AtomicUsize = AtomicUsize::new(0);
struct TestSlice(&'static [u8]);
impl SliceWrapper<u8> for TestSlice {
fn slice(&self) -> &[u8] {
self.0
}
}
struct CountingJoinable {
result: Option<Result<CompressionThreadResult<StandardAlloc>, BrotliEncoderThreadError>>,
joined_count: &'static AtomicUsize,
}
impl Joinable<CompressionThreadResult<StandardAlloc>, BrotliEncoderThreadError>
for CountingJoinable
{
fn join(mut self) -> Result<CompressionThreadResult<StandardAlloc>, BrotliEncoderThreadError> {
self.joined_count.fetch_add(1, Ordering::SeqCst);
self.result.take().unwrap()
}
}
struct TestOwnedRetriever<U: Send + 'static> {
input: Option<U>,
fail_views: bool,
}
impl<U: Send + 'static> OwnedRetriever<U> for TestOwnedRetriever<U> {
fn view<Output, Func: FnOnce(&U) -> Output>(
&self,
func: Func,
) -> Result<Output, PoisonedThreadError> {
if self.fail_views {
Err(PoisonedThreadError::default())
} else {
Ok(func(self.input.as_ref().unwrap()))
}
}
fn unwrap(self) -> Result<U, PoisonedThreadError> {
Ok(self.input.unwrap())
}
}
struct CountingSpawner {
joined_count: &'static AtomicUsize,
join_error_index: Option<usize>,
fail_views: bool,
}
impl
BatchSpawnableLite<
CompressionThreadResult<StandardAlloc>,
UnionHasher<StandardAlloc>,
StandardAlloc,
(TestSlice, BrotliEncoderParams),
> for CountingSpawner
{
type JoinHandle = CountingJoinable;
type FinalJoinHandle = TestOwnedRetriever<(TestSlice, BrotliEncoderParams)>;
fn make_spawner(
&mut self,
input: &mut Owned<(TestSlice, BrotliEncoderParams)>,
) -> Self::FinalJoinHandle {
TestOwnedRetriever {
input: Some(mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap()),
fail_views: self.fail_views,
}
}
fn spawn(
&mut self,
_handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<
CompressionThreadResult<StandardAlloc>,
UnionHasher<StandardAlloc>,
StandardAlloc,
Self::JoinHandle,
>,
index: usize,
_num_threads: usize,
_func: fn(
UnionHasher<StandardAlloc>,
usize,
usize,
&(TestSlice, BrotliEncoderParams),
StandardAlloc,
) -> CompressionThreadResult<StandardAlloc>,
) {
let (alloc, _extra_input) = alloc_per_thread.replace_with_default();
let result = if self.join_error_index == Some(index) {
Err(BrotliEncoderThreadError::OtherThreadPanic)
} else {
Ok(CompressionThreadResult {
compressed: Err(BrotliEncoderThreadError::InsufficientOutputSpace),
alloc,
})
};
alloc_per_thread.0 = InternalSendAlloc::Join(CountingJoinable {
result: Some(result),
joined_count: self.joined_count,
});
}
}
type TestSendAlloc = SendAlloc<
CompressionThreadResult<StandardAlloc>,
UnionHasher<StandardAlloc>,
StandardAlloc,
CountingJoinable,
>;
fn test_alloc() -> TestSendAlloc {
SendAlloc::new(StandardAlloc::default(), UnionHasher::Uninit)
}
/// Spawner that produces genuine, concatenatable chunks via the real
/// `compress_part` for every worker except `compressed_error_index`, whose
/// worker instead reports a compression error. This lets a test put a real
/// (streamable) result *after* an errored worker so the drain loop reaches
/// its BroCatli streaming arms, exercising the "first error wins" behavior.
struct RealChunkSpawner {
joined_count: &'static AtomicUsize,
compressed_error_index: Option<usize>,
}
impl
BatchSpawnableLite<
CompressionThreadResult<StandardAlloc>,
UnionHasher<StandardAlloc>,
StandardAlloc,
(TestSlice, BrotliEncoderParams),
> for RealChunkSpawner
{
type JoinHandle = CountingJoinable;
type FinalJoinHandle = TestOwnedRetriever<(TestSlice, BrotliEncoderParams)>;
fn make_spawner(
&mut self,
input: &mut Owned<(TestSlice, BrotliEncoderParams)>,
) -> Self::FinalJoinHandle {
TestOwnedRetriever {
input: Some(mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap()),
fail_views: false,
}
}
fn spawn(
&mut self,
handle: &mut Self::FinalJoinHandle,
alloc_per_thread: &mut SendAlloc<
CompressionThreadResult<StandardAlloc>,
UnionHasher<StandardAlloc>,
StandardAlloc,
Self::JoinHandle,
>,
index: usize,
num_threads: usize,
_func: fn(
UnionHasher<StandardAlloc>,
usize,
usize,
&(TestSlice, BrotliEncoderParams),
StandardAlloc,
) -> CompressionThreadResult<StandardAlloc>,
) {
let (alloc, _extra_input) = alloc_per_thread.replace_with_default();
let result = if self.compressed_error_index == Some(index) {
Ok(CompressionThreadResult {
compressed: Err(BrotliEncoderThreadError::ConcatenationDidNotProcessFullFile),
alloc,
})
} else {
// Compress synchronously exactly as the production spawner would,
// yielding a real chunk that BroCatli can concatenate.
Ok(handle
.view(|input_and_params| {
compress_part(
UnionHasher::Uninit,
index,
num_threads,
input_and_params,
alloc,
)
})
.unwrap())
};
alloc_per_thread.0 = InternalSendAlloc::Join(CountingJoinable {
result: Some(result),
joined_count: self.joined_count,
});
}
}
#[test]
fn compress_multi_joins_remaining_workers_after_join_error() {
static INPUT: &[u8] = b"join all workers before returning";
JOINED_AFTER_JOIN_ERROR.store(0, Ordering::SeqCst);
let mut spawner = CountingSpawner {
joined_count: &JOINED_AFTER_JOIN_ERROR,
join_error_index: Some(0),
fail_views: false,
};
let mut alloc_per_thread = [test_alloc(), test_alloc(), test_alloc(), test_alloc()];
let mut params = BrotliEncoderParams::default();
params.quality = 1;
let mut owned_input = Owned::new(TestSlice(INPUT));
let mut output = [0u8; 256];
let result = CompressMulti(
&params,
&mut owned_input,
&mut output,
&mut alloc_per_thread[..],
&mut spawner,
);
assert!(matches!(
result,
Err(BrotliEncoderThreadError::OtherThreadPanic)
));
assert_eq!(JOINED_AFTER_JOIN_ERROR.load(Ordering::SeqCst), 3);
assert_eq!(owned_input.view().slice(), INPUT);
}
#[test]
fn compress_multi_joins_spawned_worker_after_setup_view_error() {
static INPUT: &[u8] = b"restore input after setup failure";
JOINED_AFTER_SETUP_ERROR.store(0, Ordering::SeqCst);
let mut spawner = CountingSpawner {
joined_count: &JOINED_AFTER_SETUP_ERROR,
join_error_index: None,
fail_views: true,
};
let mut alloc_per_thread = [test_alloc(), test_alloc()];
let mut params = BrotliEncoderParams::default();
params.favor_cpu_efficiency = true;
let mut owned_input = Owned::new(TestSlice(INPUT));
let mut output = [0u8; 256];
let result = CompressMulti(
&params,
&mut owned_input,
&mut output,
&mut alloc_per_thread[..],
&mut spawner,
);
assert!(matches!(
result,
Err(BrotliEncoderThreadError::OtherThreadPanic)
));
assert_eq!(JOINED_AFTER_SETUP_ERROR.load(Ordering::SeqCst), 1);
assert_eq!(owned_input.view().slice(), INPUT);
}
/// Test case:
/// Worker 0 erorrs
/// Workers 1.. return valid results.
///
/// Ensure Worker 0's error is returned irrespective of latter success values.
#[test]
fn compress_multi_preserves_first_worker_error_through_stream_phase() {
static INPUT: &[u8] =
b"a sufficiently long body of text so every worker produces a real brotli chunk";
DRAINED_AFTER_STREAM_PHASE.store(0, Ordering::SeqCst);
let mut spawner = RealChunkSpawner {
joined_count: &DRAINED_AFTER_STREAM_PHASE,
compressed_error_index: Some(0),
};
let mut alloc_per_thread = [test_alloc(), test_alloc(), test_alloc()];
let mut params = BrotliEncoderParams::default();
params.quality = 1;
params.magic_number = true;
let mut owned_input = Owned::new(TestSlice(INPUT));
// Enough output that the healthy chunks could have concatted ok.
let mut output = [0u8; 4096];
let result = CompressMulti(
&params,
&mut owned_input,
&mut output,
&mut alloc_per_thread[..],
&mut spawner,
);
assert!(matches!(
result,
Err(BrotliEncoderThreadError::ConcatenationDidNotProcessFullFile)
));
// Both spawned workers (indices 0 and 1) are joined; index 2 is the
// synchronous last thread.
assert_eq!(DRAINED_AFTER_STREAM_PHASE.load(Ordering::SeqCst), 2);
assert_eq!(owned_input.view().slice(), INPUT);
}
+62
View File
@@ -0,0 +1,62 @@
use crate::enc::floatX;
fn parse_as_utf8(input: &[u8], size: usize) -> (usize, i32) {
if (input[0] & 0x80) == 0 {
if input[0] > 0 {
return (1, i32::from(input[0]));
}
}
if size > 1 && (input[0] & 0xe0) == 0xc0 && (input[1] & 0xc0) == 0x80 {
let symbol = (input[0] as i32 & 0x1f) << 6 | input[1] as i32 & 0x3f;
if symbol > 0x7f {
return (2, symbol);
}
}
if size > 2
&& (input[0] & 0xf0) == 0xe0
&& (input[1] & 0xc0) == 0x80
&& (input[2] & 0xc0) == 0x80
{
let symbol = (i32::from(input[0]) & 0x0f) << 12
| (i32::from(input[1]) & 0x3f) << 6
| i32::from(input[2]) & 0x3f;
if symbol > 0x7ff {
return (3, symbol);
}
}
if size > 3
&& (input[0] & 0xf8) == 0xf0
&& (input[1] & 0xc0) == 0x80
&& (input[2] & 0xc0) == 0x80
&& (input[3] & 0xc0) == 0x80
{
let symbol = (i32::from(input[0]) & 0x07) << 18
| (i32::from(input[1]) & 0x3f) << 12
| (i32::from(input[2]) & 0x3f) << 6
| i32::from(input[3]) & 0x3f;
if symbol > 0xffff && symbol <= 0x10_ffff {
return (4, symbol);
}
}
(1, 0x11_0000 | i32::from(input[0]))
}
pub(crate) fn is_mostly_utf8(
data: &[u8],
pos: usize,
mask: usize,
length: usize,
min_fraction: floatX,
) -> bool {
let mut size_utf8: usize = 0;
let mut i: usize = 0;
while i < length {
let (bytes_read, symbol) = parse_as_utf8(&data[(pos.wrapping_add(i) & mask)..], length - i);
i = i.wrapping_add(bytes_read);
if symbol < 0x11_0000 {
size_utf8 = size_utf8.wrapping_add(bytes_read);
}
}
size_utf8 as floatX > min_fraction * length as floatX
}
+195
View File
@@ -0,0 +1,195 @@
#![allow(clippy::excessive_precision)]
use crate::enc::log_table_16::logs_16;
use crate::enc::log_table_8::logs_8;
#[cfg(feature = "float64")]
pub type floatX = f64;
#[cfg(not(feature = "float64"))]
pub type floatX = f32;
#[inline(always)]
pub fn FastLog2u16(v: u16) -> floatX {
logs_16[v as usize]
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn FastLog2(v: u64) -> floatX {
if v < 256 {
logs_8[v as usize]
} else {
(v as f32).log2() as floatX
}
}
#[cfg(not(feature = "std"))]
#[inline(always)]
pub fn FastLog2(v: u64) -> floatX {
if v < 256 {
logs_8[v as usize]
} else {
FastLog2u64(v)
}
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn FastLog2f64(v: u64) -> floatX {
if v < 256 {
logs_8[v as usize]
} else {
(v as floatX).log2()
}
}
#[cfg(not(feature = "std"))]
#[inline(always)]
pub fn FastLog2f64(v: u64) -> floatX {
FastLog2(v) as floatX
}
#[inline]
pub fn FastLog2u64(v: u64) -> floatX {
let bsr_8 = 56i8 - v.leading_zeros() as i8;
let offset = bsr_8 & -((bsr_8 >= 0) as i8);
(offset as floatX) + logs_8[(v >> offset) as usize]
}
#[inline(always)]
pub fn FastLog2u32(v: i32) -> floatX {
let bsr_8 = 24i8 - v.leading_zeros() as i8;
let offset = bsr_8 & -((bsr_8 >= 0) as i8);
(offset as floatX) + logs_8[(v >> offset) as usize]
}
#[inline(always)]
pub fn xFastLog2u16(v: u16) -> floatX {
let bsr_8 = 8i8 - v.leading_zeros() as i8;
let offset = (bsr_8 & -((bsr_8 >= 0) as i8));
(offset as floatX) + logs_8[(v >> offset) as usize]
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn FastPow2(v: floatX) -> floatX {
(2 as floatX).powf(v)
}
#[cfg(not(feature = "std"))]
#[inline(always)]
pub fn FastPow2(v: floatX) -> floatX {
assert!(v >= 0 as floatX);
let round_down = v as i32;
let remainder = v - round_down as floatX;
let mut x = 1 as floatX;
// (1 + (x/n) * ln2) ^ n
// let n = 8
x += remainder * (0.693147180559945309417232121458 / 256.0) as floatX;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
x *= x;
(1 << round_down) as floatX * x
}
#[inline(always)]
pub fn Log2FloorNonZero(v: u64) -> u32 {
63u32 ^ v.leading_zeros()
}
#[cfg(test)]
mod test {
fn baseline_log2_floor_non_zero(mut n: u64) -> u32 {
let mut result: u32 = 0;
while {
n >>= 1i32;
n
} != 0
{
result = result.wrapping_add(1);
}
result
}
#[test]
fn log2floor_non_zero_works() {
let examples = [
4u64,
254,
256,
1428,
25412509,
21350891256,
65536,
1258912591,
60968101,
1,
12589125190825,
105912059215091,
0,
];
for example in examples.iter() {
let fast_version = super::Log2FloorNonZero(*example);
let baseline_version = baseline_log2_floor_non_zero(*example);
if *example != 0 {
// make sure we don't panic when computing...but don't care about result
assert_eq!(fast_version, baseline_version);
}
}
}
pub fn approx_eq(a: f64, b: f64, tol: f64) {
let mut t0 = a - b;
let mut t1 = b - a;
if t0 < 0.0 {
t0 = -t0;
}
if t1 < 0.0 {
t1 = -t1;
}
if (!(t1 < tol)) {
assert_eq!(a, b);
}
if (!(t0 < tol)) {
assert_eq!(a, b);
}
}
#[test]
fn fast_log2_works() {
let examples = [
4u64,
254,
256,
1428,
25412509,
21350891256,
65536,
1258912591,
60968101,
1,
12589125190825,
105912059215091,
0,
];
let tol = [
0.00001, 0.0001, 0.0001, 0.005, 0.007, 0.008, 0.01, 0.01, 0.01, 0.000001, 0.01, 0.01,
0.0001,
];
for (index, example) in examples.iter().enumerate() {
let fast_version = super::FastLog2(*example);
if *example != 0 {
// make sure we don't panic when computing...but don't care about result
let baseline_version = (*example as f64).log2();
approx_eq(fast_version as f64, baseline_version, tol[index]);
} else {
//assert_eq!(fast_version as f64, 0.0 as f64);
}
}
}
}
+62
View File
@@ -0,0 +1,62 @@
#![allow(unknown_lints)]
#![allow(unused_macros)]
use crate::enc::util::FastLog2;
use crate::enc::{s8, v8};
pub type Mem256f = v8;
pub type Mem256i = s8;
pub type v256 = v8;
pub type v256i = s8;
pub fn sum8(x: v256) -> f32 {
x[0] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7]
}
pub fn sum8i(x: v256i) -> i32 {
x[0].wrapping_add(x[1])
.wrapping_add(x[2])
.wrapping_add(x[3])
.wrapping_add(x[4])
.wrapping_add(x[5])
.wrapping_add(x[6])
.wrapping_add(x[7])
}
pub fn log2i(x: v256i) -> v256 {
[
FastLog2(x[0] as u64),
FastLog2(x[1] as u64),
FastLog2(x[2] as u64),
FastLog2(x[3] as u64),
FastLog2(x[4] as u64),
FastLog2(x[5] as u64),
FastLog2(x[6] as u64),
FastLog2(x[7] as u64),
]
.into()
}
pub fn cast_i32_to_f32(x: v256i) -> v256 {
[
x[0] as f32,
x[1] as f32,
x[2] as f32,
x[3] as f32,
x[4] as f32,
x[5] as f32,
x[6] as f32,
x[7] as f32,
]
.into()
}
pub fn cast_f32_to_i32(x: v256) -> v256i {
[
x[0] as i32,
x[1] as i32,
x[2] as i32,
x[3] as i32,
x[4] as i32,
x[5] as i32,
x[6] as i32,
x[7] as i32,
]
.into()
}
+147
View File
@@ -0,0 +1,147 @@
use core::cmp::{max, min};
pub type Prob = u16;
pub const BLEND_FIXED_POINT_PRECISION: i8 = 15;
#[allow(dead_code)]
pub const LOG2_SCALE: i32 = 15;
#[derive(Debug, Copy, Clone)]
pub struct Weights {
model_weights: [i32; 2],
mixing_param: u8,
normalized_weight: Prob,
}
impl Default for Weights {
fn default() -> Self {
Self::new()
}
}
impl Weights {
pub fn new() -> Self {
Weights {
model_weights: [1; 2],
mixing_param: 1,
normalized_weight: 1 << (BLEND_FIXED_POINT_PRECISION - 1),
}
}
#[allow(unused)]
#[inline(always)]
pub fn update(&mut self, model_probs: [Prob; 2], weighted_prob: Prob) {
debug_assert!(self.mixing_param != 0);
normalize_weights(&mut self.model_weights);
let w0new = compute_new_weight(
model_probs,
weighted_prob,
self.model_weights,
false,
self.mixing_param - 1,
);
let w1new = compute_new_weight(
model_probs,
weighted_prob,
self.model_weights,
true,
self.mixing_param - 1,
);
self.model_weights = [w0new, w1new];
self.normalized_weight = compute_normalized_weight(self.model_weights);
}
#[allow(dead_code)]
#[inline(always)]
pub fn norm_weight(&self) -> Prob {
self.normalized_weight
}
}
#[allow(dead_code)]
#[inline(always)]
fn compute_normalized_weight(model_weights: [i32; 2]) -> Prob {
let total = i64::from(model_weights[0]) + i64::from(model_weights[1]);
let leading_zeros = total.leading_zeros();
let shift = max(56 - (leading_zeros as i8), 0);
let total_8bit = total >> shift;
/*::probability::numeric::fast_divide_16bit_by_8bit(
((model_weights[0] >> shift) as u16)<< 8,
::probability::numeric::lookup_divisor8(total_8bit as u8)) << (BLEND_FIXED_POINT_PRECISION - 8)
*/
((((model_weights[0] >> shift) as u16) << 8) / total_8bit as u16/*fixme??*/)
<< (BLEND_FIXED_POINT_PRECISION - 8)
}
#[allow(dead_code)]
#[cold]
fn fix_weights(weights: &mut [i32; 2]) {
let ilog = 32 - min(weights[0].leading_zeros(), weights[1].leading_zeros());
let max_log = 24;
if ilog >= max_log {
weights[0] >>= ilog - max_log;
weights[1] >>= ilog - max_log;
}
}
#[allow(dead_code)]
#[inline(always)]
fn normalize_weights(weights: &mut [i32; 2]) {
if ((weights[0] | weights[1]) & 0x7f00_0000) != 0 {
fix_weights(weights);
}
}
#[allow(dead_code)]
#[cfg(feature = "floating_point_context_mixing")]
fn compute_new_weight(
probs: [Prob; 2],
weighted_prob: Prob,
weights: [i32; 2],
index_equal_1: bool,
_speed: u8,
) -> i32 {
// speed ranges from 1 to 14 inclusive
let index = index_equal_1 as usize;
let n1i = probs[index] as f64 / ((1i64 << LOG2_SCALE) as f64);
//let n0i = 1.0f64 - n1i;
let ni = 1.0f64;
let s1 = weighted_prob as f64 / ((1i64 << LOG2_SCALE) as f64);
let s0 = 1.0f64 - s1;
let s = 1.0f64;
//let p0 = s0;
let p1 = s1;
let wi = weights[index] as f64 / ((1i64 << LOG2_SCALE) as f64);
let mut wi_new = wi + (1.0 - p1) * (s * n1i - s1 * ni) / (s0 * s1);
let eps = 0.00001f64;
if !(wi_new > eps) {
wi_new = eps;
}
(wi_new * ((1i64 << LOG2_SCALE) as f64)) as i32
}
#[allow(dead_code)]
#[cfg(not(feature = "floating_point_context_mixing"))]
#[inline(always)]
fn compute_new_weight(
probs: [Prob; 2],
weighted_prob: Prob,
weights: [i32; 2],
index_equal_1: bool,
_speed: u8,
) -> i32 {
// speed ranges from 1 to 14 inclusive
let index = index_equal_1 as usize;
let full_model_sum_p1 = i64::from(weighted_prob);
let full_model_total = 1i64 << LOG2_SCALE;
let full_model_sum_p0 = full_model_total.wrapping_sub(i64::from(weighted_prob));
let n1i = i64::from(probs[index]);
let ni = 1i64 << LOG2_SCALE;
let error = full_model_total.wrapping_sub(full_model_sum_p1);
let wi = i64::from(weights[index]);
let efficacy = full_model_total.wrapping_mul(n1i) - full_model_sum_p1.wrapping_mul(ni);
//let geometric_probabilities = full_model_sum_p1 * full_model_sum_p0;
let log_geometric_probabilities =
64 - (full_model_sum_p1.wrapping_mul(full_model_sum_p0)).leading_zeros();
//let scaled_geometric_probabilities = geometric_probabilities * S;
//let new_weight_adj = (error * efficacy) >> log_geometric_probabilities;// / geometric_probabilities;
//let new_weight_adj = (error * efficacy)/(full_model_sum_p1 * full_model_sum_p0);
let new_weight_adj = (error.wrapping_mul(efficacy)) >> log_geometric_probabilities;
// assert!(wi + new_weight_adj < (1i64 << 31));
//print!("{} -> {} due to {:?} vs {}\n", wi as f64 / (weights[0] + weights[1]) as f64, (wi + new_weight_adj) as f64 /(weights[0] as i64 + new_weight_adj as i64 + weights[1] as i64) as f64, probs[index], weighted_prob);
max(1, wi.wrapping_add(new_weight_adj) as i32)
}
+442
View File
@@ -0,0 +1,442 @@
#![cfg(feature = "std")]
use alloc::{Allocator, SliceWrapper};
use core::mem;
use std;
// in-place thread create
use std::sync::RwLock;
use std::sync::{Arc, Condvar, Mutex};
use crate::enc::backward_references::UnionHasher;
use crate::enc::fixed_queue::{FixedQueue, MAX_THREADS};
use crate::enc::threading::{
BatchSpawnableLite, BrotliEncoderThreadError, CompressMulti, CompressionThreadResult,
InternalOwned, InternalSendAlloc, Joinable, Owned, SendAlloc,
};
use crate::enc::{BrotliAlloc, BrotliEncoderParams};
struct JobReply<T: Send + 'static> {
result: T,
work_id: u64,
}
struct JobRequest<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> {
func: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
extra_input: ExtraInput,
index: usize,
thread_size: usize,
data: Arc<RwLock<U>>,
alloc: Alloc,
work_id: u64,
}
struct WorkQueue<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> {
jobs: FixedQueue<JobRequest<ReturnValue, ExtraInput, Alloc, U>>,
results: FixedQueue<JobReply<ReturnValue>>,
shutdown: bool,
immediate_shutdown: bool,
num_in_progress: usize,
cur_work_id: u64,
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> Default for WorkQueue<ReturnValue, ExtraInput, Alloc, U>
{
fn default() -> Self {
WorkQueue {
jobs: FixedQueue::default(),
results: FixedQueue::default(),
num_in_progress: 0,
immediate_shutdown: false,
shutdown: false,
cur_work_id: 0,
}
}
}
pub struct GuardedQueue<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
>(Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>);
pub struct WorkerPool<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> {
queue: GuardedQueue<ReturnValue, ExtraInput, Alloc, U>,
join: [Option<std::thread::JoinHandle<()>>; MAX_THREADS],
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> Drop for WorkerPool<ReturnValue, ExtraInput, Alloc, U>
{
fn drop(&mut self) {
{
let (lock, cvar) = &*self.queue.0;
let mut local_queue = lock.lock().unwrap();
local_queue.immediate_shutdown = true;
cvar.notify_all();
}
for thread_handle in self.join.iter_mut() {
if let Some(th) = thread_handle.take() {
th.join().unwrap();
}
}
}
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> WorkerPool<ReturnValue, ExtraInput, Alloc, U>
{
fn do_work(queue: Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>) {
loop {
let ret;
{
// need to drop possible job before the final lock is taken,
// so refcount of possible_job Arc is 0 by the time the job is delivered
// to the caller. We basically need a barrier (the lock) to happen
// after the destructor that decrefs possible_job
let possible_job;
{
let (lock, cvar) = &*queue;
let mut local_queue = lock.lock().unwrap();
if local_queue.immediate_shutdown {
break;
}
possible_job = if let Some(res) = local_queue.jobs.pop() {
cvar.notify_all();
local_queue.num_in_progress += 1;
res
} else if local_queue.shutdown {
break;
} else {
let _lock = cvar.wait(local_queue); // unlock immediately, unfortunately
continue;
};
}
ret = if let Ok(job_data) = possible_job.data.read() {
JobReply {
result: (possible_job.func)(
possible_job.extra_input,
possible_job.index,
possible_job.thread_size,
&*job_data,
possible_job.alloc,
),
work_id: possible_job.work_id,
}
} else {
break; // poisoned lock
};
}
{
let (lock, cvar) = &*queue;
let mut local_queue = lock.lock().unwrap();
local_queue.num_in_progress -= 1;
local_queue.results.push(ret).unwrap();
cvar.notify_all();
}
}
}
fn _push_job(&mut self, job: JobRequest<ReturnValue, ExtraInput, Alloc, U>) {
let (lock, cvar) = &*self.queue.0;
let mut local_queue = lock.lock().unwrap();
loop {
if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
< MAX_THREADS
{
local_queue.jobs.push(job).unwrap();
cvar.notify_all();
break;
}
local_queue = cvar.wait(local_queue).unwrap();
}
}
fn _try_push_job(
&mut self,
job: JobRequest<ReturnValue, ExtraInput, Alloc, U>,
) -> Result<(), JobRequest<ReturnValue, ExtraInput, Alloc, U>> {
let (lock, cvar) = &*self.queue.0;
let mut local_queue = lock.lock().unwrap();
if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
< MAX_THREADS
{
local_queue.jobs.push(job).unwrap();
cvar.notify_all();
Ok(())
} else {
Err(job)
}
}
fn start(
queue: Arc<(Mutex<WorkQueue<ReturnValue, ExtraInput, Alloc, U>>, Condvar)>,
) -> std::thread::JoinHandle<()> {
std::thread::spawn(move || Self::do_work(queue))
}
pub fn new(num_threads: usize) -> Self {
let queue = Arc::new((Mutex::new(WorkQueue::default()), Condvar::new()));
WorkerPool {
queue: GuardedQueue(queue.clone()),
join: [
Some(Self::start(queue.clone())),
if 1 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 2 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 3 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 4 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 5 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 6 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 7 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 8 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 9 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 10 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 11 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 12 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 13 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 14 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
if 15 < num_threads {
Some(Self::start(queue.clone()))
} else {
None
},
],
}
}
}
pub fn new_work_pool<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
num_threads: usize,
) -> WorkerPool<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
WorkerPool::new(num_threads)
}
pub struct WorkerJoinable<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> {
queue: GuardedQueue<ReturnValue, ExtraInput, Alloc, U>,
work_id: u64,
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> Joinable<ReturnValue, BrotliEncoderThreadError>
for WorkerJoinable<ReturnValue, ExtraInput, Alloc, U>
{
fn join(self) -> Result<ReturnValue, BrotliEncoderThreadError> {
let (lock, cvar) = &*self.queue.0;
let mut local_queue = lock.lock().unwrap();
loop {
match local_queue
.results
.remove(|data: &Option<JobReply<ReturnValue>>| {
if let Some(ref item) = *data {
item.work_id == self.work_id
} else {
false
}
}) {
Some(matched) => return Ok(matched.result),
None => local_queue = cvar.wait(local_queue).unwrap(),
};
}
}
}
impl<
ReturnValue: Send + 'static,
ExtraInput: Send + 'static,
Alloc: BrotliAlloc + Send + 'static,
U: Send + 'static + Sync,
> BatchSpawnableLite<ReturnValue, ExtraInput, Alloc, U>
for WorkerPool<ReturnValue, ExtraInput, Alloc, U>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send + 'static,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
type FinalJoinHandle = Arc<RwLock<U>>;
type JoinHandle = WorkerJoinable<ReturnValue, ExtraInput, Alloc, U>;
fn make_spawner(&mut self, input: &mut Owned<U>) -> Self::FinalJoinHandle {
std::sync::Arc::<RwLock<U>>::new(RwLock::new(
mem::replace(input, Owned(InternalOwned::Borrowed)).unwrap(),
))
}
fn spawn(
&mut self,
locked_input: &mut Self::FinalJoinHandle,
work: &mut SendAlloc<ReturnValue, ExtraInput, Alloc, Self::JoinHandle>,
index: usize,
num_threads: usize,
f: fn(ExtraInput, usize, usize, &U, Alloc) -> ReturnValue,
) {
assert!(num_threads <= MAX_THREADS);
let (lock, cvar) = &*self.queue.0;
let mut local_queue = lock.lock().unwrap();
loop {
if local_queue.jobs.size() + local_queue.num_in_progress + local_queue.results.size()
<= MAX_THREADS
{
let work_id = local_queue.cur_work_id;
local_queue.cur_work_id += 1;
let (local_alloc, local_extra) = work.replace_with_default();
local_queue
.jobs
.push(JobRequest {
func: f,
extra_input: local_extra,
index,
thread_size: num_threads,
data: locked_input.clone(),
alloc: local_alloc,
work_id,
})
.unwrap();
*work = SendAlloc(InternalSendAlloc::Join(WorkerJoinable {
queue: GuardedQueue(self.queue.0.clone()),
work_id,
}));
cvar.notify_all();
break;
} else {
local_queue = cvar.wait(local_queue).unwrap(); // hope room frees up
}
}
}
}
pub fn compress_worker_pool<
Alloc: BrotliAlloc + Send + 'static,
SliceW: SliceWrapper<u8> + Send + 'static + Sync,
>(
params: &BrotliEncoderParams,
owned_input: &mut Owned<SliceW>,
output: &mut [u8],
alloc_per_thread: &mut [SendAlloc<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
<WorkerPool<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
> as BatchSpawnableLite<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>>::JoinHandle,
>],
work_pool: &mut WorkerPool<
CompressionThreadResult<Alloc>,
UnionHasher<Alloc>,
Alloc,
(SliceW, BrotliEncoderParams),
>,
) -> Result<usize, BrotliEncoderThreadError>
where
<Alloc as Allocator<u8>>::AllocatedMemory: Send,
<Alloc as Allocator<u16>>::AllocatedMemory: Send + Sync,
<Alloc as Allocator<u32>>::AllocatedMemory: Send + Sync,
{
CompressMulti(params, owned_input, output, alloc_per_thread, work_pool)
}
// out of place thread create
+321
View File
@@ -0,0 +1,321 @@
use alloc::{Allocator, SliceWrapperMut};
#[cfg(feature = "std")]
use std::io;
#[cfg(feature = "std")]
use std::io::{Error, ErrorKind, Write};
#[cfg(feature = "std")]
pub use alloc_stdlib::StandardAlloc;
use brotli_decompressor::CustomWrite;
#[cfg(feature = "std")]
pub use brotli_decompressor::{IntoIoWriter, IoWriterWrapper};
use super::backward_references::BrotliEncoderParams;
use super::combined_alloc::BrotliAlloc;
use super::encode::{
BrotliEncoderDestroyInstance, BrotliEncoderOperation, BrotliEncoderParameter,
BrotliEncoderStateStruct,
};
use super::interface;
use crate::enc::combined_alloc::allocate;
#[cfg(feature = "std")]
pub struct CompressorWriterCustomAlloc<
W: Write,
BufferType: SliceWrapperMut<u8>,
Alloc: BrotliAlloc,
>(CompressorWriterCustomIo<io::Error, IntoIoWriter<W>, BufferType, Alloc>);
#[cfg(feature = "std")]
impl<W: Write, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CompressorWriterCustomAlloc<W, BufferType, Alloc>
{
pub fn new(w: W, buffer: BufferType, alloc: Alloc, q: u32, lgwin: u32) -> Self {
CompressorWriterCustomAlloc::<W, BufferType, Alloc>(CompressorWriterCustomIo::<
Error,
IntoIoWriter<W>,
BufferType,
Alloc,
>::new(
IntoIoWriter::<W>(w),
buffer,
alloc,
Error::new(ErrorKind::InvalidData, "Invalid Data"),
Error::new(ErrorKind::WriteZero, "No room in output."),
q,
lgwin,
))
}
pub fn get_ref(&self) -> &W {
&self.0.get_ref().0
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.0.get_mut().0
}
pub fn into_inner(self) -> W {
self.0.into_inner().0
}
}
#[cfg(feature = "std")]
impl<W: Write, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc> Write
for CompressorWriterCustomAlloc<W, BufferType, Alloc>
{
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.0.write(buf)
}
fn flush(&mut self) -> Result<(), Error> {
self.0.flush()
}
}
#[cfg(feature = "std")]
pub struct CompressorWriter<W: Write>(
CompressorWriterCustomAlloc<
W,
<StandardAlloc as Allocator<u8>>::AllocatedMemory,
StandardAlloc,
>,
);
#[cfg(feature = "std")]
impl<W: Write> CompressorWriter<W> {
pub fn new(w: W, buffer_size: usize, q: u32, lgwin: u32) -> Self {
let mut alloc = StandardAlloc::default();
let buffer = allocate::<u8, _>(
&mut alloc,
if buffer_size == 0 { 4096 } else { buffer_size },
);
CompressorWriter::<W>(CompressorWriterCustomAlloc::new(w, buffer, alloc, q, lgwin))
}
pub fn with_params(w: W, buffer_size: usize, params: &BrotliEncoderParams) -> Self {
let mut writer = Self::new(w, buffer_size, params.quality as u32, params.lgwin as u32);
(writer.0).0.state.params = params.clone();
writer
}
pub fn get_ref(&self) -> &W {
self.0.get_ref()
}
pub fn get_mut(&mut self) -> &mut W {
self.0.get_mut()
}
pub fn into_inner(self) -> W {
self.0.into_inner()
}
}
#[cfg(feature = "std")]
impl<W: Write> Write for CompressorWriter<W> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.0.write(buf)
}
fn flush(&mut self) -> Result<(), Error> {
self.0.flush()
}
}
pub struct CompressorWriterCustomIo<
ErrType,
W: CustomWrite<ErrType>,
BufferType: SliceWrapperMut<u8>,
Alloc: BrotliAlloc,
> {
output_buffer: BufferType,
total_out: Option<usize>,
output: Option<W>,
error_if_invalid_data: Option<ErrType>,
state: BrotliEncoderStateStruct<Alloc>,
error_if_zero_bytes_written: Option<ErrType>,
}
pub fn write_all<ErrType, W: CustomWrite<ErrType>, ErrMaker: FnMut() -> Option<ErrType>>(
writer: &mut W,
mut buf: &[u8],
mut error_to_return_if_zero_bytes_written: ErrMaker,
) -> Result<(), ErrType> {
while !buf.is_empty() {
match writer.write(buf) {
Ok(bytes_written) => {
if bytes_written != 0 {
buf = &buf[bytes_written..]
} else {
if let Some(err) = error_to_return_if_zero_bytes_written() {
return Err(err);
} else {
return Ok(());
}
}
}
Err(e) => return Err(e),
}
}
Ok(())
}
impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
{
pub fn new(
w: W,
buffer: BufferType,
alloc: Alloc,
invalid_data_error_type: ErrType,
error_if_zero_bytes_written: ErrType,
q: u32,
lgwin: u32,
) -> Self {
let mut ret = CompressorWriterCustomIo {
output_buffer: buffer,
total_out: Some(0),
output: Some(w),
state: BrotliEncoderStateStruct::new(alloc),
error_if_invalid_data: Some(invalid_data_error_type),
error_if_zero_bytes_written: Some(error_if_zero_bytes_written),
};
ret.state
.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, q);
ret.state
.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin);
ret
}
fn flush_or_close(&mut self, op: BrotliEncoderOperation) -> Result<(), ErrType> {
let mut nop_callback =
|_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
_cmds: &mut [interface::StaticCommand],
_mb: interface::InputPair,
_mfv: &mut Alloc| ();
loop {
let mut avail_in: usize = 0;
let mut input_offset: usize = 0;
let mut avail_out: usize = self.output_buffer.slice_mut().len();
let mut output_offset: usize = 0;
let ret = self.state.compress_stream(
op,
&mut avail_in,
&[],
&mut input_offset,
&mut avail_out,
self.output_buffer.slice_mut(),
&mut output_offset,
&mut self.total_out,
&mut nop_callback,
);
if output_offset > 0 {
let zero_err = &mut self.error_if_zero_bytes_written;
let fallback = &mut self.error_if_invalid_data;
match write_all(
self.output.as_mut().unwrap(),
&self.output_buffer.slice_mut()[..output_offset],
|| {
if let Some(err) = zero_err.take() {
return Some(err);
}
fallback.take()
},
) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
if !ret {
return Err(self.error_if_invalid_data.take().unwrap());
}
if let BrotliEncoderOperation::BROTLI_OPERATION_FLUSH = op {
if self.state.has_more_output() {
continue;
}
return Ok(());
}
if self.state.is_finished() {
return Ok(());
}
}
}
pub fn get_ref(&self) -> &W {
self.output.as_ref().unwrap()
}
pub fn get_mut(&mut self) -> &mut W {
self.output.as_mut().unwrap()
}
pub fn into_inner(mut self) -> W {
match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FINISH) {
Ok(_) => {}
Err(_) => {}
}
self.output.take().unwrap()
}
}
impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc> Drop
for CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
{
fn drop(&mut self) {
if self.output.is_some() {
match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FINISH) {
Ok(_) => {}
Err(_) => {}
}
}
BrotliEncoderDestroyInstance(&mut self.state);
}
}
impl<ErrType, W: CustomWrite<ErrType>, BufferType: SliceWrapperMut<u8>, Alloc: BrotliAlloc>
CustomWrite<ErrType> for CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
{
fn write(&mut self, buf: &[u8]) -> Result<usize, ErrType> {
let mut nop_callback =
|_data: &mut interface::PredictionModeContextMap<interface::InputReferenceMut>,
_cmds: &mut [interface::StaticCommand],
_mb: interface::InputPair,
_mfv: &mut Alloc| ();
let mut avail_in = buf.len();
let mut input_offset: usize = 0;
while avail_in != 0 {
let mut output_offset = 0;
let mut avail_out = self.output_buffer.slice_mut().len();
let ret = self.state.compress_stream(
BrotliEncoderOperation::BROTLI_OPERATION_PROCESS,
&mut avail_in,
buf,
&mut input_offset,
&mut avail_out,
self.output_buffer.slice_mut(),
&mut output_offset,
&mut self.total_out,
&mut nop_callback,
);
if output_offset > 0 {
let zero_err = &mut self.error_if_zero_bytes_written;
let fallback = &mut self.error_if_invalid_data;
match write_all(
self.output.as_mut().unwrap(),
&self.output_buffer.slice_mut()[..output_offset],
|| {
if let Some(err) = zero_err.take() {
return Some(err);
}
fallback.take()
},
) {
Ok(_) => {}
Err(e) => return Err(e),
}
}
if !ret {
return Err(self.error_if_invalid_data.take().unwrap());
}
}
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), ErrType> {
match self.flush_or_close(BrotliEncoderOperation::BROTLI_OPERATION_FLUSH) {
Ok(_) => {}
Err(e) => return Err(e),
}
self.output.as_mut().unwrap().flush()
}
}
+56
View File
@@ -0,0 +1,56 @@
use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
use brotli_decompressor::ffi::alloc_util::SubclassableAllocator;
use crate::enc::BrotliAlloc;
pub struct BrotliSubclassableAllocator(SubclassableAllocator);
impl BrotliSubclassableAllocator {
pub fn new(s: SubclassableAllocator) -> BrotliSubclassableAllocator {
BrotliSubclassableAllocator(s)
}
}
#[derive(Default)]
pub struct SendableMemoryBlock<T: Clone + Default>(
<SubclassableAllocator as Allocator<T>>::AllocatedMemory,
);
impl<T: Clone + Default> SliceWrapperMut<T> for SendableMemoryBlock<T> {
fn slice_mut(&mut self) -> &mut [T] {
self.0.slice_mut()
}
}
impl<T: Clone + Default> SliceWrapper<T> for SendableMemoryBlock<T> {
fn slice(&self) -> &[T] {
self.0.slice()
}
}
impl<T: Clone + Default> Allocator<T> for BrotliSubclassableAllocator {
type AllocatedMemory = SendableMemoryBlock<T>;
fn alloc_cell(&mut self, s: usize) -> Self::AllocatedMemory {
SendableMemoryBlock(self.0.alloc_cell(s))
}
fn free_cell(&mut self, data: Self::AllocatedMemory) {
self.0.free_cell(data.0)
}
}
impl BrotliAlloc for BrotliSubclassableAllocator {}
#[cfg(not(feature = "safe"))]
unsafe impl Send for BrotliSubclassableAllocator {}
#[cfg(not(feature = "safe"))]
unsafe impl<T: Clone + Default> Send for SendableMemoryBlock<T> {}
#[cfg(not(feature = "std"))]
#[cfg(feature = "no-stdlib-ffi-binding")]
#[panic_handler]
extern "C" fn panic_impl(_: &::core::panic::PanicInfo) -> ! {
loop {}
}
#[cfg(not(feature = "std"))]
#[cfg(feature = "no-stdlib-ffi-binding")]
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
+236
View File
@@ -0,0 +1,236 @@
use core;
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
use std::io::Write;
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
use std::{io, panic, thread};
pub use brotli_decompressor::ffi::interface::c_void;
use brotli_decompressor::ffi::{slice_from_raw_parts_or_nil, slice_from_raw_parts_or_nil_mut};
use crate::concat::BroCatli;
pub use crate::concat::BroCatliResult;
pub type BroccoliResult = BroCatliResult;
// a tool to concatenate brotli files together
#[repr(C)]
pub struct BroccoliState {
more_data: *mut c_void,
current_data: [u8; 120],
}
impl Clone for BroccoliState {
fn clone(&self) -> BroccoliState {
let mut cd = [0u8; 120];
cd.clone_from_slice(&self.current_data[..]);
BroccoliState {
more_data: self.more_data,
current_data: cd,
}
}
}
impl Copy for BroccoliState {}
impl Default for BroccoliState {
fn default() -> BroccoliState {
BroCatli::new().into()
}
}
impl From<BroCatli> for BroccoliState {
fn from(data: BroCatli) -> BroccoliState {
let mut buffer = [0u8; 120];
data.serialize_to_buffer(&mut buffer[..]).unwrap();
BroccoliState {
more_data: core::ptr::null_mut(),
current_data: buffer,
}
}
}
impl From<BroccoliState> for BroCatli {
fn from(val: BroccoliState) -> Self {
BroCatli::deserialize_from_buffer(&val.current_data[..]).unwrap()
}
}
#[no_mangle]
pub extern "C" fn BroccoliCreateInstance() -> BroccoliState {
BroCatli::new().into()
}
#[no_mangle]
pub extern "C" fn BroccoliCreateInstanceWithWindowSize(window_size: u8) -> BroccoliState {
match BroCatli::try_new_with_window_size(window_size) {
Ok(bro_catli) => bro_catli.into(),
Err(_) => BroCatli::new().into(),
}
}
#[no_mangle]
pub extern "C" fn BroccoliDestroyInstance(_state: BroccoliState) {}
#[no_mangle]
pub unsafe extern "C" fn BroccoliNewBrotliFile(state: *mut BroccoliState) {
if let Err(panic_err) = catch_panic(|| {
let mut bro_catli: BroCatli = (*state).into();
bro_catli.new_brotli_file();
*state = BroccoliState::from(bro_catli);
BroCatliResult::Success
}) {
error_print(panic_err);
}
}
#[no_mangle]
pub unsafe extern "C" fn BroccoliConcatStream(
state: *mut BroccoliState,
available_in: *mut usize,
input_buf_ptr: *mut *const u8,
available_out: *mut usize,
output_buf_ptr: *mut *mut u8,
) -> BroccoliResult {
catch_panic(|| {
let input_buf = slice_from_raw_parts_or_nil(*input_buf_ptr, *available_in);
let output_buf = slice_from_raw_parts_or_nil_mut(*output_buf_ptr, *available_out);
let mut input_offset = 0usize;
let mut output_offset = 0usize;
let mut bro_catli: BroCatli = (*state).into();
let ret = bro_catli.stream(input_buf, &mut input_offset, output_buf, &mut output_offset);
*input_buf_ptr = (*input_buf_ptr).add(input_offset);
*output_buf_ptr = (*output_buf_ptr).add(output_offset);
*available_in -= input_offset;
*available_out -= output_offset;
*state = BroccoliState::from(bro_catli);
ret
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
BroCatliResult::BrotliFileNotCraftedForConcatenation
})
}
#[no_mangle]
pub unsafe extern "C" fn BroccoliConcatStreaming(
state: *mut BroccoliState,
available_in: *mut usize,
mut input_buf: *const u8,
available_out: *mut usize,
mut output_buf: *mut u8,
) -> BroccoliResult {
catch_panic(|| {
BroccoliConcatStream(
state,
available_in,
&mut input_buf,
available_out,
&mut output_buf,
)
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
BroCatliResult::BrotliFileNotCraftedForConcatenation
})
}
#[no_mangle]
pub unsafe extern "C" fn BroccoliConcatFinish(
state: *mut BroccoliState,
available_out: *mut usize,
output_buf_ptr: *mut *mut u8,
) -> BroCatliResult {
catch_panic(|| {
let output_buf = slice_from_raw_parts_or_nil_mut(*output_buf_ptr, *available_out);
let mut output_offset = 0usize;
let mut bro_catli: BroCatli = (*state).into();
let ret = bro_catli.finish(output_buf, &mut output_offset);
*output_buf_ptr = (*output_buf_ptr).add(output_offset);
*available_out -= output_offset;
*state = BroccoliState::from(bro_catli);
ret
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
BroCatliResult::BrotliFileNotCraftedForConcatenation
})
}
// exactly the same as BrotliConcatFinish but without the indirect
#[no_mangle]
pub unsafe extern "C" fn BroccoliConcatFinished(
state: *mut BroccoliState,
available_out: *mut usize,
mut output_buf: *mut u8,
) -> BroCatliResult {
catch_panic(|| BroccoliConcatFinish(state, available_out, &mut output_buf)).unwrap_or_else(
|panic_err| {
error_print(panic_err);
BroCatliResult::BrotliFileNotCraftedForConcatenation
},
)
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn catch_panic<F: FnOnce() -> BroccoliResult>(f: F) -> thread::Result<BroccoliResult> {
panic::catch_unwind(panic::AssertUnwindSafe(f))
}
// can't catch panics in a reliable way without std:: configure with panic=abort. These shouldn't happen
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn catch_panic<F: FnOnce() -> BroccoliResult>(f: F) -> Result<BroccoliResult, ()> {
Ok(f())
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn error_print<Err: core::fmt::Debug>(err: Err) {
let _ign = writeln!(&mut io::stderr(), "Internal Error {:?}", err);
}
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn error_print<Err>(_err: Err) {}
#[cfg(test)]
mod test {
#[test]
fn test_create_instance_with_invalid_window_size_does_not_panic() {
let _ = super::BroccoliCreateInstanceWithWindowSize(5);
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
#[test]
fn test_concat_stream_panic_returns_error() {
let mut state: super::BroccoliState = super::BroccoliCreateInstanceWithWindowSize(22);
let empty_catable = [b';'];
let mut input_buf = empty_catable.as_ptr();
let mut available_in = empty_catable.len();
let mut output = [0u8; 32];
let mut output_buf = output.as_mut_ptr();
let mut available_out = output.len();
let mut result = unsafe {
super::BroccoliConcatStream(
&mut state,
&mut available_in,
&mut input_buf,
&mut available_out,
&mut output_buf,
)
};
assert_eq!(result, super::BroccoliResult::NeedsMoreInput);
unsafe { super::BroccoliNewBrotliFile(&mut state) };
let truncated_metadata = [0x71, 0x1b, 0, 0];
input_buf = truncated_metadata.as_ptr();
available_in = truncated_metadata.len();
output_buf = output.as_mut_ptr();
available_out = output.len();
result = unsafe {
super::BroccoliConcatStream(
&mut state,
&mut available_in,
&mut input_buf,
&mut available_out,
&mut output_buf,
)
};
assert_eq!(
result,
super::BroccoliResult::BrotliFileNotCraftedForConcatenation
);
}
}
+473
View File
@@ -0,0 +1,473 @@
#![cfg(not(feature = "safe"))]
use core;
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
use std::{io, panic, thread};
use brotli_decompressor::ffi::alloc_util::SubclassableAllocator;
use brotli_decompressor::ffi::interface::{
brotli_alloc_func, brotli_free_func, c_void, CAllocator,
};
use brotli_decompressor::ffi::{
alloc_util, slice_from_raw_parts_or_nil, slice_from_raw_parts_or_nil_mut,
};
use super::alloc_util::BrotliSubclassableAllocator;
use crate::enc::encode::BrotliEncoderDestroyInstance as InternalBrotliEncoderDestroyInstance;
use crate::enc::encode::BrotliEncoderStateStruct;
#[repr(C)]
pub enum BrotliEncoderOperation {
BROTLI_OPERATION_PROCESS = 0,
BROTLI_OPERATION_FLUSH = 1,
BROTLI_OPERATION_FINISH = 2,
BROTLI_OPERATION_EMIT_METADATA = 3,
}
#[repr(C)]
pub enum BrotliEncoderMode {
BROTLI_MODE_GENERIC = 0,
BROTLI_MODE_TEXT = 1,
BROTLI_MODE_FONT = 2,
BROTLI_MODE_FORCE_LSB_PRIOR = 3,
BROTLI_MODE_FORCE_MSB_PRIOR = 4,
BROTLI_MODE_FORCE_UTF8_PRIOR = 5,
BROTLI_MODE_FORCE_SIGNED_PRIOR = 6,
}
#[repr(C)]
pub struct BrotliEncoderState {
pub custom_allocator: CAllocator,
pub compressor: BrotliEncoderStateStruct<BrotliSubclassableAllocator>,
}
#[cfg(not(feature = "std"))]
fn brotli_new_compressor_without_custom_alloc(
_to_box: BrotliEncoderState,
) -> *mut BrotliEncoderState {
panic!("Must supply allocators if calling divans when compiled without features=std");
}
#[cfg(feature = "std")]
fn brotli_new_compressor_without_custom_alloc(
to_box: BrotliEncoderState,
) -> *mut BrotliEncoderState {
alloc_util::Box::<BrotliEncoderState>::into_raw(alloc_util::Box::<BrotliEncoderState>::new(
to_box,
))
}
#[cfg(feature = "std")]
unsafe fn free_compressor_no_custom_alloc(state_ptr: *mut BrotliEncoderState) {
let _state = alloc_util::Box::from_raw(state_ptr);
}
#[cfg(not(feature = "std"))]
unsafe fn free_compressor_no_custom_alloc(_state_ptr: *mut BrotliEncoderState) {
unreachable!();
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCreateInstance(
alloc_func: brotli_alloc_func,
free_func: brotli_free_func,
opaque: *mut c_void,
) -> *mut BrotliEncoderState {
catch_panic_cstate(|| {
let allocators = CAllocator {
alloc_func,
free_func,
opaque,
};
let to_box = BrotliEncoderState {
custom_allocator: allocators.clone(),
compressor: BrotliEncoderStateStruct::new(BrotliSubclassableAllocator::new(
SubclassableAllocator::new(allocators.clone()),
)),
};
if let Some(alloc) = alloc_func {
if free_func.is_none() {
panic!("either both alloc and free must exist or neither");
}
let ptr = alloc(
allocators.opaque,
core::mem::size_of::<BrotliEncoderState>(),
);
if ptr.is_null() {
return core::ptr::null_mut();
}
let brotli_decoder_state_ptr =
core::mem::transmute::<*mut c_void, *mut BrotliEncoderState>(ptr);
core::ptr::write(brotli_decoder_state_ptr, to_box);
brotli_decoder_state_ptr
} else {
brotli_new_compressor_without_custom_alloc(to_box)
}
})
.unwrap_or_else(|err| {
error_print(err);
core::ptr::null_mut()
})
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderSetParameter(
state_ptr: *mut BrotliEncoderState,
param: ::enc::encode::BrotliEncoderParameter,
value: u32,
) -> i32 {
if (*state_ptr).compressor.set_parameter(param, value) {
1
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderDestroyInstance(state_ptr: *mut BrotliEncoderState) {
if state_ptr.is_null() {
return;
}
InternalBrotliEncoderDestroyInstance(&mut (*state_ptr).compressor);
if (*state_ptr).custom_allocator.alloc_func.is_some() {
if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
let _to_free = core::ptr::read(state_ptr);
let ptr = core::mem::transmute::<*mut BrotliEncoderState, *mut c_void>(state_ptr);
free_fn((*state_ptr).custom_allocator.opaque, ptr);
}
} else {
free_compressor_no_custom_alloc(state_ptr);
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderIsFinished(state_ptr: *mut BrotliEncoderState) -> i32 {
if (*state_ptr).compressor.is_finished() {
1
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderHasMoreOutput(state_ptr: *mut BrotliEncoderState) -> i32 {
if (*state_ptr).compressor.has_more_output() {
1
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderSetCustomDictionary(
state_ptr: *mut BrotliEncoderState,
size: usize,
dict: *const u8,
) {
if let Err(panic_err) = catch_panic(|| {
let dict_slice = slice_from_raw_parts_or_nil(dict, size);
(*state_ptr)
.compressor
.set_custom_dictionary(size, dict_slice);
0
}) {
error_print(panic_err);
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderTakeOutput(
state_ptr: *mut BrotliEncoderState,
size: *mut usize,
) -> *const u8 {
(*state_ptr).compressor.take_output(&mut *size).as_ptr()
}
#[no_mangle]
pub extern "C" fn BrotliEncoderVersion() -> u32 {
::enc::encode::BrotliEncoderVersion()
}
#[no_mangle]
pub extern "C" fn BrotliEncoderMaxCompressedSize(input_size: usize) -> usize {
::enc::encode::BrotliEncoderMaxCompressedSize(input_size)
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCompress(
quality: i32,
lgwin: i32,
mode: BrotliEncoderMode,
input_size: usize,
input_buffer: *const u8,
encoded_size: *mut usize,
encoded_buffer: *mut u8,
) -> i32 {
catch_panic(|| {
let input_buf = slice_from_raw_parts_or_nil(input_buffer, input_size);
let encoded_buf = slice_from_raw_parts_or_nil_mut(encoded_buffer, *encoded_size);
let allocators = CAllocator {
alloc_func: None,
free_func: None,
opaque: core::ptr::null_mut(),
};
let translated_mode = match mode {
BrotliEncoderMode::BROTLI_MODE_GENERIC => {
::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_GENERIC
}
BrotliEncoderMode::BROTLI_MODE_TEXT => {
::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_TEXT
}
BrotliEncoderMode::BROTLI_MODE_FONT => {
::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_FONT
}
BrotliEncoderMode::BROTLI_MODE_FORCE_LSB_PRIOR => {
::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_LSB_PRIOR
}
BrotliEncoderMode::BROTLI_MODE_FORCE_MSB_PRIOR => {
::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_MSB_PRIOR
}
BrotliEncoderMode::BROTLI_MODE_FORCE_UTF8_PRIOR => {
::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_UTF8_PRIOR
}
BrotliEncoderMode::BROTLI_MODE_FORCE_SIGNED_PRIOR => {
::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_SIGNED_PRIOR
}
};
let mut m8 =
BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
let empty_m8 =
BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
crate::enc::encode::encoder_compress(
empty_m8,
&mut m8,
quality,
lgwin,
translated_mode,
input_size,
input_buf,
&mut *encoded_size,
encoded_buf,
&mut |_a, _b, _c, _d| (),
)
.into()
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
0
})
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCompressStreaming(
state_ptr: *mut BrotliEncoderState,
op: BrotliEncoderOperation,
available_in: *mut usize,
mut input_buf: *const u8,
available_out: *mut usize,
mut output_buf: *mut u8,
) -> i32 {
BrotliEncoderCompressStream(
state_ptr,
op,
available_in,
&mut input_buf,
available_out,
&mut output_buf,
core::ptr::null_mut(),
)
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCompressStream(
state_ptr: *mut BrotliEncoderState,
op: BrotliEncoderOperation,
available_in: *mut usize,
input_buf_ptr: *mut *const u8,
available_out: *mut usize,
output_buf_ptr: *mut *mut u8,
total_out: *mut usize,
) -> i32 {
catch_panic(|| {
let mut input_offset = 0usize;
let mut output_offset = 0usize;
let result;
let translated_op = match op {
BrotliEncoderOperation::BROTLI_OPERATION_PROCESS => {
::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_PROCESS
}
BrotliEncoderOperation::BROTLI_OPERATION_FLUSH => {
::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FLUSH
}
BrotliEncoderOperation::BROTLI_OPERATION_FINISH => {
::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FINISH
}
BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA => {
::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA
}
};
{
let (input_buf, input_any): (&[u8], bool) = if *available_in != 0 {
(
slice_from_raw_parts_or_nil(*input_buf_ptr, *available_in),
true,
)
} else {
(&[], false)
};
let (output_buf, output_any): (&mut [u8], bool) = if *available_out != 0 {
(
slice_from_raw_parts_or_nil_mut(*output_buf_ptr, *available_out),
true,
)
} else {
(&mut [], false)
};
let mut to = Some(0);
result = (*state_ptr).compressor.compress_stream(
translated_op,
&mut *available_in,
input_buf,
&mut input_offset,
&mut *available_out,
output_buf,
&mut output_offset,
&mut to,
&mut |_a, _b, _c, _d| (),
);
if !total_out.is_null() {
*total_out = to.unwrap_or(0);
}
if input_any {
*input_buf_ptr = (*input_buf_ptr).add(input_offset);
}
if output_any {
*output_buf_ptr = (*output_buf_ptr).add(output_offset);
}
}
if result {
1
} else {
0
}
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
0
})
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderMallocU8(
state_ptr: *mut BrotliEncoderState,
size: usize,
) -> *mut u8 {
if let Some(alloc_fn) = (*state_ptr).custom_allocator.alloc_func {
core::mem::transmute::<*mut c_void, *mut u8>(alloc_fn(
(*state_ptr).custom_allocator.opaque,
size,
))
} else {
alloc_util::alloc_stdlib(size)
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderFreeU8(
state_ptr: *mut BrotliEncoderState,
data: *mut u8,
size: usize,
) {
if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
free_fn(
(*state_ptr).custom_allocator.opaque,
core::mem::transmute::<*mut u8, *mut c_void>(data),
);
} else {
alloc_util::free_stdlib(data, size);
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderMallocUsize(
state_ptr: *mut BrotliEncoderState,
size: usize,
) -> *mut usize {
if let Some(alloc_fn) = (*state_ptr).custom_allocator.alloc_func {
core::mem::transmute::<*mut c_void, *mut usize>(alloc_fn(
(*state_ptr).custom_allocator.opaque,
size * core::mem::size_of::<usize>(),
))
} else {
alloc_util::alloc_stdlib(size)
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderFreeUsize(
state_ptr: *mut BrotliEncoderState,
data: *mut usize,
size: usize,
) {
if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
free_fn(
(*state_ptr).custom_allocator.opaque,
core::mem::transmute::<*mut usize, *mut c_void>(data),
);
} else {
alloc_util::free_stdlib(data, size);
}
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
pub fn catch_panic<F: FnOnce() -> i32 + panic::UnwindSafe>(f: F) -> thread::Result<i32> {
panic::catch_unwind(f)
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn catch_panic_cstate<F: FnOnce() -> *mut BrotliEncoderState + panic::UnwindSafe>(
f: F,
) -> thread::Result<*mut BrotliEncoderState> {
panic::catch_unwind(f)
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn error_print<Err: core::fmt::Debug>(err: Err) {
let _ign = writeln!(&mut io::stderr(), "Internal Error {:?}", err);
}
// can't catch panics in a reliable way without std:: configure with panic=abort. These shouldn't happen
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
pub fn catch_panic<F: FnOnce() -> i32>(f: F) -> Result<i32, ()> {
Ok(f())
}
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn catch_panic_cstate<F: FnOnce() -> *mut BrotliEncoderState>(
f: F,
) -> Result<*mut BrotliEncoderState, ()> {
Ok(f())
}
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn error_print<Err>(_err: Err) {}
#[cfg(test)]
mod test {
use super::*;
extern "C" fn failing_alloc(_opaque: *mut c_void, _size: usize) -> *mut c_void {
core::ptr::null_mut()
}
extern "C" fn failing_free(_opaque: *mut c_void, _address: *mut c_void) {}
#[test]
fn test_create_instance_returns_null_on_allocator_failure() {
let state = unsafe {
BrotliEncoderCreateInstance(
Some(failing_alloc),
Some(failing_free),
core::ptr::null_mut(),
)
};
assert!(state.is_null());
}
}
+183
View File
@@ -0,0 +1,183 @@
pub use brotli_decompressor::ffi::interface::{brotli_alloc_func, brotli_free_func, c_void};
pub use brotli_decompressor::{ffi, BrotliDecoderReturnInfo, HuffmanCode};
pub unsafe extern "C" fn CBrotliDecoderCreateInstance(
alloc_func: brotli_alloc_func,
free_func: brotli_free_func,
opaque: *mut c_void,
) -> *mut ffi::BrotliDecoderState {
ffi::BrotliDecoderCreateInstance(alloc_func, free_func, opaque)
}
pub unsafe extern "C" fn CBrotliDecoderSetParameter(
state_ptr: *mut ffi::BrotliDecoderState,
selector: ffi::interface::BrotliDecoderParameter,
value: u32,
) {
ffi::BrotliDecoderSetParameter(state_ptr, selector, value)
}
#[cfg(feature = "std")] // this requires a default allocator
pub unsafe extern "C" fn CBrotliDecoderDecompress(
encoded_size: usize,
encoded_buffer: *const u8,
decoded_size: *mut usize,
decoded_buffer: *mut u8,
) -> ffi::interface::BrotliDecoderResult {
ffi::BrotliDecoderDecompress(encoded_size, encoded_buffer, decoded_size, decoded_buffer)
}
pub unsafe extern "C" fn CBrotliDecoderDecompressStream(
state_ptr: *mut ffi::BrotliDecoderState,
available_in: *mut usize,
input_buf_ptr: *mut *const u8,
available_out: *mut usize,
output_buf_ptr: *mut *mut u8,
total_out: *mut usize,
) -> ffi::interface::BrotliDecoderResult {
ffi::BrotliDecoderDecompressStream(
state_ptr,
available_in,
input_buf_ptr,
available_out,
output_buf_ptr,
total_out,
)
}
pub unsafe extern "C" fn CBrotliDecoderDecompressStreaming(
state_ptr: *mut ffi::BrotliDecoderState,
available_in: *mut usize,
input_buf_ptr: *const u8,
available_out: *mut usize,
output_buf_ptr: *mut u8,
) -> ffi::interface::BrotliDecoderResult {
ffi::BrotliDecoderDecompressStreaming(
state_ptr,
available_in,
input_buf_ptr,
available_out,
output_buf_ptr,
)
}
pub unsafe extern "C" fn CBrotliDecoderDecompressWithReturnInfo(
available_in: usize,
input_buf_ptr: *const u8,
available_out_and_scratch: usize,
output_buf_and_scratch: *mut u8,
) -> BrotliDecoderReturnInfo {
ffi::BrotliDecoderDecompressWithReturnInfo(
available_in,
input_buf_ptr,
available_out_and_scratch,
output_buf_and_scratch,
)
}
pub unsafe extern "C" fn CBrotliDecoderDecompressPrealloc(
available_in: usize,
input_buf_ptr: *const u8,
available_out: usize,
output_buf_ptr: *mut u8,
available_u8: usize,
u8_ptr: *mut u8,
available_u32: usize,
u32_ptr: *mut u32,
available_hc: usize,
hc_ptr: *mut HuffmanCode,
) -> BrotliDecoderReturnInfo {
ffi::BrotliDecoderDecompressPrealloc(
available_in,
input_buf_ptr,
available_out,
output_buf_ptr,
available_u8,
u8_ptr,
available_u32,
u32_ptr,
available_hc,
hc_ptr,
)
}
pub unsafe extern "C" fn CBrotliDecoderMallocU8(
state_ptr: *mut ffi::BrotliDecoderState,
size: usize,
) -> *mut u8 {
ffi::BrotliDecoderMallocU8(state_ptr, size)
}
pub unsafe extern "C" fn CBrotliDecoderFreeU8(
state_ptr: *mut ffi::BrotliDecoderState,
data: *mut u8,
size: usize,
) {
ffi::BrotliDecoderFreeU8(state_ptr, data, size)
}
pub unsafe extern "C" fn CBrotliDecoderMallocUsize(
state_ptr: *mut ffi::BrotliDecoderState,
size: usize,
) -> *mut usize {
ffi::BrotliDecoderMallocUsize(state_ptr, size)
}
pub unsafe extern "C" fn CBrotliDecoderFreeUsize(
state_ptr: *mut ffi::BrotliDecoderState,
data: *mut usize,
size: usize,
) {
ffi::BrotliDecoderFreeUsize(state_ptr, data, size)
}
pub unsafe extern "C" fn CBrotliDecoderDestroyInstance(state_ptr: *mut ffi::BrotliDecoderState) {
ffi::BrotliDecoderDestroyInstance(state_ptr)
}
pub extern "C" fn CBrotliDecoderVersion() -> u32 {
ffi::BrotliDecoderVersion()
}
#[no_mangle]
pub extern "C" fn CBrotliDecoderErrorString(c: ffi::BrotliDecoderErrorCode) -> *const u8 {
ffi::BrotliDecoderErrorString(c)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderHasMoreOutput(
state_ptr: *const ffi::BrotliDecoderState,
) -> i32 {
ffi::BrotliDecoderHasMoreOutput(state_ptr)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderTakeOutput(
state_ptr: *mut ffi::BrotliDecoderState,
size: *mut usize,
) -> *const u8 {
ffi::BrotliDecoderTakeOutput(state_ptr, size)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderIsUsed(state_ptr: *const ffi::BrotliDecoderState) -> i32 {
ffi::BrotliDecoderIsUsed(state_ptr)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderIsFinished(
state_ptr: *const ffi::BrotliDecoderState,
) -> i32 {
ffi::BrotliDecoderIsFinished(state_ptr)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderGetErrorCode(
state_ptr: *const ffi::BrotliDecoderState,
) -> ffi::BrotliDecoderErrorCode {
ffi::BrotliDecoderGetErrorCode(state_ptr)
}
#[no_mangle]
pub unsafe extern "C" fn CBrotliDecoderGetErrorString(
state_ptr: *const ffi::BrotliDecoderState,
) -> *const u8 {
ffi::BrotliDecoderGetErrorString(state_ptr)
}
+5
View File
@@ -0,0 +1,5 @@
pub mod alloc_util;
pub mod broccoli;
pub mod compressor;
pub mod decompressor;
pub mod multicompress;
+450
View File
@@ -0,0 +1,450 @@
#![cfg(not(feature = "safe"))]
mod test;
use alloc::SliceWrapper;
use core::cmp::min;
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
use std::panic;
use brotli_decompressor::ffi::alloc_util::SubclassableAllocator;
use brotli_decompressor::ffi::interface::{
brotli_alloc_func, brotli_free_func, c_void, CAllocator,
};
use brotli_decompressor::ffi::{slice_from_raw_parts_or_nil, slice_from_raw_parts_or_nil_mut};
use {brotli_decompressor, core, enc};
use super::alloc_util::BrotliSubclassableAllocator;
use super::compressor;
use crate::enc::backward_references::{BrotliEncoderParams, UnionHasher};
use crate::enc::encode::{
set_parameter, BrotliEncoderOperation, BrotliEncoderParameter, BrotliEncoderStateStruct,
};
use crate::enc::threading::{Owned, SendAlloc};
pub const MAX_THREADS: usize = 16;
struct SliceRef<'a>(&'a [u8]);
impl<'a> SliceWrapper<u8> for SliceRef<'a> {
fn slice(&self) -> &[u8] {
self.0
}
}
macro_rules! make_send_alloc {
($alloc_func: expr, $free_func: expr, $opaque: expr) => {
SendAlloc::new(
BrotliSubclassableAllocator::new(SubclassableAllocator::new(CAllocator {
alloc_func: $alloc_func,
free_func: $free_func,
opaque: $opaque,
})),
UnionHasher::Uninit,
)
};
}
#[no_mangle]
pub extern "C" fn BrotliEncoderMaxCompressedSizeMulti(
input_size: usize,
num_threads: usize,
) -> usize {
::enc::encode::BrotliEncoderMaxCompressedSizeMulti(input_size, num_threads)
}
fn help_brotli_encoder_compress_single(
param_keys: &[BrotliEncoderParameter],
param_values: &[u32],
input: &[u8],
output: &mut [u8],
encoded_size: &mut usize,
m8: BrotliSubclassableAllocator,
) -> bool {
let mut encoder = BrotliEncoderStateStruct::new(m8);
for (p, v) in param_keys.iter().zip(param_values.iter()) {
encoder.set_parameter(*p, *v);
}
let mut available_in = input.len();
let mut next_in_offset = 0usize;
let mut available_out = output.len();
let mut next_out_offset = 0usize;
let mut total_out = Some(0);
let mut result = encoder.compress_stream(
BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
&mut available_in,
input,
&mut next_in_offset,
&mut available_out,
output,
&mut next_out_offset,
&mut total_out,
&mut |_a, _b, _c, _d| (),
);
if !encoder.is_finished() {
result = false;
}
*encoded_size = total_out.unwrap();
result
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCompressMulti(
num_params: usize,
param_keys: *const BrotliEncoderParameter,
param_values: *const u32,
input_size: usize,
input: *const u8,
encoded_size: *mut usize,
encoded: *mut u8,
desired_num_threads: usize,
alloc_func: brotli_alloc_func,
free_func: brotli_free_func,
alloc_opaque_per_thread: *mut *mut c_void,
) -> i32 {
if desired_num_threads == 0 {
return 0;
}
let num_threads = min(desired_num_threads, MAX_THREADS);
compressor::catch_panic(|| {
let param_keys_slice = slice_from_raw_parts_or_nil(param_keys, num_params);
let param_values_slice = slice_from_raw_parts_or_nil(param_values, num_params);
let input_slice = slice_from_raw_parts_or_nil(input, input_size);
let output_slice = slice_from_raw_parts_or_nil_mut(encoded, *encoded_size);
if num_threads == 1 {
let allocators = CAllocator {
alloc_func,
free_func,
opaque: if alloc_opaque_per_thread.is_null() {
core::ptr::null_mut()
} else {
*alloc_opaque_per_thread
},
};
let m8 =
BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
return help_brotli_encoder_compress_single(
param_keys_slice,
param_values_slice,
input_slice,
output_slice,
&mut *encoded_size,
m8,
)
.into();
}
let null_opaques = [core::ptr::null_mut::<c_void>(); MAX_THREADS];
let alloc_opaque = if alloc_opaque_per_thread.is_null() {
&null_opaques[..]
} else {
slice_from_raw_parts_or_nil(alloc_opaque_per_thread, desired_num_threads)
};
let mut params = BrotliEncoderParams::default();
for (k, v) in param_keys_slice.iter().zip(param_values_slice.iter()) {
if !set_parameter(&mut params, *k, *v) {
return 0;
}
}
let mut alloc_array: [_; MAX_THREADS] = [
make_send_alloc!(alloc_func, free_func, alloc_opaque[0]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[1 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[2 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[3 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[4 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[5 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[6 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[7 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[8 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[9 % desired_num_threads]),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[10 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[11 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[12 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[13 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[14 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[15 % desired_num_threads]
),
];
let owned_input = &mut Owned::new(SliceRef(input_slice));
let res = enc::compress_multi_no_threadpool(
&params,
owned_input,
output_slice,
&mut alloc_array[..num_threads],
);
match res {
Ok(size) => {
*encoded_size = size;
1
}
Err(_err) => 0,
}
})
.unwrap_or_else(|panic_err| {
error_print(panic_err);
0
})
}
#[repr(C)]
pub struct BrotliEncoderWorkPool {
custom_allocator: CAllocator,
work_pool: enc::WorkerPool<
enc::CompressionThreadResult<BrotliSubclassableAllocator>,
UnionHasher<BrotliSubclassableAllocator>,
BrotliSubclassableAllocator,
(SliceRef<'static>, BrotliEncoderParams),
>,
}
#[cfg(not(feature = "std"))]
fn brotli_new_work_pool_without_custom_alloc(
_to_box: BrotliEncoderWorkPool,
) -> *mut BrotliEncoderWorkPool {
panic!("Must supply allocators if calling divans when compiled without features=std");
}
#[cfg(feature = "std")]
fn brotli_new_work_pool_without_custom_alloc(
to_box: BrotliEncoderWorkPool,
) -> *mut BrotliEncoderWorkPool {
brotli_decompressor::ffi::alloc_util::Box::<BrotliEncoderWorkPool>::into_raw(
brotli_decompressor::ffi::alloc_util::Box::<BrotliEncoderWorkPool>::new(to_box),
)
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCreateWorkPool(
num_threads: usize,
alloc_func: brotli_alloc_func,
free_func: brotli_free_func,
opaque: *mut c_void,
) -> *mut BrotliEncoderWorkPool {
catch_panic_wstate(|| {
let allocators = CAllocator {
alloc_func,
free_func,
opaque,
};
let to_box = BrotliEncoderWorkPool {
custom_allocator: allocators.clone(),
work_pool: enc::new_work_pool(min(num_threads, MAX_THREADS)),
};
if let Some(alloc) = alloc_func {
if free_func.is_none() {
panic!("either both alloc and free must exist or neither");
}
let ptr = alloc(
allocators.opaque,
core::mem::size_of::<BrotliEncoderWorkPool>(),
);
if ptr.is_null() {
return core::ptr::null_mut();
}
let brotli_work_pool_ptr =
core::mem::transmute::<*mut c_void, *mut BrotliEncoderWorkPool>(ptr);
core::ptr::write(brotli_work_pool_ptr, to_box);
brotli_work_pool_ptr
} else {
brotli_new_work_pool_without_custom_alloc(to_box)
}
})
.unwrap_or_else(|err| {
error_print(err);
core::ptr::null_mut()
})
}
#[cfg(feature = "std")]
unsafe fn free_work_pool_no_custom_alloc(_work_pool: *mut BrotliEncoderWorkPool) {
let _state = brotli_decompressor::ffi::alloc_util::Box::from_raw(_work_pool);
}
#[cfg(not(feature = "std"))]
unsafe fn free_work_pool_no_custom_alloc(_work_pool: *mut BrotliEncoderWorkPool) {
unreachable!();
}
struct UnsafeUnwindBox(*mut BrotliEncoderWorkPool);
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
impl panic::RefUnwindSafe for UnsafeUnwindBox {}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderDestroyWorkPool(work_pool_ptr: *mut BrotliEncoderWorkPool) {
let wpp = UnsafeUnwindBox(work_pool_ptr);
if let Err(panic_err) = compressor::catch_panic(|| {
if (*wpp.0).custom_allocator.alloc_func.is_some() {
if let Some(free_fn) = (*wpp.0).custom_allocator.free_func {
let _to_free = core::ptr::read(wpp.0);
let ptr = core::mem::transmute::<*mut BrotliEncoderWorkPool, *mut c_void>(wpp.0);
free_fn((*wpp.0).custom_allocator.opaque, ptr);
}
} else {
free_work_pool_no_custom_alloc(wpp.0);
}
0
}) {
error_print(panic_err);
}
}
#[no_mangle]
pub unsafe extern "C" fn BrotliEncoderCompressWorkPool(
work_pool: *mut BrotliEncoderWorkPool,
num_params: usize,
param_keys: *const BrotliEncoderParameter,
param_values: *const u32,
input_size: usize,
input: *const u8,
encoded_size: *mut usize,
encoded: *mut u8,
desired_num_threads: usize,
alloc_func: brotli_alloc_func,
free_func: brotli_free_func,
alloc_opaque_per_thread: *mut *mut c_void,
) -> i32 {
if desired_num_threads == 0 {
return 0;
}
if work_pool.is_null() {
return compressor::catch_panic(|| {
BrotliEncoderCompressMulti(
num_params,
param_keys,
param_values,
input_size,
input,
encoded_size,
encoded,
desired_num_threads,
alloc_func,
free_func,
alloc_opaque_per_thread,
)
})
.unwrap_or_else(|panic_err| {
error_print(panic_err); // print panic
0 // fail
});
}
let work_pool_wrapper = UnsafeUnwindBox(work_pool);
compressor::catch_panic(|| {
let null_opaques = [core::ptr::null_mut::<c_void>(); MAX_THREADS];
let alloc_opaque = if alloc_opaque_per_thread.is_null() {
&null_opaques[..]
} else {
slice_from_raw_parts_or_nil(alloc_opaque_per_thread, desired_num_threads)
};
let param_keys_slice = slice_from_raw_parts_or_nil(param_keys, num_params);
let param_values_slice = slice_from_raw_parts_or_nil(param_values, num_params);
let mut params = BrotliEncoderParams::default();
for (k, v) in param_keys_slice.iter().zip(param_values_slice.iter()) {
if !set_parameter(&mut params, *k, *v) {
return 0;
}
}
let num_threads = min(desired_num_threads, MAX_THREADS);
let mut alloc_array: [_; MAX_THREADS] = [
make_send_alloc!(alloc_func, free_func, alloc_opaque[0]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[1 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[2 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[3 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[4 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[5 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[6 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[7 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[8 % desired_num_threads]),
make_send_alloc!(alloc_func, free_func, alloc_opaque[9 % desired_num_threads]),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[10 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[11 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[12 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[13 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[14 % desired_num_threads]
),
make_send_alloc!(
alloc_func,
free_func,
alloc_opaque[15 % desired_num_threads]
),
];
let res = enc::compress_worker_pool(
&params,
&mut Owned::new(SliceRef(slice_from_raw_parts_or_nil(input, input_size))),
slice_from_raw_parts_or_nil_mut(encoded, *encoded_size),
&mut alloc_array[..num_threads],
&mut (*work_pool_wrapper.0).work_pool,
);
match res {
Ok(size) => {
*encoded_size = size;
1
}
Err(_err) => 0,
}
})
.unwrap_or_else(|panic_err| {
error_print(panic_err); // print panic
0 // fail
})
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn catch_panic_wstate<F: FnOnce() -> *mut BrotliEncoderWorkPool + panic::UnwindSafe>(
f: F,
) -> std::thread::Result<*mut BrotliEncoderWorkPool> {
panic::catch_unwind(f)
}
#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
fn error_print<Err: core::fmt::Debug>(err: Err) {
let _ign = writeln!(&mut std::io::stderr(), "Internal Error {:?}", err);
}
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn catch_panic_wstate<F: FnOnce() -> *mut BrotliEncoderWorkPool>(
f: F,
) -> Result<*mut BrotliEncoderWorkPool, ()> {
Ok(f())
}
#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
fn error_print<Err>(_err: Err) {}
@@ -0,0 +1,344 @@
#![cfg(test)]
#![cfg(feature = "std")]
use core;
use super::*;
use crate::enc::encode::BrotliEncoderParameter;
extern "C" fn failing_alloc(_opaque: *mut c_void, _size: usize) -> *mut c_void {
core::ptr::null_mut()
}
extern "C" fn failing_free(_opaque: *mut c_void, _address: *mut c_void) {}
#[test]
fn test_create_work_pool_returns_null_on_allocator_failure() {
let wp = unsafe {
BrotliEncoderCreateWorkPool(
8,
Some(failing_alloc),
Some(failing_free),
core::ptr::null_mut(),
)
};
assert!(wp.is_null());
}
#[test]
fn test_compress_workpool() {
let input = [
102, 114, 111, 109, 32, 99, 116, 121, 112, 101, 115, 32, 105, 109, 112, 111, 114, 116, 32,
42, 10, 10, 99, 108, 97, 115, 115, 32, 69, 110, 117, 109, 84, 121, 112, 101, 40, 116, 121,
112, 101, 40, 99, 95, 117, 105, 110, 116, 41, 41, 58, 10, 32, 32, 32, 32, 100, 101, 102,
32, 95, 95, 110, 101, 119, 95, 95, 40, 109, 101, 116, 97, 99, 108, 115, 41, 58, 10, 32, 32,
32, 32, 32, 32, 32, 32, 112, 97, 115, 115, 10,
];
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
];
let values = [11u32, 16, 91, 0, 0, 0];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(input.len(), 4);
let mut encoded_backing = [0u8; 145];
let encoded = &mut encoded_backing[..encoded_size];
let ret = unsafe {
let wp = BrotliEncoderCreateWorkPool(8, None, None, core::ptr::null_mut());
let inner_ret = BrotliEncoderCompressWorkPool(
wp,
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
input.len(),
input[..].as_ptr(),
&mut encoded_size,
encoded.as_mut_ptr(),
4,
None,
None,
core::ptr::null_mut(),
);
BrotliEncoderDestroyWorkPool(wp);
inner_ret
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{}", ret2 as i32),
}
assert_eq!(rt_size, input.len());
assert_eq!(&rt_buffer[..rt_size], &input[..]);
}
#[test]
fn test_compress_empty_workpool() {
let input = [];
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
];
let values = [3u32, 16, 91, 0, 0, 0];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(input.len(), 4);
let mut encoded_backing = [0u8; 145];
let encoded = &mut encoded_backing[..encoded_size];
let ret = unsafe {
let wp = BrotliEncoderCreateWorkPool(8, None, None, core::ptr::null_mut());
let inner_ret = BrotliEncoderCompressWorkPool(
wp,
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
input.len(),
input[..].as_ptr(),
&mut encoded_size,
encoded.as_mut_ptr(),
4,
None,
None,
core::ptr::null_mut(),
);
BrotliEncoderDestroyWorkPool(wp);
inner_ret
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
assert_ne!(encoded_size, 0);
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{}", ret2 as i32),
}
assert_eq!(rt_size, input.len());
assert_eq!(&rt_buffer[..rt_size], &input[..]);
}
#[test]
fn test_compress_empty_multi_raw() {
let input = [];
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
];
let values = [3u32, 16, 0, 0, 0, 0];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(input.len(), 4);
let mut encoded_backing = [0u8; 145];
let encoded = &mut encoded_backing[..encoded_size];
let ret = unsafe {
BrotliEncoderCompressMulti(
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
input.len(),
input[..].as_ptr(),
&mut encoded_size,
encoded.as_mut_ptr(),
4,
None,
None,
core::ptr::null_mut(),
)
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
assert_ne!(encoded_size, 0);
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{}", ret2 as i32),
}
assert_eq!(rt_size, input.len());
assert_eq!(&rt_buffer[..rt_size], &input[..]);
}
#[test]
fn test_compress_null_multi_raw() {
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
];
let values = [3u32, 16, 0, 0, 0, 0];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(0, 4);
let mut encoded_backing = [0u8; 145];
let encoded = &mut encoded_backing[..encoded_size];
let ret = unsafe {
BrotliEncoderCompressMulti(
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
0,
core::ptr::null(),
&mut encoded_size,
encoded.as_mut_ptr(),
4,
None,
None,
core::ptr::null_mut(),
)
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
assert_ne!(encoded_size, 0);
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{}", ret2 as i32),
}
assert_eq!(rt_size, 0);
}
#[test]
fn test_compress_empty_multi_raw_one_thread() {
let input = [];
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_APPENDABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
];
let values = [10u32, 1, 1, 1, 1];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(input.len(), 1);
let mut encoded_backing = [0u8; 25];
let encoded = &mut encoded_backing[..encoded_size];
assert_eq!(params.len(), 5);
assert_eq!(encoded_size, 25);
let ret = unsafe {
BrotliEncoderCompressMulti(
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
input.len(),
input[..].as_ptr(),
&mut encoded_size,
encoded.as_mut_ptr(),
1,
None,
None,
core::ptr::null_mut(),
)
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
assert_ne!(encoded_size, 0);
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{}", ret2 as i32),
}
assert_eq!(rt_size, input.len());
assert_eq!(&rt_buffer[..rt_size], &input[..]);
}
#[test]
fn test_compress_empty_multi_catable() {
let input = [];
let params = [
BrotliEncoderParameter::BROTLI_PARAM_QUALITY,
BrotliEncoderParameter::BROTLI_PARAM_LGWIN,
BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
BrotliEncoderParameter::BROTLI_PARAM_CATABLE,
BrotliEncoderParameter::BROTLI_PARAM_MAGIC_NUMBER,
BrotliEncoderParameter::BROTLI_PARAM_Q9_5,
];
let values = [3u32, 16, 0, 1, 1, 0];
let mut encoded_size = BrotliEncoderMaxCompressedSizeMulti(input.len(), 4);
let mut encoded_backing = [0u8; 145];
let encoded = &mut encoded_backing[..encoded_size];
let ret = unsafe {
BrotliEncoderCompressMulti(
params.len(),
params[..].as_ptr(),
values[..].as_ptr(),
input.len(),
input[..].as_ptr(),
&mut encoded_size,
encoded.as_mut_ptr(),
4,
None,
None,
core::ptr::null_mut(),
)
};
assert_eq!(ret, 1);
let mut rt_size = 256;
let mut rt_buffer = [0u8; 256];
assert_ne!(encoded_size, 0);
let ret2 = unsafe {
super::super::decompressor::CBrotliDecoderDecompress(
encoded_size,
encoded.as_ptr(),
&mut rt_size,
rt_buffer.as_mut_ptr(),
)
};
match ret2 {
super::super::decompressor::ffi::interface::BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => {
},
_ => panic!("{:?}", ret2 as i32),
}
assert_eq!(rt_size, input.len());
assert_eq!(&rt_buffer[..rt_size], &input[..]);
}
+77
View File
@@ -0,0 +1,77 @@
#![no_std]
#![allow(non_snake_case)]
#![allow(unused_parens)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![cfg_attr(feature = "benchmark", feature(test))]
#![cfg_attr(feature = "simd", feature(portable_simd))]
#![cfg_attr(
feature = "no-stdlib-ffi-binding",
cfg_attr(not(feature = "std"), feature(lang_items))
)]
#[macro_use]
// <-- for debugging, remove xprintln from bit_reader and replace with println
#[cfg(feature = "std")]
extern crate std;
extern crate alloc_no_stdlib as alloc;
#[cfg(feature = "std")]
extern crate alloc_stdlib;
extern crate brotli_decompressor;
pub mod concat;
pub mod enc;
#[cfg(feature = "ffi-api")]
pub mod ffi;
pub use alloc::{AllocatedStackMemory, Allocator, SliceWrapper, SliceWrapperMut, StackAllocator};
#[cfg(feature = "std")]
pub use alloc_stdlib::HeapAlloc;
#[cfg(feature = "std")]
pub use brotli_decompressor::copy_from_to;
pub use brotli_decompressor::io_wrappers::{CustomRead, CustomWrite};
#[cfg(feature = "std")]
pub use brotli_decompressor::io_wrappers::{IntoIoReader, IoReaderWrapper, IoWriterWrapper};
#[cfg(feature = "std")]
pub use brotli_decompressor::reader::Decompressor;
pub use brotli_decompressor::reader::DecompressorCustomIo;
pub use brotli_decompressor::transform::TransformDictionaryWord;
#[cfg(feature = "std")]
pub use brotli_decompressor::writer::DecompressorWriter;
pub use brotli_decompressor::writer::DecompressorWriterCustomIo;
#[cfg(feature = "std")]
pub use brotli_decompressor::BrotliDecompress;
#[cfg(feature = "std")]
pub use brotli_decompressor::BrotliDecompressCustomAlloc;
pub use brotli_decompressor::HuffmanCode; // so we can make custom allocator for decompression
pub use brotli_decompressor::{
dictionary, reader, transform, writer, BrotliDecompressCustomIo,
BrotliDecompressCustomIoCustomDict, BrotliDecompressStream, BrotliResult, BrotliState,
};
pub use self::enc::combined_alloc::CombiningAllocator;
pub use crate::enc::input_pair::{InputPair, InputReference, InputReferenceMut};
pub use crate::enc::interface::SliceOffset;
#[cfg(feature = "std")]
pub use crate::enc::reader::CompressorReader;
pub use crate::enc::reader::CompressorReaderCustomIo;
#[cfg(feature = "std")]
pub use crate::enc::writer::CompressorWriter;
pub use crate::enc::writer::CompressorWriterCustomIo;
pub use crate::enc::{interface, BrotliCompressCustomIo, BrotliCompressCustomIoCustomDict};
#[cfg(feature = "std")]
pub use crate::enc::{BrotliCompress, BrotliCompressCustomAlloc};
pub const VERSION: u8 = 1;
// interface
// pub fn BrotliDecompressStream(mut available_in: &mut usize,
// input_offset: &mut usize,
// input: &[u8],
// mut available_out: &mut usize,
// mut output_offset: &mut usize,
// mut output: &mut [u8],
// mut total_out: &mut usize,
// mut s: &mut BrotliState<AllocU8, AllocU32, AllocHC>);