use crate::{
declaration::{AllocationDeclaration, DeclarationCollector, DeclarationSnapshot},
schema::SchemaMetadata,
slot::{
MemoryManagerAuthorityRecord, MemoryManagerIdRange, MemoryManagerRangeAuthority,
MemoryManagerRangeAuthorityError, MemoryManagerRangeMode,
},
};
use std::sync::Mutex;
#[cfg(test)]
pub(crate) static TEST_REGISTRY_LOCK: Mutex<()> = Mutex::new(());
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StaticMemoryDeclaration {
declaring_crate: String,
declaration: AllocationDeclaration,
}
impl StaticMemoryDeclaration {
pub fn new(declaring_crate: impl Into<String>, declaration: AllocationDeclaration) -> Self {
Self {
declaring_crate: declaring_crate.into(),
declaration,
}
}
#[must_use]
pub fn declaring_crate(&self) -> &str {
&self.declaring_crate
}
#[must_use]
pub const fn declaration(&self) -> &AllocationDeclaration {
&self.declaration
}
#[must_use]
pub fn into_declaration(self) -> AllocationDeclaration {
self.declaration
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StaticMemoryRangeDeclaration {
declaring_crate: String,
record: MemoryManagerAuthorityRecord,
}
impl StaticMemoryRangeDeclaration {
#[must_use]
pub fn new(declaring_crate: impl Into<String>, record: MemoryManagerAuthorityRecord) -> Self {
Self {
declaring_crate: declaring_crate.into(),
record,
}
}
#[must_use]
pub fn declaring_crate(&self) -> &str {
&self.declaring_crate
}
#[must_use]
pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
&self.record
}
#[must_use]
pub fn into_record(self) -> MemoryManagerAuthorityRecord {
self.record
}
}
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
pub enum StaticMemoryDeclarationError {
#[error("static memory declaration registry lock poisoned")]
RegistryPoisoned,
#[error("static memory declaration registry is already sealed")]
RegistrySealed,
#[error(transparent)]
Declaration(#[from] crate::DeclarationSnapshotError),
#[error(transparent)]
Range(#[from] MemoryManagerRangeAuthorityError),
}
#[derive(Debug, Default)]
struct StaticMemoryDeclarationRegistry {
declarations: Vec<StaticMemoryDeclaration>,
ranges: Vec<StaticMemoryRangeDeclaration>,
sealed: bool,
}
static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
Mutex::new(StaticMemoryDeclarationRegistry {
declarations: Vec::new(),
ranges: Vec::new(),
sealed: false,
});
pub fn register_static_memory_declaration(
declaring_crate: impl Into<String>,
declaration: AllocationDeclaration,
) -> Result<(), StaticMemoryDeclarationError> {
let mut registry = STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
if registry.sealed {
return Err(StaticMemoryDeclarationError::RegistrySealed);
}
registry
.declarations
.push(StaticMemoryDeclaration::new(declaring_crate, declaration));
Ok(())
}
pub fn register_static_memory_manager_range(
start: u8,
end: u8,
declaring_crate: impl Into<String>,
mode: MemoryManagerRangeMode,
purpose: Option<String>,
) -> Result<(), StaticMemoryDeclarationError> {
let declaring_crate = declaring_crate.into();
let record = MemoryManagerAuthorityRecord::new(
MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
declaring_crate.clone(),
mode,
purpose,
)?;
register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(
declaring_crate,
record,
))
}
pub fn register_static_memory_range_declaration(
declaration: StaticMemoryRangeDeclaration,
) -> Result<(), StaticMemoryDeclarationError> {
let mut registry = STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
if registry.sealed {
return Err(StaticMemoryDeclarationError::RegistrySealed);
}
registry.ranges.push(declaration);
Ok(())
}
pub fn register_static_memory_manager_declaration(
id: u8,
declaring_crate: impl Into<String>,
label: impl Into<String>,
stable_key: impl AsRef<str>,
) -> Result<(), StaticMemoryDeclarationError> {
register_static_memory_manager_declaration_with_schema(
id,
declaring_crate,
label,
stable_key,
SchemaMetadata::default(),
)
}
pub fn register_static_memory_manager_declaration_with_schema(
id: u8,
declaring_crate: impl Into<String>,
label: impl Into<String>,
stable_key: impl AsRef<str>,
schema: SchemaMetadata,
) -> Result<(), StaticMemoryDeclarationError> {
let declaration =
AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
register_static_memory_declaration(declaring_crate, declaration)
}
pub fn static_memory_declarations()
-> Result<Vec<StaticMemoryDeclaration>, StaticMemoryDeclarationError> {
STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
.map(|registry| registry.declarations.clone())
}
pub fn static_memory_range_declarations()
-> Result<Vec<StaticMemoryRangeDeclaration>, StaticMemoryDeclarationError> {
STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
.map(|registry| registry.ranges.clone())
}
pub fn static_memory_range_authority()
-> Result<MemoryManagerRangeAuthority, StaticMemoryDeclarationError> {
MemoryManagerRangeAuthority::from_records(
static_memory_range_declarations()?
.into_iter()
.map(StaticMemoryRangeDeclaration::into_record)
.collect(),
)
.map_err(StaticMemoryDeclarationError::Range)
}
pub(crate) fn seal_static_memory_registry() -> Result<(), StaticMemoryDeclarationError> {
let mut registry = STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
registry.sealed = true;
Ok(())
}
pub fn collect_static_memory_declarations(
collector: &mut DeclarationCollector,
) -> Result<(), StaticMemoryDeclarationError> {
for registration in static_memory_declarations()? {
collector.push(registration.into_declaration());
}
Ok(())
}
pub fn static_memory_declaration_snapshot()
-> Result<DeclarationSnapshot, StaticMemoryDeclarationError> {
let declarations = {
let mut registry = STATIC_MEMORY_DECLARATIONS
.lock()
.map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
registry.sealed = true;
registry
.declarations
.iter()
.map(|registration| registration.declaration.clone())
.collect()
};
DeclarationSnapshot::new(declarations).map_err(StaticMemoryDeclarationError::Declaration)
}
#[cfg(test)]
pub(crate) fn reset_static_memory_declarations_for_tests() {
let mut registry = STATIC_MEMORY_DECLARATIONS
.lock()
.expect("static memory declaration registry poisoned");
registry.declarations.clear();
registry.ranges.clear();
registry.sealed = false;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registers_and_seals_static_memory_declarations() {
let _guard = TEST_REGISTRY_LOCK.lock().expect("test lock poisoned");
reset_static_memory_declarations_for_tests();
register_static_memory_manager_declaration(100, "icydb", "users", "icydb.users.data.v1")
.expect("register declaration");
let registrations = static_memory_declarations().expect("registrations");
assert_eq!(registrations.len(), 1);
assert_eq!(registrations[0].declaring_crate(), "icydb");
assert_eq!(
registrations[0].declaration().stable_key().as_str(),
"icydb.users.data.v1"
);
let snapshot = static_memory_declaration_snapshot().expect("snapshot");
assert_eq!(snapshot.len(), 1);
let err =
register_static_memory_manager_declaration(101, "icydb", "orders", "icydb.orders.v1")
.expect_err("late registration must fail");
assert_eq!(err, StaticMemoryDeclarationError::RegistrySealed);
}
#[test]
fn registers_static_memory_ranges() {
let _guard = TEST_REGISTRY_LOCK.lock().expect("test lock poisoned");
reset_static_memory_declarations_for_tests();
register_static_memory_manager_range(
100,
109,
"crate_a",
MemoryManagerRangeMode::Reserved,
Some("crate A stores".to_string()),
)
.expect("register range");
let ranges = static_memory_range_declarations().expect("ranges");
assert_eq!(ranges.len(), 1);
assert_eq!(ranges[0].declaring_crate(), "crate_a");
assert_eq!(ranges[0].record().range.start(), 100);
assert_eq!(ranges[0].record().range.end(), 109);
}
#[test]
fn snapshot_rejects_duplicate_static_memory_declarations() {
let _guard = TEST_REGISTRY_LOCK.lock().expect("test lock poisoned");
reset_static_memory_declarations_for_tests();
register_static_memory_manager_declaration(100, "icydb", "users", "icydb.users.data.v1")
.expect("register first declaration");
register_static_memory_manager_declaration(100, "icydb", "orders", "icydb.orders.v1")
.expect("register duplicate slot declaration");
let err = static_memory_declaration_snapshot().expect_err("duplicate slot must fail");
assert!(matches!(
err,
StaticMemoryDeclarationError::Declaration(
crate::DeclarationSnapshotError::DuplicateSlot(_)
)
));
}
}