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 #[error(
95 "memory id {id} is reserved for stable-structures internals and cannot be used by application code"
96 )]
97 ReservedInternalId { id: u8 },
98}
99
100thread_local! {
105 static RESERVED_RANGES: RefCell<Vec<(String, MemoryRange)>> = const { RefCell::new(Vec::new()) };
106 static REGISTRY: RefCell<BTreeMap<u8, MemoryRegistryEntry>> = const { RefCell::new(BTreeMap::new()) };
107
108 static PENDING_RANGES: RefCell<Vec<(String, u8, u8)>> = const { RefCell::new(Vec::new()) };
110 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, String, String)>> = const { RefCell::new(Vec::new()) };
111}
112
113pub struct MemoryRegistry;
119
120impl MemoryRegistry {
121 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
123 if start > end {
124 return Err(MemoryRegistryError::InvalidRange { start, end });
125 }
126 validate_range_excludes_reserved_internal_id(start, end)?;
127
128 let range = MemoryRange { start, end };
129
130 RESERVED_RANGES.with_borrow(|ranges| {
131 for (existing_crate, existing_range) in ranges {
132 if ranges_overlap(*existing_range, range) {
133 if existing_crate == crate_name
134 && existing_range.start == start
135 && existing_range.end == end
136 {
137 return Ok(());
139 }
140 return Err(MemoryRegistryError::Overlap {
141 existing_crate: existing_crate.clone(),
142 existing_start: existing_range.start,
143 existing_end: existing_range.end,
144 new_crate: crate_name.to_string(),
145 new_start: start,
146 new_end: end,
147 });
148 }
149 }
150
151 Ok(())
152 })?;
153
154 RESERVED_RANGES.with_borrow_mut(|ranges| {
155 ranges.push((crate_name.to_string(), range));
156 });
157
158 Ok(())
159 }
160
161 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
163 validate_non_internal_id(id)?;
164 validate_registration_range(crate_name, id)?;
165
166 REGISTRY.with_borrow(|reg| {
167 if reg.contains_key(&id) {
168 return Err(MemoryRegistryError::DuplicateId(id));
169 }
170 Ok(())
171 })?;
172
173 REGISTRY.with_borrow_mut(|reg| {
174 reg.insert(
175 id,
176 MemoryRegistryEntry {
177 crate_name: crate_name.to_string(),
178 label: label.to_string(),
179 },
180 );
181 });
182
183 Ok(())
184 }
185
186 #[must_use]
188 pub fn export() -> Vec<(u8, MemoryRegistryEntry)> {
189 REGISTRY.with_borrow(|reg| reg.iter().map(|(k, v)| (*k, v.clone())).collect())
190 }
191
192 #[must_use]
194 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
195 RESERVED_RANGES.with_borrow(std::clone::Clone::clone)
196 }
197
198 #[must_use]
200 pub fn export_range_entries() -> Vec<MemoryRangeEntry> {
201 RESERVED_RANGES.with_borrow(|ranges| {
202 ranges
203 .iter()
204 .map(|(owner, range)| MemoryRangeEntry {
205 owner: owner.clone(),
206 range: *range,
207 })
208 .collect()
209 })
210 }
211
212 #[must_use]
214 pub fn export_ids_by_range() -> Vec<MemoryRangeSnapshot> {
215 let mut ranges = RESERVED_RANGES.with_borrow(std::clone::Clone::clone);
216 let entries = REGISTRY.with_borrow(std::clone::Clone::clone);
217
218 ranges.sort_by_key(|(_, range)| range.start);
219
220 ranges
221 .into_iter()
222 .map(|(owner, range)| {
223 let entries = entries
224 .iter()
225 .filter(|(id, _)| range.contains(**id))
226 .map(|(id, entry)| (*id, entry.clone()))
227 .collect();
228
229 MemoryRangeSnapshot {
230 owner,
231 range,
232 entries,
233 }
234 })
235 .collect()
236 }
237
238 #[must_use]
240 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
241 REGISTRY.with_borrow(|reg| reg.get(&id).cloned())
242 }
243}
244
245pub fn defer_reserve_range(
250 crate_name: &str,
251 start: u8,
252 end: u8,
253) -> Result<(), MemoryRegistryError> {
254 if start > end {
255 return Err(MemoryRegistryError::InvalidRange { start, end });
256 }
257 validate_range_excludes_reserved_internal_id(start, end)?;
258
259 PENDING_RANGES.with_borrow_mut(|ranges| {
261 ranges.push((crate_name.to_string(), start, end));
262 });
263
264 Ok(())
265}
266
267pub fn defer_register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
268 validate_non_internal_id(id)?;
269
270 PENDING_REGISTRATIONS.with_borrow_mut(|regs| {
272 regs.push((id, crate_name.to_string(), label.to_string()));
273 });
274
275 Ok(())
276}
277
278#[must_use]
279pub fn drain_pending_ranges() -> Vec<(String, u8, u8)> {
280 PENDING_RANGES.with_borrow_mut(std::mem::take)
281}
282
283#[must_use]
284pub fn drain_pending_registrations() -> Vec<(u8, String, String)> {
285 PENDING_REGISTRATIONS.with_borrow_mut(std::mem::take)
286}
287
288#[cfg(test)]
293pub fn reset_for_tests() {
294 RESERVED_RANGES.with_borrow_mut(Vec::clear);
295 REGISTRY.with_borrow_mut(BTreeMap::clear);
296 PENDING_RANGES.with_borrow_mut(Vec::clear);
297 PENDING_REGISTRATIONS.with_borrow_mut(Vec::clear);
298}
299
300const fn ranges_overlap(a: MemoryRange, b: MemoryRange) -> bool {
305 a.start <= b.end && b.start <= a.end
306}
307
308const INTERNAL_RESERVED_MEMORY_ID: u8 = u8::MAX;
309
310const fn validate_non_internal_id(id: u8) -> Result<(), MemoryRegistryError> {
311 if id == INTERNAL_RESERVED_MEMORY_ID {
312 return Err(MemoryRegistryError::ReservedInternalId { id });
313 }
314 Ok(())
315}
316
317const fn validate_range_excludes_reserved_internal_id(
318 _start: u8,
319 end: u8,
320) -> Result<(), MemoryRegistryError> {
321 if end == INTERNAL_RESERVED_MEMORY_ID {
322 return Err(MemoryRegistryError::ReservedInternalId {
323 id: INTERNAL_RESERVED_MEMORY_ID,
324 });
325 }
326 Ok(())
327}
328
329fn validate_registration_range(crate_name: &str, id: u8) -> Result<(), MemoryRegistryError> {
330 let mut has_range = false;
331 let mut owner_match = false;
332 let mut owner_for_id: Option<(String, MemoryRange)> = None;
333
334 RESERVED_RANGES.with_borrow(|ranges| {
335 for (owner, range) in ranges {
336 if owner == crate_name {
337 has_range = true;
338 if range.contains(id) {
339 owner_match = true;
340 break;
341 }
342 }
343
344 if owner_for_id.is_none() && range.contains(id) {
345 owner_for_id = Some((owner.clone(), *range));
346 }
347 }
348 });
349
350 if owner_match {
351 return Ok(());
352 }
353
354 if !has_range {
355 return Err(MemoryRegistryError::NoReservedRange {
356 crate_name: crate_name.to_string(),
357 id,
358 });
359 }
360
361 if let Some((owner, range)) = owner_for_id {
362 return Err(MemoryRegistryError::IdOwnedByOther {
363 crate_name: crate_name.to_string(),
364 id,
365 owner,
366 owner_start: range.start,
367 owner_end: range.end,
368 });
369 }
370
371 Err(MemoryRegistryError::IdOutOfRange {
372 crate_name: crate_name.to_string(),
373 id,
374 })
375}
376
377#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn allows_in_range() {
387 reset_for_tests();
388
389 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range");
390 MemoryRegistry::register(2, "crate_a", "slot").expect("register in range");
391 }
392
393 #[test]
394 fn rejects_unreserved() {
395 reset_for_tests();
396
397 let err = MemoryRegistry::register(2, "crate_a", "slot").expect_err("missing range");
398 assert!(matches!(err, MemoryRegistryError::NoReservedRange { .. }));
399 }
400
401 #[test]
402 fn rejects_other_owner() {
403 reset_for_tests();
404
405 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
406 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
407
408 let err = MemoryRegistry::register(2, "crate_b", "slot").expect_err("owned by other");
409 assert!(matches!(err, MemoryRegistryError::IdOwnedByOther { .. }));
410 }
411
412 #[test]
413 fn export_ids_by_range_groups_entries() {
414 reset_for_tests();
415
416 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
417 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
418 MemoryRegistry::register(1, "crate_a", "a1").expect("register a1");
419 MemoryRegistry::register(5, "crate_b", "b5").expect("register b5");
420
421 let snapshots = MemoryRegistry::export_ids_by_range();
422 assert_eq!(snapshots.len(), 2);
423 assert_eq!(snapshots[0].entries.len(), 1);
424 assert_eq!(snapshots[1].entries.len(), 1);
425 }
426
427 #[test]
428 fn rejects_internal_reserved_id_on_register() {
429 reset_for_tests();
430
431 MemoryRegistry::reserve_range("crate_a", 1, 254).expect("reserve range");
432 let err = MemoryRegistry::register(u8::MAX, "crate_a", "slot")
433 .expect_err("reserved id should be rejected");
434 assert!(matches!(
435 err,
436 MemoryRegistryError::ReservedInternalId { .. }
437 ));
438 }
439
440 #[test]
441 fn rejects_internal_reserved_id_on_range_reservation() {
442 reset_for_tests();
443
444 let err = MemoryRegistry::reserve_range("crate_a", 250, u8::MAX)
445 .expect_err("reserved internal id must not be reservable");
446 assert!(matches!(
447 err,
448 MemoryRegistryError::ReservedInternalId { .. }
449 ));
450 }
451
452 #[test]
453 fn rejects_internal_reserved_id_on_deferred_register() {
454 reset_for_tests();
455
456 let err = defer_register(u8::MAX, "crate_a", "slot")
457 .expect_err("reserved id should fail before init");
458 assert!(matches!(
459 err,
460 MemoryRegistryError::ReservedInternalId { .. }
461 ));
462 }
463
464 #[test]
465 fn rejects_internal_reserved_id_on_deferred_range_reservation() {
466 reset_for_tests();
467
468 let err = defer_reserve_range("crate_a", 240, u8::MAX)
469 .expect_err("reserved id should fail before init");
470 assert!(matches!(
471 err,
472 MemoryRegistryError::ReservedInternalId { .. }
473 ));
474 }
475}