mirror of
https://github.com/exoticorn/upkr.git
synced 2026-01-20 11:36:42 +01:00
more config options, unpack error handling, fuzzing
This commit is contained in:
58
Cargo.lock
generated
58
Cargo.lock
generated
@@ -95,6 +95,24 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sacabase"
|
name = "sacabase"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -104,6 +122,37 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@@ -115,14 +164,21 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "upkr"
|
name = "upkr"
|
||||||
version = "0.2.0-pre2"
|
version = "0.2.0-pre3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cdivsufsort",
|
"cdivsufsort",
|
||||||
"lexopt",
|
"lexopt",
|
||||||
"pbr",
|
"pbr",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "upkr"
|
name = "upkr"
|
||||||
version = "0.2.0-pre2"
|
version = "0.2.0-pre3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@@ -9,4 +9,5 @@ edition = "2021"
|
|||||||
cdivsufsort = "2"
|
cdivsufsort = "2"
|
||||||
lexopt = "0.2.1"
|
lexopt = "0.2.1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
thiserror = "1.0.36"
|
||||||
pbr = "1"
|
pbr = "1"
|
||||||
3
fuzz/.gitignore
vendored
Normal file
3
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
||||||
247
fuzz/Cargo.lock
generated
Normal file
247
fuzz/Cargo.lock
generated
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f44124848854b941eafdb34f05b3bcf59472f643c7e151eba7c2b69daa469ed5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cdivsufsort"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edefce019197609da416762da75bb000bbd2224b2d89a7e722c2296cbff79b8c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"sacabase",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lexopt"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.133"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae185684fe19814afd066da15a7cc41e126886c21282934225d9fc847582da58"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"libc",
|
||||||
|
"time",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sacabase"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9883fc3d6ce3d78bb54d908602f8bc1f7b5f983afe601dabe083009d86267a84"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "upkr"
|
||||||
|
version = "0.2.0-pre3"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cdivsufsort",
|
||||||
|
"lexopt",
|
||||||
|
"pbr",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "upkr-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"upkr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
31
fuzz/Cargo.toml
Normal file
31
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "upkr-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["Automatically generated"]
|
||||||
|
publish = false
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
cargo-fuzz = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libfuzzer-sys = "0.4"
|
||||||
|
|
||||||
|
[dependencies.upkr]
|
||||||
|
path = ".."
|
||||||
|
|
||||||
|
# Prevent this from interfering with workspaces
|
||||||
|
[workspace]
|
||||||
|
members = ["."]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "all_configs"
|
||||||
|
path = "fuzz_targets/all_configs.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "unpack"
|
||||||
|
path = "fuzz_targets/unpack.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
29
fuzz/fuzz_targets/all_configs.rs
Normal file
29
fuzz/fuzz_targets/all_configs.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
let mut config = upkr::Config::default();
|
||||||
|
let mut level = 1;
|
||||||
|
let mut data = data;
|
||||||
|
if data.len() > 2 {
|
||||||
|
let flags1 = data[0];
|
||||||
|
let flags2 = data[1];
|
||||||
|
data = &data[2..];
|
||||||
|
config.use_bitstream = (flags1 & 1) != 0;
|
||||||
|
config.parity_contexts = if (flags1 & 2) == 0 { 1 } else { 2 };
|
||||||
|
config.invert_bit_encoding = (flags1 & 4) != 0;
|
||||||
|
config.is_match_bit = (flags1 & 8) != 0;
|
||||||
|
config.new_offset_bit = (flags1 & 16) != 0;
|
||||||
|
config.continue_value_bit = (flags1 & 32) != 0;
|
||||||
|
config.bitstream_is_big_endian = (flags1 & 64) != 0;
|
||||||
|
config.simplified_prob_update = (flags1 & 128) != 0;
|
||||||
|
config.no_repeated_offsets = (flags2 & 32) != 0;
|
||||||
|
config.eof_in_length = (flags2 & 1) != 0;
|
||||||
|
config.max_offset = if (flags2 & 2) == 0 { usize::MAX } else { 32 };
|
||||||
|
config.max_length = if (flags2 & 4) == 0 { usize::MAX } else { 5 };
|
||||||
|
level = (flags2 >> 3) & 3;
|
||||||
|
}
|
||||||
|
let packed = upkr::pack(data, level, &config, None);
|
||||||
|
let unpacked = upkr::unpack(&packed, &config, 1024 * 1024).unwrap();
|
||||||
|
assert!(unpacked == data);
|
||||||
|
});
|
||||||
6
fuzz/fuzz_targets/unpack.rs
Normal file
6
fuzz/fuzz_targets/unpack.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
let _ = upkr::unpack(data, &upkr::Config::default(), 64 * 1024);
|
||||||
|
});
|
||||||
@@ -19,15 +19,16 @@ pub fn pack(
|
|||||||
}
|
}
|
||||||
let mut encoded_match = false;
|
let mut encoded_match = false;
|
||||||
if let Some(m) = match_finder.matches(pos).next() {
|
if let Some(m) = match_finder.matches(pos).next() {
|
||||||
let max_offset = 1 << (m.length * 3 - 1).min(31);
|
let max_offset = config.max_offset.min(1 << (m.length * 3 - 1).min(31));
|
||||||
let offset = pos - m.pos;
|
let offset = pos - m.pos;
|
||||||
if offset < max_offset && m.length >= config.min_length() {
|
if offset < max_offset && m.length >= config.min_length() {
|
||||||
|
let length = m.length.min(config.max_length);
|
||||||
lz::Op::Match {
|
lz::Op::Match {
|
||||||
offset: offset as u32,
|
offset: offset as u32,
|
||||||
len: m.length as u32,
|
len: length as u32,
|
||||||
}
|
}
|
||||||
.encode(&mut rans_coder, &mut state, config);
|
.encode(&mut rans_coder, &mut state, config);
|
||||||
pos += m.length;
|
pos += length;
|
||||||
encoded_match = true;
|
encoded_match = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,8 @@ pub fn pack(
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(data[(pos - offset)..].iter())
|
.zip(data[(pos - offset)..].iter())
|
||||||
.take_while(|(a, b)| a == b)
|
.take_while(|(a, b)| a == b)
|
||||||
.count();
|
.count()
|
||||||
|
.min(config.max_length);
|
||||||
if length >= config.min_length() {
|
if length >= config.min_length() {
|
||||||
lz::Op::Match {
|
lz::Op::Match {
|
||||||
offset: offset as u32,
|
offset: offset as u32,
|
||||||
|
|||||||
15
src/lib.rs
15
src/lib.rs
@@ -5,10 +5,11 @@ mod match_finder;
|
|||||||
mod parsing_packer;
|
mod parsing_packer;
|
||||||
mod rans;
|
mod rans;
|
||||||
|
|
||||||
pub use lz::{calculate_margin, unpack};
|
pub use lz::{calculate_margin, unpack, UnpackError};
|
||||||
|
|
||||||
pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize);
|
pub type ProgressCallback<'a> = &'a mut dyn FnMut(usize);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub use_bitstream: bool,
|
pub use_bitstream: bool,
|
||||||
pub parity_contexts: usize,
|
pub parity_contexts: usize,
|
||||||
@@ -23,6 +24,9 @@ pub struct Config {
|
|||||||
|
|
||||||
pub no_repeated_offsets: bool,
|
pub no_repeated_offsets: bool,
|
||||||
pub eof_in_length: bool,
|
pub eof_in_length: bool,
|
||||||
|
|
||||||
|
pub max_offset: usize,
|
||||||
|
pub max_length: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -41,6 +45,9 @@ impl Default for Config {
|
|||||||
|
|
||||||
no_repeated_offsets: false,
|
no_repeated_offsets: false,
|
||||||
eof_in_length: false,
|
eof_in_length: false,
|
||||||
|
|
||||||
|
max_offset: usize::MAX,
|
||||||
|
max_length: usize::MAX,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,13 +65,13 @@ impl Config {
|
|||||||
pub fn pack(
|
pub fn pack(
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
level: u8,
|
level: u8,
|
||||||
config: Config,
|
config: &Config,
|
||||||
progress_callback: Option<ProgressCallback>,
|
progress_callback: Option<ProgressCallback>,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
greedy_packer::pack(data, &config, progress_callback)
|
greedy_packer::pack(data, config, progress_callback)
|
||||||
} else {
|
} else {
|
||||||
parsing_packer::pack(data, level, &config, progress_callback)
|
parsing_packer::pack(data, level, config, progress_callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
89
src/lz.rs
89
src/lz.rs
@@ -1,6 +1,7 @@
|
|||||||
use crate::context_state::ContextState;
|
use crate::context_state::ContextState;
|
||||||
use crate::rans::{EntropyCoder, RansDecoder};
|
use crate::rans::{EntropyCoder, RansDecoder};
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
@@ -25,17 +26,18 @@ impl Op {
|
|||||||
}
|
}
|
||||||
&Op::Match { offset, len } => {
|
&Op::Match { offset, len } => {
|
||||||
encode_bit(coder, state, literal_base, config.is_match_bit);
|
encode_bit(coder, state, literal_base, config.is_match_bit);
|
||||||
|
let mut new_offset = true;
|
||||||
if !state.prev_was_match && !config.no_repeated_offsets {
|
if !state.prev_was_match && !config.no_repeated_offsets {
|
||||||
|
new_offset = offset != state.last_offset;
|
||||||
encode_bit(
|
encode_bit(
|
||||||
coder,
|
coder,
|
||||||
state,
|
state,
|
||||||
256 * state.parity_contexts,
|
256 * state.parity_contexts,
|
||||||
(offset != state.last_offset) == config.new_offset_bit,
|
new_offset == config.new_offset_bit,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
assert!(offset != state.last_offset || config.no_repeated_offsets);
|
|
||||||
}
|
}
|
||||||
if offset != state.last_offset || config.no_repeated_offsets {
|
assert!(offset as usize <= config.max_offset);
|
||||||
|
if new_offset {
|
||||||
encode_length(
|
encode_length(
|
||||||
coder,
|
coder,
|
||||||
state,
|
state,
|
||||||
@@ -45,7 +47,7 @@ impl Op {
|
|||||||
);
|
);
|
||||||
state.last_offset = offset;
|
state.last_offset = offset;
|
||||||
}
|
}
|
||||||
assert!(!config.eof_in_length || len > 1);
|
assert!(len as usize >= config.min_length() && len as usize <= config.max_length);
|
||||||
encode_length(coder, state, 256 * state.parity_contexts + 65, len, config);
|
encode_length(coder, state, 256 * state.parity_contexts + 65, len, config);
|
||||||
state.prev_was_match = true;
|
state.prev_was_match = true;
|
||||||
state.pos += len as usize;
|
state.pos += len as usize;
|
||||||
@@ -69,7 +71,7 @@ pub fn encode_eof(coder: &mut dyn EntropyCoder, state: &mut CoderState, config:
|
|||||||
config.new_offset_bit ^ config.eof_in_length,
|
config.new_offset_bit ^ config.eof_in_length,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !config.eof_in_length || config.no_repeated_offsets {
|
if !config.eof_in_length || state.prev_was_match || config.no_repeated_offsets {
|
||||||
encode_length(coder, state, 256 * state.parity_contexts + 1, 1, config);
|
encode_length(coder, state, 256 * state.parity_contexts + 1, 1, config);
|
||||||
}
|
}
|
||||||
if config.eof_in_length {
|
if config.eof_in_length {
|
||||||
@@ -130,24 +132,44 @@ impl CoderState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack(packed_data: &[u8], config: &Config) -> Vec<u8> {
|
#[derive(Error, Debug)]
|
||||||
let mut result = vec![];
|
pub enum UnpackError {
|
||||||
let _ = unpack_internal(Some(&mut result), packed_data, config);
|
#[error("match offset out of range: {offset} > {position}")]
|
||||||
result
|
OffsetOutOfRange { offset: usize, position: usize },
|
||||||
|
#[error("Unpacked data over size limit: {size} > {limit}")]
|
||||||
|
OverSize { size: usize, limit: usize },
|
||||||
|
#[error("Unexpected end of input data")]
|
||||||
|
UnexpectedEOF {
|
||||||
|
#[from]
|
||||||
|
source: crate::rans::UnexpectedEOF,
|
||||||
|
},
|
||||||
|
#[error("Overflow while reading value")]
|
||||||
|
ValueOverflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_margin(packed_data: &[u8], config: &Config) -> isize {
|
pub fn unpack(
|
||||||
unpack_internal(None, packed_data, config)
|
packed_data: &[u8],
|
||||||
|
config: &Config,
|
||||||
|
max_size: usize,
|
||||||
|
) -> Result<Vec<u8>, UnpackError> {
|
||||||
|
let mut result = vec![];
|
||||||
|
let _ = unpack_internal(Some(&mut result), packed_data, config, max_size)?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_margin(packed_data: &[u8], config: &Config) -> Result<isize, UnpackError> {
|
||||||
|
unpack_internal(None, packed_data, config, usize::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack_internal(
|
pub fn unpack_internal(
|
||||||
mut result: Option<&mut Vec<u8>>,
|
mut result: Option<&mut Vec<u8>>,
|
||||||
packed_data: &[u8],
|
packed_data: &[u8],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> isize {
|
max_size: usize,
|
||||||
|
) -> Result<isize, UnpackError> {
|
||||||
let mut decoder = RansDecoder::new(packed_data, &config);
|
let mut decoder = RansDecoder::new(packed_data, &config);
|
||||||
let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64, &config);
|
let mut contexts = ContextState::new((1 + 255) * config.parity_contexts + 1 + 64 + 64, &config);
|
||||||
let mut offset = 0;
|
let mut offset = usize::MAX;
|
||||||
let mut position = 0usize;
|
let mut position = 0usize;
|
||||||
let mut prev_was_match = false;
|
let mut prev_was_match = false;
|
||||||
let mut margin = 0isize;
|
let mut margin = 0isize;
|
||||||
@@ -157,31 +179,34 @@ pub fn unpack_internal(
|
|||||||
contexts: &mut ContextState,
|
contexts: &mut ContextState,
|
||||||
mut context_index: usize,
|
mut context_index: usize,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> usize {
|
) -> Result<usize, UnpackError> {
|
||||||
let mut length = 0;
|
let mut length = 0;
|
||||||
let mut bit_pos = 0;
|
let mut bit_pos = 0;
|
||||||
while decoder.decode_with_context(&mut contexts.context_mut(context_index))
|
while decoder.decode_with_context(&mut contexts.context_mut(context_index))?
|
||||||
== config.continue_value_bit
|
== config.continue_value_bit
|
||||||
{
|
{
|
||||||
length |= (decoder.decode_with_context(&mut contexts.context_mut(context_index + 1))
|
length |= (decoder.decode_with_context(&mut contexts.context_mut(context_index + 1))?
|
||||||
as usize)
|
as usize)
|
||||||
<< bit_pos;
|
<< bit_pos;
|
||||||
bit_pos += 1;
|
bit_pos += 1;
|
||||||
|
if bit_pos >= 32 {
|
||||||
|
return Err(UnpackError::ValueOverflow);
|
||||||
|
}
|
||||||
context_index += 2;
|
context_index += 2;
|
||||||
}
|
}
|
||||||
length | (1 << bit_pos)
|
Ok(length | (1 << bit_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
margin = margin.max(position as isize - decoder.pos() as isize);
|
margin = margin.max(position as isize - decoder.pos() as isize);
|
||||||
let literal_base = position % config.parity_contexts * 256;
|
let literal_base = position % config.parity_contexts * 256;
|
||||||
if decoder.decode_with_context(&mut contexts.context_mut(literal_base))
|
if decoder.decode_with_context(&mut contexts.context_mut(literal_base))?
|
||||||
== config.is_match_bit
|
== config.is_match_bit
|
||||||
{
|
{
|
||||||
if config.no_repeated_offsets
|
if config.no_repeated_offsets
|
||||||
|| prev_was_match
|
|| prev_was_match
|
||||||
|| decoder
|
|| decoder
|
||||||
.decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts))
|
.decode_with_context(&mut contexts.context_mut(256 * config.parity_contexts))?
|
||||||
== config.new_offset_bit
|
== config.new_offset_bit
|
||||||
{
|
{
|
||||||
offset = decode_length(
|
offset = decode_length(
|
||||||
@@ -189,7 +214,7 @@ pub fn unpack_internal(
|
|||||||
&mut contexts,
|
&mut contexts,
|
||||||
256 * config.parity_contexts + 1,
|
256 * config.parity_contexts + 1,
|
||||||
&config,
|
&config,
|
||||||
) - if config.eof_in_length { 0 } else { 1 };
|
)? - if config.eof_in_length { 0 } else { 1 };
|
||||||
if offset == 0 {
|
if offset == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -199,13 +224,20 @@ pub fn unpack_internal(
|
|||||||
&mut contexts,
|
&mut contexts,
|
||||||
256 * config.parity_contexts + 65,
|
256 * config.parity_contexts + 65,
|
||||||
&config,
|
&config,
|
||||||
);
|
)?;
|
||||||
if config.eof_in_length && length == 1 {
|
if config.eof_in_length && length == 1 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if offset > position {
|
||||||
|
return Err(UnpackError::OffsetOutOfRange { offset, position });
|
||||||
|
}
|
||||||
if let Some(ref mut result) = result {
|
if let Some(ref mut result) = result {
|
||||||
for _ in 0..length {
|
for _ in 0..length {
|
||||||
|
if result.len() < max_size {
|
||||||
result.push(result[result.len() - offset]);
|
result.push(result[result.len() - offset]);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
position += length;
|
position += length;
|
||||||
@@ -215,17 +247,26 @@ pub fn unpack_internal(
|
|||||||
let mut byte = 0;
|
let mut byte = 0;
|
||||||
for i in (0..8).rev() {
|
for i in (0..8).rev() {
|
||||||
let bit = decoder
|
let bit = decoder
|
||||||
.decode_with_context(&mut contexts.context_mut(literal_base + context_index));
|
.decode_with_context(&mut contexts.context_mut(literal_base + context_index))?;
|
||||||
context_index = (context_index << 1) | bit as usize;
|
context_index = (context_index << 1) | bit as usize;
|
||||||
byte |= (bit as u8) << i;
|
byte |= (bit as u8) << i;
|
||||||
}
|
}
|
||||||
if let Some(ref mut result) = result {
|
if let Some(ref mut result) = result {
|
||||||
|
if result.len() < max_size {
|
||||||
result.push(byte);
|
result.push(byte);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
position += 1;
|
position += 1;
|
||||||
prev_was_match = false;
|
prev_was_match = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
margin + decoder.pos() as isize - position as isize
|
if position > max_size {
|
||||||
|
return Err(UnpackError::OverSize {
|
||||||
|
size: position,
|
||||||
|
limit: max_size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(margin + decoder.pos() as isize - position as isize)
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/main.rs
23
src/main.rs
@@ -12,6 +12,7 @@ fn main() -> Result<()> {
|
|||||||
let mut level = 2;
|
let mut level = 2;
|
||||||
let mut infile: Option<PathBuf> = None;
|
let mut infile: Option<PathBuf> = None;
|
||||||
let mut outfile: Option<PathBuf> = None;
|
let mut outfile: Option<PathBuf> = None;
|
||||||
|
let mut max_unpacked_size = 512 * 1024 * 1024;
|
||||||
|
|
||||||
let mut parser = lexopt::Parser::from_env();
|
let mut parser = lexopt::Parser::from_env();
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
@@ -32,6 +33,9 @@ fn main() -> Result<()> {
|
|||||||
Long("no-repeated-offsets") => config.no_repeated_offsets = true,
|
Long("no-repeated-offsets") => config.no_repeated_offsets = true,
|
||||||
Long("eof-in-length") => config.eof_in_length = true,
|
Long("eof-in-length") => config.eof_in_length = true,
|
||||||
|
|
||||||
|
Long("max-offset") => config.max_offset = parser.value()?.parse()?,
|
||||||
|
Long("max-length") => config.max_length = parser.value()?.parse()?,
|
||||||
|
|
||||||
Long("z80") => {
|
Long("z80") => {
|
||||||
config.use_bitstream = true;
|
config.use_bitstream = true;
|
||||||
config.bitstream_is_big_endian = true;
|
config.bitstream_is_big_endian = true;
|
||||||
@@ -43,12 +47,15 @@ fn main() -> Result<()> {
|
|||||||
config.use_bitstream = true;
|
config.use_bitstream = true;
|
||||||
config.continue_value_bit = false;
|
config.continue_value_bit = false;
|
||||||
config.is_match_bit = false;
|
config.is_match_bit = false;
|
||||||
|
config.new_offset_bit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Short('u') | Long("unpack") => unpack = true,
|
Short('u') | Long("unpack") => unpack = true,
|
||||||
Long("margin") => calculate_margin = true,
|
Long("margin") => calculate_margin = true,
|
||||||
Short('l') | Long("level") => level = parser.value()?.parse()?,
|
Short('l') | Long("level") => level = parser.value()?.parse()?,
|
||||||
|
Short(n) if n.is_ascii_digit() => level = n as u8 - b'0',
|
||||||
Short('h') | Long("help") => print_help(0),
|
Short('h') | Long("help") => print_help(0),
|
||||||
|
Long("max-unpacked-size") => max_unpacked_size = parser.value()?.parse()?,
|
||||||
Value(val) if infile.is_none() => infile = Some(val.try_into()?),
|
Value(val) if infile.is_none() => infile = Some(val.try_into()?),
|
||||||
Value(val) if outfile.is_none() => outfile = Some(val.try_into()?),
|
Value(val) if outfile.is_none() => outfile = Some(val.try_into()?),
|
||||||
_ => return Err(arg.unexpected().into()),
|
_ => return Err(arg.unexpected().into()),
|
||||||
@@ -92,7 +99,7 @@ fn main() -> Result<()> {
|
|||||||
let mut packed_data = upkr::pack(
|
let mut packed_data = upkr::pack(
|
||||||
&data,
|
&data,
|
||||||
level,
|
level,
|
||||||
config,
|
&config,
|
||||||
Some(&mut |pos| {
|
Some(&mut |pos| {
|
||||||
pb.set(pos as u64);
|
pb.set(pos as u64);
|
||||||
}),
|
}),
|
||||||
@@ -117,14 +124,14 @@ fn main() -> Result<()> {
|
|||||||
data.reverse();
|
data.reverse();
|
||||||
}
|
}
|
||||||
if unpack {
|
if unpack {
|
||||||
let mut unpacked_data = upkr::unpack(&data, &config);
|
let mut unpacked_data = upkr::unpack(&data, &config, max_unpacked_size)?;
|
||||||
if reverse {
|
if reverse {
|
||||||
unpacked_data.reverse();
|
unpacked_data.reverse();
|
||||||
}
|
}
|
||||||
File::create(outfile)?.write_all(&unpacked_data)?;
|
File::create(outfile)?.write_all(&unpacked_data)?;
|
||||||
}
|
}
|
||||||
if calculate_margin {
|
if calculate_margin {
|
||||||
println!("{}", upkr::calculate_margin(&data, &config));
|
println!("{}", upkr::calculate_margin(&data, &config)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +145,15 @@ fn print_help(exit_code: i32) -> ! {
|
|||||||
eprintln!(" upkr --margin [config options] <infile>");
|
eprintln!(" upkr --margin [config options] <infile>");
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!(" -l, --level N compression level 0-9");
|
eprintln!(" -l, --level N compression level 0-9");
|
||||||
|
eprintln!(" -0, ..., -9 short form for setting compression level");
|
||||||
eprintln!(" -u, --unpack unpack infile");
|
eprintln!(" -u, --unpack unpack infile");
|
||||||
eprintln!(" --margin calculate margin for overlapped unpacking of a packed file");
|
eprintln!(" --margin calculate margin for overlapped unpacking of a packed file");
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!("Config presets for specific unpackers:");
|
eprintln!("Config presets for specific unpackers:");
|
||||||
eprintln!(" --z80 --big-endian-bitstream --invert-bit-encoding --simplified-prob-update");
|
eprintln!(" --z80 --big-endian-bitstream --invert-bit-encoding --simplified-prob-update -9");
|
||||||
eprintln!(" --x86 --bitstream --invert-is-match-bit --invert-continue-value-bit");
|
eprintln!(
|
||||||
|
" --x86 --bitstream --invert-is-match-bit --invert-continue-value-bit --invert-new-offset-bit"
|
||||||
|
);
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!("Config options (need to match when packing/unpacking):");
|
eprintln!("Config options (need to match when packing/unpacking):");
|
||||||
eprintln!(" -b, --bitstream bitstream mode");
|
eprintln!(" -b, --bitstream bitstream mode");
|
||||||
@@ -158,5 +168,8 @@ fn print_help(exit_code: i32) -> ! {
|
|||||||
eprintln!(" --simplified-prob-update");
|
eprintln!(" --simplified-prob-update");
|
||||||
eprintln!(" --big-endian-bitstream (implies --bitstream)");
|
eprintln!(" --big-endian-bitstream (implies --bitstream)");
|
||||||
eprintln!(" --no-repeated-offsets");
|
eprintln!(" --no-repeated-offsets");
|
||||||
|
eprintln!(" --eof-in-length");
|
||||||
|
eprintln!(" --max-offset N");
|
||||||
|
eprintln!(" --max-length N");
|
||||||
process::exit(exit_code);
|
process::exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ fn parse(
|
|||||||
cost_counter: &mut CostCounter,
|
cost_counter: &mut CostCounter,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
length: usize,
|
mut length: usize,
|
||||||
arrival: &Arrival,
|
arrival: &Arrival,
|
||||||
max_arrivals: usize,
|
max_arrivals: usize,
|
||||||
config: &crate::Config,
|
config: &crate::Config,
|
||||||
@@ -113,6 +113,7 @@ fn parse(
|
|||||||
if length < config.min_length() {
|
if length < config.min_length() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
length = length.min(config.max_length);
|
||||||
cost_counter.reset();
|
cost_counter.reset();
|
||||||
let mut state = arrival.state.clone();
|
let mut state = arrival.state.clone();
|
||||||
let op = lz::Op::Match {
|
let op = lz::Op::Match {
|
||||||
@@ -186,6 +187,7 @@ fn parse(
|
|||||||
for m in match_finder.matches(pos) {
|
for m in match_finder.matches(pos) {
|
||||||
closest_match = Some(closest_match.unwrap_or(0).max(m.pos));
|
closest_match = Some(closest_match.unwrap_or(0).max(m.pos));
|
||||||
let offset = pos - m.pos;
|
let offset = pos - m.pos;
|
||||||
|
if offset <= encoding_config.max_offset {
|
||||||
found_last_offset |= offset as u32 == arrival.state.last_offset();
|
found_last_offset |= offset as u32 == arrival.state.last_offset();
|
||||||
add_match(
|
add_match(
|
||||||
&mut arrivals,
|
&mut arrivals,
|
||||||
@@ -201,6 +203,7 @@ fn parse(
|
|||||||
break 'arrival_loop;
|
break 'arrival_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut near_matches_left = config.num_near_matches;
|
let mut near_matches_left = config.num_near_matches;
|
||||||
let mut match_pos = last_seen[data[pos] as usize];
|
let mut match_pos = last_seen[data[pos] as usize];
|
||||||
@@ -209,6 +212,9 @@ fn parse(
|
|||||||
&& closest_match.iter().all(|p| *p < match_pos)
|
&& closest_match.iter().all(|p| *p < match_pos)
|
||||||
{
|
{
|
||||||
let offset = pos - match_pos;
|
let offset = pos - match_pos;
|
||||||
|
if offset > encoding_config.max_offset {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let length = match_length(offset);
|
let length = match_length(offset);
|
||||||
assert!(length > 0);
|
assert!(length > 0);
|
||||||
add_match(
|
add_match(
|
||||||
|
|||||||
25
src/rans.rs
25
src/rans.rs
@@ -1,4 +1,5 @@
|
|||||||
use crate::{context_state::Context, Config};
|
use crate::{context_state::Context, Config};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub const PROB_BITS: u32 = 8;
|
pub const PROB_BITS: u32 = 8;
|
||||||
pub const ONE_PROB: u32 = 1 << PROB_BITS;
|
pub const ONE_PROB: u32 = 1 << PROB_BITS;
|
||||||
@@ -160,6 +161,10 @@ pub struct RansDecoder<'a> {
|
|||||||
|
|
||||||
const PROB_MASK: u32 = ONE_PROB - 1;
|
const PROB_MASK: u32 = ONE_PROB - 1;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("Unexpected end of input")]
|
||||||
|
pub struct UnexpectedEOF;
|
||||||
|
|
||||||
impl<'a> RansDecoder<'a> {
|
impl<'a> RansDecoder<'a> {
|
||||||
pub fn new(data: &'a [u8], config: &Config) -> RansDecoder<'a> {
|
pub fn new(data: &'a [u8], config: &Config) -> RansDecoder<'a> {
|
||||||
RansDecoder {
|
RansDecoder {
|
||||||
@@ -178,17 +183,20 @@ impl<'a> RansDecoder<'a> {
|
|||||||
self.pos
|
self.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_with_context(&mut self, context: &mut Context) -> bool {
|
pub fn decode_with_context(&mut self, context: &mut Context) -> Result<bool, UnexpectedEOF> {
|
||||||
let bit = self.decode_bit(context.prob());
|
let bit = self.decode_bit(context.prob())?;
|
||||||
context.update(bit);
|
context.update(bit);
|
||||||
bit
|
Ok(bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_bit(&mut self, prob: u16) -> bool {
|
pub fn decode_bit(&mut self, prob: u16) -> Result<bool, UnexpectedEOF> {
|
||||||
let prob = prob as u32;
|
let prob = prob as u32;
|
||||||
if self.use_bitstream {
|
if self.use_bitstream {
|
||||||
while self.state < 32768 {
|
while self.state < 32768 {
|
||||||
if self.bits_left == 0 {
|
if self.bits_left == 0 {
|
||||||
|
if self.pos >= self.data.len() {
|
||||||
|
return Err(UnexpectedEOF);
|
||||||
|
}
|
||||||
self.byte = self.data[self.pos];
|
self.byte = self.data[self.pos];
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
self.bits_left = 8;
|
self.bits_left = 8;
|
||||||
@@ -204,8 +212,11 @@ impl<'a> RansDecoder<'a> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while self.state < 4096 {
|
while self.state < 4096 {
|
||||||
self.state = (self.state << 8) | self.data[0] as u32;
|
if self.pos >= self.data.len() {
|
||||||
self.data = &self.data[1..];
|
return Err(UnexpectedEOF);
|
||||||
|
}
|
||||||
|
self.state = (self.state << 8) | self.data[self.pos] as u32;
|
||||||
|
self.pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +229,6 @@ impl<'a> RansDecoder<'a> {
|
|||||||
};
|
};
|
||||||
self.state = prob * (self.state >> PROB_BITS) + (self.state & PROB_MASK) - start;
|
self.state = prob * (self.state >> PROB_BITS) + (self.state & PROB_MASK) - start;
|
||||||
|
|
||||||
bit ^ self.invert_bit_encoding
|
Ok(bit ^ self.invert_bit_encoding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user