1use serde::{Deserialize, Serialize};
2
3pub const MEMORY_MANAGER_SUBSTRATE: &str = "ic-stable-structures.memory_manager";
5
6pub const MEMORY_MANAGER_DESCRIPTOR_VERSION: u32 = 1;
8
9pub const MEMORY_MANAGER_MIN_ID: u8 = 0;
11
12pub const MEMORY_MANAGER_MAX_ID: u8 = 254;
14
15pub const MEMORY_MANAGER_INVALID_ID: u8 = u8::MAX;
17
18#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
23pub enum AllocationSlot {
24 MemoryManagerId(u8),
26 NamedPartition(String),
28 Custom {
30 kind: String,
32 version: u32,
34 value: Vec<u8>,
36 },
37}
38
39#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
44pub struct AllocationSlotDescriptor {
45 pub slot: AllocationSlot,
47 pub substrate: String,
49 pub descriptor_version: u32,
51}
52
53impl AllocationSlotDescriptor {
54 pub fn memory_manager(id: u8) -> Result<Self, MemoryManagerSlotError> {
56 validate_memory_manager_id(id)?;
57 Ok(Self::memory_manager_unchecked(id))
58 }
59
60 #[must_use]
66 pub fn memory_manager_unchecked(id: u8) -> Self {
67 Self {
68 slot: AllocationSlot::MemoryManagerId(id),
69 substrate: MEMORY_MANAGER_SUBSTRATE.to_string(),
70 descriptor_version: MEMORY_MANAGER_DESCRIPTOR_VERSION,
71 }
72 }
73
74 pub fn memory_manager_checked(id: u8) -> Result<Self, MemoryManagerSlotError> {
76 Self::memory_manager(id)
77 }
78
79 pub fn memory_manager_id(&self) -> Result<u8, MemoryManagerSlotError> {
81 if self.substrate != MEMORY_MANAGER_SUBSTRATE {
82 return Err(MemoryManagerSlotError::UnsupportedSubstrate {
83 substrate: self.substrate.clone(),
84 });
85 }
86 if self.descriptor_version != MEMORY_MANAGER_DESCRIPTOR_VERSION {
87 return Err(MemoryManagerSlotError::UnsupportedDescriptorVersion {
88 version: self.descriptor_version,
89 });
90 }
91
92 let AllocationSlot::MemoryManagerId(id) = self.slot else {
93 return Err(MemoryManagerSlotError::UnsupportedSlot);
94 };
95
96 validate_memory_manager_id(id)?;
97 Ok(id)
98 }
99}
100
101#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
106pub enum MemoryManagerSlotError {
107 #[error("allocation slot is not a MemoryManager virtual memory ID")]
109 UnsupportedSlot,
110 #[error("allocation slot substrate '{substrate}' is not supported as a MemoryManager slot")]
112 UnsupportedSubstrate {
113 substrate: String,
115 },
116 #[error("MemoryManager slot descriptor version {version} is unsupported")]
118 UnsupportedDescriptorVersion {
119 version: u32,
121 },
122 #[error("MemoryManager ID {id} is not a usable allocation slot")]
124 InvalidMemoryManagerId {
125 id: u8,
127 },
128}
129
130#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135pub struct MemoryManagerIdRange {
136 start: u8,
137 end: u8,
138}
139
140impl MemoryManagerIdRange {
141 pub const fn new(start: u8, end: u8) -> Result<Self, MemoryManagerRangeError> {
143 if start > end {
144 return Err(MemoryManagerRangeError::InvalidRange { start, end });
145 }
146 if start == MEMORY_MANAGER_INVALID_ID {
147 return Err(MemoryManagerRangeError::InvalidMemoryManagerId { id: start });
148 }
149 if end == MEMORY_MANAGER_INVALID_ID {
150 return Err(MemoryManagerRangeError::InvalidMemoryManagerId { id: end });
151 }
152 Ok(Self { start, end })
153 }
154
155 #[must_use]
157 pub const fn contains(&self, id: u8) -> bool {
158 id >= self.start && id <= self.end
159 }
160
161 #[must_use]
163 pub const fn start(&self) -> u8 {
164 self.start
165 }
166
167 #[must_use]
169 pub const fn end(&self) -> u8 {
170 self.end
171 }
172}
173
174#[derive(Clone, Copy, Debug, Eq, thiserror::Error, PartialEq)]
179pub enum MemoryManagerRangeError {
180 #[error("MemoryManager ID range is invalid: start={start} end={end}")]
182 InvalidRange {
183 start: u8,
185 end: u8,
187 },
188 #[error("MemoryManager ID {id} is not a usable allocation slot")]
190 InvalidMemoryManagerId {
191 id: u8,
193 },
194}
195
196pub const fn validate_memory_manager_id(id: u8) -> Result<(), MemoryManagerSlotError> {
198 if id == MEMORY_MANAGER_INVALID_ID {
199 return Err(MemoryManagerSlotError::InvalidMemoryManagerId { id });
200 }
201 Ok(())
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn memory_manager_checked_accepts_usable_ids() {
210 assert!(AllocationSlotDescriptor::memory_manager_checked(MEMORY_MANAGER_MIN_ID).is_ok());
211 assert!(AllocationSlotDescriptor::memory_manager_checked(MEMORY_MANAGER_MAX_ID).is_ok());
212 }
213
214 #[test]
215 fn memory_manager_checked_rejects_sentinel() {
216 let err = AllocationSlotDescriptor::memory_manager_checked(MEMORY_MANAGER_INVALID_ID)
217 .expect_err("sentinel must fail");
218
219 assert_eq!(
220 err,
221 MemoryManagerSlotError::InvalidMemoryManagerId {
222 id: MEMORY_MANAGER_INVALID_ID
223 }
224 );
225 }
226
227 #[test]
228 fn memory_manager_default_constructor_rejects_sentinel() {
229 let err = AllocationSlotDescriptor::memory_manager(MEMORY_MANAGER_INVALID_ID)
230 .expect_err("sentinel must fail");
231
232 assert_eq!(
233 err,
234 MemoryManagerSlotError::InvalidMemoryManagerId {
235 id: MEMORY_MANAGER_INVALID_ID
236 }
237 );
238 }
239
240 #[test]
241 fn memory_manager_id_validates_descriptor_shape() {
242 let slot = AllocationSlotDescriptor::memory_manager(42).expect("usable slot");
243 assert_eq!(slot.memory_manager_id().expect("usable ID"), 42);
244
245 let err = AllocationSlotDescriptor {
246 slot: AllocationSlot::NamedPartition("ledger".to_string()),
247 substrate: MEMORY_MANAGER_SUBSTRATE.to_string(),
248 descriptor_version: MEMORY_MANAGER_DESCRIPTOR_VERSION,
249 }
250 .memory_manager_id()
251 .expect_err("slot kind should fail");
252 assert_eq!(err, MemoryManagerSlotError::UnsupportedSlot);
253
254 let err = AllocationSlotDescriptor {
255 slot: AllocationSlot::MemoryManagerId(42),
256 substrate: "other".to_string(),
257 descriptor_version: MEMORY_MANAGER_DESCRIPTOR_VERSION,
258 }
259 .memory_manager_id()
260 .expect_err("substrate should fail");
261 assert_eq!(
262 err,
263 MemoryManagerSlotError::UnsupportedSubstrate {
264 substrate: "other".to_string()
265 }
266 );
267
268 let err = AllocationSlotDescriptor {
269 slot: AllocationSlot::MemoryManagerId(42),
270 substrate: MEMORY_MANAGER_SUBSTRATE.to_string(),
271 descriptor_version: MEMORY_MANAGER_DESCRIPTOR_VERSION + 1,
272 }
273 .memory_manager_id()
274 .expect_err("version should fail");
275 assert_eq!(
276 err,
277 MemoryManagerSlotError::UnsupportedDescriptorVersion {
278 version: MEMORY_MANAGER_DESCRIPTOR_VERSION + 1
279 }
280 );
281 }
282
283 #[test]
284 fn memory_manager_range_accepts_usable_ranges() {
285 let range = MemoryManagerIdRange::new(MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_MAX_ID)
286 .expect("usable full range");
287
288 assert!(range.contains(MEMORY_MANAGER_MIN_ID));
289 assert!(range.contains(MEMORY_MANAGER_MAX_ID));
290 assert!(!range.contains(MEMORY_MANAGER_INVALID_ID));
291 }
292
293 #[test]
294 fn memory_manager_range_rejects_reversed_bounds() {
295 let err = MemoryManagerIdRange::new(10, 9).expect_err("reversed range");
296
297 assert_eq!(
298 err,
299 MemoryManagerRangeError::InvalidRange { start: 10, end: 9 }
300 );
301 }
302
303 #[test]
304 fn memory_manager_range_rejects_sentinel_bounds() {
305 let err =
306 MemoryManagerIdRange::new(240, MEMORY_MANAGER_INVALID_ID).expect_err("sentinel range");
307
308 assert_eq!(
309 err,
310 MemoryManagerRangeError::InvalidMemoryManagerId {
311 id: MEMORY_MANAGER_INVALID_ID
312 }
313 );
314 }
315}