libbz2-rs-sys-0.1.3/.cargo_vcs_info.json 0000644 00000000153 00000000001 0013456 0 ustar {
"git": {
"sha1": "c6b9b3d23d27d7ab64ba81a0acc1f2592401bbfc"
},
"path_in_vcs": "libbz2-rs-sys"
} libbz2-rs-sys-0.1.3/Cargo.lock 0000644 00000000567 00000000001 0011442 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "libbz2-rs-sys"
version = "0.1.3"
dependencies = [
"libc",
]
[[package]]
name = "libc"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
libbz2-rs-sys-0.1.3/Cargo.toml 0000644 00000002360 00000000001 0011456 0 ustar # 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 = "2021"
rust-version = "1.82"
name = "libbz2-rs-sys"
version = "0.1.3"
build = false
publish = true
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "a drop-in compatible rust bzip2 implementation"
homepage = "https://github.com/trifectatechfoundation/libbzip2-rs"
readme = "README.md"
license = "bzip2-1.0.6"
repository = "https://github.com/trifectatechfoundation/libbzip2-rs"
[lib]
name = "libbz2_rs_sys"
path = "src/lib.rs"
[dependencies.libc]
version = "0.2"
optional = true
[features]
__internal-fuzz-disable-checksum = []
c-allocator = ["dep:libc"]
custom-prefix = []
default = [
"std",
"stdio",
]
rust-allocator = []
semver-prefix = []
std = ["rust-allocator"]
stdio = ["dep:libc"]
testing-prefix = []
libbz2-rs-sys-0.1.3/Cargo.toml.orig 0000644 0000000 0000000 00000001722 10461020230 0015140 0 ustar 0000000 0000000 [package]
name = "libbz2-rs-sys"
readme = "../README.md"
description.workspace = true
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
publish.workspace = true
rust-version.workspace = true
[features]
default = ["std", "stdio"]
c-allocator = ["dep:libc"] # use a malloc-based C allocator (rust is picked over c if both are configured)
rust-allocator = [] # use the rust global allocator (rust is picked over c if both are configured)
std = ["rust-allocator"]
custom-prefix = [] # use the LIBBZ2_RS_SYS_PREFIX to prefix all exported symbols
testing-prefix = [] # prefix all symbols with LIBBZ2_RS_SYS_TEST_ for testing
semver-prefix = [] # prefix all symbols in a semver-compatible way
stdio = ["dep:libc"] # corresponds to BZ_NO_STDIO; only the low-level api is available when this flag is disabled
__internal-fuzz-disable-checksum = []
[dependencies]
libc = { version = "0.2", optional = true }
libbz2-rs-sys-0.1.3/LICENSE 0000644 0000000 0000000 00000003547 10461020230 0013265 0 ustar 0000000 0000000
--------------------------------------------------------------------------
This program, "bzip2", the associated library "libbzip2", and all
documentation, are copyright (C) 1996-2010 Julian R Seward. 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. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
3. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
4. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
Julian Seward, jseward@acm.org
bzip2/libbzip2 version 1.1.0 of 6 September 2010
--------------------------------------------------------------------------
libbz2-rs-sys-0.1.3/README.md 0000644 0000000 0000000 00000004210 10461020230 0013523 0 ustar 0000000 0000000 
[](https://codecov.io/gh/trifectatechfoundation/libbzip2-rs)
[](https://crates.io/crates/libbzip2-rs-sys)
# libbzip2-rs: a safer libbzip
This repository contains a Rust implementation of the bzip2 file format that is compatible with the libbzip2 API.
This repository contains the following public crate:
* [libbz2-rs-sys](https://crates.io/crates/libbz2-rs-sys/), a libbzip2-compatible C API.
## How to use libbzip2-rs in your project
libbzip2-rs can be used in both Rust and C projects.
### Rust projects
By far the easiest way to use libbzip2-rs is through the [bzip2](https://crates.io/crates/bzip2) crate, by simply enabling the `libbz2-rs-sys` feature gate. This will enable the `libbz2-rs-sys` backend.
You can also directly use the C api exported by the `libbz2-rs-sys` crate.
## C projects
libbzip2-rs can be built as a shared object file for usage by C programs that dynamically link to libbzip2. Please see the example in [libbz2-rs-sys-cdylib](https://github.com/trifectatechfoundation/libbzip2-rs/tree/main/libbz2-rs-sys-cdylib).
## Acknowledgment
This project is based on a [c2rust](https://github.com/immunant/c2rust) translation of the original [libbzip2](https://sourceware.org/bzip2/).
## About
libbzip2-rs is part of Trifecta Tech Foundation's [Data compression initiative](https://trifectatech.org/initiatives/data-compression/).
## Funding
This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/ZipLinting).
[
](https://nlnet.nl)
[
](https://nlnet.nl/core)
libbz2-rs-sys-0.1.3/src/allocator.rs 0000644 0000000 0000000 00000015227 10461020230 0015373 0 ustar 0000000 0000000 //! # allocator infrastructure
//!
//! The public interface allows setting a custom allocator, but we need to configure a default
//! allocator if the user did not configure one. We have two choices, configured by feature flags:
//!
//! - `"rust-allocator"` uses the rust global allocator
//! - `"c-allocator"` uses an allocator based on `malloc` and `free`
//!
//! When both configured, `"rust-allocator"` is preferred.
//!
//! The interface for the allocator is not a great fit for rust. In particular, rust always needs
//! the layout of an allocation to deallocate it, and C interfaces don't usually provide this
//! information. Luckily in the library we know in all cases how big the allocation was at the
//! point where we deallocate it.
#[cfg(feature = "rust-allocator")]
extern crate alloc;
use core::ffi::{c_int, c_void};
use crate::bzlib::{BzStream, StreamState};
type AllocFunc = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> *mut c_void;
type FreeFunc = unsafe extern "C" fn(*mut c_void, *mut c_void) -> ();
pub(crate) enum Allocator {
#[cfg(feature = "rust-allocator")]
Rust,
#[cfg(feature = "c-allocator")]
C,
Custom {
allocate: AllocFunc,
deallocate: FreeFunc,
opaque: *mut c_void,
},
}
impl Allocator {
#[allow(unreachable_code)]
pub(crate) const DEFAULT: Option = 'blk: {
#[cfg(feature = "rust-allocator")]
break 'blk Some(Self::Rust);
#[cfg(feature = "c-allocator")]
break 'blk Some(Self::C);
None
};
#[allow(unreachable_code)]
pub(crate) fn default_function_pointers() -> Option<(AllocFunc, FreeFunc)> {
#[cfg(feature = "rust-allocator")]
return Some(rust_allocator::ALLOCATOR);
#[cfg(feature = "c-allocator")]
return Some(c_allocator::ALLOCATOR);
None
}
/// # Safety
///
/// - `strm.bzalloc` and `strm.opaque` must form a valid allocator, meaning `strm.bzalloc` returns either
/// * a `NULL` pointer
/// * a valid pointer to an allocation of `len * size_of::()` bytes aligned to at least `align_of::()`
/// - `strm.bzfree` frees memory allocated by `strm.bzalloc`
pub(crate) unsafe fn from_bz_stream(strm: &BzStream) -> Option {
let bzalloc = strm.bzalloc?;
let bzfree = strm.bzfree?;
#[cfg(feature = "rust-allocator")]
if (bzalloc, bzfree) == rust_allocator::ALLOCATOR {
return Some(Self::Rust);
}
#[cfg(feature = "c-allocator")]
if (bzalloc, bzfree) == c_allocator::ALLOCATOR {
return Some(Self::C);
}
Some(Self::custom(bzalloc, bzfree, strm.opaque))
}
/// # Safety
///
/// - `allocate` and `opaque` must form a valid allocator, meaning `allocate` returns either
/// * a `NULL` pointer
/// * a valid pointer to an allocation of `len * size_of::()` bytes aligned to at least `align_of::()`
/// - `deallocate` frees memory allocated by `allocate`
pub(crate) fn custom(allocate: AllocFunc, deallocate: FreeFunc, opaque: *mut c_void) -> Self {
Self::Custom {
allocate,
deallocate,
opaque,
}
}
}
#[cfg(feature = "c-allocator")]
pub(crate) mod c_allocator {
use super::*;
// make sure that the only way these function pointers leave this module is via this constant
// that way the function pointer address is a reliable way to know that the default C allocator
// is used.
pub(crate) static ALLOCATOR: (AllocFunc, FreeFunc) = (self::allocate, self::deallocate);
unsafe extern "C" fn allocate(_opaque: *mut c_void, count: c_int, size: c_int) -> *mut c_void {
unsafe { libc::malloc((count * size) as usize) }
}
unsafe extern "C" fn deallocate(_opaque: *mut c_void, ptr: *mut c_void) {
if !ptr.is_null() {
unsafe {
libc::free(ptr);
}
}
}
}
#[cfg(feature = "rust-allocator")]
mod rust_allocator {
use super::*;
// make sure that the only way these function pointers leave this module is via this constant
// that way the function pointer address is a reliable way to know that the default C allocator
// is used.
pub(crate) static ALLOCATOR: (AllocFunc, FreeFunc) = (self::allocate, self::deallocate);
unsafe extern "C" fn allocate(
_opaque: *mut c_void,
_count: c_int,
_size: c_int,
) -> *mut c_void {
unreachable!("the default rust allocation function should never be called directly");
}
unsafe extern "C" fn deallocate(_opaque: *mut c_void, _ptr: *mut c_void) {
unreachable!("the default rust deallocation function should never be called directly");
}
}
impl Allocator {
/// Allocates `count` contiguous values of type `T`, and zeros out all elements.
pub(crate) fn allocate_zeroed(&self, count: usize) -> Option<*mut T> {
const {
assert!(size_of::() != 0);
}
assert_ne!(count, 0);
match self {
#[cfg(feature = "rust-allocator")]
Allocator::Rust => {
let layout = core::alloc::Layout::array::(count).unwrap();
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
(!ptr.is_null()).then_some(ptr.cast())
}
#[cfg(feature = "c-allocator")]
Allocator::C => {
let ptr = unsafe { libc::calloc(count, core::mem::size_of::()) };
(!ptr.is_null()).then_some(ptr.cast())
}
Allocator::Custom {
allocate, opaque, ..
} => unsafe {
let ptr = (allocate)(*opaque, count as i32, core::mem::size_of::() as i32);
let ptr = ptr.cast::();
if ptr.is_null() {
return None;
}
core::ptr::write_bytes(ptr, 0, count);
Some(ptr)
},
}
}
pub(crate) unsafe fn deallocate(&self, ptr: *mut T, count: usize) {
if ptr.is_null() || count == 0 {
return;
}
match self {
#[cfg(feature = "rust-allocator")]
Allocator::Rust => {
let layout = core::alloc::Layout::array::(count).unwrap();
unsafe { alloc::alloc::dealloc(ptr.cast(), layout) }
}
#[cfg(feature = "c-allocator")]
Allocator::C => {
unsafe { libc::free(ptr.cast()) };
}
Allocator::Custom {
deallocate, opaque, ..
} => {
unsafe { deallocate(*opaque, ptr.cast()) };
}
}
}
}
libbz2-rs-sys-0.1.3/src/blocksort.rs 0000644 0000000 0000000 00000072212 10461020230 0015412 0 ustar 0000000 0000000 #![forbid(unsafe_code)]
use core::cmp::Ordering;
use core::ffi::{c_int, c_uint};
use crate::{
assert_h,
bzlib::{Arr2, EState, BZ_N_OVERSHOOT, BZ_N_QSORT, BZ_N_RADIX, FTAB_LEN},
};
use crate::{debug_log, debug_logln};
/// Fallback O(N log(N)^2) sorting algorithm, for repetitive blocks
#[inline]
fn fallbackSimpleSort(fmap: &mut [u32], eclass: &[u32], lo: i32, hi: i32) {
let mut j: i32;
let mut tmp: i32;
let mut ec_tmp: u32;
if lo == hi {
return;
}
if hi - lo > 3 {
for i in (lo..=hi - 4).rev() {
tmp = fmap[i as usize] as i32;
ec_tmp = eclass[tmp as usize];
j = i + 4;
while j <= hi && ec_tmp > eclass[fmap[j as usize] as usize] {
fmap[(j - 4) as usize] = fmap[j as usize];
j += 4;
}
fmap[(j - 4) as usize] = tmp as u32;
}
}
for i in (lo..=hi - 1).rev() {
tmp = fmap[i as usize] as i32;
ec_tmp = eclass[tmp as usize];
j = i + 1;
while j <= hi && ec_tmp > eclass[fmap[j as usize] as usize] {
fmap[(j - 1) as usize] = fmap[j as usize];
j += 1;
}
fmap[(j - 1) as usize] = tmp as u32;
}
}
const FALLBACK_QSORT_SMALL_THRESH: i32 = 10;
const FALLBACK_QSORT_STACK_SIZE: usize = 100;
fn fallbackQSort3(fmap: &mut [u32], eclass: &[u32], loSt: i32, hiSt: i32) {
let mut unLo: i32;
let mut unHi: i32;
let mut ltLo: i32;
let mut gtHi: i32;
let mut n: i32;
let mut m: i32;
let mut sp: usize;
let mut lo: i32;
let mut hi: i32;
let mut stackLo: [i32; FALLBACK_QSORT_STACK_SIZE] = [0; FALLBACK_QSORT_STACK_SIZE];
let mut stackHi: [i32; FALLBACK_QSORT_STACK_SIZE] = [0; FALLBACK_QSORT_STACK_SIZE];
macro_rules! fpush {
($lz:expr, $hz:expr) => {
stackLo[sp] = $lz;
stackHi[sp] = $hz;
sp += 1;
};
}
macro_rules! fvswap {
($zzp1:expr, $zzp2:expr, $zzn:expr) => {
let mut yyp1: i32 = $zzp1;
let mut yyp2: i32 = $zzp2;
let mut yyn: i32 = $zzn;
while (yyn > 0) {
fmap.swap(yyp1 as usize, yyp2 as usize);
yyp1 += 1;
yyp2 += 1;
yyn -= 1;
}
};
}
let mut r = 0u32;
sp = 0;
fpush!(loSt, hiSt);
while sp > 0 {
assert_h!(sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004);
// the `fpop` macro has one occurence, so it was inlined here
sp -= 1;
lo = stackLo[sp];
hi = stackHi[sp];
if hi - lo < FALLBACK_QSORT_SMALL_THRESH {
fallbackSimpleSort(fmap, eclass, lo, hi);
continue;
}
/* Random partitioning. Median of 3 sometimes fails to
avoid bad cases. Median of 9 seems to help but
looks rather expensive. This too seems to work but
is cheaper. Guidance for the magic constants
7621 and 32768 is taken from Sedgewick's algorithms
book, chapter 35.
*/
r = r.wrapping_mul(7621).wrapping_add(1).wrapping_rem(32768);
let index = match r.wrapping_rem(3) {
0 => fmap[lo as usize],
1 => fmap[((lo + hi) >> 1) as usize],
_ => fmap[hi as usize],
};
let med = eclass[index as usize];
ltLo = lo;
unLo = lo;
gtHi = hi;
unHi = hi;
loop {
while unLo <= unHi {
match eclass[fmap[unLo as usize] as usize].cmp(&med) {
Ordering::Greater => break,
Ordering::Equal => {
fmap.swap(unLo as usize, ltLo as usize);
ltLo += 1;
unLo += 1;
}
Ordering::Less => {
unLo += 1;
}
}
}
while unLo <= unHi {
match eclass[fmap[unHi as usize] as usize].cmp(&med) {
Ordering::Less => break,
Ordering::Equal => {
fmap.swap(unHi as usize, gtHi as usize);
gtHi -= 1;
unHi -= 1;
}
Ordering::Greater => {
unHi -= 1;
}
}
}
if unLo > unHi {
break;
}
fmap.swap(unLo as usize, unHi as usize);
unLo += 1;
unHi -= 1;
}
debug_assert_eq!(unHi, unLo - 1, "fallbackQSort3(2)");
if gtHi < ltLo {
continue;
}
n = Ord::min(ltLo - lo, unLo - ltLo);
fvswap!(lo, unLo - n, n);
m = Ord::min(hi - gtHi, gtHi - unHi);
fvswap!(unLo, hi - m + 1, m);
n = lo + unLo - ltLo - 1;
m = hi - (gtHi - unHi) + 1;
if n - lo > hi - m {
fpush!(lo, n);
fpush!(m, hi);
} else {
fpush!(m, hi);
fpush!(lo, n);
}
}
}
fn fallbackSort(
fmap: &mut [u32],
arr2: &mut Arr2,
bhtab: &mut [u32; FTAB_LEN],
nblock: i32,
verb: i32,
) {
macro_rules! SET_BH {
($zz:expr) => {
bhtab[$zz as usize >> 5] |= 1 << ($zz & 31);
};
}
macro_rules! CLEAR_BH {
($zz:expr) => {
bhtab[$zz as usize >> 5] &= !(1 << ($zz & 31));
};
}
macro_rules! ISSET_BH {
($zz:expr) => {
bhtab[$zz as usize >> 5] & 1u32 << ($zz & 31) != 0
};
}
macro_rules! UNALIGNED_BH {
($zz:expr) => {
($zz & 0x01f) != 0
};
}
macro_rules! WORD_BH {
($zz:expr) => {
bhtab[$zz as usize >> 5]
};
}
let mut ftab: [i32; 257] = [0; 257];
let mut ftabCopy: [i32; 256] = [0; 256];
let mut H: i32;
let mut i: i32;
let mut j: i32;
let mut k: i32;
let mut l: i32;
let mut r: i32;
let mut cc: i32;
let mut cc1: i32;
let mut nNotDone: i32;
/*--
Initial 1-char radix sort to generate
initial fmap and initial BH bits.
--*/
if verb >= 4 {
debug_logln!(" bucket sorting ...");
}
{
let eclass8 = arr2.block(nblock as usize);
for e in eclass8.iter() {
ftab[usize::from(*e)] += 1;
}
ftabCopy[0..256].copy_from_slice(&ftab[0..256]);
for i in 1..257 {
ftab[i] += ftab[i - 1];
}
for (i, e) in eclass8.iter().enumerate() {
let j = usize::from(*e);
k = ftab[j] - 1;
ftab[j] = k;
fmap[k as usize] = i as u32;
}
}
bhtab[0..2 + nblock as usize / 32].fill(0);
for i in 0..256 {
SET_BH!(ftab[i]);
}
/*--
Inductively refine the buckets. Kind-of an
"exponential radix sort" (!), inspired by the
Manber-Myers suffix array construction algorithm.
--*/
/*-- set sentinel bits for block-end detection --*/
for i in 0..32 {
SET_BH!(nblock + 2 * i);
CLEAR_BH!(nblock + 2 * i + 1);
}
/*-- the log(N) loop --*/
H = 1;
loop {
if verb >= 4 {
debug_log!(" depth {:>6} has ", H);
}
j = 0;
i = 0;
while i < nblock {
if ISSET_BH!(i) {
j = i;
}
k = fmap[i as usize].wrapping_sub(H as c_uint) as i32;
if k < 0 {
k += nblock;
}
arr2.eclass()[k as usize] = j as u32;
i += 1;
}
nNotDone = 0;
r = -1;
loop {
/*-- find the next non-singleton bucket --*/
k = r + 1;
while ISSET_BH!(k) && UNALIGNED_BH!(k) {
k += 1;
}
if ISSET_BH!(k) {
while WORD_BH!(k) == 0xffffffff {
k += 32;
}
while ISSET_BH!(k) {
k += 1;
}
}
l = k - 1;
if l >= nblock {
break;
}
while !ISSET_BH!(k) && UNALIGNED_BH!(k) {
k += 1;
}
if !ISSET_BH!(k) {
while WORD_BH!(k) == 0x00000000 {
k += 32;
}
while !ISSET_BH!(k) {
k += 1;
}
}
r = k - 1;
if r >= nblock {
break;
}
/*-- now [l, r] bracket current bucket --*/
if r > l {
nNotDone += r - l + 1;
fallbackQSort3(fmap, arr2.eclass(), l, r);
/*-- scan bucket and generate header bits-- */
cc = -1;
for i in l..=r {
cc1 = arr2.eclass()[fmap[i as usize] as usize] as i32;
if cc != cc1 {
SET_BH!(i);
cc = cc1;
}
}
}
}
if verb >= 4 {
debug_logln!("{:>6} unresolved strings", nNotDone);
}
H *= 2;
if H > nblock || nNotDone == 0 {
break;
}
}
if verb >= 4 {
debug_logln!(" reconstructing block ...");
}
{
let eclass8 = arr2.block(nblock as usize);
let mut j = 0;
for i in 0..nblock {
while ftabCopy[j] == 0 {
j += 1;
}
ftabCopy[j] -= 1;
eclass8[fmap[i as usize] as usize] = j as u8;
}
assert_h!(j < 256, 1005);
}
}
#[inline]
fn mainGtU(
mut i1: u32,
mut i2: u32,
block: &[u8],
quadrant: &[u16],
nblock: u32,
budget: &mut i32,
) -> bool {
debug_assert_ne!(i1, i2, "mainGtU");
let chunk1 = &block[i1 as usize..][..12];
let chunk2 = &block[i2 as usize..][..12];
for (c1, c2) in chunk1.chunks_exact(4).zip(chunk2.chunks_exact(4)) {
let c1 = u32::from_be_bytes(c1[..4].try_into().unwrap());
let c2 = u32::from_be_bytes(c2[..4].try_into().unwrap());
if c1 != c2 {
return c1 > c2;
}
}
i1 += 12;
i2 += 12;
for _ in 0..nblock.div_ceil(8) {
let b1 = &block[i1 as usize..][..8];
let b2 = &block[i2 as usize..][..8];
let q1 = &quadrant[i1 as usize..][..8];
let q2 = &quadrant[i2 as usize..][..8];
if b1 != b2 || q1 != q2 {
for (((c1, c2), s1), s2) in b1.iter().zip(b2).zip(q1).zip(q2) {
if c1 != c2 {
return c1 > c2;
}
if s1 != s2 {
return s1 > s2;
}
}
}
i1 += 8;
i2 += 8;
if i1 >= nblock {
i1 = i1.wrapping_sub(nblock);
}
if i2 >= nblock {
i2 = i2.wrapping_sub(nblock);
}
*budget -= 1;
}
false
}
static INCS: [i32; 14] = [
1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161, 2391484,
];
fn mainSimpleSort(
ptr: &mut [u32],
block: &[u8],
quadrant: &[u16],
nblock: i32,
lo: i32,
hi: i32,
d: i32,
budget: &mut i32,
) {
let bigN = hi - lo + 1;
let Some(index) = INCS.iter().position(|&e| e >= bigN) else {
return;
};
for &h in INCS[..index].iter().rev() {
for i in lo + h..=hi {
let v = ptr[i as usize];
let mut j = i;
while mainGtU(
(ptr[(j - h) as usize]).wrapping_add(d as u32),
v.wrapping_add(d as u32),
block,
quadrant,
nblock as u32,
budget,
) {
ptr[j as usize] = ptr[(j - h) as usize];
j -= h;
if j < lo + h {
break;
}
}
ptr[j as usize] = v;
if *budget < 0 {
return;
}
}
}
}
#[inline]
fn median_of_3(mut a: u8, mut b: u8, mut c: u8) -> u8 {
if a > b {
(a, b) = (b, a);
}
if a > c {
(_, c) = (c, a);
}
if b > c {
(b, _) = (c, b);
}
debug_assert!(a <= b && b <= c);
b
}
const MAIN_QSORT_SMALL_THRESH: i32 = 20;
const MAIN_QSORT_DEPTH_THRESH: i32 = BZ_N_RADIX + BZ_N_QSORT;
const MAIN_QSORT_STACK_SIZE: i32 = 100;
fn mainQSort3(
ptr: &mut [u32],
block: &[u8],
quadrant: &[u16],
nblock: i32,
loSt: i32,
hiSt: i32,
dSt: i32,
budget: &mut i32,
) {
let mut unLo: i32;
let mut unHi: i32;
let mut ltLo: i32;
let mut gtHi: i32;
let mut n: i32;
let mut m: i32;
let mut med: i32;
let mut stack = [(0i32, 0i32, 0i32); 100];
stack[0] = (loSt, hiSt, dSt);
let mut sp = 1;
while sp > 0 {
assert_h!(sp < MAIN_QSORT_STACK_SIZE as usize - 2, 1001);
sp -= 1;
let (lo, hi, d) = stack[sp];
if hi - lo < MAIN_QSORT_SMALL_THRESH || d > MAIN_QSORT_DEPTH_THRESH {
mainSimpleSort(ptr, block, quadrant, nblock, lo, hi, d, budget);
if *budget < 0 {
return;
}
} else {
med = median_of_3(
block[(ptr[lo as usize]).wrapping_add(d as c_uint) as usize],
block[(ptr[hi as usize]).wrapping_add(d as c_uint) as usize],
block[((ptr[((lo + hi) >> 1) as usize]).wrapping_add(d as c_uint) as isize)
as usize],
) as i32;
ltLo = lo;
unLo = ltLo;
gtHi = hi;
unHi = gtHi;
loop {
while unLo <= unHi {
n = block[(ptr[unLo as usize]).wrapping_add(d as c_uint) as usize] as i32 - med;
match n.cmp(&0) {
Ordering::Greater => break,
Ordering::Equal => {
ptr.swap(unLo as usize, ltLo as usize);
ltLo += 1;
unLo += 1;
}
Ordering::Less => unLo += 1,
}
}
while unLo <= unHi {
n = block[(ptr[unHi as usize]).wrapping_add(d as c_uint) as usize] as i32 - med;
match n.cmp(&0) {
Ordering::Less => break,
Ordering::Equal => {
ptr.swap(unHi as usize, gtHi as usize);
gtHi -= 1;
unHi -= 1;
}
Ordering::Greater => unHi -= 1,
}
}
if unLo > unHi {
break;
}
ptr.swap(unLo as usize, unHi as usize);
unLo += 1;
unHi -= 1;
}
if gtHi < ltLo {
stack[sp] = (lo, hi, d + 1);
sp += 1;
} else {
n = Ord::min(ltLo - lo, unLo - ltLo);
let mut yyp1: i32 = lo;
let mut yyp2: i32 = unLo - n;
for _ in 0..n {
ptr.swap(yyp1 as usize, yyp2 as usize);
yyp1 += 1;
yyp2 += 1;
}
m = Ord::min(hi - gtHi, gtHi - unHi);
let mut yyp1_0: i32 = unLo;
let mut yyp2_0: i32 = hi - m + 1;
for _ in 0..m {
ptr.swap(yyp1_0 as usize, yyp2_0 as usize);
yyp1_0 += 1;
yyp2_0 += 1;
}
n = lo + unLo - ltLo - 1;
m = hi - (gtHi - unHi) + 1;
let mut next = [(lo, n, d), (m, hi, d), (n + 1, m - 1, d + 1)];
if next[0].1 - next[0].0 < next[1].1 - next[1].0 {
next.swap(0, 1);
}
if next[1].1 - next[1].0 < next[2].1 - next[2].0 {
next.swap(1, 2);
}
if next[0].1 - next[0].0 < next[1].1 - next[1].0 {
next.swap(0, 1);
}
stack[sp..][..next.len()].copy_from_slice(&next);
sp += next.len();
}
}
}
}
fn mainSort(
ptr: &mut [u32],
block: &mut [u8],
quadrant: &mut [u16],
ftab: &mut [u32; FTAB_LEN],
nblock: i32,
verb: i32,
budget: &mut i32,
) {
let mut i: i32;
let mut j: i32;
let mut k: i32;
let mut ss: i32;
let mut sb: i32;
let mut runningOrder: [i32; 256] = [0; 256];
let mut bigDone: [bool; 256] = [false; 256];
let mut copyStart: [i32; 256] = [0; 256];
let mut copyEnd: [i32; 256] = [0; 256];
let mut c1: u8;
let mut numQSorted: i32;
let mut s: u16;
if verb >= 4 as c_int {
debug_logln!(" main sort initialise ...");
}
/*-- set up the 2-byte frequency table --*/
ftab.fill(0);
j = (block[0] as i32) << 8;
i = nblock - 1 as c_int;
while i >= 3 as c_int {
j = j >> 8 | (block[i as usize] as u16 as c_int) << 8;
ftab[j as usize] += 1;
j = j >> 8 | (block[(i - 1 as c_int) as usize] as u16 as c_int) << 8;
ftab[j as usize] += 1;
j = j >> 8 | (block[(i - 2 as c_int) as usize] as u16 as c_int) << 8;
ftab[j as usize] += 1;
j = j >> 8 | (block[(i - 3 as c_int) as usize] as u16 as c_int) << 8;
ftab[j as usize] += 1;
i -= 4;
}
while i >= 0 as c_int {
j = j >> 8 as c_int | (block[i as usize] as u16 as c_int) << 8 as c_int;
ftab[j as usize] += 1;
i -= 1;
}
for i in 0..BZ_N_OVERSHOOT {
block[nblock as usize + i] = block[i];
}
if verb >= 4 as c_int {
debug_logln!(" bucket sorting ...");
}
/*-- Complete the initial radix sort --*/
for i in 1..=65536 {
ftab[i] += ftab[i - 1];
}
s = ((block[0 as c_int as usize] as c_int) << 8 as c_int) as u16;
i = nblock - 1 as c_int;
while i >= 3 as c_int {
s = (s as c_int >> 8 | (block[i as usize] as c_int) << 8) as u16;
j = ftab[usize::from(s)] as i32 - 1;
ftab[usize::from(s)] = j as u32;
ptr[j as usize] = i as u32;
s = (s as c_int >> 8 | (block[(i - 1 as c_int) as usize] as c_int) << 8) as u16;
j = ftab[usize::from(s)] as i32 - 1;
ftab[usize::from(s)] = j as u32;
ptr[j as usize] = i as u32 - 1;
s = (s as c_int >> 8 | (block[(i - 2 as c_int) as usize] as c_int) << 8) as u16;
j = ftab[usize::from(s)] as i32 - 1;
ftab[usize::from(s)] = j as u32;
ptr[j as usize] = i as u32 - 2;
s = (s as c_int >> 8 | (block[(i - 3 as c_int) as usize] as c_int) << 8) as u16;
j = ftab[usize::from(s)] as i32 - 1;
ftab[usize::from(s)] = j as u32;
ptr[j as usize] = i as u32 - 3;
i -= 4 as c_int;
}
while i >= 0 as c_int {
s = (s as c_int >> 8 as c_int | (block[i as usize] as c_int) << 8 as c_int) as u16;
j = ftab[usize::from(s)] as i32 - 1;
ftab[usize::from(s)] = j as u32;
ptr[j as usize] = i as u32;
i -= 1;
}
bigDone.fill(false);
i = 0 as c_int;
while i <= 255 as c_int {
runningOrder[i as usize] = i;
i += 1;
}
let mut vv: i32;
let mut h: i32 = 1 as c_int;
loop {
h = 3 as c_int * h + 1 as c_int;
if h > 256 as c_int {
break;
}
}
macro_rules! BIGFREQ {
($b:expr) => {
ftab[(($b) + 1) << 8] - ftab[($b) << 8]
};
}
loop {
h /= 3 as c_int;
i = h;
while i <= 255 as c_int {
vv = runningOrder[i as usize];
j = i;
while BIGFREQ!(runningOrder[(j - h) as usize] as usize) > BIGFREQ!(vv as usize) {
runningOrder[j as usize] = runningOrder[(j - h) as usize];
j -= h;
if j <= h - 1 as c_int {
break;
}
}
runningOrder[j as usize] = vv;
i += 1;
}
if h == 1 as c_int {
break;
}
}
/*--
The main sorting loop.
--*/
numQSorted = 0 as c_int;
for i in 0..=255 {
/*--
Process big buckets, starting with the least full.
Basically this is a 3-step process in which we call
mainQSort3 to sort the small buckets [ss, j], but
also make a big effort to avoid the calls if we can.
--*/
ss = runningOrder[i as usize];
const SETMASK: u32 = 1 << 21;
const CLEARMASK: u32 = !SETMASK;
/*--
Step 1:
Complete the big bucket [ss] by quicksorting
any unsorted small buckets [ss, j], for j != ss.
Hopefully previous pointer-scanning phases have already
completed many of the small buckets [ss, j], so
we don't have to sort them at all.
--*/
for j in 0..=255 {
if j != ss {
sb = (ss << 8 as c_int) + j;
if ftab[sb as usize] & SETMASK == 0 {
let lo: i32 = (ftab[sb as usize] & CLEARMASK) as i32;
let hi: i32 = ((ftab[sb as usize + 1] & CLEARMASK).wrapping_sub(1)) as i32;
if hi > lo {
if verb >= 4 as c_int {
debug_logln!(
" qsort [{:#x}, {:#x}] done {} this {}",
ss,
j,
numQSorted,
hi - lo + 1 as c_int,
);
}
mainQSort3(ptr, block, quadrant, nblock, lo, hi, 2 as c_int, budget);
numQSorted += hi - lo + 1 as c_int;
if *budget < 0 as c_int {
return;
}
}
}
ftab[sb as usize] |= SETMASK;
}
}
assert_h!(!bigDone[ss as usize], 1006);
/*--
Step 2:
Now scan this big bucket [ss] so as to synthesise the
sorted order for small buckets [t, ss] for all t,
including, magically, the bucket [ss,ss] too.
This will avoid doing Real Work in subsequent Step 1's.
--*/
{
for j in 0..=255 {
copyStart[j] = (ftab[(j << 8) + ss as usize] & CLEARMASK) as i32;
copyEnd[j] = (ftab[(j << 8) + ss as usize + 1] & CLEARMASK) as i32 - 1;
}
j = (ftab[(ss as usize) << 8] & CLEARMASK) as i32;
while j < copyStart[ss as usize] {
k = (ptr[j as usize]).wrapping_sub(1) as i32;
if k < 0 as c_int {
k += nblock;
}
c1 = block[k as usize];
if !bigDone[c1 as usize] {
let fresh11 = copyStart[c1 as usize];
copyStart[c1 as usize] += 1;
ptr[fresh11 as usize] = k as u32;
}
j += 1;
}
j = (ftab[(ss as usize + 1) << 8] & CLEARMASK) as i32 - 1;
while j > copyEnd[ss as usize] {
k = (ptr[j as usize]).wrapping_sub(1) as i32;
if k < 0 as c_int {
k += nblock;
}
c1 = block[k as usize];
if !bigDone[c1 as usize] {
let fresh12 = copyEnd[c1 as usize];
copyEnd[c1 as usize] -= 1;
ptr[fresh12 as usize] = k as u32;
}
j -= 1;
}
}
assert_h!(
(copyStart[ss as usize]-1 == copyEnd[ss as usize])
||
/* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
Necessity for this case is demonstrated by compressing
a sequence of approximately 48.5 million of character
251; 1.0.0/1.0.1 will then die here. */
(copyStart[ss as usize] == 0 && copyEnd[ss as usize] == nblock-1),
1007
);
for j in 0..=255 {
ftab[(j << 8) + ss as usize] |= SETMASK
}
/*--
Step 3:
The [ss] big bucket is now done. Record this fact,
and update the quadrant descriptors. Remember to
update quadrants in the overshoot area too, if
necessary. The "if (i < 255)" test merely skips
this updating for the last bucket processed, since
updating for the last bucket is pointless.
The quadrant array provides a way to incrementally
cache sort orderings, as they appear, so as to
make subsequent comparisons in fullGtU() complete
faster. For repetitive blocks this makes a big
difference (but not big enough to be able to avoid
the fallback sorting mechanism, exponential radix sort).
The precise meaning is: at all times:
for 0 <= i < nblock and 0 <= j <= nblock
if block[i] != block[j],
then the relative values of quadrant[i] and
quadrant[j] are meaningless.
else {
if quadrant[i] < quadrant[j]
then the string starting at i lexicographically
precedes the string starting at j
else if quadrant[i] > quadrant[j]
then the string starting at j lexicographically
precedes the string starting at i
else
the relative ordering of the strings starting
at i and j has not yet been determined.
}
--*/
bigDone[ss as usize] = true;
if i < 255 as c_int {
let bbStart: i32 = (ftab[(ss as usize) << 8] & CLEARMASK) as i32;
let bbSize: i32 = (ftab[(ss as usize + 1) << 8] & CLEARMASK) as i32 - bbStart;
let mut shifts: i32 = 0 as c_int;
while bbSize >> shifts > 65534 as c_int {
shifts += 1;
}
j = bbSize - 1 as c_int;
while j >= 0 as c_int {
let a2update: i32 = ptr[(bbStart + j) as usize] as i32;
let qVal: u16 = (j >> shifts) as u16;
quadrant[a2update as usize] = qVal;
if (a2update as usize) < BZ_N_OVERSHOOT {
quadrant[(a2update + nblock) as usize] = qVal;
}
j -= 1;
}
assert_h!(((bbSize - 1) >> shifts) <= 65535, 1002);
}
}
if verb >= 4 as c_int {
debug_logln!(
" {} pointers, {} sorted, {} scanned",
nblock,
numQSorted,
nblock - numQSorted,
);
}
}
/// Pre:
/// nblock > 0
/// arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
/// ((UChar*)arr2) [0 .. nblock-1] holds block
/// arr1 exists for [0 .. nblock-1]
///
/// Post:
/// ((UChar*)arr2) [0 .. nblock-1] holds block
/// All other areas of block destroyed
/// ftab [ 0 .. 65536 ] destroyed
/// arr1 [0 .. nblock-1] holds sorted order
pub(crate) fn block_sort(s: &mut EState) {
let nblock = usize::try_from(s.nblock).unwrap();
let ptr = s.arr1.ptr();
let ftab = s.ftab.ftab();
BZ2_blockSortHelp(ptr, &mut s.arr2, ftab, nblock, s.workFactor, s.verbosity);
s.origPtr = -1 as c_int;
for i in 0..s.nblock {
if ptr[i as usize] == 0 {
s.origPtr = i;
break;
}
}
assert_h!(s.origPtr != -1, 1003);
}
fn BZ2_blockSortHelp(
ptr: &mut [u32],
arr2: &mut Arr2,
ftab: &mut [u32; FTAB_LEN],
nblock: usize,
workFactor: i32,
verbosity: i32,
) {
if nblock < 10000 {
fallbackSort(ptr, arr2, ftab, nblock as i32, verbosity);
} else {
let (block, quadrant) = arr2.block_and_quadrant(nblock);
/* (wfact-1) / 3 puts the default-factor-30
transition point at very roughly the same place as
with v0.1 and v0.9.0.
Not that it particularly matters any more, since the
resulting compressed stream is now the same regardless
of whether or not we use the main sort or fallback sort.
*/
let wfact = workFactor.clamp(1, 100);
let budgetInit = nblock as i32 * ((wfact - 1) / 3);
let mut budget = budgetInit;
mainSort(
ptr,
block,
quadrant,
ftab,
nblock as i32,
verbosity,
&mut budget,
);
if verbosity >= 3 {
debug_logln!(
" {} work, {} block, ratio {:5.2}",
budgetInit - budget,
nblock,
(budgetInit - budget) as f64 / (if nblock == 0 { 1 } else { nblock }) as f64
);
}
if budget < 0 {
if verbosity >= 2 as c_int {
debug_logln!(" too repetitive; using fallback sorting algorithm");
}
fallbackSort(ptr, arr2, ftab, nblock as i32, verbosity);
}
}
}
libbz2-rs-sys-0.1.3/src/bzlib.rs 0000644 0000000 0000000 00000201234 10461020230 0014510 0 ustar 0000000 0000000 use core::ffi::{c_char, c_int, c_uint, c_void};
use core::mem::offset_of;
use core::{mem, ptr};
use crate::allocator::Allocator;
use crate::compress::compress_block;
use crate::crctable::BZ2_CRC32TABLE;
use crate::debug_log;
use crate::decompress::{self, decompress};
#[cfg(feature = "stdio")]
use crate::libbz2_rs_sys_version;
#[cfg(feature = "stdio")]
pub use crate::high_level::*;
pub(crate) const BZ_MAX_ALPHA_SIZE: usize = 258;
pub(crate) const BZ_MAX_CODE_LEN: usize = 23;
pub(crate) const BZ_N_GROUPS: usize = 6;
pub(crate) const BZ_N_ITERS: usize = 4;
pub(crate) const BZ_G_SIZE: usize = 50;
pub(crate) const BZ_MAX_SELECTORS: u16 = {
let tmp = 2 + (900000 / BZ_G_SIZE);
assert!(tmp >> 16 == 0);
tmp as u16
};
pub(crate) const BZ_RUNA: u16 = 0;
pub(crate) const BZ_RUNB: u16 = 1;
pub(crate) const BZ_MAX_UNUSED_U32: u32 = 5000;
#[cfg(doc)]
use crate::{
BZ_CONFIG_ERROR, BZ_DATA_ERROR, BZ_DATA_ERROR_MAGIC, BZ_FINISH, BZ_FINISH_OK, BZ_FLUSH,
BZ_FLUSH_OK, BZ_IO_ERROR, BZ_MEM_ERROR, BZ_OK, BZ_OUTBUFF_FULL, BZ_PARAM_ERROR, BZ_RUN,
BZ_RUN_OK, BZ_SEQUENCE_ERROR, BZ_STREAM_END, BZ_UNEXPECTED_EOF,
};
#[cfg(feature = "custom-prefix")]
macro_rules! prefix {
($name:expr) => {
concat!(env!("LIBBZ2_RS_SYS_PREFIX"), stringify!($name))
};
}
// NOTE: once we reach 1.0.0, the macro used for the `semver-prefix` feature should no longer include the
// minor version in the name. The name is meant to be unique between semver-compatible versions!
const _PRE_ONE_DOT_O: () = assert!(env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] == b'0');
#[cfg(feature = "semver-prefix")]
macro_rules! prefix {
($name:expr) => {
concat!(
"LIBBZ2_RS_SYS_v",
env!("CARGO_PKG_VERSION_MAJOR"),
".",
env!("CARGO_PKG_VERSION_MINOR"),
".x_",
stringify!($name)
)
};
}
#[cfg(all(
not(feature = "custom-prefix"),
not(feature = "semver-prefix"),
not(any(test, feature = "testing-prefix"))
))]
macro_rules! prefix {
($name:expr) => {
stringify!($name)
};
}
#[cfg(all(
not(feature = "custom-prefix"),
not(feature = "semver-prefix"),
any(test, feature = "testing-prefix")
))]
macro_rules! prefix {
($name:expr) => {
concat!("LIBBZ2_RS_SYS_TEST_", stringify!($name))
};
}
pub(crate) use prefix;
/// The version of the zlib library.
///
/// Its value is a pointer to a NULL-terminated sequence of bytes.
///
/// The version string for this release is `
#[doc = libbz2_rs_sys_version!()]
/// `:
///
/// - The first component is the version of stock zlib that this release is compatible with
/// - The final component is the zlib-rs version used to build this release.
#[export_name = prefix!(BZ2_bzlibVersion)]
#[cfg(feature = "stdio")]
pub const extern "C" fn BZ2_bzlibVersion() -> *const core::ffi::c_char {
const LIBBZ2_RS_SYS_VERSION: &str = concat!(libbz2_rs_sys_version!(), "\0");
LIBBZ2_RS_SYS_VERSION.as_ptr().cast::()
}
type AllocFunc = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> *mut c_void;
type FreeFunc = unsafe extern "C" fn(*mut c_void, *mut c_void) -> ();
/// The current stream state.
///
/// # Custom allocators
///
/// The low-level API supports passing in a custom allocator as part of the [`bz_stream`]:
///
/// ```no_check
/// struct bz_stream {
/// // ...
/// pub bzalloc: Option *mut c_void>,
/// pub bzfree: Option,
/// pub opaque: *mut c_void,
/// }
/// ```
///
/// When these fields are `None` (or `NULL` in C), the initialization functions will try to
/// put in a default allocator, based on feature flags:
///
/// - `"rust-allocator"` uses the rust global allocator
/// - `"c-allocator"` uses an allocator based on `malloc` and `free`
///
/// When both configured, `"rust-allocator"` is preferred. When no default allocator is configured,
/// the high-level interface will return a [`BZ_CONFIG_ERROR`]. The low-level interface (the
/// functions that take a [`bz_stream`] as their argument) return a [`BZ_PARAM_ERROR`], unless the
/// user set the `bzalloc` and `bzfree` fields.
///
/// When custom `bzalloc` and `bzfree` functions are given, they must adhere to the following contract
/// to be safe:
///
/// - a call `bzalloc(opaque, n, m)` must return a pointer `p` to `n * m` bytes of memory, or
/// `NULL` if out of memory
/// - a call `bzfree(opaque, p)` must free that memory
///
/// The `strm.opaque` value is passed to as the first argument to all calls to `bzalloc`
/// and `bzfree`, but is otherwise ignored by the library.
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct bz_stream {
pub next_in: *const c_char,
pub avail_in: c_uint,
pub total_in_lo32: c_uint,
pub total_in_hi32: c_uint,
pub next_out: *mut c_char,
pub avail_out: c_uint,
pub total_out_lo32: c_uint,
pub total_out_hi32: c_uint,
pub state: *mut c_void,
pub bzalloc: Option,
pub bzfree: Option,
pub opaque: *mut c_void,
}
pub(crate) use stream::*;
mod stream {
use super::*;
#[repr(C)]
pub(crate) struct BzStream {
pub next_in: *const c_char,
pub avail_in: c_uint,
pub total_in_lo32: c_uint,
pub total_in_hi32: c_uint,
pub next_out: *mut c_char,
pub avail_out: c_uint,
pub total_out_lo32: c_uint,
pub total_out_hi32: c_uint,
pub state: *mut S,
pub bzalloc: Option,
pub bzfree: Option,
pub opaque: *mut c_void,
}
macro_rules! check_layout {
($($field:ident,)*) => {
const _: () = {
$(assert!(offset_of!(bz_stream, $field) == offset_of!(BzStream, $field));)*
$(assert!(offset_of!(bz_stream, $field) == offset_of!(BzStream, $field));)*
};
};
}
check_layout!(
next_in,
avail_in,
total_in_lo32,
total_in_hi32,
next_out,
avail_out,
total_out_lo32,
total_out_hi32,
state,
bzalloc,
bzfree,
opaque,
);
pub(crate) trait StreamState {}
impl StreamState for EState {}
impl StreamState for DState {}
impl bz_stream {
pub const fn zeroed() -> Self {
Self {
next_in: ptr::null_mut::(),
avail_in: 0,
total_in_lo32: 0,
total_in_hi32: 0,
next_out: ptr::null_mut::(),
avail_out: 0,
total_out_lo32: 0,
total_out_hi32: 0,
state: ptr::null_mut::(),
bzalloc: None,
bzfree: None,
opaque: ptr::null_mut::(),
}
}
}
impl BzStream {
pub(crate) const fn zeroed() -> Self {
Self {
next_in: ptr::null_mut::(),
avail_in: 0,
total_in_lo32: 0,
total_in_hi32: 0,
next_out: ptr::null_mut::(),
avail_out: 0,
total_out_lo32: 0,
total_out_hi32: 0,
state: ptr::null_mut::(),
bzalloc: None,
bzfree: None,
opaque: ptr::null_mut::(),
}
}
/// # Safety
///
/// The given [`bz_stream`] must either have a NULL state or be initialized with the state
/// indicated by the generic param `S`. It must also have `bzalloc`/`bzfree`/`opaque` correctly
/// configured.
pub(crate) unsafe fn from_mut(s: &mut bz_stream) -> &mut Self {
unsafe { mem::transmute(s) }
}
/// # Safety
///
/// The given [`bz_stream`] must be initialized and either have a NULL state or be initialized
/// with the state indicated by the generic param `S`. It must also have
/// `bzalloc`/`bzfree`/`opaque` correctly configured.
pub(crate) unsafe fn from_ptr<'a>(p: *mut bz_stream) -> Option<&'a mut Self> {
unsafe { p.cast::().as_mut() }
}
pub(super) fn allocator(&self) -> Option {
unsafe { Allocator::from_bz_stream(self) }
}
/// Read up to 7 bytes into the bit buffer.
///
/// The caller is responsible for updating `self.total_in`!
#[must_use]
#[inline(always)]
pub(crate) fn pull_u64(
&mut self,
mut bit_buffer: u64,
bits_used: i32,
) -> Option<(u64, i32)> {
// we should only ask for more input if there are at least 8 free bits
debug_assert!(bits_used <= 56);
if self.avail_in < 8 {
return None;
}
// of course this uses big endian values
let read = u64::from_be_bytes(unsafe { self.next_in.cast::<[u8; 8]>().read() });
// because of the endianness, we can only shift in whole bytes.
// this calculates the number of available bits, rounded down to the nearest multiple
// of 8.
let increment_bits = (63 - bits_used) & !7;
// shift existing bits to the end, and or new bits in
bit_buffer = (bit_buffer << increment_bits) | (read >> (64 - increment_bits));
// we read 8 bytes above, but can only process `increment_bytes` worth of bits
let increment_bytes = increment_bits / 8;
self.next_in = unsafe { (self.next_in).add(increment_bytes as usize) };
self.avail_in -= increment_bytes as u32;
// skips updating `self.total_in`: the caller is responsible for keeping it updated
Some((bit_buffer, bits_used + increment_bits))
}
/// Read exactly 1 byte into the buffer
///
/// The caller is responsible for updating `self.total_in`!
#[must_use]
#[inline(always)]
pub(crate) fn pull_u8(
&mut self,
mut bit_buffer: u64,
bits_used: i32,
) -> Option<(u64, i32)> {
// we should only ask for more input if there are at least 8 free bits
debug_assert!(bits_used <= 56);
if self.avail_in == 0 || bits_used > 56 {
return None;
}
let read = unsafe { *(self.next_in as *mut u8) };
bit_buffer <<= 8;
bit_buffer |= u64::from(read);
self.next_in = unsafe { (self.next_in).offset(1) };
self.avail_in -= 1;
// skips updating `self.total_in`: the caller is responsible for keeping it updated
Some((bit_buffer, bits_used + 8))
}
#[must_use]
pub(crate) fn read_byte(&mut self) -> Option {
if self.avail_in == 0 {
return None;
}
let b = unsafe { *(self.next_in as *mut u8) };
self.next_in = unsafe { (self.next_in).offset(1) };
self.avail_in -= 1;
self.total_in_lo32 = (self.total_in_lo32).wrapping_add(1);
if self.total_in_lo32 == 0 {
self.total_in_hi32 = (self.total_in_hi32).wrapping_add(1);
}
Some(b)
}
#[must_use]
pub(super) fn write_byte(&mut self, byte: u8) -> bool {
if self.avail_out == 0 {
return false;
}
unsafe {
*self.next_out = byte as c_char;
}
self.avail_out -= 1;
self.next_out = unsafe { (self.next_out).offset(1) };
self.total_out_lo32 = (self.total_out_lo32).wrapping_add(1);
if self.total_out_lo32 == 0 {
self.total_out_hi32 = (self.total_out_hi32).wrapping_add(1);
}
true
}
}
pub(super) fn configure_allocator(strm: &mut BzStream) -> Option {
match (strm.bzalloc, strm.bzfree) {
(Some(allocate), Some(deallocate)) => {
Some(Allocator::custom(allocate, deallocate, strm.opaque))
}
(None, None) => {
let allocator = Allocator::DEFAULT?;
let (bzalloc, bzfree) = Allocator::default_function_pointers()?;
strm.bzalloc = Some(bzalloc);
strm.bzfree = Some(bzfree);
Some(allocator)
}
// Using a different allocator for alloc and free is UB. The user of libbzip2-rs can't get a
// reference to the default alloc or free function, so hitting this path means that using
// the default alloc or free function would cause two allocators to be mixed. As such return
// an error to prevent UB.
#[cfg(any(feature = "rust-allocator", not(feature = "c-allocator")))]
_ => None,
#[cfg(all(feature = "c-allocator", not(feature = "rust-allocator")))]
_ => {
// this is almost certainly a bug, but replicates the original C behavior.
//
// Note that this logic does not really work with the default rust allocator, because
// it will panic at runtime when called directly. Usually the idea here is that
// allocation is special, and free is just the default `libc::free` that we configure
// by default with the default C allocator.
let (default_bzalloc, default_bzfree) = crate::allocator::c_allocator::ALLOCATOR;
let bzalloc = strm.bzalloc.get_or_insert(default_bzalloc);
let bzfree = strm.bzfree.get_or_insert(default_bzfree);
Some(Allocator::custom(*bzalloc, *bzfree, strm.opaque))
}
}
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub(crate) enum ReturnCode {
BZ_OK = 0,
BZ_RUN_OK = 1,
BZ_FLUSH_OK = 2,
BZ_FINISH_OK = 3,
BZ_STREAM_END = 4,
BZ_SEQUENCE_ERROR = -1,
BZ_PARAM_ERROR = -2,
BZ_MEM_ERROR = -3,
BZ_DATA_ERROR = -4,
BZ_DATA_ERROR_MAGIC = -5,
BZ_IO_ERROR = -6,
BZ_UNEXPECTED_EOF = -7,
BZ_OUTBUFF_FULL = -8,
BZ_CONFIG_ERROR = -9,
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub(crate) enum Mode {
Idle,
Running,
Flushing,
Finishing,
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub(crate) enum State {
Output,
Input,
}
pub(crate) const BZ_N_RADIX: i32 = 2;
pub(crate) const BZ_N_QSORT: i32 = 12;
pub(crate) const BZ_N_SHELL: i32 = 18;
pub(crate) const BZ_N_OVERSHOOT: usize = (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) as usize;
pub(crate) const FTAB_LEN: usize = u16::MAX as usize + 2;
pub(crate) struct EState {
pub strm_addr: usize, // Only for a consistency check
pub mode: Mode,
pub state: State,
pub avail_in_expect: u32,
pub arr1: Arr1,
pub arr2: Arr2,
pub ftab: Ftab,
pub origPtr: i32,
pub writer: crate::compress::EWriter,
pub workFactor: i32,
pub state_in_ch: u32,
pub state_in_len: i32,
pub nblock: i32,
pub nblockMAX: i32,
pub state_out_pos: i32,
pub nInUse: i32,
pub inUse: [bool; 256],
pub unseqToSeq: [u8; 256],
pub blockCRC: u32,
pub combinedCRC: u32,
pub verbosity: i32,
pub blockNo: i32,
pub blockSize100k: i32,
pub nMTF: i32,
pub mtfFreq: [i32; 258],
pub selector: [u8; 18002],
pub selectorMtf: [u8; 18002],
pub len: [[u8; BZ_MAX_ALPHA_SIZE]; BZ_N_GROUPS],
pub code: [[u32; 258]; 6],
pub rfreq: [[i32; 258]; 6],
pub len_pack: [[u32; 4]; 258],
}
/// Creates a new pointer that is dangling, but well-aligned.
pub(crate) fn dangling() -> *mut T {
ptr::null_mut::().wrapping_add(mem::align_of::())
}
pub(crate) struct Arr1 {
ptr: *mut u32,
len: usize,
}
impl Arr1 {
fn alloc(allocator: &Allocator, len: usize) -> Option {
let ptr = allocator.allocate_zeroed(len)?;
Some(Self { ptr, len })
}
unsafe fn dealloc(&mut self, allocator: &Allocator) {
let this = mem::replace(
self,
Self {
ptr: dangling(),
len: 0,
},
);
if this.len != 0 {
unsafe { allocator.deallocate(this.ptr, this.len) }
}
}
pub(crate) fn mtfv(&mut self) -> &mut [u16] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast(), self.len * 2) }
}
pub(crate) fn ptr(&mut self) -> &mut [u32] {
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
}
}
pub(crate) struct Arr2 {
ptr: *mut u32,
len: usize,
}
impl Arr2 {
fn alloc(allocator: &Allocator, len: usize) -> Option {
let ptr = allocator.allocate_zeroed(len)?;
Some(Self { ptr, len })
}
unsafe fn dealloc(&mut self, allocator: &Allocator) {
let this = mem::replace(
self,
Self {
ptr: dangling(),
len: 0,
},
);
if this.len != 0 {
unsafe { allocator.deallocate(this.ptr, this.len) }
}
}
pub(crate) fn eclass(&mut self) -> &mut [u32] {
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
}
pub(crate) fn zbits(&mut self, nblock: usize) -> &mut [u8] {
assert!(nblock <= 4 * self.len);
unsafe {
core::slice::from_raw_parts_mut(
self.ptr.cast::().add(nblock),
self.len * 4 - nblock,
)
}
}
pub(crate) fn raw_block(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast(), self.len * 4) }
}
pub(crate) fn block(&mut self, nblock: usize) -> &mut [u8] {
assert!(nblock <= 4 * self.len);
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast(), nblock) }
}
pub(crate) fn block_and_quadrant(&mut self, nblock: usize) -> (&mut [u8], &mut [u16]) {
let len = nblock + BZ_N_OVERSHOOT;
assert!(3 * len.next_multiple_of(2) <= 4 * self.len);
let block = unsafe { core::slice::from_raw_parts_mut(self.ptr.cast(), len) };
let start_byte = len.next_multiple_of(2);
let quadrant: *mut u16 = unsafe { self.ptr.cast::().byte_add(start_byte) };
let quadrant = unsafe { core::slice::from_raw_parts_mut(quadrant, len) };
quadrant.fill(0);
(block, quadrant)
}
}
pub(crate) struct Ftab {
ptr: *mut u32,
}
impl Ftab {
fn alloc(allocator: &Allocator) -> Option {
let ptr = allocator.allocate_zeroed(FTAB_LEN)?;
Some(Self { ptr })
}
unsafe fn dealloc(&mut self, allocator: &Allocator) {
let this = mem::replace(
self,
Self {
ptr: ptr::null_mut(),
},
);
if !this.ptr.is_null() {
unsafe { allocator.deallocate(this.ptr, FTAB_LEN) }
}
}
pub(crate) fn ftab(&mut self) -> &mut [u32; FTAB_LEN] {
// NOTE: this panics if the pointer is NULL, that is important!
unsafe { self.ptr.cast::<[u32; FTAB_LEN]>().as_mut().unwrap() }
}
}
#[repr(C)]
pub(crate) struct DState {
pub strm_addr: usize, // Only for a consistency check
pub state: decompress::State,
pub state_out_len: u32,
pub state_out_ch: u8,
pub blockRandomised: bool,
pub blockSize100k: u8,
pub k0: u8,
pub rNToGo: i32,
pub rTPos: i32,
pub bsBuff: u64,
pub bsLive: i32,
pub smallDecompress: DecompressMode,
pub currBlockNo: i32,
pub verbosity: i32,
pub origPtr: i32,
pub tPos: u32,
pub nblock_used: i32,
pub unzftab: [u32; 256],
pub cftab: [u32; 257],
pub cftabCopy: [u32; 257],
pub tt: DSlice,
pub ll16: DSlice,
pub ll4: DSlice,
pub storedBlockCRC: u32,
pub storedCombinedCRC: u32,
pub calculatedBlockCRC: u32,
pub calculatedCombinedCRC: u32,
pub nInUse: u16,
pub inUse: [bool; 256],
pub inUse16: [bool; 16],
pub seqToUnseq: [u8; 256],
pub mtfa: [u8; 4096],
pub mtfbase: [u16; 16],
pub selector: [u8; 18002],
pub selectorMtf: [u8; 18002],
pub len: [[u8; 258]; 6],
pub limit: [[i32; 258]; 6],
pub base: [[i32; 258]; 6],
pub perm: [[u16; 258]; 6],
pub minLens: [u8; 6],
pub save: SaveArea,
}
#[derive(Default)]
#[repr(C)]
pub(crate) struct SaveArea {
pub i: i32,
pub j: i32,
pub alphaSize: u16,
pub EOB: u16,
pub groupNo: i32,
pub nblock: u32,
pub es: u32,
pub zvec: i32,
pub nextSym: u16,
pub nSelectors: u16,
pub groupPos: u8,
pub zn: u8,
pub nGroups: u8,
pub t: u8,
pub curr: u8,
pub nblockMAX100k: u8,
pub logN: u8, // the log_2 of N
pub zj: bool,
pub gMinlen: u8,
pub gSel: u8,
}
pub(crate) struct DSlice {
ptr: *mut T,
len: usize,
}
impl DSlice {
fn new() -> Self {
Self {
ptr: dangling(),
len: 0,
}
}
pub(crate) fn alloc(allocator: &Allocator, len: usize) -> Option {
let ptr = allocator.allocate_zeroed::(len)?;
Some(Self { ptr, len })
}
pub(crate) unsafe fn dealloc(&mut self, allocator: &Allocator) {
let this = mem::replace(self, Self::new());
if this.len != 0 {
unsafe { allocator.deallocate(this.ptr, this.len) }
}
}
pub(crate) fn as_slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
}
pub(crate) fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
}
}
const _C_INT_SIZE: () = assert!(core::mem::size_of::() == 4);
const _C_SHORT_SIZE: () = assert!(core::mem::size_of::() == 2);
const _C_CHAR_SIZE: () = assert!(core::mem::size_of::() == 1);
fn prepare_new_block(s: &mut EState) {
s.nblock = 0;
s.writer.num_z = 0;
s.state_out_pos = 0;
s.blockCRC = 0xffffffff;
s.inUse.fill(false);
s.blockNo += 1;
}
fn init_rl(s: &mut EState) {
s.state_in_ch = 256 as c_int as u32;
s.state_in_len = 0 as c_int;
}
fn isempty_rl(s: &mut EState) -> bool {
!(s.state_in_ch < 256 && s.state_in_len > 0)
}
/// Prepares the stream for compression.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `!(1..=9).contains(&blockSize100k)`
/// - `!(0..=4).contains(&verbosity)`
/// - `!(0..=250).contains(&workFactor)`
/// - no [valid allocator](bz_stream#custom-allocators) could be configured
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm`
/// * The `bzalloc`, `bzfree` and `opaque` fields form a [valid allocator](bz_stream#custom-allocators).
#[export_name = prefix!(BZ2_bzCompressInit)]
pub unsafe extern "C" fn BZ2_bzCompressInit(
strm: *mut bz_stream,
blockSize100k: c_int,
verbosity: c_int,
workFactor: c_int,
) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzCompressInitHelp(strm, blockSize100k, verbosity, workFactor) as c_int
}
pub(crate) fn BZ2_bzCompressInitHelp(
strm: &mut BzStream,
blockSize100k: c_int,
verbosity: c_int,
mut workFactor: c_int,
) -> ReturnCode {
if !(1..=9).contains(&blockSize100k) || !(0..=250).contains(&workFactor) {
return ReturnCode::BZ_PARAM_ERROR;
}
if workFactor == 0 {
workFactor = 30;
}
// return a param error when no [valid allocator](bz_stream#custom-allocators) could be configured
let Some(allocator) = configure_allocator(strm) else {
return ReturnCode::BZ_PARAM_ERROR;
};
let Some(s) = allocator.allocate_zeroed::(1) else {
return ReturnCode::BZ_MEM_ERROR;
};
// this `s.strm` pointer should _NEVER_ be used! it exists just as a consistency check to ensure
// that a given state belongs to a given strm.
unsafe { (*s).strm_addr = strm as *const _ as usize }; // FIXME use .addr() once stable
let n = 100000 * blockSize100k;
let arr1_len = n as usize;
let arr1 = Arr1::alloc(&allocator, arr1_len);
let arr2_len = n as usize + (2 + 12 + 18 + 2);
let arr2 = Arr2::alloc(&allocator, arr2_len);
let ftab = Ftab::alloc(&allocator);
match (arr1, arr2, ftab) {
(Some(arr1), Some(arr2), Some(ftab)) => unsafe {
(*s).arr1 = arr1;
(*s).arr2 = arr2;
(*s).ftab = ftab;
},
(arr1, arr2, ftab) => {
if let Some(mut arr1) = arr1 {
unsafe { arr1.dealloc(&allocator) };
}
if let Some(mut arr2) = arr2 {
unsafe { arr2.dealloc(&allocator) };
}
if let Some(mut ftab) = ftab {
unsafe { ftab.dealloc(&allocator) };
}
unsafe { allocator.deallocate(s, 1) };
return ReturnCode::BZ_MEM_ERROR;
}
};
strm.state = s;
// safety: the EState has now been sufficiently initialized; the allocator zeroes the memory,
// and the only fields where zero is not a valid value are the arrays that were just set
//
// note in particular that if the discriminant of the first variant of an enum is unspecified,
// then it is set to zero.
let s = unsafe { &mut *s };
s.blockNo = 0;
s.state = State::Output;
s.mode = Mode::Running;
s.combinedCRC = 0;
s.blockSize100k = blockSize100k;
s.nblockMAX = 100000 * blockSize100k - 19;
s.verbosity = verbosity;
s.workFactor = workFactor;
strm.total_in_lo32 = 0;
strm.total_in_hi32 = 0;
strm.total_out_lo32 = 0;
strm.total_out_hi32 = 0;
init_rl(s);
prepare_new_block(s);
ReturnCode::BZ_OK
}
macro_rules! BZ_UPDATE_CRC {
($crcVar:expr, $cha:expr) => {
let index = ($crcVar >> 24) ^ ($cha as core::ffi::c_uint);
$crcVar = ($crcVar << 8) ^ BZ2_CRC32TABLE[index as usize];
};
}
fn add_pair_to_block(s: &mut EState) {
let ch: u8 = s.state_in_ch as u8;
for _ in 0..s.state_in_len {
BZ_UPDATE_CRC!(s.blockCRC, ch);
}
let block = s.arr2.raw_block();
s.inUse[s.state_in_ch as usize] = true;
match s.state_in_len {
1 => {
block[s.nblock as usize..][..1].fill(ch);
s.nblock += 1;
}
2 => {
block[s.nblock as usize..][..2].fill(ch);
s.nblock += 2;
}
3 => {
block[s.nblock as usize..][..3].fill(ch);
s.nblock += 3;
}
_ => {
s.inUse[(s.state_in_len - 4) as usize] = true;
block[s.nblock as usize..][..4].fill(ch);
s.nblock += 4;
block[s.nblock as usize] = (s.state_in_len - 4) as u8;
s.nblock += 1;
}
};
}
fn flush_rl(s: &mut EState) {
if s.state_in_ch < 256 {
add_pair_to_block(s);
}
init_rl(s);
}
macro_rules! ADD_CHAR_TO_BLOCK {
($zs:expr, $zchh0:expr) => {
let zchh: u32 = $zchh0 as u32;
if zchh != $zs.state_in_ch && $zs.state_in_len == 1 {
/*-- fast track the common case --*/
let ch: u8 = $zs.state_in_ch as u8;
BZ_UPDATE_CRC!($zs.blockCRC, ch);
$zs.inUse[$zs.state_in_ch as usize] = true;
$zs.arr2.raw_block()[$zs.nblock as usize] = ch;
$zs.nblock += 1;
$zs.nblock;
$zs.state_in_ch = zchh;
} else if zchh != $zs.state_in_ch || $zs.state_in_len == 255 {
/*-- general, uncommon cases --*/
if $zs.state_in_ch < 256 {
add_pair_to_block($zs);
}
$zs.state_in_ch = zchh;
$zs.state_in_len = 1;
} else {
$zs.state_in_len += 1;
}
};
}
fn copy_input_until_stop(strm: &mut BzStream, s: &mut EState) -> bool {
let mut progress_in = false;
match s.mode {
Mode::Running => loop {
if s.nblock >= s.nblockMAX {
break;
}
if let Some(b) = strm.read_byte() {
progress_in = true;
ADD_CHAR_TO_BLOCK!(s, b as u32);
} else {
break;
}
},
Mode::Idle | Mode::Flushing | Mode::Finishing => loop {
if s.nblock >= s.nblockMAX {
break;
}
if s.avail_in_expect == 0 {
break;
}
if let Some(b) = strm.read_byte() {
progress_in = true;
ADD_CHAR_TO_BLOCK!(s, b as u32);
} else {
break;
}
s.avail_in_expect -= 1;
},
}
progress_in
}
fn copy_output_until_stop(strm: &mut BzStream, s: &mut EState) -> bool {
let mut progress_out = false;
let zbits = &mut s.arr2.raw_block()[s.nblock as usize..];
loop {
if s.state_out_pos >= s.writer.num_z as i32 {
break;
}
if !strm.write_byte(zbits[s.state_out_pos as usize]) {
break;
}
progress_out = true;
s.state_out_pos += 1;
}
progress_out
}
fn handle_compress(strm: &mut BzStream, s: &mut EState) -> bool {
let mut progress_in = false;
let mut progress_out = false;
loop {
if let State::Input = s.state {
progress_out |= copy_output_until_stop(strm, s);
if s.state_out_pos < s.writer.num_z as i32 {
break;
}
if matches!(s.mode, Mode::Finishing) && s.avail_in_expect == 0 && isempty_rl(s) {
break;
}
prepare_new_block(s);
s.state = State::Output;
if matches!(s.mode, Mode::Flushing) && s.avail_in_expect == 0 && isempty_rl(s) {
break;
}
}
if let State::Input = s.state {
continue;
}
progress_in |= copy_input_until_stop(strm, s);
if !matches!(s.mode, Mode::Running) && s.avail_in_expect == 0 {
flush_rl(s);
let is_last_block = matches!(s.mode, Mode::Finishing);
compress_block(s, is_last_block);
s.state = State::Input;
} else if s.nblock >= s.nblockMAX {
compress_block(s, false);
s.state = State::Input;
} else if strm.avail_in == 0 {
break;
}
}
progress_in || progress_out
}
pub(crate) enum Action {
Run = 0,
Flush = 1,
Finish = 2,
}
impl TryFrom for Action {
type Error = ();
fn try_from(value: i32) -> Result {
match value {
0 => Ok(Self::Run),
1 => Ok(Self::Flush),
2 => Ok(Self::Finish),
_ => Err(()),
}
}
}
/// Compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full.
///
/// # Returns
///
/// - [`BZ_SEQUENCE_ERROR`] if called on an invalid stream, e.g.
/// - before [`BZ2_bzCompressInit`]
/// - after [`BZ2_bzCompressEnd`]
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `strm.s.is_null()`
/// - action is not one of [`BZ_RUN`], [`BZ_FLUSH`] or [`BZ_FINISH`]
/// - [`BZ_RUN_OK`] successfully compressed, but ran out of input or output space
/// - [`BZ_FLUSH_OK`] not all compressed data has been written to the output yet
/// - [`BZ_FINISH_OK`] if all input has been read but not all output has been written to the output
/// buffer yet
/// - [`BZ_STREAM_END`] if all input has been read all output has been written to the output buffer
///
/// # Safety
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`BZ2_bzCompressInit`]
/// * Either
/// - `strm.next_in` is `NULL` and `strm.avail_in` is 0
/// - `strm.next_in` is readable for `strm.avail_in` bytes
/// * Either
/// - `strm.next_out` is `NULL` and `strm.avail_out` is `0`
/// - `strm.next_out` is writable for `strm.avail_out` bytes
#[export_name = prefix!(BZ2_bzCompress)]
pub unsafe extern "C" fn BZ2_bzCompress(strm: *mut bz_stream, action: c_int) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzCompressHelp(strm, action) as c_int
}
pub(crate) fn BZ2_bzCompressHelp(strm: &mut BzStream, action: i32) -> ReturnCode {
let Some(s) = (unsafe { strm.state.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR;
};
// FIXME use .addr() once stable
if s.strm_addr != strm as *mut _ as usize {
return ReturnCode::BZ_PARAM_ERROR;
}
compress_loop(strm, s, action)
}
fn compress_loop(strm: &mut BzStream, s: &mut EState, action: i32) -> ReturnCode {
loop {
match s.mode {
Mode::Idle => return ReturnCode::BZ_SEQUENCE_ERROR,
Mode::Running => match Action::try_from(action) {
Ok(Action::Run) => {
let progress = handle_compress(strm, s);
return if progress {
ReturnCode::BZ_RUN_OK
} else {
ReturnCode::BZ_PARAM_ERROR
};
}
Ok(Action::Flush) => {
s.avail_in_expect = strm.avail_in;
s.mode = Mode::Flushing;
}
Ok(Action::Finish) => {
s.avail_in_expect = strm.avail_in;
s.mode = Mode::Finishing;
}
Err(()) => {
return ReturnCode::BZ_PARAM_ERROR;
}
},
Mode::Flushing => {
let Ok(Action::Flush) = Action::try_from(action) else {
return ReturnCode::BZ_SEQUENCE_ERROR;
};
if s.avail_in_expect != strm.avail_in {
return ReturnCode::BZ_SEQUENCE_ERROR;
}
handle_compress(strm, s);
if s.avail_in_expect > 0
|| !isempty_rl(s)
|| s.state_out_pos < s.writer.num_z as i32
{
return ReturnCode::BZ_FLUSH_OK;
}
s.mode = Mode::Running;
return ReturnCode::BZ_RUN_OK;
}
Mode::Finishing => {
let Ok(Action::Finish) = Action::try_from(action) else {
// unreachable in practice
return ReturnCode::BZ_SEQUENCE_ERROR;
};
if s.avail_in_expect != strm.avail_in {
// unreachable in practice
return ReturnCode::BZ_SEQUENCE_ERROR;
}
let progress = handle_compress(strm, s);
if !progress {
return ReturnCode::BZ_SEQUENCE_ERROR;
}
if s.avail_in_expect > 0
|| !isempty_rl(s)
|| s.state_out_pos < s.writer.num_z as i32
{
return ReturnCode::BZ_FINISH_OK;
}
s.mode = Mode::Idle;
return ReturnCode::BZ_STREAM_END;
}
}
}
}
/// Deallocates all dynamically allocated data structures for this stream.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `strm.s.is_null()`
/// - no [valid allocator](bz_stream#custom-allocators) could be configured
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`BZ2_bzCompressInit`]
#[export_name = prefix!(BZ2_bzCompressEnd)]
pub unsafe extern "C" fn BZ2_bzCompressEnd(strm: *mut bz_stream) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzCompressEndHelp(strm)
}
fn BZ2_bzCompressEndHelp(strm: &mut BzStream) -> c_int {
let Some(s) = (unsafe { strm.state.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
// FIXME use .addr() once stable
if s.strm_addr != strm as *mut _ as usize {
return ReturnCode::BZ_PARAM_ERROR as c_int;
}
let Some(allocator) = strm.allocator() else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
unsafe {
s.arr1.dealloc(&allocator);
s.arr2.dealloc(&allocator);
s.ftab.dealloc(&allocator);
}
unsafe {
allocator.deallocate(strm.state.cast::(), 1);
}
strm.state = ptr::null_mut::();
ReturnCode::BZ_OK as c_int
}
pub(crate) enum DecompressMode {
Small,
Fast,
}
/// Prepares the stream for decompression.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `!(0..=1).contains(&small)`
/// - `!(0..=4).contains(&verbosity)`
/// - no [valid allocator](bz_stream#custom-allocators) could be configured
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm`
/// * The `bzalloc`, `bzfree` and `opaque` fields form a [valid allocator](bz_stream#custom-allocators).
#[export_name = prefix!(BZ2_bzDecompressInit)]
pub unsafe extern "C" fn BZ2_bzDecompressInit(
strm: *mut bz_stream,
verbosity: c_int,
small: c_int,
) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzDecompressInitHelp(strm, verbosity, small) as c_int
}
pub(crate) fn BZ2_bzDecompressInitHelp(
strm: &mut BzStream,
verbosity: c_int,
small: c_int,
) -> ReturnCode {
let decompress_mode = match small {
0 => DecompressMode::Fast,
1 => DecompressMode::Small,
_ => return ReturnCode::BZ_PARAM_ERROR,
};
if !(0..=4).contains(&verbosity) {
return ReturnCode::BZ_PARAM_ERROR;
}
// return a param error when no [valid allocator](bz_stream#custom-allocators) could be configured
let Some(allocator) = configure_allocator(strm) else {
return ReturnCode::BZ_PARAM_ERROR;
};
let Some(s) = allocator.allocate_zeroed::(1) else {
return ReturnCode::BZ_MEM_ERROR;
};
// this `s.strm` pointer should _NEVER_ be used! it exists just as a consistency check to ensure
// that a given state belongs to a given strm.
unsafe { (*s).strm_addr = strm as *const _ as usize }; // FIXME use .addr() once stable
unsafe {
(*s).state = decompress::State::BZ_X_MAGIC_1;
(*s).bsLive = 0;
(*s).bsBuff = 0;
(*s).calculatedCombinedCRC = 0;
}
unsafe {
(*s).smallDecompress = decompress_mode;
(*s).ll4 = DSlice::new();
(*s).ll16 = DSlice::new();
(*s).tt = DSlice::new();
(*s).currBlockNo = 0;
(*s).verbosity = verbosity;
}
strm.state = s;
strm.total_in_lo32 = 0;
strm.total_in_hi32 = 0;
strm.total_out_lo32 = 0;
strm.total_out_hi32 = 0;
ReturnCode::BZ_OK
}
macro_rules! BZ_RAND_MASK {
($s:expr) => {
($s.rNToGo == 1) as u8
};
}
macro_rules! BZ_RAND_UPD_MASK {
($s:expr) => {
if ($s.rNToGo == 0) {
$s.rNToGo = $crate::randtable::BZ2_RNUMS[$s.rTPos as usize];
$s.rTPos += 1;
if ($s.rTPos == 512) {
$s.rTPos = 0
};
}
$s.rNToGo -= 1;
};
}
pub(crate) use BZ_RAND_UPD_MASK;
macro_rules! BZ_GET_FAST {
($s:expr) => {
match $s.tt.as_slice().get($s.tPos as usize) {
None => return true,
Some(&bits) => {
$s.tPos = bits;
let tmp = ($s.tPos & 0xff) as u8;
$s.tPos >>= 8;
tmp
}
}
};
}
fn un_rle_obuf_to_output_fast(strm: &mut BzStream, s: &mut DState) -> bool {
let mut k1: u8;
if s.blockRandomised {
loop {
/* try to finish existing run */
loop {
if s.state_out_len == 0 {
if strm.avail_out == 0 {
return false;
} else {
break;
}
}
if !strm.write_byte(s.state_out_ch) {
return false;
}
BZ_UPDATE_CRC!(s.calculatedBlockCRC, s.state_out_ch);
s.state_out_len -= 1;
}
/* can a new run be started? */
if s.nblock_used == s.save.nblock as i32 + 1 {
return false;
}
/* Only caused by corrupt data stream? */
if s.nblock_used > s.save.nblock as i32 + 1 {
return true;
}
s.state_out_ch = s.k0;
s.state_out_len = 1;
k1 = BZ_GET_FAST!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
};
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 2;
k1 = BZ_GET_FAST!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
};
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 3;
k1 = BZ_GET_FAST!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
};
if k1 != s.k0 {
s.k0 = k1;
continue;
};
k1 = BZ_GET_FAST!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
s.state_out_len = k1 as u32 + 4;
s.k0 = BZ_GET_FAST!(s);
BZ_RAND_UPD_MASK!(s);
s.k0 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
}
} else {
/* restore */
let mut c_calculatedBlockCRC: u32 = s.calculatedBlockCRC;
let mut c_state_out_ch: u8 = s.state_out_ch;
let mut c_state_out_len: u32 = s.state_out_len;
let mut c_nblock_used: i32 = s.nblock_used;
let mut c_k0: u8 = s.k0;
let mut c_tPos: u32 = s.tPos;
let mut cs_next_out: *mut c_char = strm.next_out;
let mut cs_avail_out: c_uint = strm.avail_out;
let ro_blockSize100k: u8 = s.blockSize100k;
/* end restore */
let avail_out_INIT: u32 = cs_avail_out;
let s_save_nblockPP: i32 = s.save.nblock as i32 + 1;
let tt = &s.tt.as_slice()[..100000usize.wrapping_mul(usize::from(ro_blockSize100k))];
macro_rules! BZ_GET_FAST_C {
($c_tPos:expr) => {
match tt.get($c_tPos as usize) {
None => {
// return corrupt if we're past the length of the block
return true;
}
Some(&v) => (v >> 8, (v & 0xff) as u8),
}
};
}
'return_notr: loop {
macro_rules! write_one_byte {
($byte:expr) => {
if cs_avail_out == 0 {
c_state_out_len = 1;
break 'return_notr;
} else {
unsafe { *(cs_next_out as *mut u8) = $byte };
BZ_UPDATE_CRC!(c_calculatedBlockCRC, $byte);
cs_next_out = unsafe { cs_next_out.add(1) };
cs_avail_out -= 1;
}
};
}
if c_state_out_len > 0 {
let bound = Ord::min(cs_avail_out, c_state_out_len);
unsafe {
core::ptr::write_bytes(cs_next_out as *mut u8, c_state_out_ch, bound as usize);
cs_next_out = cs_next_out.add(bound as usize);
};
for _ in 0..bound {
BZ_UPDATE_CRC!(c_calculatedBlockCRC, c_state_out_ch);
}
cs_avail_out -= bound;
c_state_out_len -= bound;
if cs_avail_out == 0 {
break 'return_notr;
}
}
loop {
/* Only caused by corrupt data stream? */
if c_nblock_used > s_save_nblockPP {
return true;
}
/* can a new run be started? */
if c_nblock_used == s_save_nblockPP {
c_state_out_len = 0;
break 'return_notr;
}
c_state_out_ch = c_k0;
(c_tPos, k1) = BZ_GET_FAST_C!(c_tPos);
c_nblock_used += 1;
if k1 != c_k0 {
c_k0 = k1;
write_one_byte!(c_state_out_ch);
continue;
}
if c_nblock_used == s_save_nblockPP {
write_one_byte!(c_state_out_ch);
continue;
}
c_state_out_len = 2;
(c_tPos, k1) = BZ_GET_FAST_C!(c_tPos);
c_nblock_used += 1;
if c_nblock_used == s_save_nblockPP {
continue 'return_notr;
}
if k1 != c_k0 {
c_k0 = k1;
continue 'return_notr;
}
c_state_out_len = 3;
(c_tPos, k1) = BZ_GET_FAST_C!(c_tPos);
c_nblock_used += 1;
if c_nblock_used == s_save_nblockPP {
continue 'return_notr;
}
if k1 != c_k0 {
c_k0 = k1;
continue 'return_notr;
}
(c_tPos, k1) = BZ_GET_FAST_C!(c_tPos);
c_nblock_used += 1;
c_state_out_len = k1 as u32 + 4;
(c_tPos, c_k0) = BZ_GET_FAST_C!(c_tPos);
c_nblock_used += 1;
continue 'return_notr;
}
}
/* save */
let total_out_lo32_old: c_uint = strm.total_out_lo32;
strm.total_out_lo32 =
(strm.total_out_lo32).wrapping_add(avail_out_INIT.wrapping_sub(cs_avail_out));
if strm.total_out_lo32 < total_out_lo32_old {
strm.total_out_hi32 = (strm.total_out_hi32).wrapping_add(1);
}
s.calculatedBlockCRC = c_calculatedBlockCRC;
s.state_out_ch = c_state_out_ch;
s.state_out_len = c_state_out_len;
s.nblock_used = c_nblock_used;
s.k0 = c_k0;
s.tPos = c_tPos;
strm.next_out = cs_next_out;
strm.avail_out = cs_avail_out;
/* end save */
}
false
}
#[inline]
pub(crate) fn index_into_f(index: u32, cftab: &[u32; 257]) -> u8 {
let mut nb = 0u16;
let mut na = 256;
loop {
let mid = (nb + na) >> 1;
if index >= cftab[mid as usize] {
nb = mid;
} else {
na = mid;
}
if na - nb == 1 {
break;
}
}
// NOTE: nb < na, hence nb will fit in a u8
debug_assert!(u8::try_from(nb).is_ok());
nb as u8
}
macro_rules! GET_LL4 {
($s:expr, $i:expr) => {
$s.ll4.as_slice()[($s.tPos >> 1) as usize] as u32 >> ($s.tPos << 2 & 0x4) & 0xf
};
}
macro_rules! BZ_GET_SMALL {
($s:expr) => {
match $s.ll16.as_slice().get($s.tPos as usize) {
None => return true,
Some(&low_bits) => {
let high_bits = GET_LL4!($s, $s.tPos);
let tmp = index_into_f($s.tPos, &$s.cftab);
$s.tPos = u32::from(low_bits) | high_bits << 16;
tmp
}
}
};
}
fn un_rle_obuf_to_output_small(strm: &mut BzStream, s: &mut DState) -> bool {
let mut k1: u8;
if s.blockRandomised {
loop {
/* try to finish existing run */
loop {
if s.state_out_len == 0 {
match strm.avail_out {
0 => return false,
_ => break,
}
}
if !strm.write_byte(s.state_out_ch) {
return false;
}
BZ_UPDATE_CRC!(s.calculatedBlockCRC, s.state_out_ch);
s.state_out_len -= 1;
}
/* can a new run be started? */
if s.nblock_used == s.save.nblock as i32 + 1 {
return false;
}
/* Only caused by corrupt data stream? */
if s.nblock_used > s.save.nblock as i32 + 1 {
return true;
}
s.state_out_ch = s.k0;
s.state_out_len = 1;
k1 = BZ_GET_SMALL!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
};
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 2;
k1 = BZ_GET_SMALL!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
}
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 3;
k1 = BZ_GET_SMALL!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
}
if k1 != s.k0 {
s.k0 = k1;
continue;
};
k1 = BZ_GET_SMALL!(s);
BZ_RAND_UPD_MASK!(s);
k1 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
s.state_out_len = k1 as u32 + 4;
s.k0 = BZ_GET_SMALL!(s);
BZ_RAND_UPD_MASK!(s);
s.k0 ^= BZ_RAND_MASK!(s);
s.nblock_used += 1;
}
} else {
loop {
loop {
if s.state_out_len == 0 {
if strm.avail_out == 0 {
return false;
} else {
break;
}
}
if !strm.write_byte(s.state_out_ch) {
return false;
}
BZ_UPDATE_CRC!(s.calculatedBlockCRC, s.state_out_ch);
s.state_out_len -= 1;
}
if s.nblock_used == s.save.nblock as i32 + 1 {
return false;
}
if s.nblock_used > s.save.nblock as i32 + 1 {
return true;
}
s.state_out_len = 1;
s.state_out_ch = s.k0;
k1 = BZ_GET_SMALL!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
}
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 2;
k1 = BZ_GET_SMALL!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
}
if k1 != s.k0 {
s.k0 = k1;
continue;
};
s.state_out_len = 3;
k1 = BZ_GET_SMALL!(s);
s.nblock_used += 1;
if s.nblock_used == s.save.nblock as i32 + 1 {
continue;
}
if k1 != s.k0 {
s.k0 = k1;
continue;
};
k1 = BZ_GET_SMALL!(s);
s.nblock_used += 1;
s.state_out_len = k1 as u32 + 4;
s.k0 = BZ_GET_SMALL!(s);
s.nblock_used += 1;
}
}
}
/// Decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `strm.s.is_null()`
/// - `strm.avail_out < 1`
/// - [`BZ_DATA_ERROR`] if a data integrity error is detected in the compressed stream
/// - [`BZ_DATA_ERROR_MAGIC`] if the compressed stream doesn't begin with the right magic bytes
/// - [`BZ_MEM_ERROR`] if there wasn't enough memory available
/// - [`BZ_STREAM_END`] if the logical end of the data stream was detected and all output has been
/// written to the output buffer
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`BZ2_bzDecompressInit`]
/// * Either
/// - `strm.next_in` is `NULL` and `strm.avail_in` is 0
/// - `strm.next_in` is readable for `strm.avail_in` bytes
/// * Either
/// - `strm.next_out` is `NULL` and `strm.avail_out` is `0`
/// - `strm.next_out` is writable for `strm.avail_out` bytes
#[export_name = prefix!(BZ2_bzDecompress)]
pub unsafe extern "C" fn BZ2_bzDecompress(strm: *mut bz_stream) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzDecompressHelp(strm) as c_int
}
pub(crate) fn BZ2_bzDecompressHelp(strm: &mut BzStream) -> ReturnCode {
let Some(s) = (unsafe { strm.state.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR;
};
// FIXME use .addr() once stable
if s.strm_addr != strm as *mut _ as usize {
return ReturnCode::BZ_PARAM_ERROR;
}
let Some(allocator) = strm.allocator() else {
return ReturnCode::BZ_PARAM_ERROR;
};
loop {
match s.state {
decompress::State::BZ_X_IDLE => {
return ReturnCode::BZ_SEQUENCE_ERROR;
}
decompress::State::BZ_X_OUTPUT => {
let corrupt = match s.smallDecompress {
DecompressMode::Small => un_rle_obuf_to_output_small(strm, s),
DecompressMode::Fast => un_rle_obuf_to_output_fast(strm, s),
};
if corrupt {
return ReturnCode::BZ_DATA_ERROR;
}
if s.nblock_used == s.save.nblock as i32 + 1 && s.state_out_len == 0 {
s.calculatedBlockCRC = !s.calculatedBlockCRC;
if s.verbosity >= 3 {
debug_log!(
" {{{:#08x}, {:#08x}}}",
s.storedBlockCRC,
s.calculatedBlockCRC,
);
}
if s.verbosity >= 2 {
debug_log!("]");
}
#[cfg(not(feature = "__internal-fuzz-disable-checksum"))]
if s.calculatedBlockCRC != s.storedBlockCRC {
return ReturnCode::BZ_DATA_ERROR;
}
s.calculatedCombinedCRC = s.calculatedCombinedCRC.rotate_left(1);
s.calculatedCombinedCRC ^= s.calculatedBlockCRC;
s.state = decompress::State::BZ_X_BLKHDR_1;
continue;
} else {
return ReturnCode::BZ_OK;
}
}
_ => match decompress(strm, s, &allocator) {
ReturnCode::BZ_STREAM_END => {
if s.verbosity >= 3 {
debug_log!(
"\n combined CRCs: stored = {:#08x}, computed = {:#08x}",
s.storedCombinedCRC,
s.calculatedCombinedCRC,
);
}
#[cfg(not(feature = "__internal-fuzz-disable-checksum"))]
if s.calculatedCombinedCRC != s.storedCombinedCRC {
return ReturnCode::BZ_DATA_ERROR;
}
return ReturnCode::BZ_STREAM_END;
}
return_code => match s.state {
decompress::State::BZ_X_OUTPUT => continue,
_ => return return_code,
},
},
}
}
}
/// Deallocates all dynamically allocated data structures for this stream.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `strm.is_null()`
/// - `strm.s.is_null()`
/// - no [valid allocator](bz_stream#custom-allocators) could be configured
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// * Either
/// - `strm` is `NULL`
/// - `strm` satisfies the requirements of `&mut *strm` and was initialized with [`BZ2_bzDecompressInit`]
#[export_name = prefix!(BZ2_bzDecompressEnd)]
pub unsafe extern "C" fn BZ2_bzDecompressEnd(strm: *mut bz_stream) -> c_int {
let Some(strm) = (unsafe { BzStream::from_ptr(strm) }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
BZ2_bzDecompressEndHelp(strm) as c_int
}
fn BZ2_bzDecompressEndHelp(strm: &mut BzStream) -> ReturnCode {
let Some(s) = (unsafe { strm.state.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR;
};
// FIXME use .addr() once stable
if s.strm_addr != strm as *mut _ as usize {
return ReturnCode::BZ_PARAM_ERROR;
}
let Some(allocator) = strm.allocator() else {
return ReturnCode::BZ_PARAM_ERROR;
};
unsafe {
s.tt.dealloc(&allocator);
s.ll16.dealloc(&allocator);
s.ll4.dealloc(&allocator);
}
unsafe { allocator.deallocate(strm.state, 1) };
strm.state = ptr::null_mut::();
ReturnCode::BZ_OK
}
/// Compress the input data into the destination buffer.
///
/// This function attempts to compress the data in `source[0 .. sourceLen]` into `dest[0 .. *destLen]`.
/// If the destination buffer is big enough, `*destLen` is set to the size of the compressed data, and [`BZ_OK`] is returned.
/// If the compressed data won't fit, `*destLen` is unchanged, and [`BZ_OUTBUFF_FULL`] is returned.
///
/// For the meaning of parameters `blockSize100k`, `verbosity` and `workFactor`, see [`BZ2_bzCompressInit`].
///
/// A safe choice for the length of the output buffer is a size 1% larger than the input length,
/// plus 600 extra bytes.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `dest.is_null()`
/// - `destLen.is_null()`
/// - `source.is_null()`
/// - `!(1..=9).contains(&blockSize100k)`
/// - `!(0..=4).contains(&verbosity)`
/// - `!(0..=250).contains(&workFactor)`
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OUTBUFF_FULL`] if the size of the compressed data exceeds `*destLen`
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `destLen` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `dest` is `NULL`
/// - `dest` is writable for `*destLen` bytes
/// * Either
/// - `source` is `NULL`
/// - `source` is readable for `sourceLen`
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzBuffToBuffCompress)]
pub unsafe extern "C" fn BZ2_bzBuffToBuffCompress(
dest: *mut c_char,
destLen: *mut c_uint,
source: *mut c_char,
sourceLen: c_uint,
blockSize100k: c_int,
verbosity: c_int,
workFactor: c_int,
) -> c_int {
if dest.is_null() || source.is_null() {
return ReturnCode::BZ_PARAM_ERROR as c_int;
}
let Some(destLen) = (unsafe { destLen.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
match unsafe {
BZ2_bzBuffToBuffCompressHelp(
dest,
*destLen,
source,
sourceLen,
blockSize100k,
verbosity,
workFactor,
)
} {
Ok(written) => {
*destLen -= written;
ReturnCode::BZ_OK as c_int
}
Err(err) => err as c_int,
}
}
unsafe fn BZ2_bzBuffToBuffCompressHelp(
dest: *mut c_char,
destLen: c_uint,
source: *mut c_char,
sourceLen: c_uint,
blockSize100k: c_int,
verbosity: c_int,
workFactor: c_int,
) -> Result {
let mut strm = BzStream::zeroed();
match BZ2_bzCompressInitHelp(&mut strm, blockSize100k, verbosity, workFactor) {
ReturnCode::BZ_OK => {}
ret => return Err(ret),
}
strm.next_in = source;
strm.next_out = dest;
strm.avail_in = sourceLen;
strm.avail_out = destLen;
match BZ2_bzCompressHelp(&mut strm, Action::Finish as i32) {
ReturnCode::BZ_FINISH_OK => {
BZ2_bzCompressEndHelp(&mut strm);
Err(ReturnCode::BZ_OUTBUFF_FULL)
}
ReturnCode::BZ_STREAM_END => {
BZ2_bzCompressEndHelp(&mut strm);
Ok(strm.avail_out)
}
error => {
BZ2_bzCompressEndHelp(&mut strm);
Err(error)
}
}
}
/// Decompress the input data into the destination buffer.
///
/// This function attempts to decompress the data in `source[0 .. sourceLen]` into `dest[0 .. *destLen]`.
/// If the destination buffer is big enough, `*destLen` is set to the size of the decompressed data, and [`BZ_OK`] is returned.
/// If the decompressed data won't fit, `*destLen` is unchanged, and [`BZ_OUTBUFF_FULL`] is returned.
///
/// For the meaning of parameters `small`, `verbosity`, see [`BZ2_bzDecompressInit`].
///
/// Because the compression ratio of the compressed data cannot be known in advance,
/// there is no easy way to guarantee that the output buffer will be big enough.
/// You may of course make arrangements in your code to record the size of the uncompressed data,
/// but such a mechanism is beyond the scope of this library.
///
/// # Returns
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `dest.is_null()`
/// - `destLen.is_null()`
/// - `source.is_null()`
/// - `!(0..=1).contains(&small)`
/// - `!(0..=4).contains(&verbosity)`
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OUTBUFF_FULL`] if the size of the compressed data exceeds `*destLen`
/// - [`BZ_DATA_ERROR`] if a data integrity error is detected in the compressed stream
/// - [`BZ_DATA_ERROR_MAGIC`] if the compressed stream doesn't begin with the right magic bytes
/// - [`BZ_UNEXPECTED_EOF`] if the compressed data ends before the logical end-of-stream was detected
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `destLen` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `dest` is `NULL`
/// - `dest` is writable for `*destLen` bytes
/// * Either
/// - `source` is `NULL`
/// - `source` is readable for `sourceLen`
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzBuffToBuffDecompress)]
pub unsafe extern "C" fn BZ2_bzBuffToBuffDecompress(
dest: *mut c_char,
destLen: *mut c_uint,
source: *mut c_char,
sourceLen: c_uint,
small: c_int,
verbosity: c_int,
) -> c_int {
if dest.is_null() || destLen.is_null() || source.is_null() {
return ReturnCode::BZ_PARAM_ERROR as c_int;
}
let Some(destLen) = (unsafe { destLen.as_mut() }) else {
return ReturnCode::BZ_PARAM_ERROR as c_int;
};
match unsafe {
BZ2_bzBuffToBuffDecompressHelp(dest, *destLen, source, sourceLen, small, verbosity)
} {
Ok(written) => {
*destLen -= written;
ReturnCode::BZ_OK as c_int
}
Err(err) => err as c_int,
}
}
unsafe fn BZ2_bzBuffToBuffDecompressHelp(
dest: *mut c_char,
destLen: c_uint,
source: *mut c_char,
sourceLen: c_uint,
small: c_int,
verbosity: c_int,
) -> Result {
let mut strm = BzStream::zeroed();
match BZ2_bzDecompressInitHelp(&mut strm, verbosity, small) {
ReturnCode::BZ_OK => {}
ret => return Err(ret),
}
strm.next_in = source;
strm.next_out = dest;
strm.avail_in = sourceLen;
strm.avail_out = destLen;
match BZ2_bzDecompressHelp(&mut strm) {
ReturnCode::BZ_OK => {
BZ2_bzDecompressEndHelp(&mut strm);
match strm.avail_out {
0 => Err(ReturnCode::BZ_OUTBUFF_FULL),
_ => Err(ReturnCode::BZ_UNEXPECTED_EOF),
}
}
ReturnCode::BZ_STREAM_END => {
BZ2_bzDecompressEndHelp(&mut strm);
Ok(strm.avail_out)
}
error => {
BZ2_bzDecompressEndHelp(&mut strm);
Err(error)
}
}
}
libbz2-rs-sys-0.1.3/src/compress.rs 0000644 0000000 0000000 00000046372 10461020230 0015253 0 ustar 0000000 0000000 #![forbid(unsafe_code)]
use crate::blocksort::block_sort;
use crate::bzlib::{EState, BZ_MAX_SELECTORS, BZ_N_GROUPS, BZ_N_ITERS, BZ_RUNA, BZ_RUNB};
use crate::{assert_h, debug_log, debug_logln, huffman};
pub(crate) struct EWriter {
pub num_z: u32,
bs_live: i32,
bs_buff: u32,
}
pub(crate) struct LiveWriter<'a> {
zbits: &'a mut [u8],
writer: &'a mut EWriter,
num_z: u32,
bs_live: i32,
bs_buff: u32,
}
impl Drop for LiveWriter<'_> {
fn drop(&mut self) {
self.writer.num_z = self.num_z;
self.writer.bs_buff = self.bs_buff;
self.writer.bs_live = self.bs_live;
}
}
impl<'a> LiveWriter<'a> {
fn new(writer: &'a mut EWriter, zbits: &'a mut [u8]) -> Self {
Self {
num_z: writer.num_z,
bs_live: writer.bs_live,
bs_buff: writer.bs_buff,
zbits,
writer,
}
}
fn initialize(&mut self) {
self.bs_live = 0;
self.bs_buff = 0;
}
#[inline]
fn finish(&mut self) {
while self.bs_live > 0 {
self.zbits[self.num_z as usize] = (self.bs_buff >> 24) as u8;
self.num_z += 1;
self.bs_buff <<= 8;
self.bs_live -= 8;
}
}
#[inline]
fn flush_whole_bytes(&mut self) {
let range = self.num_z as usize..self.num_z as usize + 4;
if let Some(dst) = self.zbits.get_mut(range) {
dst.copy_from_slice(&self.bs_buff.to_be_bytes());
let bits_written = self.bs_live & !7;
self.bs_buff <<= bits_written;
self.bs_live -= bits_written;
self.num_z += (bits_written / 8) as u32;
}
while self.bs_live >= 8 {
self.zbits[self.num_z as usize] = (self.bs_buff >> 24) as u8;
self.num_z += 1;
self.bs_buff <<= 8;
self.bs_live -= 8;
}
}
fn write(&mut self, n: u8, v: u32) {
self.flush_whole_bytes();
self.bs_buff |= v << (32 - self.bs_live - i32::from(n));
self.bs_live += i32::from(n);
}
fn write_u8(&mut self, c: u8) {
self.write(8, c as u32);
}
fn write_u32(&mut self, u: u32) {
let [a, b, c, d] = u.to_le_bytes();
self.write(8, d as u32);
self.write(8, c as u32);
self.write(8, b as u32);
self.write(8, a as u32);
}
}
fn make_maps_e(s: &mut EState) {
s.nInUse = 0;
for (i, in_use) in s.inUse.iter().enumerate() {
if *in_use {
s.unseqToSeq[i] = s.nInUse as u8;
s.nInUse += 1;
}
}
}
fn generate_mtf_values(s: &mut EState) {
/*
After sorting (eg, here),
s.arr1 [ 0 .. s->nblock-1 ] holds sorted order,
and
s.arr2 [ 0 .. s->nblock-1 ]
holds the original block data.
The first thing to do is generate the MTF values,
and put them in
s.arr1 [ 0 .. s->nblock-1 ].
Because there are strictly fewer or equal MTF values
than block values, ptr values in this area are overwritten
with MTF values only when they are no longer needed.
The final compressed bitstream is generated into the
area starting at
(UChar*) (&((UChar*)s->arr2)[s->nblock])
These storage aliases are set up in bzCompressInit(),
except for the last one, which is arranged in
compressBlock().
*/
make_maps_e(s);
let EOB = s.nInUse + 1;
s.mtfFreq[..=EOB as usize].fill(0);
let mut wr = 0;
let mut zPend = 0;
let mut yy: [u8; 256] = [0; 256];
for i in 0..s.nInUse {
yy[i as usize] = i as u8;
}
for i in 0..s.nblock {
debug_assert!(wr <= i, "generateMTFValues(1)");
let mut j = s.arr1.ptr()[i as usize].wrapping_sub(1) as i32;
if j < 0 {
j += s.nblock;
}
let ll_i: u8 = s.unseqToSeq[s.arr2.block(s.nblock as usize)[j as usize] as usize];
debug_assert!((ll_i as i32) < s.nInUse, "generateMTFValues(2a)");
if yy[0] == ll_i {
zPend += 1;
} else {
if zPend > 0 {
zPend -= 1;
loop {
if zPend & 1 != 0 {
s.arr1.mtfv()[wr as usize] = 1;
wr += 1;
s.mtfFreq[1] += 1;
} else {
s.arr1.mtfv()[wr as usize] = 0;
wr += 1;
s.mtfFreq[0] += 1;
}
if zPend < 2 {
break;
}
zPend = (zPend - 2) / 2;
}
zPend = 0;
}
{
let mut rtmp: u8;
rtmp = yy[1];
yy[1] = yy[0];
j = 1;
while ll_i != rtmp {
j += 1;
core::mem::swap(&mut rtmp, &mut yy[j as usize]);
}
yy[0] = rtmp;
s.arr1.mtfv()[wr as usize] = (j + 1) as u16;
wr += 1;
s.mtfFreq[(j + 1) as usize] += 1;
}
}
}
if zPend > 0 {
zPend -= 1;
loop {
if zPend & 1 != 0 {
s.arr1.mtfv()[wr as usize] = BZ_RUNB;
wr += 1;
s.mtfFreq[BZ_RUNB as usize] += 1;
} else {
s.arr1.mtfv()[wr as usize] = BZ_RUNA;
wr += 1;
s.mtfFreq[BZ_RUNA as usize] += 1;
}
if zPend < 2 {
break;
}
zPend = (zPend - 2) / 2;
}
}
s.arr1.mtfv()[wr as usize] = EOB as u16;
wr += 1;
s.mtfFreq[EOB as usize] += 1;
s.nMTF = wr;
}
fn send_mtf_values(s: &mut EState) {
const BZ_LESSER_ICOST: u8 = 0;
const BZ_GREATER_ICOST: u8 = 15;
let mut gs: i32;
let mut ge: i32;
let mut totc: i32;
let mut bt: i32;
let mut bc: i32;
let mut nSelectors: usize = 0;
let mut selCtr: usize;
let mut nBytes: i32;
/*--
s.len: [[u8; BZ_MAX_ALPHA_SIZE]; BZ_N_GROUPS];
is a global because the decoder also needs it.
s.code: [[i32; BZ_MAX_ALPHA_SIZE]; BZ_N_GROUPS];
s.rfreq: [[i32; BZ_MAX_ALPHA_SIZE]; BZ_N_GROUPS];
are also globals only used in this proc.
Made global to keep stack frame size small.
--*/
let mtfv = s.arr1.mtfv();
if s.verbosity >= 3 {
debug_logln!(
" {} in block, {} after MTF & 1-2 coding, {}+2 syms in use",
s.nblock,
s.nMTF,
s.nInUse,
);
}
let alphaSize = usize::try_from(s.nInUse + 2).unwrap_or(0);
for t in s.len.iter_mut() {
t[..alphaSize].fill(BZ_GREATER_ICOST);
}
/*--- Decide how many coding tables to use ---*/
assert_h!(s.nMTF > 0, 3001);
let nGroups: usize = match s.nMTF {
0..200 => 2,
200..600 => 3,
600..1200 => 4,
1200..2400 => 5,
_ => 6,
};
let mut cost: [u16; 6] = [0; 6];
let cost = &mut cost[..nGroups];
let mut fave: [i32; 6] = [0; 6];
let fave = &mut fave[..nGroups];
/*--- Generate an initial set of coding tables ---*/
{
let mut tFreq: i32;
let mut aFreq: i32;
let mut nPart = nGroups;
let mut remF = s.nMTF;
let mut gs = 0i32;
while nPart > 0 {
tFreq = remF / nPart as i32;
ge = gs - 1;
aFreq = 0;
while aFreq < tFreq && ge < alphaSize as i32 - 1 {
ge += 1;
aFreq += s.mtfFreq[ge as usize];
}
if ge > gs && nPart != nGroups && nPart != 1 && (nGroups - nPart) % 2 == 1 {
aFreq -= s.mtfFreq[ge as usize];
ge -= 1;
}
if s.verbosity >= 3 {
debug_logln!(
" initial group {}, [{} .. {}], has {} syms ({:4.1}%)",
nPart,
gs,
ge,
aFreq,
100.0f64 * aFreq as f64 / s.nMTF as f64,
);
}
for v in 0..alphaSize {
s.len[nPart - 1][v] = if (gs..=ge).contains(&(v as i32)) {
BZ_LESSER_ICOST
} else {
BZ_GREATER_ICOST
};
}
nPart -= 1;
gs = ge + 1;
remF -= aFreq;
}
}
/*---
Iterate up to BZ_N_ITERS times to improve the tables.
---*/
for iter in 0..BZ_N_ITERS {
fave.fill(0);
for t in 0..nGroups {
s.rfreq[t][..alphaSize].fill(0);
}
/*---
Set up an auxiliary length table which is used to fast-track
the common case (nGroups == 6).
---*/
if nGroups == 6 {
for v in 0..alphaSize {
s.len_pack[v][0] = (s.len[1][v] as u32) << 16 | s.len[0][v] as u32;
s.len_pack[v][1] = (s.len[3][v] as u32) << 16 | s.len[2][v] as u32;
s.len_pack[v][2] = (s.len[5][v] as u32) << 16 | s.len[4][v] as u32;
}
}
nSelectors = 0;
totc = 0;
gs = 0;
loop {
/*--- Set group start & end marks. --*/
if gs >= s.nMTF {
break;
}
ge = gs + 50 - 1;
if ge >= s.nMTF {
ge = s.nMTF - 1;
}
/*--
Calculate the cost of this group as coded
by each of the coding tables.
--*/
cost.fill(0);
if nGroups == 6 && 50 == ge - gs + 1 {
let mut cost01: u32 = 0;
let mut cost23: u32 = 0;
let mut cost45: u32 = 0;
for chunk in mtfv[gs as usize..][..50].chunks_exact(10) {
for icv in chunk {
let [a, b, c, _] = s.len_pack[usize::from(*icv)];
cost01 = cost01.wrapping_add(a);
cost23 = cost23.wrapping_add(b);
cost45 = cost45.wrapping_add(c);
}
}
cost[0] = (cost01 & 0xffff) as u16;
cost[1] = (cost01 >> 16) as u16;
cost[2] = (cost23 & 0xffff) as u16;
cost[3] = (cost23 >> 16) as u16;
cost[4] = (cost45 & 0xffff) as u16;
cost[5] = (cost45 >> 16) as u16;
} else {
/*--- slow version which correctly handles all situations ---*/
for i in gs..=ge {
let icv_0: u16 = mtfv[i as usize];
for (t, c) in cost.iter_mut().enumerate() {
*c = (*c as i32 + s.len[t][icv_0 as usize] as i32) as u16;
}
}
}
/*--
Find the coding table which is best for this group,
and record its identity in the selector table.
--*/
bc = 999999999;
bt = -1;
for (t, &c) in cost.iter().enumerate() {
if (c as i32) < bc {
bc = c as i32;
bt = t as i32;
}
}
totc += bc;
fave[bt as usize] += 1;
s.selector[nSelectors] = bt as u8;
nSelectors += 1;
if nGroups == 6 && 50 == ge - gs + 1 {
for chunk in mtfv[gs as usize..][..50].chunks_exact(10) {
for &mtfv_i in chunk {
s.rfreq[bt as usize][usize::from(mtfv_i)] += 1;
}
}
} else {
for i in gs..=ge {
s.rfreq[bt as usize][mtfv[i as usize] as usize] += 1;
}
}
gs = ge + 1;
}
if s.verbosity >= 3 {
debug_log!(
" pass {}: size is {}, grp uses are ",
iter + 1,
totc / 8,
);
for f in fave.iter() {
debug_log!("{} ", f);
}
debug_logln!();
}
/*--
Recompute the tables based on the accumulated frequencies.
--*/
/* maxLen was changed from 20 to 17 in bzip2-1.0.3. See
comment in huffman.c for details. */
for t in 0..nGroups {
huffman::make_code_lengths(&mut s.len[t], &s.rfreq[t], alphaSize, 17);
}
}
assert_h!(nGroups < 8, 3002);
assert_h!(nSelectors < 32768, 3003);
assert_h!(nSelectors <= usize::from(BZ_MAX_SELECTORS), 3003);
/*--- Compute MTF values for the selectors. ---*/
{
let mut pos: [u8; BZ_N_GROUPS] = [0, 1, 2, 3, 4, 5];
let mut tmp2: u8;
let mut tmp: u8;
for (i, &ll_i) in s.selector[..nSelectors].iter().enumerate() {
let mut j = 0;
tmp = pos[j as usize];
while ll_i != tmp {
j += 1;
tmp2 = tmp;
tmp = pos[j as usize];
pos[j as usize] = tmp2;
}
pos[0] = tmp;
s.selectorMtf[i] = j as u8;
}
}
/*--- Assign actual codes for the tables. --*/
for (t, len) in s.len[..nGroups].iter().enumerate() {
let len = &len[..alphaSize];
let mut minLen = 32;
let mut maxLen = 0;
for &l in len {
maxLen = Ord::max(maxLen, l);
minLen = Ord::min(minLen, l);
}
assert_h!(maxLen <= 17, 3004);
assert_h!(minLen >= 1, 3005);
huffman::assign_codes(&mut s.code[t], len, minLen, maxLen);
}
/*--- Transmit the mapping table. ---*/
let mut writer = LiveWriter::new(&mut s.writer, s.arr2.zbits(s.nblock as usize));
{
let inUse16: [bool; 16] =
core::array::from_fn(|i| s.inUse[i * 16..][..16].iter().any(|x| *x));
nBytes = writer.num_z as i32;
for in_use in inUse16 {
writer.write(1, in_use as u32);
}
for (i, any_in_use) in inUse16.iter().enumerate() {
if *any_in_use {
for j in 0..16 {
writer.write(1, s.inUse[i * 16 + j] as u32);
}
}
}
if s.verbosity >= 3 {
debug_log!(" bytes: mapping {}, ", writer.num_z as i32 - nBytes,);
}
}
/*--- Now the selectors. ---*/
nBytes = writer.num_z as i32;
writer.write(3, nGroups as u32);
writer.write(15, nSelectors as u32);
for i in 0..nSelectors {
for _ in 0..s.selectorMtf[i] {
writer.write(1, 1);
}
writer.write(1, 0);
}
if s.verbosity >= 3 {
debug_log!("selectors {}, ", writer.num_z as i32 - nBytes);
}
/*--- Now the coding tables. ---*/
nBytes = writer.num_z as i32;
for t in 0..nGroups {
let mut curr = s.len[t][0];
writer.write(5, curr as u32);
for i in 0..alphaSize {
while curr < s.len[t][i] {
writer.write(2, 2);
curr += 1;
}
while curr > s.len[t][i] {
writer.write(2, 3);
curr -= 1;
}
writer.write(1, 0);
}
}
if s.verbosity >= 3 {
debug_log!("code lengths {}, ", writer.num_z as i32 - nBytes);
}
/*--- And finally, the block data proper ---*/
nBytes = writer.num_z as i32;
selCtr = 0;
gs = 0;
loop {
if gs >= s.nMTF {
break;
}
ge = gs + 50 - 1;
if ge >= s.nMTF {
ge = s.nMTF - 1;
}
assert_h!((s.selector[selCtr] as usize) < nGroups, 3006);
if nGroups == 6 && 50 == ge - gs + 1 {
/*--- fast track the common case ---*/
let s_len_sel_selCtr = s.len[s.selector[selCtr] as usize];
let s_code_sel_selCtr = s.code[s.selector[selCtr] as usize];
for chunk in mtfv[gs as usize..][..50].chunks_exact(10) {
for &mtfv_i in chunk {
writer.write(
s_len_sel_selCtr[usize::from(mtfv_i)],
s_code_sel_selCtr[usize::from(mtfv_i)],
);
}
}
} else {
/*--- slow version which correctly handles all situations ---*/
for i in gs..=ge {
writer.write(
s.len[s.selector[selCtr] as usize][mtfv[i as usize] as usize],
s.code[s.selector[selCtr] as usize][mtfv[i as usize] as usize],
);
}
}
gs = ge + 1;
selCtr += 1;
}
assert_h!(selCtr == nSelectors, 3007);
if s.verbosity >= 3 {
debug_logln!("codes {}", writer.num_z as i32 - nBytes);
}
}
pub(crate) fn compress_block(s: &mut EState, is_last_block: bool) {
if s.nblock > 0 {
s.blockCRC = !s.blockCRC;
s.combinedCRC = s.combinedCRC.rotate_left(1);
s.combinedCRC ^= s.blockCRC;
if s.blockNo > 1 {
s.writer.num_z = 0;
}
if s.verbosity >= 2 {
debug_logln!(
" block {}: crc = 0x{:08x}, combined CRC = 0x{:08x}, size = {}",
s.blockNo,
s.blockCRC,
s.combinedCRC,
s.nblock,
);
}
block_sort(s);
}
{
/*-- If this is the first block, create the stream header. --*/
if s.blockNo == 1 {
let mut writer = LiveWriter::new(&mut s.writer, s.arr2.zbits(s.nblock as usize));
writer.initialize();
writer.write_u8(b'B');
writer.write_u8(b'Z');
writer.write_u8(b'h');
writer.write_u8(b'0' + s.blockSize100k as u8);
}
if s.nblock > 0 {
let mut writer = LiveWriter::new(&mut s.writer, s.arr2.zbits(s.nblock as usize));
writer.write_u8(0x31);
writer.write_u8(0x41);
writer.write_u8(0x59);
writer.write_u8(0x26);
writer.write_u8(0x53);
writer.write_u8(0x59);
/*-- Now the block's CRC, so it is in a known place. --*/
writer.write_u32(s.blockCRC);
/*--
Now a single bit indicating (non-)randomisation.
As of version 0.9.5, we use a better sorting algorithm
which makes randomisation unnecessary. So always set
the randomised bit to 'no'. Of course, the decoder
still needs to be able to handle randomised blocks
so as to maintain backwards compatibility with
older versions of bzip2.
--*/
writer.write(1, 0);
writer.write(24, s.origPtr as u32);
drop(writer);
generate_mtf_values(s);
send_mtf_values(s);
}
}
/*-- If this is the last block, add the stream trailer. --*/
if is_last_block {
let mut writer = LiveWriter::new(&mut s.writer, s.arr2.zbits(s.nblock as usize));
writer.write_u8(0x17);
writer.write_u8(0x72);
writer.write_u8(0x45);
writer.write_u8(0x38);
writer.write_u8(0x50);
writer.write_u8(0x90);
writer.write_u32(s.combinedCRC);
if s.verbosity >= 2 {
debug_log!(" final combined CRC = 0x{:08x}\n ", s.combinedCRC);
}
writer.finish();
}
}
libbz2-rs-sys-0.1.3/src/crctable.rs 0000644 0000000 0000000 00000003075 10461020230 0015170 0 ustar 0000000 0000000 /// The polynomial used for the crc32 lookup table.
///
/// See also https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Polynomial_representations
const POLYNOMIAL: u32 = 0x04C11DB7;
/// Most implementations (ethernet, zlib) use the reflected version of this polynomial.
const _: () = assert!(POLYNOMIAL.reverse_bits() == 0xEDB88320);
/// Lookup table to speed up crc32 checksum calculation.
///
/// The original C implementation notes:
///
/// > I think this is an implementation of the AUTODIN-II,
/// > Ethernet & FDDI 32-bit CRC standard. Vaguely derived
/// > from code by Rob Warnock, in Section 51 of the
/// > comp.compression FAQ.
pub(crate) static BZ2_CRC32TABLE: [u32; 256] = generate_crc32_table(POLYNOMIAL);
/// Generate the crc32 lookup table.
///
/// Note that contrary to most material you'll find on the internet, we're using the non-reflected
/// polynomial, which impacts some of the logic (e.g. we bitwise and with 0x80000000 instead of 0x1).
///
/// This [article] has some excellent additional detail on how crc works, and how to make it fast.
///
/// [article]: https://create.stephan-brumme.com/crc32/
const fn generate_crc32_table(polynomial: u32) -> [u32; 256] {
let mut table = [0u32; 256];
let mut i = 0;
while i < 256 {
let mut crc = (i as u32) << 24;
let mut j = 0;
while j < 8 {
if (crc & 0x80000000) != 0 {
crc = (crc << 1) ^ polynomial;
} else {
crc <<= 1;
}
j += 1;
}
table[i] = crc;
i += 1;
}
table
}
libbz2-rs-sys-0.1.3/src/decompress.rs 0000644 0000000 0000000 00000126555 10461020230 0015566 0 ustar 0000000 0000000 #![forbid(unsafe_code)]
use core::ffi::c_int;
use crate::allocator::Allocator;
use crate::bzlib::{
index_into_f, BzStream, DSlice, DState, DecompressMode, ReturnCode, SaveArea, BZ_MAX_SELECTORS,
BZ_RAND_UPD_MASK, BZ_RUNA, BZ_RUNB,
};
use crate::{debug_log, huffman};
/*-- Constants for the fast MTF decoder. --*/
const MTFA_SIZE: u16 = 4096;
const MTFL_SIZE: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
pub(crate) enum State {
BZ_X_IDLE = 1,
BZ_X_OUTPUT = 2,
BZ_X_MAGIC_1 = 10,
BZ_X_MAGIC_2 = 11,
BZ_X_MAGIC_3 = 12,
BZ_X_MAGIC_4 = 13,
BZ_X_BLKHDR_1 = 14,
BZ_X_BLKHDR_2 = 15,
BZ_X_BLKHDR_3 = 16,
BZ_X_BLKHDR_4 = 17,
BZ_X_BLKHDR_5 = 18,
BZ_X_BLKHDR_6 = 19,
BZ_X_BCRC_1 = 20,
BZ_X_BCRC_2 = 21,
BZ_X_BCRC_3 = 22,
BZ_X_BCRC_4 = 23,
BZ_X_RANDBIT = 24,
BZ_X_ORIGPTR_1 = 25,
BZ_X_ORIGPTR_2 = 26,
BZ_X_ORIGPTR_3 = 27,
BZ_X_MAPPING_1 = 28,
BZ_X_MAPPING_2 = 29,
BZ_X_SELECTOR_1 = 30,
BZ_X_SELECTOR_2 = 31,
BZ_X_SELECTOR_3 = 32,
BZ_X_CODING_1 = 33,
BZ_X_CODING_2 = 34,
BZ_X_CODING_3 = 35,
BZ_X_MTF_1 = 36,
BZ_X_MTF_2 = 37,
BZ_X_MTF_3 = 38,
BZ_X_MTF_4 = 39,
BZ_X_MTF_5 = 40,
BZ_X_MTF_6 = 41,
BZ_X_ENDHDR_2 = 42,
BZ_X_ENDHDR_3 = 43,
BZ_X_ENDHDR_4 = 44,
BZ_X_ENDHDR_5 = 45,
BZ_X_ENDHDR_6 = 46,
BZ_X_CCRC_1 = 47,
BZ_X_CCRC_2 = 48,
BZ_X_CCRC_3 = 49,
BZ_X_CCRC_4 = 50,
}
#[allow(non_camel_case_types)]
#[derive(Eq, PartialEq)]
enum Block {
BZ_X_MAGIC_2,
BZ_X_MAGIC_3,
BZ_X_MAGIC_4,
BZ_X_BLKHDR_1,
BZ_X_BLKHDR_2,
BZ_X_BLKHDR_3,
BZ_X_BLKHDR_4,
BZ_X_BLKHDR_5,
BZ_X_BLKHDR_6,
BZ_X_BCRC_1,
BZ_X_BCRC_2,
BZ_X_BCRC_3,
BZ_X_BCRC_4,
BZ_X_RANDBIT,
BZ_X_ORIGPTR_1,
BZ_X_ORIGPTR_2,
BZ_X_ORIGPTR_3,
BZ_X_MAPPING_1,
BZ_X_MAPPING_2,
BZ_X_SELECTOR_1,
BZ_X_SELECTOR_2,
BZ_X_SELECTOR_3,
BZ_X_CODING_1,
BZ_X_CODING_2,
BZ_X_CODING_3,
BZ_X_MTF_1,
BZ_X_MTF_2,
BZ_X_MTF_3,
BZ_X_MTF_4,
BZ_X_MTF_5,
BZ_X_MTF_6,
BZ_X_ENDHDR_2,
BZ_X_ENDHDR_3,
BZ_X_ENDHDR_4,
BZ_X_ENDHDR_5,
BZ_X_ENDHDR_6,
BZ_X_CCRC_1,
BZ_X_CCRC_2,
BZ_X_CCRC_3,
BZ_X_CCRC_4,
Block1,
Block11,
Block18,
Block24,
Block25,
Block26,
Block28,
Block35,
Block39,
Block40,
Block41,
Block43,
Block45,
Block46,
Block51,
Block52,
Block56,
Block58,
}
use Block::*;
pub(crate) fn decompress(
strm: &mut BzStream,
s: &mut DState,
allocator: &Allocator,
) -> ReturnCode {
let mut current_block: Block;
let mut uc: u8;
let old_avail_in = strm.avail_in;
if let State::BZ_X_MAGIC_1 = s.state {
/*zero out the save area*/
s.save = SaveArea::default();
}
/*restore from the save area*/
let SaveArea {
mut i,
mut j,
mut t,
mut alphaSize,
mut nGroups,
mut nSelectors,
mut EOB,
mut groupNo,
mut groupPos,
mut nextSym,
mut nblockMAX100k,
mut nblock,
mut es,
mut logN,
mut curr,
mut zn,
mut zvec,
mut zj,
mut gSel,
mut gMinlen,
} = s.save;
let ret_val: ReturnCode = 'save_state_and_return: {
macro_rules! GET_BYTE {
($strm:expr, $s:expr) => {
(GET_BITS!($strm, $s, 8) & 0xFF) as u8
};
}
macro_rules! GET_BIT {
($strm:expr, $s:expr) => {
GET_BITS!($strm, $s, 1) != 0
};
}
macro_rules! GET_BITS {
($strm:expr, $s:expr, $nnn:expr) => {
loop {
if $s.bsLive >= $nnn {
let v: u64 = ($s.bsBuff >> ($s.bsLive - $nnn)) & ((1 << $nnn) - 1);
$s.bsLive -= $nnn;
break v;
}
// try and read up to 8 bytes, but only if there is no risk of reading past the
// end of the file. This is important in a multistream scenario (where 2 bzip2
// files are stored back-to-back)
//
// Before `State::BZ_X_ENDHDR_2` is reached, at least 9 more bytes are expected
// (in a valid file), so reading 8 bytes will not cross the boundary between two files.
if $s.state < State::BZ_X_ENDHDR_2 {
if let Some((bit_buffer, bits_used)) = strm.pull_u64($s.bsBuff, $s.bsLive) {
$s.bsBuff = bit_buffer;
$s.bsLive = bits_used;
continue;
}
}
if let Some((bit_buffer, bits_used)) = strm.pull_u8($s.bsBuff, $s.bsLive) {
$s.bsBuff = bit_buffer;
$s.bsLive = bits_used;
} else {
break 'save_state_and_return ReturnCode::BZ_OK;
}
}
};
}
macro_rules! update_group_pos {
($s:expr) => {
if groupPos == 0 {
groupNo += 1;
gSel = match $s.selector[..usize::from(nSelectors)].get(groupNo as usize) {
Some(&gSel) => gSel,
None => error!(BZ_DATA_ERROR),
};
gMinlen = $s.minLens[usize::from(gSel)];
groupPos = 50;
}
groupPos -= 1;
};
}
macro_rules! error {
($code:ident) => {{
break 'save_state_and_return ReturnCode::$code;
}};
}
match s.state {
State::BZ_X_MAGIC_1 => {
s.state = State::BZ_X_MAGIC_1;
uc = GET_BYTE!(strm, s);
if uc != b'B' {
error!(BZ_DATA_ERROR_MAGIC);
}
current_block = BZ_X_MAGIC_2;
}
State::BZ_X_MAGIC_2 => current_block = BZ_X_MAGIC_2,
State::BZ_X_MAGIC_3 => current_block = BZ_X_MAGIC_3,
State::BZ_X_MAGIC_4 => current_block = BZ_X_MAGIC_4,
State::BZ_X_BLKHDR_1 => current_block = BZ_X_BLKHDR_1,
State::BZ_X_BLKHDR_2 => current_block = BZ_X_BLKHDR_2,
State::BZ_X_BLKHDR_3 => current_block = BZ_X_BLKHDR_3,
State::BZ_X_BLKHDR_4 => current_block = BZ_X_BLKHDR_4,
State::BZ_X_BLKHDR_5 => current_block = BZ_X_BLKHDR_5,
State::BZ_X_BLKHDR_6 => current_block = BZ_X_BLKHDR_6,
State::BZ_X_BCRC_1 => current_block = BZ_X_BCRC_1,
State::BZ_X_BCRC_2 => current_block = BZ_X_BCRC_2,
State::BZ_X_BCRC_3 => current_block = BZ_X_BCRC_3,
State::BZ_X_BCRC_4 => current_block = BZ_X_BCRC_4,
State::BZ_X_RANDBIT => current_block = BZ_X_RANDBIT,
State::BZ_X_ORIGPTR_1 => current_block = BZ_X_ORIGPTR_1,
State::BZ_X_ORIGPTR_2 => current_block = BZ_X_ORIGPTR_2,
State::BZ_X_ORIGPTR_3 => current_block = BZ_X_ORIGPTR_3,
State::BZ_X_MAPPING_1 => current_block = BZ_X_MAPPING_1,
State::BZ_X_MAPPING_2 => current_block = BZ_X_MAPPING_2,
State::BZ_X_SELECTOR_1 => current_block = BZ_X_SELECTOR_1,
State::BZ_X_SELECTOR_2 => current_block = BZ_X_SELECTOR_2,
State::BZ_X_SELECTOR_3 => current_block = BZ_X_SELECTOR_3,
State::BZ_X_CODING_1 => current_block = BZ_X_CODING_1,
State::BZ_X_CODING_2 => current_block = BZ_X_CODING_2,
State::BZ_X_CODING_3 => current_block = BZ_X_CODING_3,
State::BZ_X_MTF_1 => current_block = BZ_X_MTF_1,
State::BZ_X_MTF_2 => current_block = BZ_X_MTF_2,
State::BZ_X_MTF_3 => current_block = BZ_X_MTF_3,
State::BZ_X_MTF_4 => current_block = BZ_X_MTF_4,
State::BZ_X_MTF_5 => current_block = BZ_X_MTF_5,
State::BZ_X_MTF_6 => current_block = BZ_X_MTF_6,
State::BZ_X_ENDHDR_2 => current_block = BZ_X_ENDHDR_2,
State::BZ_X_ENDHDR_3 => current_block = BZ_X_ENDHDR_3,
State::BZ_X_ENDHDR_4 => current_block = BZ_X_ENDHDR_4,
State::BZ_X_ENDHDR_5 => current_block = BZ_X_ENDHDR_5,
State::BZ_X_ENDHDR_6 => current_block = BZ_X_ENDHDR_6,
State::BZ_X_CCRC_1 => current_block = BZ_X_CCRC_1,
State::BZ_X_CCRC_2 => current_block = BZ_X_CCRC_2,
State::BZ_X_CCRC_3 => current_block = BZ_X_CCRC_3,
State::BZ_X_CCRC_4 => current_block = BZ_X_CCRC_4,
State::BZ_X_IDLE | State::BZ_X_OUTPUT => unreachable!(),
}
if current_block == BZ_X_MAGIC_2 {
s.state = State::BZ_X_MAGIC_2;
uc = GET_BYTE!(strm, s);
if uc != b'Z' {
error!(BZ_DATA_ERROR_MAGIC);
}
current_block = BZ_X_MAGIC_3;
}
if current_block == BZ_X_MAGIC_3 {
s.state = State::BZ_X_MAGIC_3;
uc = GET_BYTE!(strm, s);
if uc != b'h' {
error!(BZ_DATA_ERROR_MAGIC);
}
current_block = BZ_X_MAGIC_4;
}
if current_block == BZ_X_MAGIC_4 {
s.state = State::BZ_X_MAGIC_4;
s.blockSize100k = GET_BYTE!(strm, s);
if !(b'1'..=b'9').contains(&s.blockSize100k) {
error!(BZ_DATA_ERROR_MAGIC);
}
s.blockSize100k -= b'0';
match s.smallDecompress {
DecompressMode::Small => {
// SAFETY: we assume allocation is safe
let ll16_len = usize::from(s.blockSize100k) * 100000;
let Some(ll16) = DSlice::alloc(allocator, ll16_len) else {
error!(BZ_MEM_ERROR);
};
// SAFETY: we assume allocation is safe
let ll4_len = (1 + usize::from(s.blockSize100k) * 100000) >> 1;
let Some(ll4) = DSlice::alloc(allocator, ll4_len) else {
error!(BZ_MEM_ERROR);
};
s.ll16 = ll16;
s.ll4 = ll4;
}
DecompressMode::Fast => {
// SAFETY: we assume allocation is safe
let tt_len = usize::from(s.blockSize100k) * 100000;
let Some(tt) = DSlice::alloc(allocator, tt_len) else {
error!(BZ_MEM_ERROR);
};
s.tt = tt;
}
}
current_block = BZ_X_BLKHDR_1;
}
if current_block == BZ_X_BLKHDR_1 {
s.state = State::BZ_X_BLKHDR_1;
uc = GET_BYTE!(strm, s);
match uc {
0x17 => current_block = BZ_X_ENDHDR_2,
0x31 => current_block = BZ_X_BLKHDR_2,
_ => error!(BZ_DATA_ERROR),
};
}
match current_block {
BZ_X_ENDHDR_2 => {
s.state = State::BZ_X_ENDHDR_2;
uc = GET_BYTE!(strm, s);
if uc != 0x72 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_ENDHDR_3;
}
BZ_X_BLKHDR_2 => {
s.state = State::BZ_X_BLKHDR_2;
uc = GET_BYTE!(strm, s);
if uc != 0x41 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_BLKHDR_3;
}
_ => {}
}
match current_block {
BZ_X_ENDHDR_3 => {
s.state = State::BZ_X_ENDHDR_3;
uc = GET_BYTE!(strm, s);
if uc != 0x45 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_ENDHDR_4;
}
BZ_X_BLKHDR_3 => {
s.state = State::BZ_X_BLKHDR_3;
uc = GET_BYTE!(strm, s);
if uc != 0x59 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_BLKHDR_4;
}
_ => {}
}
match current_block {
BZ_X_ENDHDR_4 => {
s.state = State::BZ_X_ENDHDR_4;
uc = GET_BYTE!(strm, s);
if uc != 0x38 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_ENDHDR_5;
}
BZ_X_BLKHDR_4 => {
s.state = State::BZ_X_BLKHDR_4;
uc = GET_BYTE!(strm, s);
if uc != 0x26 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_BLKHDR_5;
}
_ => {}
}
match current_block {
BZ_X_ENDHDR_5 => {
s.state = State::BZ_X_ENDHDR_5;
uc = GET_BYTE!(strm, s);
if uc != 0x50 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_ENDHDR_6;
}
BZ_X_BLKHDR_5 => {
s.state = State::BZ_X_BLKHDR_5;
uc = GET_BYTE!(strm, s);
if uc != 0x53 {
error!(BZ_DATA_ERROR);
}
current_block = BZ_X_BLKHDR_6;
}
_ => {}
}
match current_block {
BZ_X_ENDHDR_6 => {
s.state = State::BZ_X_ENDHDR_6;
uc = GET_BYTE!(strm, s);
if uc != 0x90 {
error!(BZ_DATA_ERROR);
}
s.storedCombinedCRC = 0_u32;
current_block = BZ_X_CCRC_1;
}
BZ_X_BLKHDR_6 => {
s.state = State::BZ_X_BLKHDR_6;
uc = GET_BYTE!(strm, s);
if uc != 0x59 {
error!(BZ_DATA_ERROR);
}
s.currBlockNo += 1;
if s.verbosity >= 2 {
debug_log!("\n [{}: huff+mtf ", s.currBlockNo);
}
s.storedBlockCRC = 0_u32;
current_block = BZ_X_BCRC_1;
}
_ => {}
}
match current_block {
BZ_X_CCRC_1 => {
s.state = State::BZ_X_CCRC_1;
uc = GET_BYTE!(strm, s);
s.storedCombinedCRC = s.storedCombinedCRC << 8 | uc as u32;
current_block = BZ_X_CCRC_2;
}
BZ_X_BCRC_1 => {
s.state = State::BZ_X_BCRC_1;
uc = GET_BYTE!(strm, s);
s.storedBlockCRC = s.storedBlockCRC << 8 | uc as u32;
current_block = BZ_X_BCRC_2;
}
_ => {}
}
match current_block {
BZ_X_CCRC_2 => {
s.state = State::BZ_X_CCRC_2;
uc = GET_BYTE!(strm, s);
s.storedCombinedCRC = s.storedCombinedCRC << 8 | uc as u32;
current_block = BZ_X_CCRC_3;
}
BZ_X_BCRC_2 => {
s.state = State::BZ_X_BCRC_2;
uc = GET_BYTE!(strm, s);
s.storedBlockCRC = s.storedBlockCRC << 8 | uc as u32;
current_block = BZ_X_BCRC_3;
}
_ => {}
}
match current_block {
BZ_X_CCRC_3 => {
s.state = State::BZ_X_CCRC_3;
uc = GET_BYTE!(strm, s);
s.storedCombinedCRC = s.storedCombinedCRC << 8 | uc as u32;
current_block = BZ_X_CCRC_4;
}
BZ_X_BCRC_3 => {
s.state = State::BZ_X_BCRC_3;
uc = GET_BYTE!(strm, s);
s.storedBlockCRC = s.storedBlockCRC << 8 | uc as u32;
current_block = BZ_X_BCRC_4;
}
_ => {}
}
match current_block {
BZ_X_BCRC_4 => {
s.state = State::BZ_X_BCRC_4;
uc = GET_BYTE!(strm, s);
s.storedBlockCRC = s.storedBlockCRC << 8 | uc as u32;
current_block = BZ_X_RANDBIT;
}
BZ_X_CCRC_4 => {
s.state = State::BZ_X_CCRC_4;
uc = GET_BYTE!(strm, s);
s.storedCombinedCRC = s.storedCombinedCRC << 8 | uc as u32;
s.state = State::BZ_X_IDLE;
error!(BZ_STREAM_END);
}
_ => {}
}
if current_block == BZ_X_RANDBIT {
s.state = State::BZ_X_RANDBIT;
s.blockRandomised = GET_BITS!(strm, s, 1) != 0;
s.origPtr = 0;
current_block = BZ_X_ORIGPTR_1;
}
if current_block == BZ_X_ORIGPTR_1 {
s.state = State::BZ_X_ORIGPTR_1;
uc = GET_BYTE!(strm, s);
s.origPtr = s.origPtr << 8 | i32::from(uc);
current_block = BZ_X_ORIGPTR_2;
}
if current_block == BZ_X_ORIGPTR_2 {
s.state = State::BZ_X_ORIGPTR_2;
uc = GET_BYTE!(strm, s);
s.origPtr = s.origPtr << 8 | i32::from(uc);
current_block = BZ_X_ORIGPTR_3;
}
if current_block == BZ_X_ORIGPTR_3 {
s.state = State::BZ_X_ORIGPTR_3;
uc = GET_BYTE!(strm, s);
s.origPtr = s.origPtr << 8 | i32::from(uc);
if !(0..10 + 100000 * i32::from(s.blockSize100k)).contains(&s.origPtr) {
error!(BZ_DATA_ERROR);
}
i = 0;
current_block = Block43;
}
// mutable because they need to be reborrowed
let tt = s.tt.as_mut_slice();
let ll16 = s.ll16.as_mut_slice();
let ll4 = s.ll4.as_mut_slice();
'state_machine: loop {
match current_block {
BZ_X_MAPPING_1 => {
s.state = State::BZ_X_MAPPING_1;
uc = GET_BIT!(strm, s) as u8;
s.inUse16[i as usize] = uc == 1;
i += 1;
current_block = Block43;
continue;
}
Block43 => {
if i < 16 {
current_block = BZ_X_MAPPING_1;
continue;
}
s.inUse.fill(false);
i = 0;
current_block = Block18;
}
BZ_X_MAPPING_2 => {
s.state = State::BZ_X_MAPPING_2;
uc = GET_BIT!(strm, s) as u8;
if uc == 1 {
s.inUse[(i * 16 + j) as usize] = true;
}
j += 1;
current_block = Block28;
}
BZ_X_SELECTOR_1 => {
s.state = State::BZ_X_SELECTOR_1;
nGroups = GET_BITS!(strm, s, 3) as u8;
if (2..=6).contains(&nGroups) {
current_block = BZ_X_SELECTOR_2;
continue;
}
error!(BZ_DATA_ERROR);
}
BZ_X_SELECTOR_2 => {
s.state = State::BZ_X_SELECTOR_2;
nSelectors = GET_BITS!(strm, s, 15) as u16;
if nSelectors < 1 {
error!(BZ_DATA_ERROR);
} else {
i = 0;
}
current_block = Block39;
}
BZ_X_SELECTOR_3 => {
s.state = State::BZ_X_SELECTOR_3;
uc = GET_BIT!(strm, s) as u8;
if uc == 0 {
current_block = Block1;
} else {
j += 1;
if j >= i32::from(nGroups) {
error!(BZ_DATA_ERROR);
} else {
current_block = Block25;
}
}
}
BZ_X_CODING_1 => {
s.state = State::BZ_X_CODING_1;
curr = GET_BITS!(strm, s, 5) as u8;
i = 0;
current_block = Block26;
}
BZ_X_CODING_2 => {
s.state = State::BZ_X_CODING_2;
uc = GET_BIT!(strm, s) as u8;
if uc != 0 {
current_block = BZ_X_CODING_3;
continue;
}
current_block = Block51;
}
BZ_X_CODING_3 => {
s.state = State::BZ_X_CODING_3;
uc = GET_BIT!(strm, s) as u8;
match uc {
0 => curr += 1,
_ => curr -= 1,
}
current_block = Block45;
}
BZ_X_MTF_1 => {
s.state = State::BZ_X_MTF_1;
zvec = GET_BITS!(strm, s, zn as i32) as i32;
current_block = Block56;
}
BZ_X_MTF_2 => {
s.state = State::BZ_X_MTF_2;
zj = GET_BIT!(strm, s);
zvec = zvec << 1 | zj as i32;
current_block = Block56;
}
BZ_X_MTF_3 => {
s.state = State::BZ_X_MTF_3;
zvec = GET_BITS!(strm, s, zn as i32) as i32;
current_block = Block52;
}
BZ_X_MTF_4 => {
s.state = State::BZ_X_MTF_4;
zj = GET_BIT!(strm, s);
zvec = zvec << 1 | zj as i32;
current_block = Block52;
}
BZ_X_MTF_5 => {
s.state = State::BZ_X_MTF_5;
zvec = GET_BITS!(strm, s, zn as i32) as i32;
current_block = Block24;
}
_ => {
s.state = State::BZ_X_MTF_6;
zj = GET_BIT!(strm, s);
zvec = zvec << 1 | zj as i32;
current_block = Block24;
}
}
macro_rules! get_next_sym {
($next_block:ident) => {
if zn > 20 {
// zn is higher than the longest code, that's invalid input
error!(BZ_DATA_ERROR);
} else if zvec <= s.limit[usize::from(gSel)][zn as usize] {
let index = zvec - s.base[usize::from(gSel)][zn as usize];
match s.perm[usize::from(gSel)].get(index as usize) {
Some(&nextSym) => nextSym,
None => error!(BZ_DATA_ERROR),
}
} else {
zn += 1;
current_block = $next_block;
continue 'state_machine;
}
};
}
match current_block {
Block24 => {
nextSym = get_next_sym!(BZ_X_MTF_6);
current_block = Block40;
}
Block52 => {
nextSym = get_next_sym!(BZ_X_MTF_4);
if nextSym == BZ_RUNA || nextSym == BZ_RUNB {
current_block = Block46;
} else {
let uc = s.seqToUnseq[usize::from(s.mtfa[usize::from(s.mtfbase[0])])];
s.unzftab[usize::from(uc)] += es;
match s.smallDecompress {
DecompressMode::Small => {
match ll16.get_mut(nblock as usize..(nblock + es) as usize) {
Some(slice) => slice.fill(u16::from(uc)),
None => error!(BZ_DATA_ERROR),
};
nblock += es;
}
DecompressMode::Fast => {
match tt.get_mut(nblock as usize..(nblock + es) as usize) {
Some(slice) => slice.fill(u32::from(uc)),
None => error!(BZ_DATA_ERROR),
};
nblock += es;
}
}
current_block = Block40;
}
}
Block56 => {
nextSym = get_next_sym!(BZ_X_MTF_2);
current_block = Block40;
}
_ => {}
}
if current_block == Block40 {
if nextSym == EOB {
current_block = Block41;
} else if nextSym == BZ_RUNA || nextSym == BZ_RUNB {
es = 0;
logN = 0;
current_block = Block46;
} else if nblock >= 100000 * u32::from(nblockMAX100k) {
error!(BZ_DATA_ERROR);
} else {
let uc = usize::from(initialize_mtfa(&mut s.mtfa, &mut s.mtfbase, nextSym));
let index = s.seqToUnseq[uc];
s.unzftab[usize::from(index)] += 1;
match s.smallDecompress {
DecompressMode::Small => ll16[nblock as usize] = u16::from(index),
DecompressMode::Fast => tt[nblock as usize] = u32::from(index),
}
nblock += 1;
update_group_pos!(s);
zn = gMinlen;
current_block = BZ_X_MTF_5;
continue;
}
match current_block {
Block46 => {}
_ => {
if s.origPtr < 0 || s.origPtr >= nblock as i32 {
error!(BZ_DATA_ERROR);
} else {
if s.unzftab.iter().any(|e| !(0..=nblock).contains(e)) {
error!(BZ_DATA_ERROR);
}
s.cftab[0] = 0;
s.cftab[1..].copy_from_slice(&s.unzftab);
for i in 1..s.cftab.len() {
s.cftab[i] += s.cftab[i - 1];
}
if s.cftab.iter().any(|e| !(0..=nblock).contains(e)) {
error!(BZ_DATA_ERROR);
}
// FIXME: use https://doc.rust-lang.org/std/primitive.slice.html#method.is_sorted
// when available in our MSRV (requires >= 1.82.0)
if s.cftab.windows(2).any(|w| w[0] > w[1]) {
error!(BZ_DATA_ERROR);
}
s.state_out_len = 0;
s.state_out_ch = 0;
s.calculatedBlockCRC = u32::MAX;
s.state = State::BZ_X_OUTPUT;
if s.verbosity >= 2 {
debug_log!("rt+rld");
}
match s.smallDecompress {
DecompressMode::Small => {
// Make a copy of cftab, used in generation of T
s.cftabCopy = s.cftab;
// compute the T vector
for i in 0..nblock as usize {
let uc = usize::from(ll16[i]);
ll16[i] = (s.cftabCopy[uc] & 0xffff) as u16;
// set the lower or higher nibble depending on i
let (mask, shift) = match i & 0x1 {
0 => (0xF0, 0),
_ => (0x0F, 4),
};
ll4[i / 2] &= mask;
ll4[i / 2] |= ((s.cftabCopy[uc] >> 16) << shift) as u8;
s.cftabCopy[uc] += 1;
}
// Compute T^(-1) by pointer reversal on T
i = s.origPtr;
j = (ll16[i as usize] as u32
| (ll4[(i >> 1) as usize] as u32 >> (i << 2 & 0x4) & 0xf)
<< 16) as i32;
loop {
let tmp_0: i32 = (ll16[j as usize] as u32
| (ll4[(j >> 1) as usize] as u32 >> (j << 2 & 0x4)
& 0xf)
<< 16)
as i32;
ll16[j as usize] = (i & 0xffff) as u16;
if j & 0x1 == 0 {
ll4[(j >> 1) as usize] =
(ll4[(j >> 1) as usize] as c_int & 0xf0 | i >> 16)
as u8;
} else {
ll4[(j >> 1) as usize] =
(ll4[(j >> 1) as usize] as c_int & 0xf
| (i >> 16) << 4)
as u8;
}
i = j;
j = tmp_0;
if i == s.origPtr {
break;
}
}
s.tPos = s.origPtr as u32;
s.nblock_used = 0;
s.k0 = index_into_f(s.tPos, &s.cftab);
s.tPos = match ll16.get(s.tPos as usize) {
None => error!(BZ_DATA_ERROR),
Some(&low_bits) => {
let high_bits = ll4[(s.tPos >> 1) as usize]
>> (s.tPos << 2 & 0x4)
& 0xf;
u32::from(low_bits) | u32::from(high_bits) << 16
}
};
s.nblock_used += 1;
if s.blockRandomised {
s.rNToGo = 0;
s.rTPos = 0;
BZ_RAND_UPD_MASK!(s);
s.k0 ^= u8::from(s.rNToGo == 1)
}
}
DecompressMode::Fast => {
for i in 0..nblock as usize {
let uc = (tt[i] & 0xff) as usize;
tt[s.cftab[uc] as usize] |= (i << 8) as u32;
s.cftab[uc] += 1;
}
s.tPos = tt[s.origPtr as usize] >> 8;
s.nblock_used = 0;
s.tPos = match tt.get(s.tPos as usize) {
Some(&tPos) => tPos,
None => error!(BZ_DATA_ERROR),
};
s.k0 = (s.tPos & 0xff) as u8;
s.tPos >>= 8;
s.nblock_used += 1;
if s.blockRandomised {
s.rNToGo = 0;
s.rTPos = 0;
BZ_RAND_UPD_MASK!(s);
s.k0 ^= u8::from(s.rNToGo == 1)
}
}
}
break 'save_state_and_return ReturnCode::BZ_OK;
}
}
}
}
if current_block == Block46 {
// Check that N doesn't get too big, so that es doesn't
// go negative. The maximum value that can be
// RUNA/RUNB encoded is equal to the block size (post
// the initial RLE), viz, 900k, so bounding N at 2
// million should guard against overflow without
// rejecting any legitimate inputs.
const LOG_2MB: u8 = 21; // 2 * 1024 * 1024
if logN >= LOG_2MB {
error!(BZ_DATA_ERROR);
} else {
let mul = match nextSym {
BZ_RUNA => 1,
BZ_RUNB => 2,
_ => 0,
};
es += mul * (1 << logN);
logN += 1;
update_group_pos!(s);
zn = gMinlen;
current_block = BZ_X_MTF_3;
continue;
}
}
loop {
match current_block {
Block28 => {
if j < 16 {
current_block = BZ_X_MAPPING_2;
continue 'state_machine;
}
}
Block39 => {
if i < i32::from(nSelectors) {
j = 0;
current_block = Block25;
continue;
} else {
// make sure that the constant fits in a u16
nSelectors = Ord::min(nSelectors, BZ_MAX_SELECTORS);
let mut pos: [u8; 6] = [0, 1, 2, 3, 4, 5];
for i in 0..usize::from(nSelectors) {
pos[..=usize::from(s.selectorMtf[i])].rotate_right(1);
s.selector[i] = pos[0];
}
// try to read the coding tables in one go if there is sufficient input
let bits_needed =
usize::from(nGroups) * (5 + (usize::from(alphaSize) * 2 * 20));
let bytes_needed = bits_needed.div_ceil(8);
if strm.avail_in as usize >= bytes_needed {
for t in 0..usize::from(nGroups) {
let mut curr = GET_BITS!(strm, s, 5);
for i in 0..usize::from(alphaSize) {
loop {
if !(1..=20).contains(&curr) {
error!(BZ_DATA_ERROR);
}
if !GET_BIT!(strm, s) {
break;
};
match GET_BIT!(strm, s) {
false => curr += 1,
true => curr -= 1,
}
}
s.len[t][i] = curr as u8;
}
}
t = nGroups;
current_block = Block35;
break;
} else {
t = 0;
current_block = Block35;
break;
}
}
}
Block18 => {
if let Some(&in_use) = s.inUse16.get(i as usize) {
if in_use {
j = 0;
current_block = Block28;
continue;
}
} else {
// inlined `make_maps_d`
s.nInUse = 0;
for (i, in_use) in s.inUse.iter().enumerate() {
if *in_use {
s.seqToUnseq[usize::from(s.nInUse)] = i as u8;
s.nInUse += 1;
}
}
if s.nInUse == 0 {
current_block = Block11;
break;
} else {
current_block = Block58;
break;
}
}
}
Block51 => {
s.len[t as usize][i as usize] = curr;
i += 1;
current_block = Block26;
continue;
}
Block26 => {
if i < i32::from(alphaSize) {
current_block = Block45;
continue;
}
t += 1;
current_block = Block35;
break;
}
Block1 => {
if i < i32::from(BZ_MAX_SELECTORS) {
s.selectorMtf[i as usize] = j as u8;
}
i += 1;
current_block = Block39;
continue;
}
Block25 => {
current_block = BZ_X_SELECTOR_3;
continue 'state_machine;
}
_ => {
if false {
current_block = Block51;
continue;
}
if (1..=20).contains(&curr) {
current_block = BZ_X_CODING_2;
continue 'state_machine;
}
error!(BZ_DATA_ERROR);
}
}
i += 1;
current_block = Block18;
}
match current_block {
Block58 => {
alphaSize = s.nInUse + 2;
current_block = BZ_X_SELECTOR_1;
}
Block11 => {
error!(BZ_DATA_ERROR);
}
_ => {
if t < nGroups {
current_block = BZ_X_CODING_1;
continue;
}
/*--- Create the Huffman decoding tables ---*/
for t in 0..usize::from(nGroups) {
// NOTE: s.nInUse <= 256, alphaSize <= 258
let len = &s.len[t][..usize::from(alphaSize)];
let mut minLen = 32u8;
let mut maxLen = 0u8;
for ¤t in len {
maxLen = Ord::max(maxLen, current);
minLen = Ord::min(minLen, current);
}
s.minLens[t] = minLen;
huffman::create_decode_tables(
&mut s.limit[t],
&mut s.base[t],
&mut s.perm[t],
len,
minLen,
maxLen,
);
}
/*--- Now the MTF values ---*/
EOB = s.nInUse + 1;
nblockMAX100k = s.blockSize100k;
s.unzftab.fill(0);
/*-- MTF init --*/
let mut kk: u16 = MTFA_SIZE - 1;
for ii in (0..256 / MTFL_SIZE).rev() {
for jj in (0..MTFL_SIZE).rev() {
s.mtfa[usize::from(kk)] = (ii * MTFL_SIZE + jj) as u8;
kk -= 1;
}
s.mtfbase[ii] = kk + 1;
}
/*-- end MTF init --*/
nblock = 0;
groupNo = -1;
groupPos = 0;
update_group_pos!(s);
zn = gMinlen;
current_block = BZ_X_MTF_1;
}
}
}
};
s.save = SaveArea {
i,
j,
t,
alphaSize,
nGroups,
nSelectors,
EOB,
groupNo,
groupPos,
nextSym,
nblockMAX100k,
nblock,
es,
logN,
curr,
zn,
zvec,
zj,
gSel,
gMinlen,
};
// update total_in with how many bytes were read during this call
let bytes_read = old_avail_in - strm.avail_in;
let old_total_in_lo32 = strm.total_in_lo32;
strm.total_in_lo32 = strm.total_in_lo32.wrapping_add(bytes_read);
strm.total_in_hi32 += (strm.total_in_lo32 < old_total_in_lo32) as u32;
ret_val
}
fn initialize_mtfa(mtfa: &mut [u8; 4096], mtfbase: &mut [u16; 16], nextSym: u16) -> u8 {
let nn = usize::from(nextSym - 1);
if nn < MTFL_SIZE {
// avoid general case expense
let pp = usize::from(mtfbase[0]);
let uc = mtfa[pp + nn];
mtfa[pp..][..=nn].rotate_right(1);
uc
} else {
// general case
let mut lno = nn.wrapping_div(MTFL_SIZE);
let off = nn.wrapping_rem(MTFL_SIZE);
let base = usize::from(mtfbase[lno]);
let uc = mtfa[base + off];
// shift this range one to the right
mtfa.copy_within(base..base + off, base + 1);
mtfbase[lno] += 1;
while lno > 0 {
mtfbase[lno] -= 1;
mtfa[usize::from(mtfbase[lno])] = mtfa[usize::from(mtfbase[lno - 1] + 16 - 1)];
lno -= 1;
}
mtfbase[0] -= 1;
mtfa[usize::from(mtfbase[0])] = uc;
if mtfbase[0] == 0 {
let mut kk = MTFA_SIZE - 1;
for ii in (0..256 / MTFL_SIZE).rev() {
for jj in (0..MTFL_SIZE).rev() {
mtfa[usize::from(kk)] = mtfa[usize::from(mtfbase[ii]) + jj];
kk -= 1;
}
mtfbase[ii] = kk + 1;
}
}
uc
}
}
libbz2-rs-sys-0.1.3/src/high_level.rs 0000644 0000000 0000000 00000117416 10461020230 0015524 0 ustar 0000000 0000000 #![allow(unsafe_op_in_unsafe_fn)]
use core::ffi::{c_char, c_int, c_uint, c_void, CStr};
use core::{mem, ptr};
use libc::FILE;
use libc::{fclose, fdopen, ferror, fflush, fgetc, fopen, fread, fwrite, ungetc};
use crate::allocator::Allocator;
use crate::bzlib::prefix;
use crate::bzlib::BZ_MAX_UNUSED_U32;
use crate::bzlib::{bz_stream, BZ2_bzCompressEnd, BZ2_bzDecompressEnd};
use crate::bzlib::{Action, BzStream, ReturnCode};
use crate::bzlib::{
BZ2_bzCompressHelp, BZ2_bzCompressInitHelp, BZ2_bzDecompressHelp, BZ2_bzDecompressInitHelp,
};
use crate::BZ_MAX_UNUSED;
#[cfg(doc)]
use crate::{
BZ2_bzCompressInit, BZ2_bzDecompressInit, BZ_CONFIG_ERROR, BZ_DATA_ERROR, BZ_DATA_ERROR_MAGIC,
BZ_FINISH, BZ_FINISH_OK, BZ_FLUSH, BZ_FLUSH_OK, BZ_IO_ERROR, BZ_MEM_ERROR, BZ_OK,
BZ_OUTBUFF_FULL, BZ_PARAM_ERROR, BZ_RUN, BZ_RUN_OK, BZ_SEQUENCE_ERROR, BZ_STREAM_END,
BZ_UNEXPECTED_EOF,
};
// FIXME remove this
#[cfg(not(target_os = "windows"))]
extern "C" {
#[cfg_attr(target_os = "macos", link_name = "__stdinp")]
static mut stdin: *mut FILE;
#[cfg_attr(target_os = "macos", link_name = "__stdoutp")]
static mut stdout: *mut FILE;
}
#[cfg(all(target_os = "windows", target_env = "gnu"))]
extern "C" {
fn __acrt_iob_func(idx: libc::c_uint) -> *mut FILE;
}
#[cfg(not(target_os = "windows"))]
macro_rules! STDIN {
() => {
stdin
};
}
#[cfg(all(target_os = "windows", target_env = "gnu"))]
macro_rules! STDIN {
() => {
__acrt_iob_func(0)
};
}
#[cfg(not(target_os = "windows"))]
macro_rules! STDOUT {
() => {
stdout
};
}
#[cfg(all(target_os = "windows", target_env = "gnu"))]
macro_rules! STDOUT {
() => {
__acrt_iob_func(1)
};
}
/// Abstract handle to a `.bz2` file.
///
/// This type is created by:
///
/// - [`BZ2_bzReadOpen`]
/// - [`BZ2_bzWriteOpen`]
/// - [`BZ2_bzopen`]
///
/// And destructed by:
///
/// - [`BZ2_bzReadClose`]
/// - [`BZ2_bzWriteClose`]
/// - [`BZ2_bzclose`]
#[allow(non_camel_case_types)]
pub struct BZFILE {
handle: *mut FILE,
buf: [i8; BZ_MAX_UNUSED as usize],
bufN: i32,
strm: bz_stream,
lastErr: ReturnCode,
operation: Operation,
initialisedOk: bool,
}
unsafe fn myfeof(f: *mut FILE) -> bool {
let c = fgetc(f);
if c == -1 {
return true;
}
ungetc(c, f);
false
}
macro_rules! BZ_SETERR_RAW {
($bzerror:expr, $bzf:expr, $return_code:expr) => {
if let Some(bzerror) = $bzerror.as_deref_mut() {
*bzerror = $return_code as c_int;
}
if let Some(bzf) = $bzf.as_deref_mut() {
bzf.lastErr = $return_code;
}
};
}
macro_rules! BZ_SETERR {
($bzerror:expr, $bzf:expr, $return_code:expr) => {
if let Some(bzerror) = $bzerror.as_deref_mut() {
*bzerror = $return_code as c_int;
}
$bzf.lastErr = $return_code;
};
}
/// Prepare to write compressed data to a file handle.
///
/// The file handle `f` should refer to a file which has been opened for writing, and for which the error indicator `libc::ferror(f)` is not set.
///
/// For the meaning of parameters `blockSize100k`, `verbosity` and `workFactor`, see [`BZ2_bzCompressInit`].
///
/// # Returns
///
/// - if `*bzerror` is [`BZ_OK`], a valid pointer to an abstract `BZFILE`
/// - otherwise `NULL`
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `f.is_null`
/// - `!(1..=9).contains(&blockSize100k)`
/// - `!(0..=4).contains(&verbosity)`
/// - `!(0..=250).contains(&workFactor)`
/// - [`BZ_CONFIG_ERROR`] if no default allocator is configured
/// - [`BZ_IO_ERROR`] if `libc::ferror(f)` is nonzero
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `f` is `NULL`
/// - `f` a valid pointer to a `FILE`
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzWriteOpen)]
pub unsafe extern "C" fn BZ2_bzWriteOpen(
bzerror: *mut c_int,
f: *mut FILE,
blockSize100k: c_int,
verbosity: c_int,
workFactor: c_int,
) -> *mut BZFILE {
BZ2_bzWriteOpenHelp(bzerror.as_mut(), f, blockSize100k, verbosity, workFactor)
}
unsafe fn BZ2_bzWriteOpenHelp(
mut bzerror: Option<&mut c_int>,
f: *mut FILE,
blockSize100k: c_int,
verbosity: c_int,
mut workFactor: c_int,
) -> *mut BZFILE {
let mut bzf: Option<&mut BZFILE> = None;
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_OK);
if f.is_null()
|| !(1..=9).contains(&blockSize100k)
|| !(0..=250).contains(&workFactor)
|| !(0..=4).contains(&verbosity)
{
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
return ptr::null_mut();
}
if ferror(f) != 0 {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return ptr::null_mut();
}
let Some(allocator) = Allocator::DEFAULT else {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
return ptr::null_mut();
};
let Some(bzf) = allocator.allocate_zeroed::(1) else {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_MEM_ERROR);
return ptr::null_mut();
};
// SAFETY: bzf is non-null and correctly initalized
let bzf = unsafe { &mut *bzf };
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
bzf.initialisedOk = false;
bzf.bufN = 0;
bzf.handle = f;
bzf.operation = Operation::Writing;
bzf.strm.bzalloc = None;
bzf.strm.bzfree = None;
bzf.strm.opaque = ptr::null_mut();
if workFactor == 0 {
workFactor = 30;
}
match BZ2_bzCompressInitHelp(
BzStream::from_mut(&mut bzf.strm),
blockSize100k,
verbosity,
workFactor,
) {
ReturnCode::BZ_OK => {
bzf.strm.avail_in = 0;
bzf.initialisedOk = true;
bzf as *mut BZFILE
}
error => {
BZ_SETERR!(bzerror, bzf, error);
allocator.deallocate(bzf, 1);
ptr::null_mut()
}
}
}
/// Absorbs `len` bytes from the buffer `buf`, eventually to be compressed and written to the file.
///
/// # Returns
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `b.is_null()`
/// - `buf.is_null()`
/// - `len < 0`
/// - [`BZ_SEQUENCE_ERROR`] if b was opened with [`BZ2_bzReadOpen`]
/// - [`BZ_IO_ERROR`] if there is an error writing to the compressed file
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzWriteOpen`] or [`BZ2_bzReadOpen`]
/// * Either
/// - `buf` is `NULL`
/// - `buf` is writable for `len` bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzWrite)]
pub unsafe extern "C" fn BZ2_bzWrite(
bzerror: *mut c_int,
b: *mut BZFILE,
buf: *const c_void,
len: c_int,
) {
BZ2_bzWriteHelp(bzerror.as_mut(), b.as_mut(), buf, len)
}
unsafe fn BZ2_bzWriteHelp(
mut bzerror: Option<&mut c_int>,
mut b: Option<&mut BZFILE>,
buf: *const c_void,
len: c_int,
) {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
let Some(bzf) = b.as_mut() else {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
return;
};
if buf.is_null() || len < 0 as c_int {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
return;
}
if !matches!(bzf.operation, Operation::Writing) {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
return;
}
if ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return;
}
if len == 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
return;
}
bzf.strm.avail_in = len as c_uint;
bzf.strm.next_in = buf.cast::();
loop {
bzf.strm.avail_out = BZ_MAX_UNUSED_U32;
bzf.strm.next_out = bzf.buf.as_mut_ptr().cast::();
match BZ2_bzCompressHelp(
unsafe { BzStream::from_mut(&mut bzf.strm) },
Action::Run as c_int,
) {
ReturnCode::BZ_RUN_OK => {
if bzf.strm.avail_out < BZ_MAX_UNUSED_U32 {
let n1 = (BZ_MAX_UNUSED_U32 - bzf.strm.avail_out) as usize;
let n2 = fwrite(
bzf.buf.as_mut_ptr().cast::(),
mem::size_of::(),
n1,
bzf.handle,
);
if n1 != n2 || ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return;
}
}
if bzf.strm.avail_in == 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
return;
}
}
error => {
BZ_SETERR!(bzerror, bzf, error);
return;
}
}
}
}
/// Compresses and flushes to the compressed file all data so far supplied by [`BZ2_bzWrite`].
///
/// The logical end-of-stream markers are also written, so subsequent calls to [`BZ2_bzWrite`] are illegal.
/// All memory associated with the compressed file `b` is released. [`libc::fflush`] is called on the compressed file,
/// but it is not [`libc::fclose`]'d.
///
/// If [`BZ2_bzWriteClose`] is called to clean up after an error, the only action is to release the memory.
/// The library records the error codes issued by previous calls, so this situation will be detected automatically.
/// There is no attempt to complete the compression operation, nor to [`libc::fflush`] the compressed file.
/// You can force this behaviour to happen even in the case of no error, by passing a nonzero value to `abandon`.
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_CONFIG_ERROR`] if no default allocator is configured
/// - [`BZ_SEQUENCE_ERROR`] if b was opened with [`BZ2_bzWriteOpen`]
/// - [`BZ_IO_ERROR`] if there is an error writing to the compressed file
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
/// * `nbytes_in` satisfies the requirements of [`pointer::as_mut`]
/// * `nbytes_out` satisfies the requirements of [`pointer::as_mut`]
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzWriteClose)]
pub unsafe extern "C" fn BZ2_bzWriteClose(
bzerror: *mut c_int,
b: *mut BZFILE,
abandon: c_int,
nbytes_in: *mut c_uint,
nbytes_out: *mut c_uint,
) {
BZ2_bzWriteCloseHelp(
bzerror.as_mut(),
b.as_mut(),
abandon,
nbytes_in.as_mut(),
nbytes_out.as_mut(),
)
}
unsafe fn BZ2_bzWriteCloseHelp(
bzerror: Option<&mut c_int>,
b: Option<&mut BZFILE>,
abandon: c_int,
nbytes_in: Option<&mut c_uint>,
nbytes_out: Option<&mut c_uint>,
) {
BZ2_bzWriteClose64Help(bzerror, b, abandon, nbytes_in, None, nbytes_out, None);
}
/// Compresses and flushes to the compressed file all data so far supplied by [`BZ2_bzWrite`].
///
/// The logical end-of-stream markers are also written, so subsequent calls to [`BZ2_bzWrite`] are illegal.
/// All memory associated with the compressed file `b` is released. [`libc::fflush`] is called on the compressed file,
/// but it is not [`libc::fclose`]'d.
///
/// If [`BZ2_bzWriteClose64`] is called to clean up after an error, the only action is to release the memory.
/// The library records the error codes issued by previous calls, so this situation will be detected automatically.
/// There is no attempt to complete the compression operation, nor to [`libc::fflush`] the compressed file.
/// You can force this behaviour to happen even in the case of no error, by passing a nonzero value to `abandon`.
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_CONFIG_ERROR`] if no default allocator is configured
/// - [`BZ_SEQUENCE_ERROR`] if b was opened with [`BZ2_bzWriteOpen`]
/// - [`BZ_IO_ERROR`] if there is an error writing to the compressed file
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
/// * `nbytes_in_lo32: satisfies the requirements of [`pointer::as_mut`]
/// * `nbytes_in_hi32: satisfies the requirements of [`pointer::as_mut`]
/// * `nbytes_out_lo32: satisfies the requirements of [`pointer::as_mut`]
/// * `nbytes_out_hi32: satisfies the requirements of [`pointer::as_mut`]
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzWriteClose64)]
pub unsafe extern "C" fn BZ2_bzWriteClose64(
bzerror: *mut c_int,
b: *mut BZFILE,
abandon: c_int,
nbytes_in_lo32: *mut c_uint,
nbytes_in_hi32: *mut c_uint,
nbytes_out_lo32: *mut c_uint,
nbytes_out_hi32: *mut c_uint,
) {
BZ2_bzWriteClose64Help(
bzerror.as_mut(),
b.as_mut(),
abandon,
nbytes_in_lo32.as_mut(),
nbytes_in_hi32.as_mut(),
nbytes_out_lo32.as_mut(),
nbytes_out_hi32.as_mut(),
)
}
unsafe fn BZ2_bzWriteClose64Help(
mut bzerror: Option<&mut c_int>,
mut b: Option<&mut BZFILE>,
abandon: c_int,
mut nbytes_in_lo32: Option<&mut c_uint>,
mut nbytes_in_hi32: Option<&mut c_uint>,
mut nbytes_out_lo32: Option<&mut c_uint>,
mut nbytes_out_hi32: Option<&mut c_uint>,
) {
let Some(bzf) = b else {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
return;
};
if !matches!(bzf.operation, Operation::Writing) {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
return;
}
if ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return;
}
if let Some(nbytes_in_lo32) = nbytes_in_lo32.as_deref_mut() {
*nbytes_in_lo32 = 0
}
if let Some(nbytes_in_hi32) = nbytes_in_hi32.as_deref_mut() {
*nbytes_in_hi32 = 0;
}
if let Some(nbytes_out_lo32) = nbytes_out_lo32.as_deref_mut() {
*nbytes_out_lo32 = 0;
}
if let Some(nbytes_out_hi32) = nbytes_out_hi32.as_deref_mut() {
*nbytes_out_hi32 = 0;
}
if abandon == 0 && bzf.lastErr == ReturnCode::BZ_OK {
loop {
bzf.strm.avail_out = BZ_MAX_UNUSED_U32;
bzf.strm.next_out = (bzf.buf).as_mut_ptr().cast::();
match BZ2_bzCompressHelp(BzStream::from_mut(&mut bzf.strm), 2 as c_int) {
ret @ (ReturnCode::BZ_FINISH_OK | ReturnCode::BZ_STREAM_END) => {
if bzf.strm.avail_out < BZ_MAX_UNUSED_U32 {
let n1 = (BZ_MAX_UNUSED_U32 - bzf.strm.avail_out) as usize;
let n2 = fwrite(
bzf.buf.as_mut_ptr().cast::(),
mem::size_of::(),
n1,
bzf.handle,
);
if n1 != n2 || ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
}
}
if let ReturnCode::BZ_STREAM_END = ret {
break;
}
}
ret => {
BZ_SETERR!(bzerror, bzf, ret);
return;
}
}
}
}
if abandon == 0 && ferror(bzf.handle) == 0 {
fflush(bzf.handle);
if ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return;
}
}
if let Some(nbytes_in_lo32) = nbytes_in_lo32 {
*nbytes_in_lo32 = bzf.strm.total_in_lo32;
}
if let Some(nbytes_in_hi32) = nbytes_in_hi32 {
*nbytes_in_hi32 = bzf.strm.total_in_hi32;
}
if let Some(nbytes_out_lo32) = nbytes_out_lo32 {
*nbytes_out_lo32 = bzf.strm.total_out_lo32;
}
if let Some(nbytes_out_hi32) = nbytes_out_hi32 {
*nbytes_out_hi32 = bzf.strm.total_out_hi32;
}
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
BZ2_bzCompressEnd(&mut bzf.strm);
let Some(allocator) = Allocator::DEFAULT else {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
return;
};
allocator.deallocate(bzf, 1);
}
/// Prepare to read compressed data from a file handle.
///
/// The file handle `f` should refer to a file which has been opened for reading, and for which the error indicator `libc::ferror(f)` is not set.
///
/// If small is 1, the library will try to decompress using less memory, at the expense of speed.
///
/// For reasons explained below, [`BZ2_bzRead`] will decompress the nUnused bytes starting at unused, before starting to read from the file `f`.
/// At most [`BZ_MAX_UNUSED`] bytes may be supplied like this. If this facility is not required, you should pass NULL and 0 for unused and nUnused respectively.
///
/// For the meaning of parameters `small`, `verbosity`, see [`BZ2_bzDecompressInit`].
///
/// Because the compression ratio of the compressed data cannot be known in advance,
/// there is no easy way to guarantee that the output buffer will be big enough.
/// You may of course make arrangements in your code to record the size of the uncompressed data,
/// but such a mechanism is beyond the scope of this library.
///
/// # Returns
///
/// - if `*bzerror` is [`BZ_OK`], a valid pointer to an abstract `BZFILE`
/// - otherwise `NULL`
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `(unused.is_null() && nUnused != 0)`
/// - `(!unused.is_null() && !(0..=BZ_MAX_UNUSED).contains(&nUnused))`
/// - `!(0..=1).contains(&small)`
/// - `!(0..=4).contains(&verbosity)`
/// - [`BZ_CONFIG_ERROR`] if no default allocator is configured
/// - [`BZ_IO_ERROR`] if `libc::ferror(f)` is nonzero
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `unused` is `NULL`
/// - `unused` is readable for `nUnused` bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzReadOpen)]
pub unsafe extern "C" fn BZ2_bzReadOpen(
bzerror: *mut c_int,
f: *mut FILE,
verbosity: c_int,
small: c_int,
unused: *mut c_void,
nUnused: c_int,
) -> *mut BZFILE {
BZ2_bzReadOpenHelp(bzerror.as_mut(), f, verbosity, small, unused, nUnused)
}
unsafe fn BZ2_bzReadOpenHelp(
mut bzerror: Option<&mut c_int>,
f: *mut FILE,
verbosity: c_int,
small: c_int,
unused: *mut c_void,
nUnused: c_int,
) -> *mut BZFILE {
let mut bzf: Option<&mut BZFILE> = None;
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_OK);
if f.is_null()
|| !(0..=1).contains(&small)
|| !(0..=4).contains(&verbosity)
|| (unused.is_null() && nUnused != 0)
|| (!unused.is_null() && !(0..=BZ_MAX_UNUSED_U32 as c_int).contains(&nUnused))
{
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
return ptr::null_mut::();
}
if ferror(f) != 0 {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return ptr::null_mut::();
}
let Some(allocator) = Allocator::DEFAULT else {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
return ptr::null_mut();
};
let Some(bzf) = allocator.allocate_zeroed::(1) else {
BZ_SETERR_RAW!(bzerror, bzf, ReturnCode::BZ_MEM_ERROR);
return ptr::null_mut();
};
// SAFETY: bzf is non-null and correctly initalized
let bzf = unsafe { &mut *bzf };
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
bzf.initialisedOk = false;
bzf.handle = f;
bzf.bufN = 0;
bzf.operation = Operation::Reading;
bzf.strm.bzalloc = None;
bzf.strm.bzfree = None;
bzf.strm.opaque = ptr::null_mut();
if nUnused > 0 {
ptr::copy(
unused as *mut i8,
bzf.buf[bzf.bufN as usize..].as_mut_ptr(),
nUnused as usize,
);
bzf.bufN += nUnused;
}
match BZ2_bzDecompressInitHelp(BzStream::from_mut(&mut bzf.strm), verbosity, small) {
ReturnCode::BZ_OK => {
bzf.strm.avail_in = bzf.bufN as c_uint;
bzf.strm.next_in = bzf.buf.as_mut_ptr().cast::();
bzf.initialisedOk = true;
}
ret => {
BZ_SETERR!(bzerror, bzf, ret);
allocator.deallocate(bzf, 1);
return ptr::null_mut();
}
}
bzf as *mut BZFILE
}
/// Releases all memory associated with a [`BZFILE`] opened with [`BZ2_bzReadOpen`].
///
/// This function does not call `fclose` on the underlying file handle, the caller should close the
/// file if appropriate.
///
/// This function should be called to clean up after all error situations on `BZFILE`s opened with
/// [`BZ2_bzReadOpen`].
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_CONFIG_ERROR`] if no default allocator is configured
/// - [`BZ_SEQUENCE_ERROR`] if b was opened with [`BZ2_bzWriteOpen`]
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzReadClose)]
pub unsafe extern "C" fn BZ2_bzReadClose(bzerror: *mut c_int, b: *mut BZFILE) {
BZ2_bzReadCloseHelp(bzerror.as_mut(), b.as_mut())
}
unsafe fn BZ2_bzReadCloseHelp(mut bzerror: Option<&mut c_int>, mut b: Option<&mut BZFILE>) {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
let Some(bzf) = b else {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
return;
};
if !matches!(bzf.operation, Operation::Reading) {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
return;
}
if bzf.initialisedOk {
BZ2_bzDecompressEnd(&mut bzf.strm);
}
let Some(allocator) = Allocator::DEFAULT else {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_CONFIG_ERROR);
return;
};
allocator.deallocate(bzf, 1)
}
/// Reads up to `len` (uncompressed) bytes from the compressed file `b` into the buffer `buf`.
///
/// # Returns
///
/// The number of bytes read
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `b.is_null()`
/// - `buf.is_null()`
/// - `len < 0`
/// - [`BZ_SEQUENCE_ERROR`] if b was opened with [`BZ2_bzWriteOpen`]
/// - [`BZ_IO_ERROR`] if there is an error reading from the compressed file
/// - [`BZ_UNEXPECTED_EOF`] if the compressed data ends before the logical end-of-stream was detected
/// - [`BZ_DATA_ERROR`] if a data integrity error is detected in the compressed stream
/// - [`BZ_DATA_ERROR_MAGIC`] if the compressed stream doesn't begin with the right magic bytes
/// - [`BZ_MEM_ERROR`] if insufficient memory is available
/// - [`BZ_STREAM_END`] if the logical end-of-stream was detected
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
/// * Either
/// - `buf` is `NULL`
/// - `buf` is writable for `len` bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzRead)]
pub unsafe extern "C" fn BZ2_bzRead(
bzerror: *mut c_int,
b: *mut BZFILE,
buf: *mut c_void,
len: c_int,
) -> c_int {
BZ2_bzReadHelp(bzerror.as_mut(), b.as_mut(), buf, len)
}
unsafe fn BZ2_bzReadHelp(
mut bzerror: Option<&mut c_int>,
mut b: Option<&mut BZFILE>,
buf: *mut c_void,
len: c_int,
) -> c_int {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_OK);
let Some(bzf) = b.as_mut() else {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
return 0;
};
if buf.is_null() || len < 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
return 0;
}
if !matches!(bzf.operation, Operation::Reading) {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
return 0;
}
if len == 0 as c_int {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
return 0;
}
bzf.strm.avail_out = len as c_uint;
bzf.strm.next_out = buf as *mut c_char;
loop {
if ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return 0;
}
if bzf.strm.avail_in == 0 && !myfeof(bzf.handle) {
let n = fread(
(bzf.buf).as_mut_ptr() as *mut c_void,
::core::mem::size_of::(),
5000,
bzf.handle,
) as i32;
if ferror(bzf.handle) != 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_IO_ERROR);
return 0;
}
bzf.bufN = n;
bzf.strm.avail_in = bzf.bufN as c_uint;
bzf.strm.next_in = (bzf.buf).as_mut_ptr().cast::();
}
match BZ2_bzDecompressHelp(unsafe { BzStream::from_mut(&mut bzf.strm) }) {
ReturnCode::BZ_OK => {
if myfeof(bzf.handle) && bzf.strm.avail_in == 0 && bzf.strm.avail_out > 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_UNEXPECTED_EOF);
return 0;
} else if bzf.strm.avail_out == 0 {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
return len;
} else {
continue;
}
}
ReturnCode::BZ_STREAM_END => {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_STREAM_END);
return (len as c_uint - bzf.strm.avail_out) as c_int;
}
error => {
BZ_SETERR!(bzerror, bzf, error);
return 0;
}
}
}
}
/// Returns data which was read from the compressed file but was not needed to get to the logical end-of-stream.
///
/// # Returns
///
/// - `*unused` is set to the address of the data
/// - `*nUnused` is set to the number of bytes.
///
/// `*nUnused` will be set to a value contained in `0..=BZ_MAX_UNUSED`.
///
/// # Possible assignments to `bzerror`
///
/// - [`BZ_PARAM_ERROR`] if any of
/// - `b.is_null()`
/// - `unused.is_null()`
/// - `nUnused.is_null()`
/// - [`BZ_SEQUENCE_ERROR`] if any of
/// - [`BZ_STREAM_END`] has not been signaled
/// - b was opened with [`BZ2_bzWriteOpen`]
/// - [`BZ_OK`] otherwise
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `bzerror` satisfies the requirements of [`pointer::as_mut`]
/// * `unused` satisfies the requirements of [`pointer::as_mut`]
/// * `nUnused` satisfies the requirements of [`pointer::as_mut`]
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzReadGetUnused)]
pub unsafe extern "C" fn BZ2_bzReadGetUnused(
bzerror: *mut c_int,
b: *mut BZFILE,
unused: *mut *mut c_void,
nUnused: *mut c_int,
) {
BZ2_bzReadGetUnusedHelp(
bzerror.as_mut(),
b.as_mut(),
unused.as_mut(),
nUnused.as_mut(),
)
}
unsafe fn BZ2_bzReadGetUnusedHelp(
mut bzerror: Option<&mut c_int>,
mut b: Option<&mut BZFILE>,
unused: Option<&mut *mut c_void>,
nUnused: Option<&mut c_int>,
) {
let Some(bzf) = b.as_mut() else {
BZ_SETERR_RAW!(bzerror, b, ReturnCode::BZ_PARAM_ERROR);
return;
};
if bzf.lastErr != ReturnCode::BZ_STREAM_END {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_SEQUENCE_ERROR);
return;
}
let (Some(unused), Some(nUnused)) = (unused, nUnused) else {
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_PARAM_ERROR);
return;
};
BZ_SETERR!(bzerror, bzf, ReturnCode::BZ_OK);
*nUnused = bzf.strm.avail_in as c_int;
*unused = bzf.strm.next_in as *mut c_void;
}
#[derive(Copy, Clone)]
pub(crate) enum Operation {
Reading,
Writing,
}
enum OpenMode {
Pointer,
FileDescriptor(i32),
}
unsafe fn bzopen_or_bzdopen(path: Option<&CStr>, open_mode: OpenMode, mode: &CStr) -> *mut BZFILE {
let mut bzerr = 0;
let mut unused: [c_char; BZ_MAX_UNUSED as usize] = [0; BZ_MAX_UNUSED as usize];
let mut blockSize100k = 9;
let verbosity = 0;
let workFactor = 30;
let nUnused = 0;
let mut smallMode = false;
let mut operation = Operation::Reading;
for c in mode.to_bytes() {
match c {
b'r' => operation = Operation::Reading,
b'w' => operation = Operation::Writing,
b's' => smallMode = true,
b'0'..=b'9' => blockSize100k = (*c - b'0') as i32,
_ => {}
}
}
let mode = match open_mode {
OpenMode::Pointer => match operation {
Operation::Reading => b"rbe\0".as_slice(),
Operation::Writing => b"rbe\0".as_slice(),
},
OpenMode::FileDescriptor(_) => match operation {
Operation::Reading => b"rb\0".as_slice(),
Operation::Writing => b"rb\0".as_slice(),
},
};
let mode2 = mode.as_ptr().cast_mut().cast::();
let default_file = match operation {
Operation::Reading => STDIN!(),
Operation::Writing => STDOUT!(),
};
let fp = match open_mode {
OpenMode::Pointer => match path {
None => default_file,
Some(path) if path.is_empty() => default_file,
Some(path) => fopen(path.as_ptr(), mode2),
},
OpenMode::FileDescriptor(fd) => fdopen(fd, mode2),
};
if fp.is_null() {
return ptr::null_mut();
}
let bzfp = match operation {
Operation::Reading => BZ2_bzReadOpen(
&mut bzerr,
fp,
verbosity,
smallMode as i32,
unused.as_mut_ptr() as *mut c_void,
nUnused,
),
Operation::Writing => BZ2_bzWriteOpen(
&mut bzerr,
fp,
blockSize100k.clamp(1, 9),
verbosity,
workFactor,
),
};
if bzfp.is_null() {
if fp != STDIN!() && fp != STDOUT!() {
fclose(fp);
}
return ptr::null_mut();
}
bzfp
}
/// Opens a `.bz2` file for reading or writing using its name. Analogous to [`libc::fopen`].
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `path` is `NULL`
/// - `path` is a null-terminated sequence of bytes
/// * Either
/// - `mode` is `NULL`
/// - `mode` is a null-terminated sequence of bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzopen)]
pub unsafe extern "C" fn BZ2_bzopen(path: *const c_char, mode: *const c_char) -> *mut BZFILE {
let mode = if mode.is_null() {
return ptr::null_mut();
} else {
CStr::from_ptr(mode)
};
let path = if path.is_null() {
None
} else {
Some(CStr::from_ptr(path))
};
bzopen_or_bzdopen(path, OpenMode::Pointer, mode)
}
/// Opens a `.bz2` file for reading or writing using a pre-existing file descriptor. Analogous to [`libc::fdopen`].
///
/// # Safety
///
/// The caller must guarantee that
///
/// * `fd` must be a valid file descriptor for the duration of [`BZ2_bzdopen`]
/// * Either
/// - `mode` is `NULL`
/// - `mode` is a null-terminated sequence of bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzdopen)]
pub unsafe extern "C" fn BZ2_bzdopen(fd: c_int, mode: *const c_char) -> *mut BZFILE {
let mode = if mode.is_null() {
return ptr::null_mut();
} else {
CStr::from_ptr(mode)
};
bzopen_or_bzdopen(None, OpenMode::FileDescriptor(fd), mode)
}
/// Reads up to `len` (uncompressed) bytes from the compressed file `b` into the buffer `buf`.
///
/// Analogous to [`libc::fread`].
///
/// # Returns
///
/// Number of bytes read on success, or `-1` on failure.
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzWriteOpen`] or [`BZ2_bzReadOpen`]
/// * Either
/// - `buf` is `NULL`
/// - `buf` is writable for `len` bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzread)]
pub unsafe extern "C" fn BZ2_bzread(b: *mut BZFILE, buf: *mut c_void, len: c_int) -> c_int {
BZ2_bzreadHelp(b.as_mut(), buf, len)
}
unsafe fn BZ2_bzreadHelp(mut b: Option<&mut BZFILE>, buf: *mut c_void, len: c_int) -> c_int {
let mut bzerr = 0;
if let Some(b) = b.as_deref_mut() {
if b.lastErr == ReturnCode::BZ_STREAM_END {
return 0;
}
}
let nread = BZ2_bzReadHelp(Some(&mut bzerr), b, buf, len);
if bzerr == 0 || bzerr == ReturnCode::BZ_STREAM_END as i32 {
nread
} else {
-1
}
}
/// Absorbs `len` bytes from the buffer `buf`, eventually to be compressed and written to the file.
///
/// Analogous to [`libc::fwrite`].
///
/// # Returns
///
/// The value `len` on success, or `-1` on failure.
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzWriteOpen`] or [`BZ2_bzReadOpen`]
/// * Either
/// - `buf` is `NULL`
/// - `buf` is readable for `len` bytes
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzwrite)]
pub unsafe extern "C" fn BZ2_bzwrite(b: *mut BZFILE, buf: *const c_void, len: c_int) -> c_int {
BZ2_bzwriteHelp(b.as_mut(), buf, len)
}
unsafe fn BZ2_bzwriteHelp(b: Option<&mut BZFILE>, buf: *const c_void, len: c_int) -> c_int {
let mut bzerr = 0;
BZ2_bzWriteHelp(Some(&mut bzerr), b, buf, len);
match bzerr {
0 => len,
_ => -1,
}
}
/// Flushes a [`BZFILE`].
///
/// Analogous to [`libc::fflush`].
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
#[export_name = prefix!(BZ2_bzflush)]
pub unsafe extern "C" fn BZ2_bzflush(mut _b: *mut BZFILE) -> c_int {
/* do nothing now... */
0
}
/// Closes a [`BZFILE`].
///
/// Analogous to [`libc::fclose`].
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
#[export_name = prefix!(BZ2_bzclose)]
pub unsafe extern "C" fn BZ2_bzclose(b: *mut BZFILE) {
BZ2_bzcloseHelp(b.as_mut())
}
unsafe fn BZ2_bzcloseHelp(mut b: Option<&mut BZFILE>) {
let mut bzerr: c_int = 0;
let operation = if let Some(bzf) = &mut b {
bzf.operation
} else {
return;
};
match operation {
Operation::Reading => {
BZ2_bzReadCloseHelp(Some(&mut bzerr), b.as_deref_mut());
}
Operation::Writing => {
BZ2_bzWriteCloseHelp(Some(&mut bzerr), b.as_deref_mut(), false as i32, None, None);
if bzerr != 0 {
BZ2_bzWriteCloseHelp(None, b.as_deref_mut(), true as i32, None, None);
}
}
}
if let Some(bzf) = b {
if bzf.handle != STDIN!() && bzf.handle != STDOUT!() {
fclose(bzf.handle);
}
}
}
const BZERRORSTRINGS: [&str; 16] = [
"OK\0",
"SEQUENCE_ERROR\0",
"PARAM_ERROR\0",
"MEM_ERROR\0",
"DATA_ERROR\0",
"DATA_ERROR_MAGIC\0",
"IO_ERROR\0",
"UNEXPECTED_EOF\0",
"OUTBUFF_FULL\0",
"CONFIG_ERROR\0",
"???\0",
"???\0",
"???\0",
"???\0",
"???\0",
"???\0",
];
/// Describes the most recent error.
///
/// # Returns
///
/// A null-terminated string describing the most recent error status of `b`, and also sets `*errnum` to its numerical value.
///
/// # Safety
///
/// The caller must guarantee that
///
/// * Either
/// - `b` is `NULL`
/// - `b` is initialized with [`BZ2_bzReadOpen`] or [`BZ2_bzWriteOpen`]
/// * `errnum` satisfies the requirements of [`pointer::as_mut`]
///
/// [`pointer::as_mut`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.as_mut
#[export_name = prefix!(BZ2_bzerror)]
pub unsafe extern "C" fn BZ2_bzerror(b: *const BZFILE, errnum: *mut c_int) -> *const c_char {
BZ2_bzerrorHelp(
b.as_ref().expect("Passed null pointer to BZ2_bzerror"),
errnum.as_mut(),
)
}
fn BZ2_bzerrorHelp(b: &BZFILE, errnum: Option<&mut c_int>) -> *const c_char {
let err = Ord::min(0, b.lastErr as c_int);
if let Some(errnum) = errnum {
*errnum = err;
};
let msg = match BZERRORSTRINGS.get(-err as usize) {
Some(msg) => msg,
None => "???\0",
};
msg.as_ptr().cast::()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_messages() {
let mut bz_file = BZFILE {
handle: core::ptr::null_mut(),
buf: [0; 5000],
bufN: 0,
strm: bz_stream::zeroed(),
lastErr: ReturnCode::BZ_OK,
operation: Operation::Reading,
initialisedOk: false,
};
let return_codes = [
ReturnCode::BZ_OK,
ReturnCode::BZ_RUN_OK,
ReturnCode::BZ_FLUSH_OK,
ReturnCode::BZ_FINISH_OK,
ReturnCode::BZ_STREAM_END,
ReturnCode::BZ_SEQUENCE_ERROR,
ReturnCode::BZ_PARAM_ERROR,
ReturnCode::BZ_MEM_ERROR,
ReturnCode::BZ_DATA_ERROR,
ReturnCode::BZ_DATA_ERROR_MAGIC,
ReturnCode::BZ_IO_ERROR,
ReturnCode::BZ_UNEXPECTED_EOF,
ReturnCode::BZ_OUTBUFF_FULL,
ReturnCode::BZ_CONFIG_ERROR,
];
for return_code in return_codes {
bz_file.lastErr = return_code;
let mut errnum = 0;
let ptr = unsafe { BZ2_bzerror(&bz_file as *const BZFILE, &mut errnum) };
assert!(!ptr.is_null());
let cstr = unsafe { CStr::from_ptr(ptr) };
let msg = cstr.to_str().unwrap();
let expected = match return_code {
ReturnCode::BZ_OK => "OK",
ReturnCode::BZ_RUN_OK => "OK",
ReturnCode::BZ_FLUSH_OK => "OK",
ReturnCode::BZ_FINISH_OK => "OK",
ReturnCode::BZ_STREAM_END => "OK",
ReturnCode::BZ_SEQUENCE_ERROR => "SEQUENCE_ERROR",
ReturnCode::BZ_PARAM_ERROR => "PARAM_ERROR",
ReturnCode::BZ_MEM_ERROR => "MEM_ERROR",
ReturnCode::BZ_DATA_ERROR => "DATA_ERROR",
ReturnCode::BZ_DATA_ERROR_MAGIC => "DATA_ERROR_MAGIC",
ReturnCode::BZ_IO_ERROR => "IO_ERROR",
ReturnCode::BZ_UNEXPECTED_EOF => "UNEXPECTED_EOF",
ReturnCode::BZ_OUTBUFF_FULL => "OUTBUFF_FULL",
ReturnCode::BZ_CONFIG_ERROR => "CONFIG_ERROR",
};
assert_eq!(msg, expected);
if (return_code as i32) < 0 {
assert_eq!(return_code as i32, errnum);
} else {
assert_eq!(0, errnum);
}
}
}
}
libbz2-rs-sys-0.1.3/src/huffman.rs 0000644 0000000 0000000 00000013071 10461020230 0015032 0 ustar 0000000 0000000 #![forbid(unsafe_code)]
use crate::{
assert_h,
bzlib::{BZ_MAX_ALPHA_SIZE, BZ_MAX_CODE_LEN},
};
#[inline]
const fn weight_of(zz0: i32) -> i32 {
zz0 & 0xffffff00u32 as i32
}
#[inline]
const fn depth_of(zz1: i32) -> i32 {
zz1 & 0xff
}
#[inline]
fn add_weights(zw1: i32, zw2: i32) -> i32 {
(weight_of(zw1)).wrapping_add(weight_of(zw2)) | (1 + Ord::max(depth_of(zw1), depth_of(zw2)))
}
#[inline]
fn upheap(
heap: &mut [i32; BZ_MAX_ALPHA_SIZE + 2],
weight: &mut [i32; BZ_MAX_ALPHA_SIZE * 2],
mut z: usize,
) {
let tmp = heap[z];
while weight[tmp as usize] < weight[heap[z >> 1] as usize] {
heap[z] = heap[z >> 1];
z >>= 1;
}
heap[z] = tmp;
}
#[inline]
fn downheap(
heap: &mut [i32; BZ_MAX_ALPHA_SIZE + 2],
weight: &mut [i32; BZ_MAX_ALPHA_SIZE * 2],
nHeap: usize,
mut z: usize,
) {
let tmp = heap[z];
loop {
let mut yy = z << 1;
if yy > nHeap {
break;
}
if yy < nHeap && weight[heap[yy + 1] as usize] < weight[heap[yy] as usize] {
yy += 1;
}
if weight[tmp as usize] < weight[heap[yy] as usize] {
break;
}
heap[z] = heap[yy];
z = yy;
}
heap[z] = tmp;
}
pub(crate) fn make_code_lengths(len: &mut [u8], freq: &[i32], alphaSize: usize, maxLen: i32) {
/*--
Nodes and heap entries run from 1. Entry 0
for both the heap and nodes is a sentinel.
--*/
let mut nNodes: usize;
let mut nHeap: usize;
let mut j: i32;
let mut heap = [0i32; BZ_MAX_ALPHA_SIZE + 2];
let mut weight = [0i32; BZ_MAX_ALPHA_SIZE * 2];
let mut parent = [0i32; BZ_MAX_ALPHA_SIZE * 2];
for i in 0..alphaSize {
weight[i + 1] = (if freq[i] == 0 { 1 } else { freq[i] }) << 8;
}
loop {
nNodes = alphaSize;
nHeap = 0;
heap[0] = 0;
weight[0] = 0;
parent[0] = -2;
parent[1..=alphaSize].fill(-1);
for i in 1..=alphaSize {
nHeap += 1;
heap[nHeap] = i as i32;
upheap(&mut heap, &mut weight, nHeap);
}
assert_h!(nHeap < (BZ_MAX_ALPHA_SIZE + 2), 2001);
while nHeap > 1 {
let n1 = heap[1] as usize;
heap[1] = heap[nHeap];
nHeap -= 1;
downheap(&mut heap, &mut weight, nHeap, 1);
let n2 = heap[1] as usize;
heap[1] = heap[nHeap];
nHeap -= 1;
downheap(&mut heap, &mut weight, nHeap, 1);
nNodes += 1;
parent[n1] = nNodes as i32;
parent[n2] = nNodes as i32;
weight[nNodes] = add_weights(weight[n1], weight[n2]);
parent[nNodes] = -1;
nHeap += 1;
heap[nHeap] = nNodes as i32;
upheap(&mut heap, &mut weight, nHeap);
}
assert_h!(nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002);
let mut tooLong = false;
for i in 1..=alphaSize {
j = 0;
let mut k = i;
while parent[k] >= 0 {
k = parent[k] as usize;
j += 1;
}
len[i - 1] = j as u8;
if j > maxLen {
tooLong = true;
}
}
if !tooLong {
break;
}
/* 17 Oct 04: keep-going condition for the following loop used
to be 'i < alphaSize', which missed the last element,
theoretically leading to the possibility of the compressor
looping. However, this count-scaling step is only needed if
one of the generated Huffman code words is longer than
maxLen, which up to and including version 1.0.2 was 20 bits,
which is extremely unlikely. In version 1.0.3 maxLen was
changed to 17 bits, which has minimal effect on compression
ratio, but does mean this scaling step is used from time to
time, enough to verify that it works.
This means that bzip2-1.0.3 and later will only produce
Huffman codes with a maximum length of 17 bits. However, in
order to preserve backwards compatibility with bitstreams
produced by versions pre-1.0.3, the decompressor must still
handle lengths of up to 20. */
for weight in weight[1..=alphaSize].iter_mut() {
*weight = (1 + (*weight >> 8) / 2) << 8;
}
}
}
#[inline]
pub(crate) fn assign_codes(code: &mut [u32], length: &[u8], minLen: u8, maxLen: u8) {
let mut vec: u32 = 0;
for n in minLen..=maxLen {
for (i, &l) in length.iter().enumerate() {
if l == n {
code[i] = vec;
vec += 1;
}
}
vec <<= 1;
}
}
#[inline(always)]
pub(crate) fn create_decode_tables(
limit: &mut [i32; 258],
base: &mut [i32; 258],
perm: &mut [u16; 258],
length: &[u8],
minLen: u8,
maxLen: u8,
) {
assert!(length.len() <= 258);
let mut pp = 0;
for i in minLen..=maxLen {
for (j, e) in length.iter().enumerate() {
if *e == i {
perm[pp] = j as u16;
pp += 1;
}
}
}
base[0..BZ_MAX_CODE_LEN].fill(0);
for l in length {
base[usize::from(*l) + 1] += 1;
}
for i in 1..BZ_MAX_CODE_LEN {
base[i] += base[i - 1];
}
limit[0..BZ_MAX_CODE_LEN].fill(0);
let mut vec = 0;
for i in usize::from(minLen)..=usize::from(maxLen) {
vec += base[i + 1] - base[i];
limit[i] = vec - 1;
vec <<= 1;
}
for i in usize::from(minLen)..usize::from(maxLen) {
base[i + 1] = (2 * (limit[i] + 1)) - base[i + 1];
}
}
libbz2-rs-sys-0.1.3/src/lib.rs 0000644 0000000 0000000 00000017031 10461020230 0014154 0 ustar 0000000 0000000 #![no_std]
#![allow(non_snake_case)]
#![allow(clippy::too_many_arguments)]
#![deny(unreachable_pub)]
#![deny(unsafe_op_in_unsafe_fn)]
//! A drop-in compatible rust implementation of bzip2
#[cfg(feature = "std")]
extern crate std;
use core::ffi::c_int;
#[cfg(not(feature = "std"))]
use core::sync::atomic::{AtomicI32, Ordering};
mod allocator;
mod blocksort;
mod bzlib;
mod compress;
mod crctable;
mod decompress;
#[cfg(feature = "stdio")]
mod high_level;
mod huffman;
mod randtable;
pub(crate) use bzlib::{Action, ReturnCode};
pub const BZ_OK: c_int = ReturnCode::BZ_OK as c_int;
pub const BZ_RUN_OK: c_int = ReturnCode::BZ_RUN_OK as c_int;
pub const BZ_FLUSH_OK: c_int = ReturnCode::BZ_FLUSH_OK as c_int;
pub const BZ_FINISH_OK: c_int = ReturnCode::BZ_FINISH_OK as c_int;
pub const BZ_STREAM_END: c_int = ReturnCode::BZ_STREAM_END as c_int;
pub const BZ_SEQUENCE_ERROR: c_int = ReturnCode::BZ_SEQUENCE_ERROR as c_int;
pub const BZ_PARAM_ERROR: c_int = ReturnCode::BZ_PARAM_ERROR as c_int;
pub const BZ_MEM_ERROR: c_int = ReturnCode::BZ_MEM_ERROR as c_int;
pub const BZ_DATA_ERROR: c_int = ReturnCode::BZ_DATA_ERROR as c_int;
pub const BZ_DATA_ERROR_MAGIC: c_int = ReturnCode::BZ_DATA_ERROR_MAGIC as c_int;
pub const BZ_IO_ERROR: c_int = ReturnCode::BZ_IO_ERROR as c_int;
pub const BZ_UNEXPECTED_EOF: c_int = ReturnCode::BZ_UNEXPECTED_EOF as c_int;
pub const BZ_OUTBUFF_FULL: c_int = ReturnCode::BZ_OUTBUFF_FULL as c_int;
pub const BZ_CONFIG_ERROR: c_int = ReturnCode::BZ_CONFIG_ERROR as c_int;
pub const BZ_RUN: c_int = Action::Run as c_int;
pub const BZ_FLUSH: c_int = Action::Flush as c_int;
pub const BZ_FINISH: c_int = Action::Finish as c_int;
pub const BZ_MAX_UNUSED: c_int = bzlib::BZ_MAX_UNUSED_U32 as c_int;
// types
pub use bzlib::bz_stream;
#[cfg(feature = "stdio")]
pub use bzlib::BZFILE;
// the low-level interface
pub use bzlib::{BZ2_bzCompress, BZ2_bzCompressEnd, BZ2_bzCompressInit};
pub use bzlib::{BZ2_bzDecompress, BZ2_bzDecompressEnd, BZ2_bzDecompressInit};
// utility functions
pub use bzlib::{BZ2_bzBuffToBuffCompress, BZ2_bzBuffToBuffDecompress};
// the high-level interface
#[cfg(feature = "stdio")]
pub use bzlib::{BZ2_bzRead, BZ2_bzReadClose, BZ2_bzReadGetUnused, BZ2_bzReadOpen};
#[cfg(feature = "stdio")]
pub use bzlib::{BZ2_bzWrite, BZ2_bzWriteClose, BZ2_bzWriteClose64, BZ2_bzWriteOpen};
// zlib compatibility functions
#[cfg(feature = "stdio")]
pub use bzlib::{
BZ2_bzclose, BZ2_bzdopen, BZ2_bzerror, BZ2_bzflush, BZ2_bzlibVersion, BZ2_bzopen, BZ2_bzread,
BZ2_bzwrite,
};
// --- version number logic
macro_rules! libbz2_rs_sys_version {
() => {
concat!("1.1.0-libbz2-rs-sys-", env!("CARGO_PKG_VERSION"))
};
}
pub(crate) use libbz2_rs_sys_version;
// --- debug logs
#[cfg(all(not(feature = "std"), feature = "stdio"))]
pub(crate) struct StderrWritter;
#[cfg(all(not(feature = "std"), feature = "stdio"))]
impl core::fmt::Write for StderrWritter {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
use core::ffi::c_void;
use libc::write;
unsafe { write(2, s.as_ptr() as *const c_void, s.len() as _) };
Ok(())
}
}
macro_rules! debug_log {
($($arg:tt)*) => {
#[cfg(feature = "std")]
std::eprint!($($arg)*);
#[cfg(all(not(feature = "std"), feature = "stdio"))]
{
use core::fmt::Write;
let _ = write!($crate::StderrWritter, $($arg)*);
}
};
}
macro_rules! debug_logln {
($($arg:tt)*) => {
#[cfg(feature = "std")]
std::eprintln!($($arg)*);
#[cfg(all(not(feature = "std"), feature = "stdio"))]
{
use core::fmt::Write;
let _ = writeln!($crate::StderrWritter, $($arg)*);
}
};
}
pub(crate) use debug_log;
pub(crate) use debug_logln;
// --- assert failure logic
macro_rules! assert_h {
($condition:expr, $errcode:expr) => {
if !$condition {
$crate::handle_assert_failure($errcode)
}
};
}
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub static ASSERT_CODE: AtomicI32 = AtomicI32::new(-1);
#[cold]
fn handle_assert_failure(errcode: c_int) -> ! {
#[cfg(feature = "std")]
std::eprint!("{}", AssertFail(errcode));
#[cfg(feature = "std")]
std::process::exit(3);
// Stash the assertion code for the panic handler in the cdylib to pass to bz_internal_error.
// Using relaxed ordering as this will be accessed on the same thread.
#[cfg(not(feature = "std"))]
#[allow(clippy::unnecessary_cast)]
ASSERT_CODE.store(errcode as i32, Ordering::Relaxed);
#[cfg(not(feature = "std"))]
panic!("{}", AssertFail(errcode));
}
use assert_h;
struct AssertFail(i32);
impl core::fmt::Display for AssertFail {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
concat!(
"\n",
"\n",
"libbzip2-rs: internal error number {}.\n",
"This is a bug in libbzip2-rs, {}.\n",
"Please report it at: https://github.com/trifectatechfoundation/libbzip2-rs/issues\n",
"If this happened when you were using some program which uses\n",
"libbzip2-rs as a component, you should also report this bug to\n",
"the author(s) of that program.\n",
"Please make an effort to report this bug;\n",
"timely and accurate bug reports eventually lead to higher\n",
"quality software. Thanks.\n",
"\n"
),
self.0,
libbz2_rs_sys_version!(),
)?;
if self.0 == 1007 {
write!(
f,
concat!(
"\n",
"*** A special note about internal error number 1007 ***\n",
"\n",
"Experience suggests that a common cause of i.e. 1007\n",
"is unreliable memory or other hardware. The 1007 assertion\n",
"just happens to cross-check the results of huge numbers of\n",
"memory reads/writes, and so acts (unintendedly) as a stress\n",
"test of your memory system.\n",
"\n",
"I suggest the following: try compressing the file again,\n",
"possibly monitoring progress in detail with the -vv flag.\n",
"\n",
"* If the error cannot be reproduced, and/or happens at different\n",
" points in compression, you may have a flaky memory system.\n",
" Try a memory-test program. I have used Memtest86\n",
" (www.memtest86.com). At the time of writing it is free (GPLd).\n",
" Memtest86 tests memory much more thorougly than your BIOSs\n",
" power-on test, and may find failures that the BIOS doesn't.\n",
"\n",
"* If the error can be repeatably reproduced, this is a bug in\n",
" bzip2, and I would very much like to hear about it. Please\n",
" let me know, and, ideally, save a copy of the file causing the\n",
" problem -- without which I will be unable to investigate it.\n",
"\n"
)
)?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
extern crate alloc;
use super::*;
use alloc::string::String;
#[test]
fn print_assert_fail_coverage() {
use core::fmt::Write;
write!(&mut String::new(), "{}", AssertFail(1007)).unwrap();
}
}
libbz2-rs-sys-0.1.3/src/randtable.rs 0000644 0000000 0000000 00000005177 10461020230 0015352 0 ustar 0000000 0000000 pub(crate) static BZ2_RNUMS: [i32; 512] = [
619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 454, 863, 491, 741, 242, 949,
214, 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 419, 436, 278, 496, 867, 210, 399, 680,
480, 51, 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 862, 687, 507, 283, 482, 129, 807,
591, 733, 623, 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 170, 607, 520, 932, 727, 476,
693, 425, 174, 647, 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 909, 545, 703, 919, 874,
474, 882, 500, 594, 612, 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 161, 604, 958, 533,
221, 400, 386, 867, 600, 782, 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 98, 553, 163,
354, 666, 933, 424, 341, 533, 870, 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 469, 68,
770, 919, 190, 373, 294, 822, 808, 206, 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 715,
67, 618, 276, 204, 918, 873, 777, 604, 560, 951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93, 354, 99, 820,
908, 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 653, 282, 762, 623, 680, 81, 927, 626,
789, 125, 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 170, 774, 972, 275, 999, 639, 495,
78, 352, 126, 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 669, 112, 134, 694, 363, 992,
809, 743, 168, 974, 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 344, 805, 988, 739, 511,
655, 814, 334, 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 433, 837, 553, 268,
926, 240, 102, 654, 459, 51, 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 946, 670, 656,
610, 738, 392, 760, 799, 887, 653, 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 680, 879,
194, 572, 640, 724, 926, 56, 204, 700, 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 297,
59, 87, 824, 713, 663, 412, 693, 342, 606, 134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 140, 206, 73, 263, 980, 736, 876, 478, 430,
305, 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 369, 970, 294, 750, 807, 827, 150, 790,
288, 923, 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 896, 831, 547, 261, 524, 462, 293,
465, 502, 56, 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 768, 550, 608, 933, 378, 286,
215, 979, 792, 961, 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 372, 567, 466, 434, 645,
210, 389, 550, 919, 135, 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713,
857, 265, 203, 50, 668, 108, 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 936, 638,
];