1use crate::{impl_storable_bounded, manager::MEMORY_MANAGER};
4use candid::CandidType;
5use canic_cdk::{
6 structures::{
7 BTreeMap as StableBTreeMap, DefaultMemoryImpl,
8 memory::{MemoryId, VirtualMemory},
9 },
10 utils::time::now_secs,
11};
12use canic_types::BoundedString256;
13use serde::{Deserialize, Serialize};
14use std::cell::RefCell;
15use thiserror::Error as ThisError;
16
17pub const MEMORY_REGISTRY_ID: u8 = 0;
21pub const MEMORY_RANGES_ID: u8 = 1;
22
23thread_local! {
28 static MEMORY_REGISTRY: RefCell<StableBTreeMap<u8, MemoryRegistryEntry, VirtualMemory<DefaultMemoryImpl>>> =
29 RefCell::new(StableBTreeMap::init(
30 MEMORY_MANAGER.with_borrow(|this| {
31 this.get(MemoryId::new(MEMORY_REGISTRY_ID))
32 }),
33 ));
34}
35
36thread_local! {
41 static MEMORY_RANGES: RefCell<StableBTreeMap<String, MemoryRange, VirtualMemory<DefaultMemoryImpl>>> =
42 RefCell::new(StableBTreeMap::init(
43 MEMORY_MANAGER.with_borrow(|mgr| {
44 mgr.get(MemoryId::new(MEMORY_RANGES_ID))
45 }),
46 ));
47}
48
49thread_local! {
59 static PENDING_REGISTRATIONS: RefCell<Vec<(u8, &'static str, &'static str)>> = const {
60 RefCell::new(Vec::new())
61 };
62}
63
64pub fn defer_register(id: u8, crate_name: &'static str, label: &'static str) {
66 PENDING_REGISTRATIONS.with(|q| {
67 q.borrow_mut().push((id, crate_name, label));
68 });
69}
70
71#[must_use]
74pub fn drain_pending_registrations() -> Vec<(u8, &'static str, &'static str)> {
75 PENDING_REGISTRATIONS.with(|q| q.borrow_mut().drain(..).collect())
76}
77
78thread_local! {
83 pub static PENDING_RANGES: RefCell<Vec<(&'static str, u8, u8)>> = const {
84 RefCell::new(Vec::new())
85 };
86}
87
88pub fn defer_reserve_range(crate_name: &'static str, start: u8, end: u8) {
90 PENDING_RANGES.with(|q| q.borrow_mut().push((crate_name, start, end)));
91}
92
93#[must_use]
96pub fn drain_pending_ranges() -> Vec<(&'static str, u8, u8)> {
97 PENDING_RANGES.with(|q| q.borrow_mut().drain(..).collect())
98}
99
100#[derive(Debug, ThisError)]
105pub enum MemoryRegistryError {
106 #[error("ID {0} is already registered with type {1}, tried to register type {2}")]
107 AlreadyRegistered(u8, String, String),
108
109 #[error("crate `{0}` key too long ({1} bytes), max 256")]
110 CrateKeyTooLong(String, usize),
111
112 #[error("crate `{0}` already has a reserved range")]
113 DuplicateRange(String),
114
115 #[error("crate `{0}` provided invalid range {1}-{2} (start > end)")]
116 InvalidRange(String, u8, u8),
117
118 #[error("label for crate `{0}` too long ({1} bytes), max 256")]
119 LabelTooLong(String, usize),
120
121 #[error("crate `{0}` attempted to register ID {1}, but it is outside its allowed ranges")]
122 OutOfRange(String, u8),
123
124 #[error("crate `{0}` range {1}-{2} overlaps with crate `{3}` range {4}-{5}")]
125 Overlap(String, u8, u8, String, u8, u8),
126
127 #[error("crate `{0}` has not reserved any memory range")]
128 NoRange(String),
129}
130
131#[derive(Clone, Debug, Deserialize, Serialize)]
136pub struct MemoryRange {
137 pub crate_key: BoundedString256,
138 pub start: u8,
139 pub end: u8,
140 pub created_at: u64,
141}
142
143impl MemoryRange {
144 pub(crate) fn try_new(
145 crate_name: &str,
146 start: u8,
147 end: u8,
148 ) -> Result<Self, MemoryRegistryError> {
149 let crate_key = BoundedString256::try_new(crate_name).map_err(|_| {
150 MemoryRegistryError::CrateKeyTooLong(crate_name.to_string(), crate_name.len())
151 })?;
152
153 Ok(Self {
154 crate_key,
155 start,
156 end,
157 created_at: now_secs(),
158 })
159 }
160
161 #[must_use]
162 pub fn contains(&self, id: u8) -> bool {
163 (self.start..=self.end).contains(&id)
164 }
165}
166
167impl_storable_bounded!(MemoryRange, 320, false);
168
169#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
174pub struct MemoryRegistryEntry {
175 pub label: BoundedString256,
176 pub created_at: u64,
177}
178
179impl MemoryRegistryEntry {
180 pub(crate) fn try_new(crate_name: &str, label: &str) -> Result<Self, MemoryRegistryError> {
181 let label = BoundedString256::try_new(label)
182 .map_err(|_| MemoryRegistryError::LabelTooLong(crate_name.to_string(), label.len()))?;
183
184 Ok(Self {
185 label,
186 created_at: now_secs(),
187 })
188 }
189}
190
191impl_storable_bounded!(MemoryRegistryEntry, 320, false);
192
193pub type MemoryRegistryView = Vec<(u8, MemoryRegistryEntry)>;
198
199pub struct MemoryRegistry;
204
205impl MemoryRegistry {
206 pub fn register(id: u8, crate_name: &str, label: &str) -> Result<(), MemoryRegistryError> {
213 let crate_key = crate_name.to_string();
214
215 let range = MEMORY_RANGES.with_borrow(|ranges| ranges.get(&crate_key));
217 match range {
218 None => {
219 return Err(MemoryRegistryError::NoRange(crate_key));
220 }
221 Some(r) if !r.contains(id) => {
222 return Err(MemoryRegistryError::OutOfRange(crate_key, id));
223 }
224 Some(_) => {
225 }
227 }
228
229 let existing = MEMORY_REGISTRY.with_borrow(|map| map.get(&id));
231 if let Some(existing) = existing {
232 if existing.label.as_ref() != label {
233 return Err(MemoryRegistryError::AlreadyRegistered(
234 id,
235 existing.label.to_string(),
236 label.to_string(),
237 ));
238 }
239
240 return Ok(());
242 }
243
244 let entry = MemoryRegistryEntry::try_new(crate_name, label)?;
246 MEMORY_REGISTRY.with_borrow_mut(|map| {
247 map.insert(id, entry);
248 });
249
250 Ok(())
251 }
252
253 pub fn reserve_range(crate_name: &str, start: u8, end: u8) -> Result<(), MemoryRegistryError> {
257 if start > end {
258 return Err(MemoryRegistryError::InvalidRange(
259 crate_name.to_string(),
260 start,
261 end,
262 ));
263 }
264
265 let crate_key = crate_name.to_string();
266
267 let conflict = MEMORY_RANGES.with_borrow(|ranges| {
269 if let Some(existing) = ranges.get(&crate_key) {
270 if existing.start == start && existing.end == end {
271 return None;
272 }
273
274 return Some(MemoryRegistryError::DuplicateRange(crate_key.clone()));
275 }
276
277 for entry in ranges.iter() {
278 let other_crate = entry.key();
279 let other_range = entry.value();
280
281 if !(end < other_range.start || start > other_range.end) {
282 return Some(MemoryRegistryError::Overlap(
283 crate_key.clone(),
284 start,
285 end,
286 other_crate.clone(),
287 other_range.start,
288 other_range.end,
289 ));
290 }
291 }
292
293 None
294 });
295
296 if let Some(err) = conflict {
297 return Err(err);
298 }
299
300 let range = MemoryRange::try_new(crate_name, start, end)?;
302 MEMORY_RANGES.with_borrow_mut(|ranges| {
303 ranges.insert(crate_name.to_string(), range);
304 });
305
306 Ok(())
307 }
308
309 #[must_use]
310 pub fn get(id: u8) -> Option<MemoryRegistryEntry> {
311 MEMORY_REGISTRY.with_borrow(|map| map.get(&id))
312 }
313
314 #[must_use]
315 pub fn export() -> MemoryRegistryView {
316 MEMORY_REGISTRY.with_borrow(|map| {
317 map.iter()
318 .map(|entry| (*entry.key(), entry.value()))
319 .collect()
320 })
321 }
322
323 #[must_use]
324 pub fn export_ranges() -> Vec<(String, MemoryRange)> {
325 MEMORY_RANGES.with_borrow(|ranges| {
326 ranges
327 .iter()
328 .map(|e| (e.key().clone(), e.value()))
329 .collect()
330 })
331 }
332}
333
334#[cfg(test)]
335pub(crate) fn reset_for_tests() {
336 MEMORY_REGISTRY.with_borrow_mut(StableBTreeMap::clear);
337 MEMORY_RANGES.with_borrow_mut(StableBTreeMap::clear);
338 PENDING_REGISTRATIONS.with(|q| q.borrow_mut().clear());
339 PENDING_RANGES.with(|q| q.borrow_mut().clear());
340}
341
342#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn reserve_range_happy_path_and_reject_overlap() {
352 reset_for_tests();
353 MemoryRegistry::reserve_range("crate_a", 10, 20).unwrap();
354
355 let err = MemoryRegistry::reserve_range("crate_b", 15, 25).unwrap_err();
357 assert!(matches!(
358 err,
359 MemoryRegistryError::Overlap(_, _, _, _, _, _)
360 ));
361
362 MemoryRegistry::reserve_range("crate_b", 30, 40).unwrap();
364
365 let ranges = MemoryRegistry::export_ranges();
366 assert_eq!(ranges.len(), 2);
367 }
368
369 #[test]
370 fn reserve_range_rejects_invalid_order() {
371 reset_for_tests();
372 let err = MemoryRegistry::reserve_range("crate_a", 5, 4).unwrap_err();
373 assert!(matches!(err, MemoryRegistryError::InvalidRange(_, _, _)));
374 assert!(MemoryRegistry::export_ranges().is_empty());
375 }
376
377 #[test]
378 fn register_id_requires_range_and_checks_bounds() {
379 reset_for_tests();
380 MemoryRegistry::reserve_range("crate_a", 1, 3).unwrap();
381
382 let err = MemoryRegistry::register(5, "crate_a", "Foo").unwrap_err();
384 assert!(matches!(err, MemoryRegistryError::OutOfRange(_, _)));
385
386 MemoryRegistry::register(2, "crate_a", "Foo").unwrap();
388
389 MemoryRegistry::register(2, "crate_a", "Foo").unwrap();
391
392 let err = MemoryRegistry::register(2, "crate_a", "Bar").unwrap_err();
394 assert!(matches!(
395 err,
396 MemoryRegistryError::AlreadyRegistered(_, _, _)
397 ));
398
399 let view = MemoryRegistry::export();
400 assert_eq!(view.len(), 1);
401 assert_eq!(view[0].0, 2);
402 }
403
404 #[test]
405 fn pending_queues_drain_in_order() {
406 reset_for_tests();
407 defer_reserve_range("crate_a", 1, 2);
408 defer_reserve_range("crate_b", 3, 4);
409 defer_register(1, "crate_a", "A1");
410 defer_register(3, "crate_b", "B3");
411
412 let ranges = drain_pending_ranges();
413 assert_eq!(ranges, vec![("crate_a", 1, 2), ("crate_b", 3, 4)]);
414 let regs = drain_pending_registrations();
415 assert_eq!(regs, vec![(1, "crate_a", "A1"), (3, "crate_b", "B3")]);
416
417 assert!(drain_pending_ranges().is_empty());
419 assert!(drain_pending_registrations().is_empty());
420 }
421
422 #[test]
423 fn reserve_range_rejects_too_long_crate_key() {
424 reset_for_tests();
425
426 let crate_name = "a".repeat(257);
427 let err = MemoryRegistry::reserve_range(&crate_name, 1, 2).unwrap_err();
428 assert!(matches!(err, MemoryRegistryError::CrateKeyTooLong(_, 257)));
429 }
430
431 #[test]
432 fn register_rejects_too_long_label() {
433 reset_for_tests();
434 MemoryRegistry::reserve_range("crate_a", 1, 3).unwrap();
435
436 let label = "a".repeat(257);
437 let err = MemoryRegistry::register(2, "crate_a", &label).unwrap_err();
438 assert!(matches!(err, MemoryRegistryError::LabelTooLong(_, 257)));
439 }
440}