1use crate::manager::MEMORY_MANAGER;
4use candid::CandidType;
5use canic_cdk::structures::{
6 BTreeMap as StableBTreeMap, DefaultMemoryImpl,
7 memory::{MemoryId, VirtualMemory},
8};
9use canic_core::{impl_storable_bounded, types::BoundedString256, utils::time::now_secs};
10use serde::{Deserialize, Serialize};
11use std::cell::RefCell;
12use thiserror::Error as ThisError;
13
14pub const MEMORY_REGISTRY_ID: u8 = 0;
18pub const MEMORY_RANGES_ID: u8 = 1;
19
20thread_local! {
25 static MEMORY_REGISTRY: RefCell<StableBTreeMap<u8, MemoryRegistryEntry, VirtualMemory<DefaultMemoryImpl>>> =
26 RefCell::new(StableBTreeMap::init(
27 MEMORY_MANAGER.with_borrow(|this| {
28 this.get(MemoryId::new(MEMORY_REGISTRY_ID))
29 }),
30 ));
31}
32
33thread_local! {
38 static MEMORY_RANGES: RefCell<StableBTreeMap<String, MemoryRange, VirtualMemory<DefaultMemoryImpl>>> =
39 RefCell::new(StableBTreeMap::init(
40 MEMORY_MANAGER.with_borrow(|mgr| {
41 mgr.get(MemoryId::new(MEMORY_RANGES_ID))
42 }),
43 ));
44}
45
46thread_local! {
56 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, &'static str, &'static str)>> = const {
57 RefCell::new(Vec::new())
58 };
59}
60
61pub fn defer_register(id: u8, crate_name: &'static str, label: &'static str) {
63 PENDING_REGISTRATIONS.with(|q| {
64 q.borrow_mut().push((id, crate_name, label));
65 });
66}
67
68#[must_use]
71pub fn drain_pending_registrations() -> Vec<(u8, &'static str, &'static str)> {
72 PENDING_REGISTRATIONS.with(|q| q.borrow_mut().drain(..).collect())
73}
74
75thread_local! {
80 pub static PENDING_RANGES: RefCell<Vec<(&'static str, u8, u8)>> = const {
81 RefCell::new(Vec::new())
82 };
83}
84
85pub fn defer_reserve_range(crate_name: &'static str, start: u8, end: u8) {
87 PENDING_RANGES.with(|q| q.borrow_mut().push((crate_name, start, end)));
88}
89
90#[must_use]
93pub fn drain_pending_ranges() -> Vec<(&'static str, u8, u8)> {
94 PENDING_RANGES.with(|q| q.borrow_mut().drain(..).collect())
95}
96
97#[derive(Debug, ThisError)]
102pub enum MemoryRegistryError {
103 #[error("ID {0} is already registered with type {1}, tried to register type {2}")]
104 AlreadyRegistered(u8, String, String),
105
106 #[error("crate `{0}` already has a reserved range")]
107 DuplicateRange(String),
108
109 #[error("crate `{0}` provided invalid range {1}-{2} (start > end)")]
110 InvalidRange(String, u8, u8),
111
112 #[error("crate `{0}` attempted to register ID {1}, but it is outside its allowed ranges")]
113 OutOfRange(String, u8),
114
115 #[error("crate `{0}` range {1}-{2} overlaps with crate `{3}` range {4}-{5}")]
116 Overlap(String, u8, u8, String, u8, u8),
117
118 #[error("crate `{0}` has not reserved any memory range")]
119 NoRange(String),
120}
121
122#[derive(Clone, Debug, Deserialize, Serialize)]
126pub struct MemoryRange {
127 pub crate_key: BoundedString256,
128 pub start: u8,
129 pub end: u8,
130 pub created_at: u64,
131}
132
133impl MemoryRange {
134 #[must_use]
135 pub(crate) fn new(crate_key: &str, start: u8, end: u8) -> Self {
136 Self {
137 crate_key: BoundedString256::new(crate_key),
138 start,
139 end,
140 created_at: now_secs(),
141 }
142 }
143
144 #[must_use]
145 pub fn contains(&self, id: u8) -> bool {
146 (self.start..=self.end).contains(&id)
147 }
148}
149
150impl_storable_bounded!(MemoryRange, 320, false);
151
152#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
157pub struct MemoryRegistryEntry {
158 pub label: BoundedString256,
159 pub created_at: u64,
160}
161
162impl MemoryRegistryEntry {
163 #[must_use]
164 pub(crate) fn new(label: &str) -> Self {
165 Self {
166 label: BoundedString256::new(label),
167 created_at: now_secs(),
168 }
169 }
170}
171
172impl_storable_bounded!(MemoryRegistryEntry, 320, false);
173
174pub type MemoryRegistryView = Vec<(u8, MemoryRegistryEntry)>;
179
180pub struct MemoryRegistry;
185
186impl MemoryRegistry {
187 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
194 let crate_key = crate_name.to_string();
195
196 let range = MEMORY_RANGES.with_borrow(|ranges| ranges.get(&crate_key));
198 match range {
199 None => {
200 return Err(MemoryRegistryError::NoRange(crate_key));
201 }
202 Some(r) if !r.contains(id) => {
203 return Err(MemoryRegistryError::OutOfRange(crate_key, id));
204 }
205 Some(_) => {
206 }
208 }
209
210 let existing = MEMORY_REGISTRY.with_borrow(|map| map.get(&id));
212 if let Some(existing) = existing {
213 if existing.label.as_ref() != label {
214 return Err(MemoryRegistryError::AlreadyRegistered(
215 id,
216 existing.label.to_string(),
217 label.to_string(),
218 ));
219 }
220
221 return Ok(());
223 }
224
225 MEMORY_REGISTRY.with_borrow_mut(|map| {
227 map.insert(id, MemoryRegistryEntry::new(label));
228 });
229
230 Ok(())
231 }
232
233 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
237 if start > end {
238 return Err(MemoryRegistryError::InvalidRange(
239 crate_name.to_string(),
240 start,
241 end,
242 ));
243 }
244
245 let crate_key = crate_name.to_string();
246
247 let conflict = MEMORY_RANGES.with_borrow(|ranges| {
249 if ranges.contains_key(&crate_key) {
250 return Some(MemoryRegistryError::DuplicateRange(crate_key.clone()));
251 }
252
253 for entry in ranges.iter() {
254 let other_crate = entry.key();
255 let other_range = entry.value();
256
257 if !(end < other_range.start || start > other_range.end) {
258 return Some(MemoryRegistryError::Overlap(
259 crate_key.clone(),
260 start,
261 end,
262 other_crate.clone(),
263 other_range.start,
264 other_range.end,
265 ));
266 }
267 }
268
269 None
270 });
271
272 if let Some(err) = conflict {
273 return Err(err);
274 }
275
276 MEMORY_RANGES.with_borrow_mut(|ranges| {
278 let range = MemoryRange::new(crate_name, start, end);
279 ranges.insert(crate_name.to_string(), range);
280 });
281
282 Ok(())
283 }
284
285 #[must_use]
286 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
287 MEMORY_REGISTRY.with_borrow(|map| map.get(&id))
288 }
289
290 #[must_use]
291 pub fn export() -> MemoryRegistryView {
292 MEMORY_REGISTRY.with_borrow(|map| {
293 map.iter()
294 .map(|entry| (*entry.key(), entry.value()))
295 .collect()
296 })
297 }
298
299 #[must_use]
300 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
301 MEMORY_RANGES.with_borrow(|ranges| {
302 ranges
303 .iter()
304 .map(|e| (e.key().clone(), e.value()))
305 .collect()
306 })
307 }
308}
309
310#[cfg(test)]
311pub(crate) fn reset_for_tests() {
312 MEMORY_REGISTRY.with_borrow_mut(StableBTreeMap::clear);
313 MEMORY_RANGES.with_borrow_mut(StableBTreeMap::clear);
314 PENDING_REGISTRATIONS.with(|q| q.borrow_mut().clear());
315 PENDING_RANGES.with(|q| q.borrow_mut().clear());
316}
317
318#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn reserve_range_happy_path_and_reject_overlap() {
328 reset_for_tests();
329 MemoryRegistry::reserve_range("crate_a", 10, 20).unwrap();
330
331 let err = MemoryRegistry::reserve_range("crate_b", 15, 25).unwrap_err();
333 matches!(err, MemoryRegistryError::Overlap(_, _, _, _, _, _));
334
335 MemoryRegistry::reserve_range("crate_b", 30, 40).unwrap();
337
338 let ranges = MemoryRegistry::export_ranges();
339 assert_eq!(ranges.len(), 2);
340 }
341
342 #[test]
343 fn reserve_range_rejects_invalid_order() {
344 reset_for_tests();
345 let err = MemoryRegistry::reserve_range("crate_a", 5, 4).unwrap_err();
346 matches!(err, MemoryRegistryError::InvalidRange(_, _, _));
347 assert!(MemoryRegistry::export_ranges().is_empty());
348 }
349
350 #[test]
351 fn register_id_requires_range_and_checks_bounds() {
352 reset_for_tests();
353 MemoryRegistry::reserve_range("crate_a", 1, 3).unwrap();
354
355 let err = MemoryRegistry::register(5, "crate_a", "Foo").unwrap_err();
357 matches!(err, MemoryRegistryError::OutOfRange(_, _));
358
359 MemoryRegistry::register(2, "crate_a", "Foo").unwrap();
361
362 MemoryRegistry::register(2, "crate_a", "Foo").unwrap();
364
365 let err = MemoryRegistry::register(2, "crate_a", "Bar").unwrap_err();
367 matches!(err, MemoryRegistryError::AlreadyRegistered(_, _, _));
368
369 let view = MemoryRegistry::export();
370 assert_eq!(view.len(), 1);
371 assert_eq!(view[0].0, 2);
372 }
373
374 #[test]
375 fn pending_queues_drain_in_order() {
376 reset_for_tests();
377 defer_reserve_range("crate_a", 1, 2);
378 defer_reserve_range("crate_b", 3, 4);
379 defer_register(1, "crate_a", "A1");
380 defer_register(3, "crate_b", "B3");
381
382 let ranges = drain_pending_ranges();
383 assert_eq!(ranges, vec![("crate_a", 1, 2), ("crate_b", 3, 4)]);
384 let regs = drain_pending_registrations();
385 assert_eq!(regs, vec![(1, "crate_a", "A1"), (3, "crate_b", "B3")]);
386
387 assert!(drain_pending_ranges().is_empty());
389 assert!(drain_pending_registrations().is_empty());
390 }
391}