use std::fmt;
use crate::config::Preference;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Algorithms: u8 {
const LZ4 = 1;
#[cfg(test)] const ZSTD = 1 << 2;
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct Compression(u8);
impl Compression {
pub(crate) const fn empty() -> Self {
Self::new(Algorithms::empty(), Algorithms::empty())
}
pub(super) const fn from_bits(v: u8) -> Self {
Self(v)
}
pub(crate) const fn into_bits(self) -> u8 {
self.0
}
pub(crate) const fn new(supported: Algorithms, preferred: Algorithms) -> Self {
let preferred = preferred.bits();
let supported = supported.bits() | preferred;
Self(supported | (preferred << 1))
}
pub(crate) fn toggle(&mut self, algos: Algorithms, pref: Preference) {
let mut preferred = self.preferred() - algos;
let mut supported = self.supported() - algos;
match pref {
Preference::Preferred => preferred |= algos,
Preference::Supported => supported |= algos,
Preference::Disabled => {}
};
*self = Self::new(supported, preferred);
}
pub(crate) fn intersection(self, rhs: Self) -> Self {
let supported = self.supported() & rhs.supported();
let both_prefer = self.preferred() & rhs.preferred();
let some_prefer = (self.preferred() | rhs.preferred()) & supported;
let preferred = if !both_prefer.is_empty() {
both_prefer
} else {
some_prefer
};
Self::new(supported, preferred)
}
pub(crate) const fn supported(self) -> Algorithms {
Algorithms::from_bits_truncate(self.0)
}
pub(crate) const fn preferred(self) -> Algorithms {
Algorithms::from_bits_truncate(self.0 >> 1)
}
}
impl fmt::Display for Compression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut empty = true;
for (name, _) in self.preferred().iter_names() {
if !empty {
f.write_str(", ")?;
}
write!(f, "{name}")?;
empty = false;
}
if empty {
f.write_str("None")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn toggle() {
let mut compression = Compression::empty();
assert!(!compression.preferred().contains(Algorithms::LZ4));
assert!(!compression.supported().contains(Algorithms::LZ4));
assert!(!compression.preferred().contains(Algorithms::ZSTD));
assert!(!compression.supported().contains(Algorithms::ZSTD));
compression.toggle(Algorithms::ZSTD, Preference::Preferred);
assert!(compression.preferred().contains(Algorithms::ZSTD));
assert!(compression.supported().contains(Algorithms::ZSTD));
for _ in 0..2 {
compression.toggle(Algorithms::LZ4, Preference::Preferred);
assert!(compression.preferred().contains(Algorithms::LZ4));
assert!(compression.supported().contains(Algorithms::LZ4));
compression.toggle(Algorithms::LZ4, Preference::Supported);
assert!(!compression.preferred().contains(Algorithms::LZ4));
assert!(compression.supported().contains(Algorithms::LZ4));
compression.toggle(Algorithms::LZ4, Preference::Disabled);
assert!(!compression.preferred().contains(Algorithms::LZ4));
assert!(!compression.supported().contains(Algorithms::LZ4));
}
assert!(compression.preferred().contains(Algorithms::ZSTD));
assert!(compression.supported().contains(Algorithms::ZSTD));
}
#[test]
fn intersection() {
let a = Compression::empty();
let b = Compression::new(Algorithms::LZ4, Algorithms::LZ4);
assert_eq!(a.intersection(b), Compression::empty());
let a = Compression::new(Algorithms::LZ4, Algorithms::ZSTD | Algorithms::LZ4);
let b = Compression::new(Algorithms::LZ4, Algorithms::ZSTD);
let s = a.intersection(b);
assert_eq!(s.preferred(), Algorithms::ZSTD);
assert_eq!(s.supported(), Algorithms::LZ4 | Algorithms::ZSTD);
let a = Compression::new(Algorithms::LZ4, Algorithms::ZSTD);
let b = Compression::new(Algorithms::LZ4, Algorithms::empty());
let s = a.intersection(b);
assert_eq!(s.preferred(), Algorithms::empty());
assert_eq!(s.supported(), Algorithms::LZ4);
let a = Compression::new(Algorithms::LZ4, Algorithms::LZ4);
let b = Compression::new(Algorithms::LZ4, Algorithms::empty());
let s = a.intersection(b);
assert_eq!(s.preferred(), Algorithms::LZ4);
assert_eq!(s.supported(), Algorithms::LZ4);
let a = Compression::new(Algorithms::LZ4, Algorithms::empty());
let b = Compression::new(Algorithms::ZSTD, Algorithms::empty());
let s = a.intersection(b);
assert_eq!(s.preferred(), Algorithms::empty());
assert_eq!(s.supported(), Algorithms::empty());
}
}