Skip to main content

ic_memory/slot/
range_authority.rs

1use super::memory_manager::{
2    MEMORY_MANAGER_INVALID_ID, MEMORY_MANAGER_MAX_ID, MEMORY_MANAGER_MIN_ID, MemoryManagerSlot,
3    MemoryManagerSlotError, validate_memory_manager_id,
4};
5use crate::text::validate_diagnostic_text;
6use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
7
8///
9/// MemoryManagerIdRange
10///
11/// Inclusive range of usable `MemoryManager` virtual memory IDs.
12/// Construction and deserialization reject reversed bounds and sentinel ID 255.
13///
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
16pub struct MemoryManagerIdRange {
17    start: u8,
18    end: u8,
19}
20
21impl<'de> Deserialize<'de> for MemoryManagerIdRange {
22    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
23        #[derive(Deserialize)]
24        #[serde(rename = "MemoryManagerIdRange", deny_unknown_fields)]
25        struct Bounds {
26            start: u8,
27            end: u8,
28        }
29
30        let bounds = Bounds::deserialize(deserializer)?;
31        Self::new(bounds.start, bounds.end).map_err(D::Error::custom)
32    }
33}
34
35impl MemoryManagerIdRange {
36    /// Construct and validate an inclusive `MemoryManager` ID range.
37    pub const fn new(start: u8, end: u8) -> Result<Self, MemoryManagerRangeError> {
38        if start > end {
39            return Err(MemoryManagerRangeError::InvalidRange { start, end });
40        }
41        // Ordered bounds make a usable end sufficient to exclude the sentinel.
42        if end == MEMORY_MANAGER_INVALID_ID {
43            return Err(MemoryManagerRangeError::InvalidMemoryManagerId { id: end });
44        }
45        Ok(Self { start, end })
46    }
47
48    /// Return the full usable `MemoryManager` ID range.
49    #[must_use]
50    pub const fn all_usable() -> Self {
51        Self {
52            start: MEMORY_MANAGER_MIN_ID,
53            end: MEMORY_MANAGER_MAX_ID,
54        }
55    }
56
57    /// Return true when `id` is inside this inclusive range.
58    #[must_use]
59    pub const fn contains(&self, id: u8) -> bool {
60        id >= self.start && id <= self.end
61    }
62
63    /// First usable ID in the range.
64    #[must_use]
65    pub const fn start(&self) -> u8 {
66        self.start
67    }
68
69    /// Last usable ID in the range.
70    #[must_use]
71    pub const fn end(&self) -> u8 {
72        self.end
73    }
74}
75
76///
77/// MemoryManagerRangeError
78///
79/// Invalid `MemoryManager` virtual memory ID range.
80#[non_exhaustive]
81#[derive(Clone, Copy, Debug, Eq, thiserror::Error, PartialEq)]
82pub enum MemoryManagerRangeError {
83    /// Range bounds are reversed.
84    #[error("MemoryManager ID range is invalid: start={start} end={end}")]
85    InvalidRange {
86        /// Requested first ID.
87        start: u8,
88        /// Requested last ID.
89        end: u8,
90    },
91    /// ID 255 is the unallocated-bucket sentinel.
92    #[error("MemoryManager ID {id} is not a usable allocation slot")]
93    InvalidMemoryManagerId {
94        /// Invalid MemoryManager ID.
95        id: u8,
96    },
97}
98
99///
100/// MemoryManagerRangeMode
101///
102/// Allocation policy mode for a `MemoryManager` authority range.
103///
104/// These modes describe policy authority, not durable allocation state.
105/// Only `Allowed` ranges supply fresh logical placements; neither mode allocates
106/// memory by itself. [`crate::ic_memory_range!`] defaults to `Reserved` when its
107/// `mode` argument is omitted.
108///
109
110#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
111pub enum MemoryManagerRangeMode {
112    /// Range is reserved for authority-owned framework or infrastructure use.
113    ///
114    /// Reserved does not mean every ID in the range has been allocated.
115    Reserved,
116    /// Range is allowed for authority-governed application allocation use.
117    ///
118    /// Allowed does not allocate any ID in the range.
119    Allowed,
120}
121
122///
123/// MemoryManagerAuthorityRecord
124///
125/// Ordered diagnostic authority record for a `MemoryManager` ID range.
126///
127
128#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
129#[serde(deny_unknown_fields)]
130pub struct MemoryManagerAuthorityRecord {
131    /// Inclusive range governed by this authority.
132    pub(crate) range: MemoryManagerIdRange,
133    /// Stable printable ASCII authority identifier.
134    pub(crate) authority: String,
135    /// Policy mode for this authority range.
136    pub(crate) mode: MemoryManagerRangeMode,
137    /// Optional stable printable ASCII diagnostic purpose.
138    #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
139    pub(crate) purpose: Option<String>,
140}
141
142impl MemoryManagerAuthorityRecord {
143    /// Build a diagnostic authority record after validating printable metadata.
144    pub fn new(
145        range: MemoryManagerIdRange,
146        authority: impl Into<String>,
147        mode: MemoryManagerRangeMode,
148        purpose: Option<String>,
149    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
150        let record = Self {
151            range,
152            authority: authority.into(),
153            mode,
154            purpose,
155        };
156        validate_authority_record(&record)?;
157        Ok(record)
158    }
159
160    /// Return the inclusive range governed by this authority.
161    #[must_use]
162    pub const fn range(&self) -> MemoryManagerIdRange {
163        self.range
164    }
165
166    /// Return the stable printable ASCII authority identifier.
167    #[must_use]
168    pub fn authority(&self) -> &str {
169        &self.authority
170    }
171
172    /// Return the policy mode for this authority range.
173    #[must_use]
174    pub const fn mode(&self) -> MemoryManagerRangeMode {
175        self.mode
176    }
177
178    /// Return the optional stable printable ASCII diagnostic purpose.
179    #[must_use]
180    pub fn purpose(&self) -> Option<&str> {
181        self.purpose.as_deref()
182    }
183
184    /// Validate diagnostic metadata after decode or manual assembly.
185    /// Range bounds are already established by the checked range type.
186    pub fn validate(&self) -> Result<(), MemoryManagerRangeAuthorityError> {
187        validate_authority_record(self)
188    }
189}
190
191///
192/// MemoryManagerRangeAuthority
193///
194/// Substrate-specific range authority policy helper for `MemoryManager` IDs.
195///
196/// This helper records policy and diagnostic authority ranges only. It never
197/// mutates the allocation ledger, and it does not allocate or reserve durable
198/// stable-memory slots. Durable allocation remains the generic ledger mapping
199/// from stable key to allocation slot.
200///
201/// When used through the default runtime registry, registered ranges are
202/// authoritative generic policy and are checked before caller-supplied
203/// [`crate::AllocationPolicy`]. When no user ranges are registered, frameworks
204/// can enforce fixed application claims through their own policy. Logical
205/// placement and historical selection always require explicit grants; fresh
206/// placements use only `Allowed` ranges.
207///
208
209#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
210pub struct MemoryManagerRangeAuthority {
211    authorities: Vec<MemoryManagerAuthorityRecord>,
212}
213
214#[derive(Deserialize)]
215#[serde(deny_unknown_fields)]
216struct MemoryManagerRangeAuthorityDto {
217    authorities: Vec<MemoryManagerAuthorityRecord>,
218}
219
220impl<'de> Deserialize<'de> for MemoryManagerRangeAuthority {
221    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
222        let dto = MemoryManagerRangeAuthorityDto::deserialize(deserializer)?;
223        Self::from_records(dto.authorities).map_err(D::Error::custom)
224    }
225}
226
227impl MemoryManagerRangeAuthority {
228    /// Create an empty `MemoryManager` range authority policy.
229    #[must_use]
230    pub const fn new() -> Self {
231        Self {
232            authorities: Vec::new(),
233        }
234    }
235
236    /// Build a range authority from diagnostic records.
237    ///
238    /// Each record is validated in input order and overlaps are rejected.
239    /// Accepted records use ascending range order in the supplied buffer. Decoded
240    /// records pass the same checks as records built with their checked constructor.
241    pub fn from_records(
242        mut records: Vec<MemoryManagerAuthorityRecord>,
243    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
244        for index in 0..records.len() {
245            let record = &records[index];
246            validate_authority_record(record)?;
247            let accepted = &records[..index];
248            let insertion =
249                accepted.partition_point(|existing| existing.range.start() < record.range.start());
250            // Accepted ranges are ordered and disjoint. Only the predecessor
251            // and successor can be the first overlap; check them in range order.
252            let neighbours = insertion.saturating_sub(1)..(insertion + 1).min(accepted.len());
253            for existing in &accepted[neighbours] {
254                if ranges_overlap(existing.range, record.range) {
255                    return Err(MemoryManagerRangeAuthorityError::OverlappingRanges {
256                        existing_start: existing.range.start(),
257                        existing_end: existing.range.end(),
258                        candidate_start: record.range.start(),
259                        candidate_end: record.range.end(),
260                    });
261                }
262            }
263            // Move the checked candidate into the ordered prefix without
264            // changing the unprocessed tail or allocating a second vector.
265            records[insertion..=index].rotate_right(1);
266        }
267        Ok(Self {
268            authorities: records,
269        })
270    }
271
272    /// Validate that `slot` belongs to `expected_authority`.
273    pub fn validate_slot_authority(
274        &self,
275        slot: &MemoryManagerSlot,
276        expected_authority: &str,
277    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
278        let id = slot.id();
279        self.validate_id_authority(id, expected_authority)
280    }
281
282    /// Validate that `slot` belongs to `expected_authority` with `expected_mode`.
283    pub fn validate_slot_authority_mode(
284        &self,
285        slot: &MemoryManagerSlot,
286        expected_authority: &str,
287        expected_mode: MemoryManagerRangeMode,
288    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
289        let id = slot.id();
290        self.validate_id_authority_mode(id, expected_authority, expected_mode)
291    }
292
293    /// Validate that `id` belongs to `expected_authority`.
294    pub fn validate_id_authority(
295        &self,
296        id: u8,
297        expected_authority: &str,
298    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
299        validate_diagnostic_string("expected_authority", expected_authority)?;
300        let record = self
301            .authority_for_id(id)?
302            .ok_or(MemoryManagerRangeAuthorityError::UnclaimedId { id })?;
303
304        if record.authority != expected_authority {
305            return Err(MemoryManagerRangeAuthorityError::AuthorityMismatch {
306                id,
307                expected_authority: expected_authority.to_string(),
308                actual_authority: record.authority.clone(),
309            });
310        }
311
312        Ok(record)
313    }
314
315    /// Validate that `id` belongs to `expected_authority` with `expected_mode`.
316    pub fn validate_id_authority_mode(
317        &self,
318        id: u8,
319        expected_authority: &str,
320        expected_mode: MemoryManagerRangeMode,
321    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
322        let record = self.validate_id_authority(id, expected_authority)?;
323        if record.mode != expected_mode {
324            return Err(MemoryManagerRangeAuthorityError::ModeMismatch {
325                id,
326                authority: record.authority.clone(),
327                expected_mode,
328                actual_mode: record.mode,
329            });
330        }
331        Ok(record)
332    }
333
334    /// Return the authority record that governs `id`, if any.
335    pub fn authority_for_id(
336        &self,
337        id: u8,
338    ) -> Result<Option<&MemoryManagerAuthorityRecord>, MemoryManagerRangeAuthorityError> {
339        validate_memory_manager_id(id).map_err(MemoryManagerRangeAuthorityError::Slot)?;
340        // Construction and decoding establish ordered, disjoint ranges, so
341        // their ends are increasing. Only the first end at or above this ID
342        // can cover it; its start distinguishes coverage from a gap.
343        let index = self
344            .authorities
345            .partition_point(|record| record.range.end() < id);
346        Ok(self
347            .authorities
348            .get(index)
349            .filter(|record| record.range.start() <= id))
350    }
351
352    /// Ordered non-overlapping authority records.
353    ///
354    /// This is the stable diagnostic/export surface for the authority table.
355    /// Records are returned in ascending range order and do not imply ledger
356    /// allocation state.
357    #[must_use]
358    pub fn authorities(&self) -> &[MemoryManagerAuthorityRecord] {
359        &self.authorities
360    }
361
362    /// Validate that authority records exactly and contiguously cover `target`.
363    ///
364    /// All records must be inside `target`, and together they must form a
365    /// gap-free partition. This checks policy table coverage only and never
366    /// changes allocation ledger state.
367    pub fn validate_complete_coverage(
368        &self,
369        target: MemoryManagerIdRange,
370    ) -> Result<(), MemoryManagerRangeAuthorityError> {
371        if self.authorities.is_empty() {
372            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
373                start: target.start(),
374                end: target.end(),
375            });
376        }
377
378        for record in &self.authorities {
379            if record.range.start() < target.start() || record.range.end() > target.end() {
380                return Err(
381                    MemoryManagerRangeAuthorityError::RangeOutsideCoverageTarget {
382                        start: record.range.start(),
383                        end: record.range.end(),
384                        target_start: target.start(),
385                        target_end: target.end(),
386                    },
387                );
388            }
389        }
390
391        let mut next_uncovered = target.start();
392        for record in &self.authorities {
393            if record.range.start() > next_uncovered {
394                return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
395                    start: next_uncovered,
396                    end: record.range.start() - 1,
397                });
398            }
399            // Ranges are ordered and disjoint, with usable ends at most 254.
400            // The next position therefore advances and fits through 255.
401            next_uncovered = record.range.end() + 1;
402        }
403
404        if next_uncovered <= target.end() {
405            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
406                start: next_uncovered,
407                end: target.end(),
408            });
409        }
410
411        Ok(())
412    }
413}
414
415fn validate_authority_record(
416    record: &MemoryManagerAuthorityRecord,
417) -> Result<(), MemoryManagerRangeAuthorityError> {
418    validate_diagnostic_string("authority", &record.authority)?;
419    if let Some(purpose) = &record.purpose {
420        validate_diagnostic_string("purpose", purpose)?;
421    }
422    Ok(())
423}
424
425///
426/// MemoryManagerRangeAuthorityError
427///
428/// Invalid `MemoryManager` range authority policy.
429#[non_exhaustive]
430#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
431pub enum MemoryManagerRangeAuthorityError {
432    /// Authority range bounds are invalid.
433    #[error(transparent)]
434    Range(#[from] MemoryManagerRangeError),
435    /// A raw numeric ID is not a usable `MemoryManager` slot.
436    #[error("{0}")]
437    Slot(#[from] MemoryManagerSlotError),
438    /// Authority range overlaps an existing range.
439    #[error(
440        "MemoryManager authority range {candidate_start}-{candidate_end} overlaps existing range {existing_start}-{existing_end}"
441    )]
442    OverlappingRanges {
443        /// Existing range start.
444        existing_start: u8,
445        /// Existing range end.
446        existing_end: u8,
447        /// Candidate range start.
448        candidate_start: u8,
449        /// Candidate range end.
450        candidate_end: u8,
451    },
452    /// Authority or purpose text failed diagnostic string validation.
453    #[error("{field} {reason}")]
454    InvalidDiagnosticString {
455        /// Diagnostic field name.
456        field: &'static str,
457        /// Validation failure.
458        reason: &'static str,
459    },
460    /// No authority range covers the requested ID.
461    #[error("MemoryManager ID {id} is not covered by an authority range")]
462    UnclaimedId {
463        /// Unclaimed MemoryManager ID.
464        id: u8,
465    },
466    /// Slot is governed by a different authority.
467    #[error(
468        "MemoryManager ID {id} belongs to authority '{actual_authority}', not '{expected_authority}'"
469    )]
470    AuthorityMismatch {
471        /// MemoryManager ID.
472        id: u8,
473        /// Expected authority identifier.
474        expected_authority: String,
475        /// Actual authority identifier.
476        actual_authority: String,
477    },
478    /// Slot is governed by the expected authority with a different mode.
479    #[error(
480        "MemoryManager ID {id} belongs to authority '{authority}' with mode {actual_mode:?}, not {expected_mode:?}"
481    )]
482    ModeMismatch {
483        /// MemoryManager ID.
484        id: u8,
485        /// Authority identifier.
486        authority: String,
487        /// Expected authority mode.
488        expected_mode: MemoryManagerRangeMode,
489        /// Actual authority mode.
490        actual_mode: MemoryManagerRangeMode,
491    },
492    /// A complete coverage target has no authority records for part of it.
493    #[error("MemoryManager authority coverage is missing range {start}-{end}")]
494    MissingCoverage {
495        /// First missing ID.
496        start: u8,
497        /// Last missing ID.
498        end: u8,
499    },
500    /// An authority record lies outside the complete coverage target.
501    #[error(
502        "MemoryManager authority range {start}-{end} is outside coverage target {target_start}-{target_end}"
503    )]
504    RangeOutsideCoverageTarget {
505        /// Authority range start.
506        start: u8,
507        /// Authority range end.
508        end: u8,
509        /// Coverage target start.
510        target_start: u8,
511        /// Coverage target end.
512        target_end: u8,
513    },
514}
515
516const fn ranges_overlap(left: MemoryManagerIdRange, right: MemoryManagerIdRange) -> bool {
517    left.start() <= right.end() && right.start() <= left.end()
518}
519
520fn validate_diagnostic_string(
521    field: &'static str,
522    value: &str,
523) -> Result<(), MemoryManagerRangeAuthorityError> {
524    validate_diagnostic_text(value).map_err(|error| {
525        MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
526            field,
527            reason: error.reason(),
528        }
529    })
530}