canic_memory/runtime/
registry.rs1use crate::registry::{
2 MemoryRange, MemoryRangeEntry, MemoryRangeSnapshot, MemoryRegistry, MemoryRegistryEntry,
3 MemoryRegistryError, drain_pending_ranges, drain_pending_registrations,
4};
5use std::cell::Cell;
6
7thread_local! {
8 static MEMORY_REGISTRY_INITIALIZED: Cell<bool> = const { Cell::new(false) };
9}
10
11#[derive(Debug)]
20pub struct MemoryRegistryInitSummary {
21 pub ranges: Vec<(String, MemoryRange)>,
22 pub entries: Vec<(u8, MemoryRegistryEntry)>,
23}
24
25pub struct MemoryRegistryRuntime;
39
40impl MemoryRegistryRuntime {
41 pub fn init(
49 initial_range: Option<(&str, u8, u8)>,
50 ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
51 if let Some((crate_name, start, end)) = initial_range {
53 MemoryRegistry::reserve_range(crate_name, start, end)?;
54 }
55
56 let mut ranges = drain_pending_ranges();
58 ranges.sort_by_key(|(_, start, _)| *start);
59 for (crate_name, start, end) in ranges {
60 MemoryRegistry::reserve_range(&crate_name, start, end)?;
61 }
62
63 let mut regs = drain_pending_registrations();
65 regs.sort_by_key(|(id, _, _)| *id);
66 for (id, crate_name, label) in regs {
67 MemoryRegistry::register(id, &crate_name, &label)?;
68 }
69
70 let summary = MemoryRegistryInitSummary {
71 ranges: MemoryRegistry::export_ranges(),
72 entries: MemoryRegistry::export(),
73 };
74 MEMORY_REGISTRY_INITIALIZED.with(|ready| ready.set(true));
75
76 Ok(summary)
77 }
78
79 #[must_use]
80 pub fn is_initialized() -> bool {
81 MEMORY_REGISTRY_INITIALIZED.with(Cell::get)
82 }
83
84 #[must_use]
86 pub fn snapshot_entries() -> Vec<(u8, MemoryRegistryEntry)> {
87 MemoryRegistry::export()
88 }
89
90 #[must_use]
92 pub fn snapshot_ranges() -> Vec<(String, MemoryRange)> {
93 MemoryRegistry::export_ranges()
94 }
95
96 #[must_use]
98 pub fn snapshot_range_entries() -> Vec<MemoryRangeEntry> {
99 MemoryRegistry::export_range_entries()
100 }
101
102 #[must_use]
104 pub fn snapshot_ids_by_range() -> Vec<MemoryRangeSnapshot> {
105 MemoryRegistry::export_ids_by_range()
106 }
107
108 #[must_use]
110 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
111 MemoryRegistry::get(id)
112 }
113}
114
115#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::registry::{defer_register, defer_reserve_range, reset_for_tests};
123
124 #[test]
125 fn init_applies_initial_and_pending() {
126 reset_for_tests();
127 defer_reserve_range("crate_b", 5, 6);
128 defer_register(5, "crate_b", "B5");
129
130 let summary =
131 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("init should succeed");
132
133 assert_eq!(summary.ranges.len(), 2);
134 assert_eq!(summary.entries.len(), 1);
135 assert_eq!(summary.entries[0].0, 5);
136 assert_eq!(summary.entries[0].1.label, "B5");
137 }
138
139 #[test]
140 fn init_is_idempotent_for_same_initial_range() {
141 reset_for_tests();
142
143 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("first init should succeed");
144 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("second init should succeed");
145 }
146
147 #[test]
148 fn init_returns_error_on_conflict() {
149 reset_for_tests();
150 defer_reserve_range("crate_a", 1, 3);
151 defer_reserve_range("crate_b", 3, 4);
152
153 let err = MemoryRegistryRuntime::init(None).unwrap_err();
154 assert!(matches!(err, MemoryRegistryError::Overlap { .. }));
155 }
156}