Skip to main content

canic_memory/runtime/
registry.rs

1use 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///
12/// MemoryRegistryInitSummary
13///
14/// Substrate-level summary of registry state after initialization.
15/// This is intended for diagnostics and testing only.
16/// It is NOT a stable API contract or external view.
17///
18
19#[derive(Debug)]
20pub struct MemoryRegistryInitSummary {
21    pub ranges: Vec<(String, MemoryRange)>,
22    pub entries: Vec<(u8, MemoryRegistryEntry)>,
23}
24
25///
26/// MemoryRegistryRuntime
27///
28/// Substrate runtime controller responsible for initializing the
29/// global memory registry.
30///
31/// This type performs mechanical coordination only:
32/// - ordering
33/// - conflict detection
34/// - idempotent initialization
35///
36/// It encodes no application semantics.
37///
38pub struct MemoryRegistryRuntime;
39
40impl MemoryRegistryRuntime {
41    /// Initialize the memory registry.
42    ///
43    /// - Optionally reserves an initial range for the caller.
44    /// - Applies all deferred range reservations.
45    /// - Applies all deferred ID registrations.
46    ///
47    /// This function is idempotent for the same initial range.
48    pub fn init(
49        initial_range: Option<(&str, u8, u8)>,
50    ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
51        // Reserve the caller's initial range first (if provided)
52        if let Some((crate_name, start, end)) = initial_range {
53            MemoryRegistry::reserve_range(crate_name, start, end)?;
54        }
55
56        // Apply deferred range reservations deterministically
57        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        // Apply deferred registrations deterministically
64        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    /// Snapshot all registry entries.
85    #[must_use]
86    pub fn snapshot_entries() -> Vec<(u8, MemoryRegistryEntry)> {
87        MemoryRegistry::export()
88    }
89
90    /// Snapshot all reserved memory ranges.
91    #[must_use]
92    pub fn snapshot_ranges() -> Vec<(String, MemoryRange)> {
93        MemoryRegistry::export_ranges()
94    }
95
96    /// Snapshot all reserved memory ranges with owners.
97    #[must_use]
98    pub fn snapshot_range_entries() -> Vec<MemoryRangeEntry> {
99        MemoryRegistry::export_range_entries()
100    }
101
102    /// Snapshot registry entries grouped by range.
103    #[must_use]
104    pub fn snapshot_ids_by_range() -> Vec<MemoryRangeSnapshot> {
105        MemoryRegistry::export_ids_by_range()
106    }
107
108    /// Retrieve a single registry entry by ID.
109    #[must_use]
110    pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
111        MemoryRegistry::get(id)
112    }
113}
114
115///
116/// TESTS
117///
118
119#[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}