use tracing::debug;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EcsObjectType {
CommitCapsule,
IndexSegment,
CheckpointChunk,
PageHistory,
}
impl EcsObjectType {
pub const ALL: [Self; 4] = [
Self::CommitCapsule,
Self::IndexSegment,
Self::CheckpointChunk,
Self::PageHistory,
];
}
impl std::fmt::Display for EcsObjectType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CommitCapsule => write!(f, "CommitCapsule"),
Self::IndexSegment => write!(f, "IndexSegment"),
Self::CheckpointChunk => write!(f, "CheckpointChunk"),
Self::PageHistory => write!(f, "PageHistory"),
}
}
}
pub const MIN_SYMBOL_SIZE: u32 = 512;
pub const MAX_SYMBOL_SIZE: u32 = 65_536;
pub const DEFAULT_INDEX_SEGMENT_SYMBOL_SIZE: u32 = 4096;
pub const DEFAULT_CHECKPOINT_CHUNK_SYMBOL_SIZE: u32 = 4096;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SymbolSizePolicy {
pub version: u32,
pub commit_capsule_override: Option<u32>,
pub index_segment_override: Option<u32>,
pub checkpoint_chunk_override: Option<u32>,
pub page_history_override: Option<u32>,
}
impl SymbolSizePolicy {
#[must_use]
pub const fn v1() -> Self {
Self {
version: 1,
commit_capsule_override: None,
index_segment_override: None,
checkpoint_chunk_override: None,
page_history_override: None,
}
}
#[must_use]
pub fn symbol_size_for(&self, object_type: EcsObjectType, page_size: u32) -> u32 {
let computed = match object_type {
EcsObjectType::CommitCapsule => self
.commit_capsule_override
.unwrap_or_else(|| page_size.min(4096)),
EcsObjectType::IndexSegment => self
.index_segment_override
.unwrap_or(DEFAULT_INDEX_SEGMENT_SYMBOL_SIZE),
EcsObjectType::CheckpointChunk => self
.checkpoint_chunk_override
.unwrap_or(DEFAULT_CHECKPOINT_CHUNK_SYMBOL_SIZE),
EcsObjectType::PageHistory => self.page_history_override.unwrap_or(page_size),
};
let clamped = computed.clamp(MIN_SYMBOL_SIZE, MAX_SYMBOL_SIZE);
debug!(
bead_id = "bd-1hi.28",
object_type = %object_type,
page_size,
computed,
clamped,
"symbol size policy lookup"
);
clamped
}
}
impl Default for SymbolSizePolicy {
fn default() -> Self {
Self::v1()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_size_commit_capsule() {
let policy = SymbolSizePolicy::v1();
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 4096),
4096
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 65536),
4096
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 1024),
1024
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 512),
512
);
}
#[test]
fn test_symbol_size_page_history() {
let policy = SymbolSizePolicy::v1();
assert_eq!(
policy.symbol_size_for(EcsObjectType::PageHistory, 4096),
4096
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::PageHistory, 65536),
65536
);
assert_eq!(policy.symbol_size_for(EcsObjectType::PageHistory, 512), 512);
}
#[test]
fn test_symbol_size_versioned_in_manifest() {
let policy = SymbolSizePolicy::v1();
assert_eq!(policy.version, 1, "V1 policy must have version 1");
let policy_v2 = SymbolSizePolicy {
version: 2,
commit_capsule_override: Some(2048),
..SymbolSizePolicy::v1()
};
assert_eq!(policy_v2.version, 2);
assert_ne!(policy.version, policy_v2.version);
assert_ne!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 4096),
policy_v2.symbol_size_for(EcsObjectType::CommitCapsule, 4096),
);
}
#[test]
fn test_symbol_size_consistency() {
let policy = SymbolSizePolicy::v1();
let page_size = 4096;
for obj_type in EcsObjectType::ALL {
let t1 = policy.symbol_size_for(obj_type, page_size);
let t2 = policy.symbol_size_for(obj_type, page_size);
assert_eq!(
t1, t2,
"symbol size must be consistent for {obj_type:?} at page_size={page_size}"
);
}
}
#[test]
fn prop_symbol_size_within_bounds() {
let policy = SymbolSizePolicy::v1();
for &page_size in &[512_u32, 1024, 2048, 4096, 8192, 16384, 32768, 65536] {
for obj_type in EcsObjectType::ALL {
let t = policy.symbol_size_for(obj_type, page_size);
assert!(
t >= MIN_SYMBOL_SIZE,
"{obj_type:?} at page_size={page_size}: T={t} < MIN={MIN_SYMBOL_SIZE}"
);
assert!(
t <= MAX_SYMBOL_SIZE,
"{obj_type:?} at page_size={page_size}: T={t} > MAX={MAX_SYMBOL_SIZE}"
);
}
}
}
#[test]
fn test_symbol_size_overrides() {
let policy = SymbolSizePolicy {
version: 2,
commit_capsule_override: Some(2048),
index_segment_override: Some(1280),
checkpoint_chunk_override: Some(1024),
page_history_override: Some(8192),
};
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 4096),
2048
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::IndexSegment, 4096),
1280
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::CheckpointChunk, 4096),
1024
);
assert_eq!(
policy.symbol_size_for(EcsObjectType::PageHistory, 4096),
8192
);
}
#[test]
fn test_symbol_size_clamping() {
let policy = SymbolSizePolicy {
version: 3,
commit_capsule_override: Some(100),
..SymbolSizePolicy::v1()
};
assert_eq!(
policy.symbol_size_for(EcsObjectType::CommitCapsule, 4096),
MIN_SYMBOL_SIZE
);
let policy = SymbolSizePolicy {
version: 4,
page_history_override: Some(100_000),
..SymbolSizePolicy::v1()
};
assert_eq!(
policy.symbol_size_for(EcsObjectType::PageHistory, 4096),
MAX_SYMBOL_SIZE
);
}
}