#![cfg(feature = "zpaq")]
use std::fmt;
use std::num::Wrapping;
use crate::{Chunk, ChunkIncr, RangeExt, ToChunkIncr};
use std::ops::Bound;
use std::ops::RangeBounds;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Zpaq {
range: (Bound<u64>, Bound<u64>),
max_hash: u32,
}
impl Zpaq {
fn fragment_ave_from_max(max: u64) -> u8 {
(max as f64 / (64f64 * 64f64)).log2() as u8
}
fn fragment_ave_from_range<T: RangeBounds<u64>>(range: T) -> u8 {
let v = match range.end_bound() {
Bound::Included(i) => *i,
Bound::Excluded(i) => *i - 1,
Bound::Unbounded => {
64 * match range.start_bound() {
Bound::Included(i) => *i,
Bound::Excluded(i) => *i + 1,
Bound::Unbounded => {
return 16;
}
}
}
};
Self::fragment_ave_from_max(v)
}
fn range_from_fragment_ave(fragment_ave: u8) -> impl RangeBounds<u64> {
assert!(fragment_ave <= 32);
assert!(fragment_ave >= 10);
64 << (fragment_ave - 10)..8128 << fragment_ave
}
fn range_from_max(max: u64) -> impl RangeBounds<u64> {
max / 64..max
}
fn max_hash_from_fragment_ave(fragment_ave: u8) -> u32 {
assert!(fragment_ave <= 32);
1 << (32 - fragment_ave)
}
pub fn with_range(range: impl RangeBounds<u64> + Clone) -> Self {
let f = Self::fragment_ave_from_range(range.clone());
Self::with_average_and_range(f, range)
}
pub fn with_average_size_pow_2(average_size_pow_2: u8) -> Self {
let r = Self::range_from_fragment_ave(average_size_pow_2);
Self::with_average_and_range(average_size_pow_2, r)
}
pub fn with_max_size(max: u64) -> Self {
Self::with_average_and_range(Self::fragment_ave_from_max(max), Self::range_from_max(max))
}
pub fn with_average_and_range(average_size_pow_2: u8, range: impl RangeBounds<u64>) -> Self {
Zpaq {
range: range.into_tuple(),
max_hash: Self::max_hash_from_fragment_ave(average_size_pow_2),
}
}
fn split_here(&self, hash: u32, index: u64) -> bool {
(hash < self.max_hash && !self.range.under_min(&index)) || self.range.exceeds_max(&index)
}
}
impl Default for Zpaq {
fn default() -> Self {
Self::with_average_size_pow_2(16)
}
}
#[derive(Debug)]
pub struct ZpaqIncr {
params: Zpaq,
state: ZpaqHash,
idx: u64,
}
#[derive(Default, Debug)]
pub struct ZpaqSearchState {
state: ZpaqHash,
idx: u64,
}
impl ZpaqSearchState {
fn feed(&mut self, v: u8) -> u32 {
self.idx += 1;
self.state.feed(v)
}
}
impl Chunk for Zpaq {
type SearchState = ZpaqSearchState;
fn to_search_state(&self) -> Self::SearchState {
Default::default()
}
fn find_chunk_edge(
&self,
state: &mut Self::SearchState,
data: &[u8],
) -> (Option<usize>, usize) {
for i in 0..data.len() {
let h = state.feed(data[i]);
if self.split_here(h, (state.idx + 1) as u64) {
*state = self.to_search_state();
return (Some(i + 1), i + 1);
}
}
(None, data.len())
}
}
impl From<&Zpaq> for ZpaqSearchState {
fn from(_: &Zpaq) -> Self {
Default::default()
}
}
impl From<&Zpaq> for ZpaqIncr {
fn from(s: &Zpaq) -> Self {
s.clone().into()
}
}
impl ToChunkIncr for Zpaq {
type Incr = ZpaqIncr;
fn to_chunk_incr(&self) -> Self::Incr {
self.into()
}
}
impl ZpaqIncr {
fn feed(&mut self, v: u8) -> u32 {
self.idx += 1;
self.state.feed(v)
}
fn reset(&mut self) {
self.idx = 0;
self.state = Default::default();
}
}
impl ChunkIncr for ZpaqIncr {
fn push(&mut self, data: &[u8]) -> Option<usize> {
for (i, &v) in data.iter().enumerate() {
let h = self.feed(v);
if self.params.split_here(h, self.idx) {
self.reset();
return Some(i + 1);
}
}
None
}
}
impl From<Zpaq> for ZpaqIncr {
fn from(params: Zpaq) -> Self {
Self {
params,
state: Default::default(),
idx: 0,
}
}
}
#[derive(Clone)]
pub struct ZpaqHash {
hash: Wrapping<u32>,
last_byte: u8,
predicted_byte: [u8; 256],
}
impl PartialEq for ZpaqHash {
fn eq(&self, other: &Self) -> bool {
self.hash == other.hash
&& self.last_byte == other.last_byte
&& &self.predicted_byte[..] == &other.predicted_byte[..]
}
}
impl Eq for ZpaqHash {}
impl fmt::Debug for ZpaqHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("ZpaqHash")
.field("hash", &self.hash)
.field("last_byte", &self.last_byte)
.field("predicted_byte", &fmt_extra::Hs(&self.predicted_byte[..]))
.finish()
}
}
impl Default for ZpaqHash {
fn default() -> Self {
ZpaqHash {
hash: Wrapping(0),
last_byte: 0,
predicted_byte: [0; 256],
}
}
}
impl ZpaqHash {
fn feed(&mut self, c: u8) -> u32 {
self.hash = if c == self.predicted_byte[self.last_byte as usize] {
(self.hash + Wrapping(c as u32) + Wrapping(1)) * Wrapping(314159265)
} else {
(self.hash + Wrapping(c as u32) + Wrapping(1)) * Wrapping(271828182)
};
self.predicted_byte[self.last_byte as usize] = c;
self.last_byte = c;
self.hash.0
}
}