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 pub fn commit_pending_if_initialized() -> Result<(), MemoryRegistryError> {
120 if !Self::is_initialized() || crate::runtime::is_eager_tls_initializing() {
121 return Ok(());
122 }
123
124 let _ = Self::init(None)?;
125 Ok(())
126 }
127}
128
129#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::registry::{defer_register, defer_reserve_range, reset_for_tests};
137
138 #[test]
139 fn init_applies_initial_and_pending() {
140 reset_for_tests();
141 defer_reserve_range("crate_b", 5, 6).expect("defer range");
142 defer_register(5, "crate_b", "B5").expect("defer register");
143
144 let summary =
145 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("init should succeed");
146
147 assert_eq!(summary.ranges.len(), 2);
148 assert_eq!(summary.entries.len(), 1);
149 assert_eq!(summary.entries[0].0, 5);
150 assert_eq!(summary.entries[0].1.label, "B5");
151 }
152
153 #[test]
154 fn init_is_idempotent_for_same_initial_range() {
155 reset_for_tests();
156
157 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("first init should succeed");
158 MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("second init should succeed");
159 }
160
161 #[test]
162 fn init_returns_error_on_conflict() {
163 reset_for_tests();
164 defer_reserve_range("crate_a", 1, 3).expect("defer range A");
165 defer_reserve_range("crate_b", 3, 4).expect("defer range B");
166
167 let err = MemoryRegistryRuntime::init(None).unwrap_err();
168 assert!(matches!(err, MemoryRegistryError::Overlap { .. }));
169 }
170
171 #[test]
172 fn commit_pending_after_init_applies_late_deferred_items() {
173 reset_for_tests();
174
175 MemoryRegistryRuntime::init(Some(("core", 1, 10))).expect("init should succeed");
176 defer_reserve_range("late", 20, 30).expect("defer late range");
177 defer_register(22, "late", "late_slot").expect("defer late register");
178
179 MemoryRegistryRuntime::commit_pending_if_initialized()
180 .expect("late pending commit should succeed");
181
182 let entry = MemoryRegistryRuntime::get(22).expect("late entry should be registered");
183 assert_eq!(entry.crate_name, "late");
184 assert_eq!(entry.label, "late_slot");
185 }
186}