1use crate::ThisError;
2use std::{cell::RefCell, collections::BTreeMap};
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct MemoryRange {
10 pub start: u8,
11 pub end: u8,
12}
13
14impl MemoryRange {
15 #[must_use]
16 pub const fn contains(&self, id: u8) -> bool {
17 id >= self.start && id <= self.end
18 }
19}
20
21#[derive(Clone, Debug)]
26pub struct MemoryRegistryEntry {
27 pub crate_name: String,
28 pub label: String,
29}
30
31#[derive(Debug, ThisError)]
36pub enum MemoryRegistryError {
37 #[error(
38 "memory range overlap: crate '{existing_crate}' [{existing_start}-{existing_end}]
39conflicts with crate '{new_crate}' [{new_start}-{new_end}]"
40 )]
41 Overlap {
42 existing_crate: String,
43 existing_start: u8,
44 existing_end: u8,
45 new_crate: String,
46 new_start: u8,
47 new_end: u8,
48 },
49
50 #[error("memory id {0} is already registered; each memory id must be globally unique")]
51 DuplicateId(u8),
52}
53
54thread_local! {
59 static RESERVED_RANGES: RefCell<Vec<(String, MemoryRange)>> = const { RefCell::new(Vec::new()) };
60 static REGISTRY: RefCell<BTreeMap<u8, MemoryRegistryEntry>> = const { RefCell::new(BTreeMap::new()) };
61
62 static PENDING_RANGES: RefCell<Vec<(String, u8, u8)>> = const { RefCell::new(Vec::new()) };
64 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, String, String)>> = const { RefCell::new(Vec::new()) };
65}
66
67pub struct MemoryRegistry;
73
74impl MemoryRegistry {
75 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
77 let range = MemoryRange { start, end };
78
79 RESERVED_RANGES.with_borrow(|ranges| {
80 for (existing_crate, existing_range) in ranges {
81 if ranges_overlap(*existing_range, range) {
82 if existing_crate == crate_name
83 && existing_range.start == start
84 && existing_range.end == end
85 {
86 return Ok(());
88 }
89 return Err(MemoryRegistryError::Overlap {
90 existing_crate: existing_crate.clone(),
91 existing_start: existing_range.start,
92 existing_end: existing_range.end,
93 new_crate: crate_name.to_string(),
94 new_start: start,
95 new_end: end,
96 });
97 }
98 }
99
100 Ok(())
101 })?;
102
103 RESERVED_RANGES.with_borrow_mut(|ranges| {
104 ranges.push((crate_name.to_string(), range));
105 });
106
107 Ok(())
108 }
109
110 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
112 REGISTRY.with_borrow(|reg| {
113 if reg.contains_key(&id) {
114 return Err(MemoryRegistryError::DuplicateId(id));
115 }
116 Ok(())
117 })?;
118
119 REGISTRY.with_borrow_mut(|reg| {
120 reg.insert(
121 id,
122 MemoryRegistryEntry {
123 crate_name: crate_name.to_string(),
124 label: label.to_string(),
125 },
126 );
127 });
128
129 Ok(())
130 }
131
132 #[must_use]
134 pub fn export() -> Vec<(u8, MemoryRegistryEntry)> {
135 REGISTRY.with_borrow(|reg| reg.iter().map(|(k, v)| (*k, v.clone())).collect())
136 }
137
138 #[must_use]
140 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
141 RESERVED_RANGES.with_borrow(std::clone::Clone::clone)
142 }
143
144 #[must_use]
146 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
147 REGISTRY.with_borrow(|reg| reg.get(&id).cloned())
148 }
149}
150
151pub fn defer_reserve_range(crate_name: &str, start: u8, end: u8) {
156 PENDING_RANGES.with_borrow_mut(|ranges| {
157 ranges.push((crate_name.to_string(), start, end));
158 });
159}
160
161pub fn defer_register(id: u8, crate_name: &str, label: &str) {
162 PENDING_REGISTRATIONS.with_borrow_mut(|regs| {
163 regs.push((id, crate_name.to_string(), label.to_string()));
164 });
165}
166
167#[must_use]
168pub fn drain_pending_ranges() -> Vec<(String, u8, u8)> {
169 PENDING_RANGES.with_borrow_mut(std::mem::take)
170}
171
172#[must_use]
173pub fn drain_pending_registrations() -> Vec<(u8, String, String)> {
174 PENDING_REGISTRATIONS.with_borrow_mut(std::mem::take)
175}
176
177#[cfg(test)]
182pub fn reset_for_tests() {
183 RESERVED_RANGES.with_borrow_mut(Vec::clear);
184 REGISTRY.with_borrow_mut(BTreeMap::clear);
185 PENDING_RANGES.with_borrow_mut(Vec::clear);
186 PENDING_REGISTRATIONS.with_borrow_mut(Vec::clear);
187}
188
189const fn ranges_overlap(a: MemoryRange, b: MemoryRange) -> bool {
194 a.start <= b.end && b.start <= a.end
195}