use crate::registry::{
MemoryRange, MemoryRangeEntry, MemoryRangeSnapshot, MemoryRegistry, MemoryRegistryEntry,
MemoryRegistryError, drain_pending_ranges, drain_pending_registrations,
};
use std::cell::Cell;
thread_local! {
static MEMORY_REGISTRY_INITIALIZED: Cell<bool> = const { Cell::new(false) };
}
#[derive(Debug)]
pub struct MemoryRegistryInitSummary {
pub ranges: Vec<(String, MemoryRange)>,
pub entries: Vec<(u8, MemoryRegistryEntry)>,
}
pub struct MemoryRegistryRuntime;
impl MemoryRegistryRuntime {
pub fn init(
initial_range: Option<(&str, u8, u8)>,
) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
if let Some((crate_name, start, end)) = initial_range {
MemoryRegistry::reserve_range(crate_name, start, end)?;
}
let mut ranges = drain_pending_ranges();
ranges.sort_by_key(|(_, start, _)| *start);
for (crate_name, start, end) in ranges {
MemoryRegistry::reserve_range(&crate_name, start, end)?;
}
let mut regs = drain_pending_registrations();
regs.sort_by_key(|(id, _, _)| *id);
for (id, crate_name, label) in regs {
MemoryRegistry::register(id, &crate_name, &label)?;
}
let summary = MemoryRegistryInitSummary {
ranges: MemoryRegistry::export_ranges(),
entries: MemoryRegistry::export(),
};
MEMORY_REGISTRY_INITIALIZED.with(|ready| ready.set(true));
Ok(summary)
}
#[must_use]
pub fn is_initialized() -> bool {
MEMORY_REGISTRY_INITIALIZED.with(Cell::get)
}
#[must_use]
pub fn snapshot_entries() -> Vec<(u8, MemoryRegistryEntry)> {
MemoryRegistry::export()
}
#[must_use]
pub fn snapshot_ranges() -> Vec<(String, MemoryRange)> {
MemoryRegistry::export_ranges()
}
#[must_use]
pub fn snapshot_range_entries() -> Vec<MemoryRangeEntry> {
MemoryRegistry::export_range_entries()
}
#[must_use]
pub fn snapshot_ids_by_range() -> Vec<MemoryRangeSnapshot> {
MemoryRegistry::export_ids_by_range()
}
#[must_use]
pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
MemoryRegistry::get(id)
}
pub fn commit_pending_if_initialized() -> Result<(), MemoryRegistryError> {
if !Self::is_initialized() || crate::runtime::is_eager_tls_initializing() {
return Ok(());
}
let _ = Self::init(None)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::registry::{defer_register, defer_reserve_range, reset_for_tests};
#[test]
fn init_applies_initial_and_pending() {
reset_for_tests();
defer_reserve_range("crate_b", 5, 6).expect("defer range");
defer_register(5, "crate_b", "B5").expect("defer register");
let summary =
MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("init should succeed");
assert_eq!(summary.ranges.len(), 2);
assert_eq!(summary.entries.len(), 1);
assert_eq!(summary.entries[0].0, 5);
assert_eq!(summary.entries[0].1.label, "B5");
}
#[test]
fn init_is_idempotent_for_same_initial_range() {
reset_for_tests();
MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("first init should succeed");
MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("second init should succeed");
}
#[test]
fn init_returns_error_on_conflict() {
reset_for_tests();
defer_reserve_range("crate_a", 1, 3).expect("defer range A");
defer_reserve_range("crate_b", 3, 4).expect("defer range B");
let err = MemoryRegistryRuntime::init(None).unwrap_err();
assert!(matches!(err, MemoryRegistryError::Overlap { .. }));
}
#[test]
fn commit_pending_after_init_applies_late_deferred_items() {
reset_for_tests();
MemoryRegistryRuntime::init(Some(("core", 1, 10))).expect("init should succeed");
defer_reserve_range("late", 20, 30).expect("defer late range");
defer_register(22, "late", "late_slot").expect("defer late register");
MemoryRegistryRuntime::commit_pending_if_initialized()
.expect("late pending commit should succeed");
let entry = MemoryRegistryRuntime::get(22).expect("late entry should be registered");
assert_eq!(entry.crate_name, "late");
assert_eq!(entry.label, "late_slot");
}
}