#![forbid(unsafe_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Feature {
Chunk4K = 1,
Chunk16K = 2,
Chunk256K = 3,
EntropyRef = 4,
Palette = 5,
Permutation = 6,
Encrypted = 7,
ExtentDeltaIndex = 8,
OptimizerRewrite = 9,
SequenceRans = 10,
SparseBlock64 = 11,
SequenceDict = 12,
SequenceSharedDict = 13,
}
impl Feature {
pub const fn set(self) -> FeatureSetKind {
match self {
Feature::Chunk4K
| Feature::Chunk16K
| Feature::Chunk256K
| Feature::EntropyRef
| Feature::Palette
| Feature::Permutation
| Feature::SequenceRans
| Feature::SparseBlock64
| Feature::SequenceDict
| Feature::SequenceSharedDict => FeatureSetKind::Incompat,
Feature::Encrypted => FeatureSetKind::RoCompat,
Feature::ExtentDeltaIndex | Feature::OptimizerRewrite => FeatureSetKind::Compat,
}
}
pub const fn mask(self) -> u64 {
1u64 << (self as u8 - 1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FeatureSetKind {
Compat,
RoCompat,
Incompat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FeatureBits {
pub compat: u64,
pub ro_compat: u64,
pub incompat: u64,
}
impl FeatureBits {
pub const fn empty() -> Self {
Self {
compat: 0,
ro_compat: 0,
incompat: 0,
}
}
pub fn enable(&mut self, f: Feature) {
match f.set() {
FeatureSetKind::Compat => self.compat |= f.mask(),
FeatureSetKind::RoCompat => self.ro_compat |= f.mask(),
FeatureSetKind::Incompat => self.incompat |= f.mask(),
}
}
pub fn has(&self, f: Feature) -> bool {
match f.set() {
FeatureSetKind::Compat => self.compat & f.mask() != 0,
FeatureSetKind::RoCompat => self.ro_compat & f.mask() != 0,
FeatureSetKind::Incompat => self.incompat & f.mask() != 0,
}
}
pub fn for_representations(
&self,
has_entropy_ref: bool,
has_palette: bool,
has_permutation: bool,
) -> Self {
let mut out = *self;
if has_entropy_ref {
out.enable(Feature::EntropyRef);
}
if has_palette {
out.enable(Feature::Palette);
}
if has_permutation {
out.enable(Feature::Permutation);
}
out
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Compatibility {
Ok,
ReadOnlyOnly,
Refused(String),
}
pub fn check(on_disk: FeatureBits, _want_write: bool) -> Compatibility {
let supported_incompat = Feature::Chunk4K.mask()
| Feature::Chunk16K.mask()
| Feature::Chunk256K.mask()
| Feature::EntropyRef.mask()
| Feature::Palette.mask()
| Feature::Permutation.mask()
| Feature::SequenceRans.mask()
| Feature::SparseBlock64.mask()
| Feature::SequenceDict.mask()
| Feature::SequenceSharedDict.mask();
if on_disk.incompat & !supported_incompat != 0 {
return Compatibility::Refused(format!(
"unsupported incompat feature bits: 0x{:016x}",
on_disk.incompat & !supported_incompat
));
}
if on_disk.ro_compat != 0 {
return Compatibility::Refused(
"store uses features not yet supported by this tool (encryption)".into(),
);
}
Compatibility::Ok
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn feature_sets() {
let mut f = FeatureBits::empty();
assert!(!f.has(Feature::Palette));
f.enable(Feature::Palette);
assert!(f.has(Feature::Palette));
assert_eq!(
f.incompat & Feature::Palette.mask(),
Feature::Palette.mask()
);
f.enable(Feature::ExtentDeltaIndex);
assert!(f.has(Feature::ExtentDeltaIndex));
assert_eq!(f.compat, Feature::ExtentDeltaIndex.mask());
}
#[test]
fn compat_checks() {
let mut f = FeatureBits::empty();
f.incompat = 1u64 << 63;
assert!(matches!(check(f, true), Compatibility::Refused(_)));
let mut f = FeatureBits::empty();
f.enable(Feature::Palette);
f.enable(Feature::EntropyRef);
assert_eq!(check(f, true), Compatibility::Ok);
let mut f = FeatureBits::empty();
f.enable(Feature::Encrypted);
assert!(matches!(check(f, false), Compatibility::Refused(_)));
}
}