1use crate::ThisError;
2use std::{cell::RefCell, collections::BTreeMap};
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct MemoryRange {
11 pub start: u8,
13 pub end: u8,
15}
16
17impl MemoryRange {
18 #[must_use]
20 pub const fn contains(&self, id: u8) -> bool {
21 id >= self.start && id <= self.end
22 }
23}
24
25#[derive(Clone, Debug)]
31pub struct MemoryRegistryEntry {
32 pub crate_name: String,
34 pub label: String,
36}
37
38#[derive(Clone, Debug)]
44pub struct MemoryRangeEntry {
45 pub owner: String,
47 pub range: MemoryRange,
49}
50
51#[derive(Clone, Debug)]
57pub struct MemoryRangeSnapshot {
58 pub owner: String,
60 pub range: MemoryRange,
62 pub entries: Vec<(u8, MemoryRegistryEntry)>,
64}
65
66#[derive(Debug, ThisError)]
72pub enum MemoryRegistryError {
73 #[error(
75 "memory range overlap: crate '{existing_crate}' [{existing_start}-{existing_end}]
76conflicts with crate '{new_crate}' [{new_start}-{new_end}]"
77 )]
78 Overlap {
79 existing_crate: String,
81 existing_start: u8,
83 existing_end: u8,
85 new_crate: String,
87 new_start: u8,
89 new_end: u8,
91 },
92
93 #[error("memory range is invalid: start={start} end={end}")]
95 InvalidRange {
96 start: u8,
98 end: u8,
100 },
101
102 #[error("memory id {0} is already registered; each memory id must be globally unique")]
104 DuplicateId(u8),
105
106 #[error("memory id {id} has no reserved range for crate '{crate_name}'")]
108 NoReservedRange {
109 crate_name: String,
111 id: u8,
113 },
114
115 #[error(
117 "memory id {id} reserved to crate '{owner}' [{owner_start}-{owner_end}], not '{crate_name}'"
118 )]
119 IdOwnedByOther {
120 crate_name: String,
122 id: u8,
124 owner: String,
126 owner_start: u8,
128 owner_end: u8,
130 },
131
132 #[error("memory id {id} is outside reserved ranges for crate '{crate_name}'")]
134 IdOutOfRange {
135 crate_name: String,
137 id: u8,
139 },
140
141 #[error(
143 "memory id {id} is reserved for stable-structures internals and cannot be used by application code"
144 )]
145 ReservedInternalId {
146 id: u8,
148 },
149}
150
151thread_local! {
156 static RESERVED_RANGES: RefCell<Vec<(String, MemoryRange)>> = const { RefCell::new(Vec::new()) };
157 static REGISTRY: RefCell<BTreeMap<u8, MemoryRegistryEntry>> = const { RefCell::new(BTreeMap::new()) };
158
159 static PENDING_RANGES: RefCell<Vec<(String, u8, u8)>> = const { RefCell::new(Vec::new()) };
161 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, String, String)>> = const { RefCell::new(Vec::new()) };
162}
163
164pub struct MemoryRegistry;
170
171impl MemoryRegistry {
172 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
177 if start > end {
178 return Err(MemoryRegistryError::InvalidRange { start, end });
179 }
180 validate_range_excludes_reserved_internal_id(start, end)?;
181
182 let range = MemoryRange { start, end };
183
184 RESERVED_RANGES.with_borrow(|ranges| {
185 for (existing_crate, existing_range) in ranges {
186 if ranges_overlap(*existing_range, range) {
187 if existing_crate == crate_name
188 && existing_range.start == start
189 && existing_range.end == end
190 {
191 return Ok(());
193 }
194 return Err(MemoryRegistryError::Overlap {
195 existing_crate: existing_crate.clone(),
196 existing_start: existing_range.start,
197 existing_end: existing_range.end,
198 new_crate: crate_name.to_string(),
199 new_start: start,
200 new_end: end,
201 });
202 }
203 }
204
205 Ok(())
206 })?;
207
208 RESERVED_RANGES.with_borrow_mut(|ranges| {
209 ranges.push((crate_name.to_string(), range));
210 });
211
212 Ok(())
213 }
214
215 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
217 validate_non_internal_id(id)?;
218 validate_registration_range(crate_name, id)?;
219
220 REGISTRY.with_borrow(|reg| {
221 if reg.contains_key(&id) {
222 return Err(MemoryRegistryError::DuplicateId(id));
223 }
224 Ok(())
225 })?;
226
227 REGISTRY.with_borrow_mut(|reg| {
228 reg.insert(
229 id,
230 MemoryRegistryEntry {
231 crate_name: crate_name.to_string(),
232 label: label.to_string(),
233 },
234 );
235 });
236
237 Ok(())
238 }
239
240 #[must_use]
242 pub fn export() -> Vec<(u8, MemoryRegistryEntry)> {
243 REGISTRY.with_borrow(|reg| reg.iter().map(|(k, v)| (*k, v.clone())).collect())
244 }
245
246 #[must_use]
248 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
249 RESERVED_RANGES.with_borrow(std::clone::Clone::clone)
250 }
251
252 #[must_use]
254 pub fn export_range_entries() -> Vec<MemoryRangeEntry> {
255 RESERVED_RANGES.with_borrow(|ranges| {
256 ranges
257 .iter()
258 .map(|(owner, range)| MemoryRangeEntry {
259 owner: owner.clone(),
260 range: *range,
261 })
262 .collect()
263 })
264 }
265
266 #[must_use]
268 pub fn export_ids_by_range() -> Vec<MemoryRangeSnapshot> {
269 let mut ranges = RESERVED_RANGES.with_borrow(std::clone::Clone::clone);
270 let entries = REGISTRY.with_borrow(std::clone::Clone::clone);
271
272 ranges.sort_by_key(|(_, range)| range.start);
273
274 ranges
275 .into_iter()
276 .map(|(owner, range)| {
277 let entries = entries
278 .iter()
279 .filter(|(id, _)| range.contains(**id))
280 .map(|(id, entry)| (*id, entry.clone()))
281 .collect();
282
283 MemoryRangeSnapshot {
284 owner,
285 range,
286 entries,
287 }
288 })
289 .collect()
290 }
291
292 #[must_use]
294 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
295 REGISTRY.with_borrow(|reg| reg.get(&id).cloned())
296 }
297}
298
299#[doc(hidden)]
305pub fn defer_reserve_range(
306 crate_name: &str,
307 start: u8,
308 end: u8,
309) -> Result<(), MemoryRegistryError> {
310 if start > end {
311 return Err(MemoryRegistryError::InvalidRange { start, end });
312 }
313 validate_range_excludes_reserved_internal_id(start, end)?;
314
315 PENDING_RANGES.with_borrow_mut(|ranges| {
317 ranges.push((crate_name.to_string(), start, end));
318 });
319
320 Ok(())
321}
322
323#[doc(hidden)]
325pub fn defer_register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
326 validate_non_internal_id(id)?;
327
328 PENDING_REGISTRATIONS.with_borrow_mut(|regs| {
330 regs.push((id, crate_name.to_string(), label.to_string()));
331 });
332
333 Ok(())
334}
335
336#[must_use]
338pub(crate) fn drain_pending_ranges() -> Vec<(String, u8, u8)> {
339 PENDING_RANGES.with_borrow_mut(std::mem::take)
340}
341
342#[must_use]
344pub(crate) fn drain_pending_registrations() -> Vec<(u8, String, String)> {
345 PENDING_REGISTRATIONS.with_borrow_mut(std::mem::take)
346}
347
348#[cfg(test)]
353pub fn reset_for_tests() {
355 RESERVED_RANGES.with_borrow_mut(Vec::clear);
356 REGISTRY.with_borrow_mut(BTreeMap::clear);
357 PENDING_RANGES.with_borrow_mut(Vec::clear);
358 PENDING_REGISTRATIONS.with_borrow_mut(Vec::clear);
359}
360
361const fn ranges_overlap(a: MemoryRange, b: MemoryRange) -> bool {
366 a.start <= b.end && b.start <= a.end
367}
368
369const INTERNAL_RESERVED_MEMORY_ID: u8 = u8::MAX;
370
371const fn validate_non_internal_id(id: u8) -> Result<(), MemoryRegistryError> {
372 if id == INTERNAL_RESERVED_MEMORY_ID {
373 return Err(MemoryRegistryError::ReservedInternalId { id });
374 }
375 Ok(())
376}
377
378const fn validate_range_excludes_reserved_internal_id(
379 _start: u8,
380 end: u8,
381) -> Result<(), MemoryRegistryError> {
382 if end == INTERNAL_RESERVED_MEMORY_ID {
383 return Err(MemoryRegistryError::ReservedInternalId {
384 id: INTERNAL_RESERVED_MEMORY_ID,
385 });
386 }
387 Ok(())
388}
389
390fn validate_registration_range(crate_name: &str, id: u8) -> Result<(), MemoryRegistryError> {
391 let mut has_range = false;
392 let mut owner_match = false;
393 let mut owner_for_id: Option<(String, MemoryRange)> = None;
394
395 RESERVED_RANGES.with_borrow(|ranges| {
396 for (owner, range) in ranges {
397 if owner == crate_name {
398 has_range = true;
399 if range.contains(id) {
400 owner_match = true;
401 break;
402 }
403 }
404
405 if owner_for_id.is_none() && range.contains(id) {
406 owner_for_id = Some((owner.clone(), *range));
407 }
408 }
409 });
410
411 if owner_match {
412 return Ok(());
413 }
414
415 if !has_range {
416 return Err(MemoryRegistryError::NoReservedRange {
417 crate_name: crate_name.to_string(),
418 id,
419 });
420 }
421
422 if let Some((owner, range)) = owner_for_id {
423 return Err(MemoryRegistryError::IdOwnedByOther {
424 crate_name: crate_name.to_string(),
425 id,
426 owner,
427 owner_start: range.start,
428 owner_end: range.end,
429 });
430 }
431
432 Err(MemoryRegistryError::IdOutOfRange {
433 crate_name: crate_name.to_string(),
434 id,
435 })
436}
437
438#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn allows_in_range() {
448 reset_for_tests();
449
450 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range");
451 MemoryRegistry::register(2, "crate_a", "slot").expect("register in range");
452 }
453
454 #[test]
455 fn rejects_unreserved() {
456 reset_for_tests();
457
458 let err = MemoryRegistry::register(2, "crate_a", "slot").expect_err("missing range");
459 assert!(matches!(err, MemoryRegistryError::NoReservedRange { .. }));
460 }
461
462 #[test]
463 fn rejects_other_owner() {
464 reset_for_tests();
465
466 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
467 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
468
469 let err = MemoryRegistry::register(2, "crate_b", "slot").expect_err("owned by other");
470 assert!(matches!(err, MemoryRegistryError::IdOwnedByOther { .. }));
471 }
472
473 #[test]
474 fn export_ids_by_range_groups_entries() {
475 reset_for_tests();
476
477 MemoryRegistry::reserve_range("crate_a", 1, 3).expect("reserve range A");
478 MemoryRegistry::reserve_range("crate_b", 4, 6).expect("reserve range B");
479 MemoryRegistry::register(1, "crate_a", "a1").expect("register a1");
480 MemoryRegistry::register(5, "crate_b", "b5").expect("register b5");
481
482 let snapshots = MemoryRegistry::export_ids_by_range();
483 assert_eq!(snapshots.len(), 2);
484 assert_eq!(snapshots[0].entries.len(), 1);
485 assert_eq!(snapshots[1].entries.len(), 1);
486 }
487
488 #[test]
489 fn rejects_internal_reserved_id_on_register() {
490 reset_for_tests();
491
492 MemoryRegistry::reserve_range("crate_a", 1, 254).expect("reserve range");
493 let err = MemoryRegistry::register(u8::MAX, "crate_a", "slot")
494 .expect_err("reserved id should be rejected");
495 assert!(matches!(
496 err,
497 MemoryRegistryError::ReservedInternalId { .. }
498 ));
499 }
500
501 #[test]
502 fn rejects_internal_reserved_id_on_range_reservation() {
503 reset_for_tests();
504
505 let err = MemoryRegistry::reserve_range("crate_a", 250, u8::MAX)
506 .expect_err("reserved internal id must not be reservable");
507 assert!(matches!(
508 err,
509 MemoryRegistryError::ReservedInternalId { .. }
510 ));
511 }
512
513 #[test]
514 fn rejects_internal_reserved_id_on_deferred_register() {
515 reset_for_tests();
516
517 let err = defer_register(u8::MAX, "crate_a", "slot")
518 .expect_err("reserved id should fail before init");
519 assert!(matches!(
520 err,
521 MemoryRegistryError::ReservedInternalId { .. }
522 ));
523 }
524
525 #[test]
526 fn rejects_internal_reserved_id_on_deferred_range_reservation() {
527 reset_for_tests();
528
529 let err = defer_reserve_range("crate_a", 240, u8::MAX)
530 .expect_err("reserved id should fail before init");
531 assert!(matches!(
532 err,
533 MemoryRegistryError::ReservedInternalId { .. }
534 ));
535 }
536}