use crate::imp::core::codec::section::FORMAT_VERSION;
const MB: u64 = 1_000_000;
const MIB: u64 = 1024 * 1024;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CapsuleClass {
Mb2,
Mb4,
Mb8,
Mb16,
Mb32,
Mb64,
Mb128,
Mb256,
Mb512,
Mb1024,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CapsuleSpec {
pub class: CapsuleClass,
pub content_cap_bytes: u64,
pub uniform_blob_len: u64,
pub format_version: u8,
}
impl CapsuleClass {
pub const DEFAULT: CapsuleClass = CapsuleClass::Mb128;
pub const ALL: [CapsuleClass; 10] = [
CapsuleClass::Mb2,
CapsuleClass::Mb4,
CapsuleClass::Mb8,
CapsuleClass::Mb16,
CapsuleClass::Mb32,
CapsuleClass::Mb64,
CapsuleClass::Mb128,
CapsuleClass::Mb256,
CapsuleClass::Mb512,
CapsuleClass::Mb1024,
];
const fn mb(self) -> u64 {
match self {
CapsuleClass::Mb2 => 2,
CapsuleClass::Mb4 => 4,
CapsuleClass::Mb8 => 8,
CapsuleClass::Mb16 => 16,
CapsuleClass::Mb32 => 32,
CapsuleClass::Mb64 => 64,
CapsuleClass::Mb128 => 128,
CapsuleClass::Mb256 => 256,
CapsuleClass::Mb512 => 512,
CapsuleClass::Mb1024 => 1024,
}
}
pub const fn spec(self) -> CapsuleSpec {
let mb = self.mb();
CapsuleSpec {
class: self,
content_cap_bytes: mb * MB,
uniform_blob_len: mb * MIB,
format_version: FORMAT_VERSION,
}
}
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::*;
use crate::imp::core::config::MAX_STORE_BYTES;
#[test]
fn default_is_128mb_and_anchors_to_max_store_bytes() {
let spec = CapsuleClass::DEFAULT.spec();
assert_eq!(CapsuleClass::DEFAULT, CapsuleClass::Mb128);
assert_eq!(spec.content_cap_bytes, MAX_STORE_BYTES);
assert_eq!(spec.content_cap_bytes, 128_000_000);
assert_eq!(spec.uniform_blob_len, 128 * 1024 * 1024);
}
#[test]
fn ladder_is_powers_of_two_mb_from_2mb_to_1gb() {
let caps: alloc::vec::Vec<u64> = CapsuleClass::ALL
.iter()
.map(|c| c.spec().content_cap_bytes)
.collect();
assert_eq!(
caps,
alloc::vec![
2_000_000,
4_000_000,
8_000_000,
16_000_000,
32_000_000,
64_000_000,
128_000_000,
256_000_000,
512_000_000,
1_024_000_000,
]
);
assert!(caps[9] >= 1_000_000_000);
assert!(caps[8] < 1_000_000_000);
}
#[test]
fn uniform_blob_always_covers_the_content_cap() {
for class in CapsuleClass::ALL {
let spec = class.spec();
assert!(
spec.uniform_blob_len >= spec.content_cap_bytes,
"{class:?}: blob {} must cover cap {}",
spec.uniform_blob_len,
spec.content_cap_bytes
);
assert_eq!(spec.format_version, FORMAT_VERSION);
}
}
#[test]
fn seam_is_additively_expandable() {
fn total_addressable(classes: &[CapsuleClass]) -> u64 {
classes.iter().map(|c| c.spec().content_cap_bytes).sum()
}
let now = total_addressable(&CapsuleClass::ALL);
assert!(now > 0);
let spec: CapsuleSpec = CapsuleClass::DEFAULT.spec();
assert_eq!(spec.class, CapsuleClass::DEFAULT);
}
}