#![forbid(unsafe_code)]
use crate::core::extent::ChunkId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum RansCodec {
Single = 0,
Interleaved2 = 1,
}
impl RansCodec {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Single),
1 => Some(Self::Interleaved2),
_ => None,
}
}
pub const fn tag(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum UniverseId {
UniformXofV1 = 0x01,
}
impl UniverseId {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x01 => Some(Self::UniformXofV1),
_ => None,
}
}
pub const fn tag(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum TransformId {
Identity = 0x00,
}
impl TransformId {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x00 => Some(Self::Identity),
_ => None,
}
}
pub const fn tag(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Edit {
pub pos: u32,
pub val: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RangeChange {
pub start: u32,
pub end: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Residual {
XorSparse {
len: u64,
edits: Vec<Edit>,
},
RangeReplace {
len: u64,
changes: Vec<RangeChange>,
literals: Vec<u8>,
},
RansCoded {
len: u64,
enc_obj: ChunkId,
model: ChunkId,
scale_bits: u8,
codec: RansCodec,
decoded_len: u64,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Representation {
Zero {
len: u64,
},
Fill {
value: u8,
len: u64,
},
Inline {
data: Vec<u8>,
},
Raw {
obj: ChunkId,
len: u64,
},
Rans {
model: ChunkId,
enc_obj: ChunkId,
scale_bits: u8,
codec: RansCodec,
len: u64,
},
ExactRef {
target: ChunkId,
off: u64,
len: u64,
},
BaseResidual {
base: ChunkId,
base_len: u64,
residual: Residual,
len: u64,
},
Sparse {
k: u32,
rank: u128,
literals: Vec<u8>,
len: u64,
},
Palette {
palette: Vec<u8>,
counts: Vec<u32>,
rank: u128,
len: u64,
},
Periodic {
period: u32,
pattern: Vec<u8>,
count: u32,
tail: Vec<u8>,
len: u64,
},
Permutation {
rank: u128,
alphabet: Vec<u8>,
len: u64,
},
EntropyRef {
universe: UniverseId,
seed: [u8; 16],
coordinate: u64,
transform: TransformId,
residual: Residual,
len: u64,
},
}
impl Representation {
pub const fn len(&self) -> u64 {
match self {
Representation::Zero { len }
| Representation::Fill { len, .. }
| Representation::Raw { len, .. }
| Representation::Rans { len, .. }
| Representation::ExactRef { len, .. }
| Representation::BaseResidual { len, .. }
| Representation::Sparse { len, .. }
| Representation::Palette { len, .. }
| Representation::Periodic { len, .. }
| Representation::EntropyRef { len, .. }
| Representation::Permutation { len, .. } => *len,
Representation::Inline { data } => data.len() as u64,
}
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub const fn tag(&self) -> u8 {
match self {
Representation::Zero { .. } => 0x01,
Representation::Fill { .. } => 0x02,
Representation::Raw { .. } => 0x03,
Representation::Rans { .. } => 0x04,
Representation::ExactRef { .. } => 0x05,
Representation::BaseResidual { .. } => 0x06,
Representation::Sparse { .. } => 0x07,
Representation::Palette { .. } => 0x08,
Representation::Periodic { .. } => 0x09,
Representation::EntropyRef { .. } => 0x0A,
Representation::Inline { .. } => 0x0B,
Representation::Permutation { .. } => 0x0C,
}
}
pub const fn family(&self) -> &'static str {
match self {
Representation::Zero { .. } => "ZERO",
Representation::Fill { .. } => "FILL",
Representation::Raw { .. } => "RAW",
Representation::Rans { .. } => "RANS",
Representation::ExactRef { .. } => "EXACT_REF",
Representation::BaseResidual { .. } => "BASE_RESIDUAL",
Representation::Sparse { .. } => "SPARSE",
Representation::Palette { .. } => "PALETTE",
Representation::Periodic { .. } => "PERIODIC",
Representation::EntropyRef { .. } => "ENTROPY_REF",
Representation::Inline { .. } => "INLINE",
Representation::Permutation { .. } => "PERMUTATION",
}
}
pub fn encoded_size(&self) -> u64 {
let base = 5u64;
let payload: u64 = match self {
Representation::Zero { .. } => 0,
Representation::Fill { .. } => 1,
Representation::Inline { data } => data.len() as u64,
Representation::Raw { .. } => 32,
Representation::Rans { .. } => 32 + 32 + 1 + 1,
Representation::ExactRef { .. } => 32 + 4,
Representation::BaseResidual { residual, .. } => 32 + 4 + residual.encoded_size(),
Representation::Sparse { literals, .. } => 4 + 16 + literals.len() as u64,
Representation::Palette {
palette, counts, ..
} => 1 + palette.len() as u64 + 4 * counts.len() as u64 + 16,
Representation::Periodic {
period,
pattern: _,
tail,
..
} => 4 + *period as u64 + 4 + 4 + tail.len() as u64,
Representation::EntropyRef { residual, .. } => 1 + 16 + 8 + 1 + residual.encoded_size(),
Representation::Permutation { alphabet, .. } => 16 + alphabet.len() as u64,
};
base + payload
}
pub fn validate(&self, limits: &crate::core::limits::Limits) -> Result<(), ReprError> {
if self.encoded_size() > limits.max_descriptor_bytes {
return Err(ReprError::DescriptorTooLarge);
}
match self {
Representation::Zero { len } => {
check_len(*len, limits)?;
}
Representation::Fill { len, .. } => {
check_len(*len, limits)?;
}
Representation::Inline { data } => {
if data.len() as u64 > limits.max_inline_bytes {
return Err(ReprError::InlineTooLarge);
}
}
Representation::Raw { obj, len } => {
check_len(*len, limits)?;
if obj.is_zero() {
return Err(ReprError::ZeroObjectId);
}
}
Representation::Rans {
model,
enc_obj,
scale_bits,
len,
..
} => {
check_len(*len, limits)?;
if model.is_zero() || enc_obj.is_zero() {
return Err(ReprError::ZeroObjectId);
}
if !(1..=16).contains(scale_bits) {
return Err(ReprError::BadScaleBits);
}
}
Representation::ExactRef { target, off, len } => {
check_len(*len, limits)?;
if target.is_zero() {
return Err(ReprError::ZeroObjectId);
}
if off.checked_add(*len).is_none() {
return Err(ReprError::Overflow);
}
}
Representation::BaseResidual {
base,
base_len,
residual,
len,
} => {
check_len(*len, limits)?;
if base.is_zero() {
return Err(ReprError::ZeroObjectId);
}
if *base_len < *len {
return Err(ReprError::BaseTooShort);
}
residual.validate(*len, limits)?;
}
Representation::Sparse {
k,
rank,
literals,
len,
} => {
check_len(*len, limits)?;
let k64 = *k as u64;
if k64 > *len {
return Err(ReprError::SparseKTooLarge);
}
if literals.len() as u64 != k64 {
return Err(ReprError::SparseLiteralCount);
}
match crate::entropy::rank::comb(*len as u128, k64 as u128) {
Some(total) if *rank < total => {}
Some(_) => return Err(ReprError::SparseRankOutOfRange),
None => return Err(ReprError::CombOverflow),
}
}
Representation::Palette {
palette,
counts,
rank,
len,
} => {
check_len(*len, limits)?;
if palette.is_empty() || palette.len() > limits.max_palette {
return Err(ReprError::BadPalette);
}
if counts.len() != palette.len() {
return Err(ReprError::BadPalette);
}
let mut total: u64 = 0;
for &c in counts.iter() {
total = total.checked_add(c as u64).ok_or(ReprError::Overflow)?;
}
if total != *len {
return Err(ReprError::PaletteCountsMismatch);
}
if counts.contains(&0) {
return Err(ReprError::BadPalette);
}
match crate::entropy::rank::multinomial(*len, counts) {
Some(total_states) if *rank < total_states => {}
Some(_) => return Err(ReprError::PaletteRankOutOfRange),
None => return Err(ReprError::CombOverflow),
}
}
Representation::Periodic {
period,
pattern,
count,
tail,
len,
} => {
check_len(*len, limits)?;
if *period == 0 || *period as u64 > limits.max_period as u64 {
return Err(ReprError::BadPeriod);
}
if pattern.len() as u64 != *period as u64 {
return Err(ReprError::BadPeriod);
}
if tail.len() as u64 >= *period as u64 {
return Err(ReprError::BadTail);
}
let expected = (*period as u64)
.checked_mul(*count as u64)
.and_then(|v| v.checked_add(tail.len() as u64))
.ok_or(ReprError::Overflow)?;
if expected != *len {
return Err(ReprError::PeriodicLenMismatch);
}
}
Representation::EntropyRef {
universe,
seed: _,
coordinate: _,
transform,
residual,
len,
} => {
check_len(*len, limits)?;
if *universe == crate::core::representation::UniverseId::UniformXofV1 {
} else {
return Err(ReprError::UnknownUniverse);
}
if *transform != crate::core::representation::TransformId::Identity {
return Err(ReprError::UnknownTransform);
}
residual.validate(*len, limits)?;
}
Representation::Permutation {
rank,
alphabet,
len,
} => {
check_len(*len, limits)?;
let m = *len;
if m == 0 || m > 34 {
return Err(ReprError::PermutationSize);
}
if alphabet.len() as u64 != m {
return Err(ReprError::BadPermutationAlphabet);
}
for w in alphabet.windows(2) {
if w[0] >= w[1] {
return Err(ReprError::BadPermutationAlphabet);
}
}
let total =
crate::entropy::rank::factorial(m as u128).ok_or(ReprError::CombOverflow)?;
if *rank >= total {
return Err(ReprError::PermutationRankOutOfRange);
}
}
}
Ok(())
}
}
fn check_len(len: u64, limits: &crate::core::limits::Limits) -> Result<(), ReprError> {
if len > limits.max_chunk_size {
return Err(ReprError::ChunkTooLarge);
}
Ok(())
}
impl Residual {
pub const fn len(&self) -> u64 {
match self {
Residual::XorSparse { len, .. }
| Residual::RangeReplace { len, .. }
| Residual::RansCoded { len, .. } => *len,
}
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn encoded_size(&self) -> u64 {
match self {
Residual::XorSparse { edits, .. } => 1 + 4 + 5 * edits.len() as u64,
Residual::RangeReplace {
changes, literals, ..
} => 1 + 4 + 8 * changes.len() as u64 + literals.len() as u64,
Residual::RansCoded { .. } => 1 + 32 + 32 + 1 + 1 + 4,
}
}
pub fn validate(
&self,
repr_len: u64,
limits: &crate::core::limits::Limits,
) -> Result<(), ReprError> {
if self.len() != repr_len {
return Err(ReprError::ResidualLenMismatch);
}
match self {
Residual::XorSparse { edits, .. } => {
if edits.len() as u64 > limits.max_fanout as u64 {
return Err(ReprError::FanoutTooLarge);
}
let mut prev: Option<u32> = None;
for e in edits {
if e.pos as u64 >= repr_len {
return Err(ReprError::EditOutOfRange);
}
if let Some(p) = prev {
if e.pos <= p {
return Err(ReprError::EditsNotSorted);
}
}
prev = Some(e.pos);
}
}
Residual::RangeReplace {
changes, literals, ..
} => {
if changes.len() as u64 > limits.max_fanout as u64 {
return Err(ReprError::FanoutTooLarge);
}
let mut expected_lits: u64 = 0;
let mut prev: Option<u32> = None;
for c in changes {
if c.start >= c.end || c.end as u64 > repr_len {
return Err(ReprError::RangeOutOfRange);
}
if let Some(p) = prev {
if c.start <= p {
return Err(ReprError::RangesOverlap);
}
}
prev = Some(c.end);
expected_lits = expected_lits
.checked_add((c.end - c.start) as u64)
.ok_or(ReprError::Overflow)?;
}
if literals.len() as u64 != expected_lits {
return Err(ReprError::LiteralCountMismatch);
}
}
Residual::RansCoded {
enc_obj,
model,
scale_bits,
decoded_len,
..
} => {
if enc_obj.is_zero() || model.is_zero() {
return Err(ReprError::ZeroObjectId);
}
if !(1..=16).contains(scale_bits) {
return Err(ReprError::BadScaleBits);
}
if *decoded_len != repr_len {
return Err(ReprError::ResidualLenMismatch);
}
}
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReprError {
ChunkTooLarge,
ZeroObjectId,
BadScaleBits,
Overflow,
BaseTooShort,
ResidualLenMismatch,
EditOutOfRange,
EditsNotSorted,
RangeOutOfRange,
RangesOverlap,
LiteralCountMismatch,
FanoutTooLarge,
SparseKTooLarge,
SparseLiteralCount,
SparseRankOutOfRange,
CombOverflow,
BadPalette,
PaletteCountsMismatch,
PaletteRankOutOfRange,
BadPeriod,
BadTail,
PeriodicLenMismatch,
InlineTooLarge,
UnknownUniverse,
UnknownTransform,
DescriptorTooLarge,
PermutationSize,
PermutationRankOutOfRange,
BadPermutationAlphabet,
}
impl std::fmt::Display for ReprError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ReprError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::limits::Limits;
fn l() -> Limits {
Limits::default()
}
#[test]
fn zero_valid() {
let r = Representation::Zero { len: 65536 };
assert_eq!(r.len(), 65536);
assert_eq!(r.tag(), 0x01);
r.validate(&l()).unwrap();
}
#[test]
fn zero_too_large_rejected() {
let r = Representation::Zero { len: 1 << 40 };
assert_eq!(r.validate(&l()), Err(ReprError::ChunkTooLarge));
}
#[test]
fn periodic_validation() {
let r = Representation::Periodic {
period: 4,
pattern: b"abcd".to_vec(),
count: 3,
tail: b"xy".to_vec(),
len: 14,
};
r.validate(&l()).unwrap();
let bad = Representation::Periodic {
period: 4,
pattern: b"abcd".to_vec(),
count: 3,
tail: b"xy".to_vec(),
len: 15,
};
assert_eq!(bad.validate(&l()), Err(ReprError::PeriodicLenMismatch));
}
#[test]
fn sparse_validation() {
let r = Representation::Sparse {
k: 3,
rank: 55,
literals: vec![1, 2, 3],
len: 8,
};
r.validate(&l()).unwrap();
let bad = Representation::Sparse {
k: 3,
rank: 56,
literals: vec![1, 2, 3],
len: 8,
};
assert_eq!(bad.validate(&l()), Err(ReprError::SparseRankOutOfRange));
}
#[test]
fn residual_edits_sorted() {
let res = Residual::XorSparse {
len: 8,
edits: vec![Edit { pos: 5, val: 1 }, Edit { pos: 3, val: 2 }],
};
assert_eq!(res.validate(8, &l()), Err(ReprError::EditsNotSorted));
}
}