canic_memory/runtime/
registry.rs

1use crate::registry::{
2    MemoryRange, MemoryRegistry, MemoryRegistryEntry, MemoryRegistryError, drain_pending_ranges,
3    drain_pending_registrations,
4};
5
6///
7/// MemoryRegistryInitSummary
8///
9/// Substrate-level summary of registry state after initialization.
10/// This is intended for diagnostics and testing only.
11/// It is NOT a stable API contract or external view.
12///
13
14#[derive(Debug)]
15pub struct MemoryRegistryInitSummary {
16    pub ranges: Vec<(String, MemoryRange)>,
17    pub entries: Vec<(u8, MemoryRegistryEntry)>,
18}
19
20///
21/// MemoryRegistryRuntime
22///
23/// Substrate runtime controller responsible for initializing the
24/// global memory registry.
25///
26/// This type performs mechanical coordination only:
27/// - ordering
28/// - conflict detection
29/// - idempotent initialization
30///
31/// It encodes no application semantics.
32///
33pub struct MemoryRegistryRuntime;
34
35impl MemoryRegistryRuntime {
36    /// Initialize the memory registry.
37    ///
38    /// - Optionally reserves an initial range for the caller.
39    /// - Applies all deferred range reservations.
40    /// - Applies all deferred ID registrations.
41    ///
42    /// This function is idempotent for the same initial range.
43    pub fn init(
44        initial_range: Option<(&str, u8, u8)>,
45    ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
46        // Reserve the caller's initial range first (if provided)
47        if let Some((crate_name, start, end)) = initial_range {
48            MemoryRegistry::reserve_range(crate_name, start, end)?;
49        }
50
51        // Apply deferred range reservations deterministically
52        let mut ranges = drain_pending_ranges();
53        ranges.sort_by_key(|(_, start, _)| *start);
54        for (crate_name, start, end) in ranges {
55            MemoryRegistry::reserve_range(&crate_name, start, end)?;
56        }
57
58        // Apply deferred registrations deterministically
59        let mut regs = drain_pending_registrations();
60        regs.sort_by_key(|(id, _, _)| *id);
61        for (id, crate_name, label) in regs {
62            MemoryRegistry::register(id, &crate_name, &label)?;
63        }
64
65        Ok(MemoryRegistryInitSummary {
66            ranges: MemoryRegistry::export_ranges(),
67            entries: MemoryRegistry::export(),
68        })
69    }
70
71    /// Snapshot all registry entries.
72    #[must_use]
73    pub fn snapshot_entries() -> Vec<(u8, MemoryRegistryEntry)> {
74        MemoryRegistry::export()
75    }
76
77    /// Snapshot all reserved memory ranges.
78    #[must_use]
79    pub fn snapshot_ranges() -> Vec<(String, MemoryRange)> {
80        MemoryRegistry::export_ranges()
81    }
82
83    /// Retrieve a single registry entry by ID.
84    #[must_use]
85    pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
86        MemoryRegistry::get(id)
87    }
88}
89
90///
91/// TESTS
92///
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use crate::registry::{defer_register, defer_reserve_range, reset_for_tests};
98
99    #[test]
100    fn init_applies_initial_and_pending() {
101        reset_for_tests();
102        defer_reserve_range("crate_b", 5, 6);
103        defer_register(5, "crate_b", "B5");
104
105        let summary =
106            MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("init should succeed");
107
108        assert_eq!(summary.ranges.len(), 2);
109        assert_eq!(summary.entries.len(), 1);
110        assert_eq!(summary.entries[0].0, 5);
111        assert_eq!(summary.entries[0].1.label, "B5");
112    }
113
114    #[test]
115    fn init_is_idempotent_for_same_initial_range() {
116        reset_for_tests();
117
118        MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("first init should succeed");
119        MemoryRegistryRuntime::init(Some(("crate_a", 1, 3))).expect("second init should succeed");
120    }
121
122    #[test]
123    fn init_returns_error_on_conflict() {
124        reset_for_tests();
125        defer_reserve_range("crate_a", 1, 3);
126        defer_reserve_range("crate_b", 3, 4);
127
128        let err = MemoryRegistryRuntime::init(None).unwrap_err();
129        assert!(matches!(err, MemoryRegistryError::Overlap { .. }));
130    }
131}