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(Clone, Debug)]
36pub struct MemoryRangeEntry {
37 pub owner: String,
38 pub range: MemoryRange,
39}
40
41#[derive(Clone, Debug)]
46pub struct MemoryRangeSnapshot {
47 pub owner: String,
48 pub range: MemoryRange,
49 pub entries: Vec<(u8, MemoryRegistryEntry)>,
50}
51
52#[derive(Debug, ThisError)]
57pub enum MemoryRegistryError {
58 #[error(
59 "memory range overlap: crate '{existing_crate}' [{existing_start}-{existing_end}]
60conflicts with crate '{new_crate}' [{new_start}-{new_end}]"
61 )]
62 Overlap {
63 existing_crate: String,
64 existing_start: u8,
65 existing_end: u8,
66 new_crate: String,
67 new_start: u8,
68 new_end: u8,
69 },
70
71 #[error("memory range is invalid: start={start} end={end}")]
72 InvalidRange { start: u8, end: u8 },
73
74 #[error("memory id {0} is already registered; each memory id must be globally unique")]
75 DuplicateId(u8),
76
77 #[error("memory id {id} has no reserved range for crate '{crate_name}'")]
78 NoReservedRange { crate_name: String, id: u8 },
79
80 #[error(
81 "memory id {id} reserved to crate '{owner}' [{owner_start}-{owner_end}], not '{crate_name}'"
82 )]
83 IdOwnedByOther {
84 crate_name: String,
85 id: u8,
86 owner: String,
87 owner_start: u8,
88 owner_end: u8,
89 },
90
91 #[error("memory id {id} is outside reserved ranges for crate '{crate_name}'")]
92 IdOutOfRange { crate_name: String, id: u8 },
93}
94
95thread_local! {
100 static RESERVED_RANGES: RefCell<Vec<(String, MemoryRange)>> = const { RefCell::new(Vec::new()) };
101 static REGISTRY: RefCell<BTreeMap<u8, MemoryRegistryEntry>> = const { RefCell::new(BTreeMap::new()) };
102
103 static PENDING_RANGES: RefCell<Vec<(String, u8, u8)>> = const { RefCell::new(Vec::new()) };
105 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, String, String)>> = const { RefCell::new(Vec::new()) };
106}
107
108pub struct MemoryRegistry;
114
115impl MemoryRegistry {
116 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
118 if start > end {
119 return Err(MemoryRegistryError::InvalidRange { start, end });
120 }
121
122 let range = MemoryRange { start, end };
123
124 RESERVED_RANGES.with_borrow(|ranges| {
125 for (existing_crate, existing_range) in ranges {
126 if ranges_overlap(*existing_range, range) {
127 if existing_crate == crate_name
128 && existing_range.start == start
129 && existing_range.end == end
130 {
131 return Ok(());
133 }
134 return Err(MemoryRegistryError::Overlap {
135 existing_crate: existing_crate.clone(),
136 existing_start: existing_range.start,
137 existing_end: existing_range.end,
138 new_crate: crate_name.to_string(),
139 new_start: start,
140 new_end: end,
141 });
142 }
143 }
144
145 Ok(())
146 })?;
147
148 RESERVED_RANGES.with_borrow_mut(|ranges| {
149 ranges.push((crate_name.to_string(), range));
150 });
151
152 Ok(())
153 }
154
155 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
157 validate_registration_range(crate_name, id)?;
158
159 REGISTRY.with_borrow(|reg| {
160 if reg.contains_key(&id) {
161 return Err(MemoryRegistryError::DuplicateId(id));
162 }
163 Ok(())
164 })?;
165
166 REGISTRY.with_borrow_mut(|reg| {
167 reg.insert(
168 id,
169 MemoryRegistryEntry {
170 crate_name: crate_name.to_string(),
171 label: label.to_string(),
172 },
173 );
174 });
175
176 Ok(())
177 }
178
179 #[must_use]
181 pub fn export() -> Vec<(u8, MemoryRegistryEntry)> {
182 REGISTRY.with_borrow(|reg| reg.iter().map(|(k, v)| (*k, v.clone())).collect())
183 }
184
185 #[must_use]
187 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
188 RESERVED_RANGES.with_borrow(std::clone::Clone::clone)
189 }
190
191 #[must_use]
193 pub fn export_range_entries() -> Vec<MemoryRangeEntry> {
194 RESERVED_RANGES.with_borrow(|ranges| {
195 ranges
196 .iter()
197 .map(|(owner, range)| MemoryRangeEntry {
198 owner: owner.clone(),
199 range: *range,
200 })
201 .collect()
202 })
203 }
204
205 #[must_use]
207 pub fn export_ids_by_range() -> Vec<MemoryRangeSnapshot> {
208 let mut ranges = RESERVED_RANGES.with_borrow(std::clone::Clone::clone);
209 let entries = REGISTRY.with_borrow(std::clone::Clone::clone);
210
211 ranges.sort_by_key(|(_, range)| range.start);
212
213 ranges
214 .into_iter()
215 .map(|(owner, range)| {
216 let entries = entries
217 .iter()
218 .filter(|(id, _)| range.contains(**id))
219 .map(|(id, entry)| (*id, entry.clone()))
220 .collect();
221
222 MemoryRangeSnapshot {
223 owner,
224 range,
225 entries,
226 }
227 })
228 .collect()
229 }
230
231 #[must_use]
233 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
234 REGISTRY.with_borrow(|reg| reg.get(&id).cloned())
235 }
236}
237
238pub fn defer_reserve_range(crate_name: &str, start: u8, end: u8) {
243 PENDING_RANGES.with_borrow_mut(|ranges| {
245 ranges.push((crate_name.to_string(), start, end));
246 });
247}
248
249pub fn defer_register(id: u8, crate_name: &str, label: &str) {
250 PENDING_REGISTRATIONS.with_borrow_mut(|regs| {
252 regs.push((id, crate_name.to_string(), label.to_string()));
253 });
254}
255
256#[must_use]
257pub fn drain_pending_ranges() -> Vec<(String, u8, u8)> {
258 PENDING_RANGES.with_borrow_mut(std::mem::take)
259}
260
261#[must_use]
262pub fn drain_pending_registrations() -> Vec<(u8, String, String)> {
263 PENDING_REGISTRATIONS.with_borrow_mut(std::mem::take)
264}
265
266#[cfg(test)]
271pub fn reset_for_tests() {
272 RESERVED_RANGES.with_borrow_mut(Vec::clear);
273 REGISTRY.with_borrow_mut(BTreeMap::clear);
274 PENDING_RANGES.with_borrow_mut(Vec::clear);
275 PENDING_REGISTRATIONS.with_borrow_mut(Vec::clear);
276}
277
278const fn ranges_overlap(a: MemoryRange, b: MemoryRange) -> bool {
283 a.start <= b.end && b.start <= a.end
284}
285
286fn validate_registration_range(crate_name: &str, id: u8) -> Result<(), MemoryRegistryError> {
287 let mut has_range = false;
288 let mut owner_match = false;
289 let mut owner_for_id: Option<(String, MemoryRange)> = None;
290
291 RESERVED_RANGES.with_borrow(|ranges| {
292 for (owner, range) in ranges {
293 if owner == crate_name {
294 has_range = true;
295 if range.contains(id) {
296 owner_match = true;
297 break;
298 }
299 }
300
301 if owner_for_id.is_none() && range.contains(id) {
302 owner_for_id = Some((owner.clone(), *range));
303 }
304 }
305 });
306
307 if owner_match {
308 return Ok(());
309 }
310
311 if !has_range {
312 return Err(MemoryRegistryError::NoReservedRange {
313 crate_name: crate_name.to_string(),
314 id,
315 });
316 }
317
318 if let Some((owner, range)) = owner_for_id {
319 return Err(MemoryRegistryError::IdOwnedByOther {
320 crate_name: crate_name.to_string(),
321 id,
322 owner,
323 owner_start: range.start,
324 owner_end: range.end,
325 });
326 }
327
328 Err(MemoryRegistryError::IdOutOfRange {
329 crate_name: crate_name.to_string(),
330 id,
331 })
332}
333
334#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn allows_in_range() {
344 reset_for_tests();
345
346 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range");
347 MemoryRegistry::register(2, "crate_a", "slot").expect("register in range");
348 }
349
350 #[test]
351 fn rejects_unreserved() {
352 reset_for_tests();
353
354 let err = MemoryRegistry::register(2, "crate_a", "slot").expect_err("missing range");
355 assert!(matches!(err, MemoryRegistryError::NoReservedRange { .. }));
356 }
357
358 #[test]
359 fn rejects_other_owner() {
360 reset_for_tests();
361
362 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
363 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
364
365 let err = MemoryRegistry::register(2, "crate_b", "slot").expect_err("owned by other");
366 assert!(matches!(err, MemoryRegistryError::IdOwnedByOther { .. }));
367 }
368
369 #[test]
370 fn export_ids_by_range_groups_entries() {
371 reset_for_tests();
372
373 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
374 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
375 MemoryRegistry::register(1, "crate_a", "a1").expect("register a1");
376 MemoryRegistry::register(5, "crate_b", "b5").expect("register b5");
377
378 let snapshots = MemoryRegistry::export_ids_by_range();
379 assert_eq!(snapshots.len(), 2);
380 assert_eq!(snapshots[0].entries.len(), 1);
381 assert_eq!(snapshots[1].entries.len(), 1);
382 }
383}