Skip to main content

ic_memory/slot/
range_authority.rs

1use super::descriptor::AllocationSlotDescriptor;
2use super::memory_manager::{
3    MEMORY_MANAGER_INVALID_ID, MEMORY_MANAGER_MAX_ID, MEMORY_MANAGER_MIN_ID,
4    MemoryManagerSlotError, validate_memory_manager_id,
5};
6use crate::text::validate_diagnostic_text;
7use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
8
9///
10/// MemoryManagerIdRange
11///
12/// Inclusive range of usable `MemoryManager` virtual memory IDs.
13#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
14#[serde(deny_unknown_fields)]
15pub struct MemoryManagerIdRange {
16    pub(crate) start: u8,
17    pub(crate) end: u8,
18}
19
20impl MemoryManagerIdRange {
21    /// Construct and validate an inclusive `MemoryManager` ID range.
22    pub const fn new(start: u8, end: u8) -> Result<Self, MemoryManagerRangeError> {
23        if start > end {
24            return Err(MemoryManagerRangeError::InvalidRange { start, end });
25        }
26        if start == MEMORY_MANAGER_INVALID_ID {
27            return Err(MemoryManagerRangeError::InvalidMemoryManagerId { id: start });
28        }
29        if end == MEMORY_MANAGER_INVALID_ID {
30            return Err(MemoryManagerRangeError::InvalidMemoryManagerId { id: end });
31        }
32        Ok(Self { start, end })
33    }
34
35    /// Return the full usable `MemoryManager` ID range.
36    #[must_use]
37    pub const fn all_usable() -> Self {
38        Self {
39            start: MEMORY_MANAGER_MIN_ID,
40            end: MEMORY_MANAGER_MAX_ID,
41        }
42    }
43
44    /// Return true when `id` is inside this inclusive range.
45    #[must_use]
46    pub const fn contains(&self, id: u8) -> bool {
47        id >= self.start && id <= self.end
48    }
49
50    /// Validate this range's decoded bounds.
51    pub const fn validate(&self) -> Result<(), MemoryManagerRangeError> {
52        match Self::new(self.start, self.end) {
53            Ok(_) => Ok(()),
54            Err(err) => Err(err),
55        }
56    }
57
58    /// First usable ID in the range.
59    #[must_use]
60    pub const fn start(&self) -> u8 {
61        self.start
62    }
63
64    /// Last usable ID in the range.
65    #[must_use]
66    pub const fn end(&self) -> u8 {
67        self.end
68    }
69}
70
71///
72/// MemoryManagerRangeError
73///
74/// Invalid `MemoryManager` virtual memory ID range.
75#[non_exhaustive]
76#[derive(Clone, Copy, Debug, Eq, thiserror::Error, PartialEq)]
77pub enum MemoryManagerRangeError {
78    /// Range bounds are reversed.
79    #[error("MemoryManager ID range is invalid: start={start} end={end}")]
80    InvalidRange {
81        /// Requested first ID.
82        start: u8,
83        /// Requested last ID.
84        end: u8,
85    },
86    /// ID 255 is the unallocated-bucket sentinel.
87    #[error("MemoryManager ID {id} is not a usable allocation slot")]
88    InvalidMemoryManagerId {
89        /// Invalid MemoryManager ID.
90        id: u8,
91    },
92}
93
94///
95/// MemoryManagerRangeMode
96///
97/// Allocation policy mode for a `MemoryManager` authority range.
98///
99/// These modes describe policy authority, not durable allocation state.
100/// Only `Allowed` ranges supply fresh logical placements; neither mode allocates
101/// memory by itself. [`crate::ic_memory_range!`] defaults to `Reserved` when its
102/// `mode` argument is omitted.
103///
104
105#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
106pub enum MemoryManagerRangeMode {
107    /// Range is reserved for authority-owned framework or infrastructure use.
108    ///
109    /// Reserved does not mean every ID in the range has been allocated.
110    Reserved,
111    /// Range is allowed for authority-governed application allocation use.
112    ///
113    /// Allowed does not allocate any ID in the range.
114    Allowed,
115}
116
117///
118/// MemoryManagerAuthorityRecord
119///
120/// Ordered diagnostic authority record for a `MemoryManager` ID range.
121///
122
123#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
124#[serde(deny_unknown_fields)]
125pub struct MemoryManagerAuthorityRecord {
126    /// Inclusive range governed by this authority.
127    pub(crate) range: MemoryManagerIdRange,
128    /// Stable printable ASCII authority identifier.
129    pub(crate) authority: String,
130    /// Policy mode for this authority range.
131    pub(crate) mode: MemoryManagerRangeMode,
132    /// Optional stable printable ASCII diagnostic purpose.
133    #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
134    pub(crate) purpose: Option<String>,
135}
136
137impl MemoryManagerAuthorityRecord {
138    /// Build a diagnostic authority record after validating printable metadata.
139    pub fn new(
140        range: MemoryManagerIdRange,
141        authority: impl Into<String>,
142        mode: MemoryManagerRangeMode,
143        purpose: Option<String>,
144    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
145        let record = Self {
146            range,
147            authority: authority.into(),
148            mode,
149            purpose,
150        };
151        validate_authority_record(&record)?;
152        Ok(record)
153    }
154
155    /// Return the inclusive range governed by this authority.
156    #[must_use]
157    pub const fn range(&self) -> MemoryManagerIdRange {
158        self.range
159    }
160
161    /// Return the stable printable ASCII authority identifier.
162    #[must_use]
163    pub fn authority(&self) -> &str {
164        &self.authority
165    }
166
167    /// Return the policy mode for this authority range.
168    #[must_use]
169    pub const fn mode(&self) -> MemoryManagerRangeMode {
170        self.mode
171    }
172
173    /// Return the optional stable printable ASCII diagnostic purpose.
174    #[must_use]
175    pub fn purpose(&self) -> Option<&str> {
176        self.purpose.as_deref()
177    }
178
179    /// Validate constructor invariants after decode or manual assembly.
180    pub fn validate(&self) -> Result<(), MemoryManagerRangeAuthorityError> {
181        validate_authority_record(self)
182    }
183}
184
185///
186/// MemoryManagerRangeAuthority
187///
188/// Substrate-specific range authority policy helper for `MemoryManager` IDs.
189///
190/// This helper records policy and diagnostic authority ranges only. It never
191/// mutates the allocation ledger, and it does not allocate or reserve durable
192/// stable-memory slots. Durable allocation remains the generic ledger mapping
193/// from stable key to allocation slot.
194///
195/// When used through the default runtime registry, registered ranges are
196/// authoritative generic policy and are checked before caller-supplied
197/// [`crate::AllocationPolicy`]. When no user ranges are registered, frameworks
198/// can enforce fixed application claims through their own policy. Logical
199/// placement and historical selection always require explicit grants; fresh
200/// placements use only `Allowed` ranges.
201///
202
203#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
204#[serde(deny_unknown_fields)]
205pub struct MemoryManagerRangeAuthority {
206    authorities: Vec<MemoryManagerAuthorityRecord>,
207}
208
209#[derive(Deserialize)]
210#[serde(deny_unknown_fields)]
211struct MemoryManagerRangeAuthorityDto {
212    authorities: Vec<MemoryManagerAuthorityRecord>,
213}
214
215impl<'de> Deserialize<'de> for MemoryManagerRangeAuthority {
216    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
217        let dto = MemoryManagerRangeAuthorityDto::deserialize(deserializer)?;
218        Self::from_records(dto.authorities).map_err(D::Error::custom)
219    }
220}
221
222impl MemoryManagerRangeAuthority {
223    /// Create an empty `MemoryManager` range authority policy.
224    #[must_use]
225    pub const fn new() -> Self {
226        Self {
227            authorities: Vec::new(),
228        }
229    }
230
231    /// Build a range authority from diagnostic records.
232    ///
233    /// Records are validated with the same rules as the builder methods and
234    /// stored in ascending range order.
235    pub fn from_records(
236        records: Vec<MemoryManagerAuthorityRecord>,
237    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
238        let mut authority = Self::new();
239        for record in records {
240            authority = authority.insert_record(record)?;
241        }
242        Ok(authority)
243    }
244
245    /// Add a reserved authority range.
246    ///
247    /// Reserved is a policy authority mode. It does not allocate every ID in
248    /// the range and does not write to the allocation ledger.
249    pub fn reserve(
250        self,
251        range: MemoryManagerIdRange,
252        authority: impl Into<String>,
253    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
254        self.reserve_with_purpose(range, authority, None)
255    }
256
257    /// Add a reserved authority range from inclusive ID bounds.
258    ///
259    /// Reserved is a policy authority mode. It does not allocate every ID in
260    /// the range and does not write to the allocation ledger.
261    pub fn reserve_ids(
262        self,
263        start: u8,
264        end: u8,
265        authority: impl Into<String>,
266    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
267        self.reserve(MemoryManagerIdRange::new(start, end)?, authority)
268    }
269
270    /// Add a reserved authority range with a diagnostic purpose.
271    ///
272    /// Reserved is a policy authority mode. It does not allocate every ID in
273    /// the range and does not write to the allocation ledger.
274    pub fn reserve_with_purpose(
275        self,
276        range: MemoryManagerIdRange,
277        authority: impl Into<String>,
278        purpose: Option<String>,
279    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
280        self.insert(range, authority, MemoryManagerRangeMode::Reserved, purpose)
281    }
282
283    /// Add a reserved authority range from inclusive ID bounds with a diagnostic purpose.
284    ///
285    /// Reserved is a policy authority mode. It does not allocate every ID in
286    /// the range and does not write to the allocation ledger.
287    pub fn reserve_ids_with_purpose(
288        self,
289        start: u8,
290        end: u8,
291        authority: impl Into<String>,
292        purpose: Option<String>,
293    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
294        self.reserve_with_purpose(MemoryManagerIdRange::new(start, end)?, authority, purpose)
295    }
296
297    /// Add an allowed authority range.
298    ///
299    /// Allowed is a policy authority mode. It does not allocate any ID in the
300    /// range and does not write to the allocation ledger.
301    pub fn allow(
302        self,
303        range: MemoryManagerIdRange,
304        authority: impl Into<String>,
305    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
306        self.allow_with_purpose(range, authority, None)
307    }
308
309    /// Add an allowed authority range from inclusive ID bounds.
310    ///
311    /// Allowed is a policy authority mode. It does not allocate any ID in the
312    /// range and does not write to the allocation ledger.
313    pub fn allow_ids(
314        self,
315        start: u8,
316        end: u8,
317        authority: impl Into<String>,
318    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
319        self.allow(MemoryManagerIdRange::new(start, end)?, authority)
320    }
321
322    /// Add an allowed authority range with a diagnostic purpose.
323    ///
324    /// Allowed is a policy authority mode. It does not allocate any ID in the
325    /// range and does not write to the allocation ledger.
326    pub fn allow_with_purpose(
327        self,
328        range: MemoryManagerIdRange,
329        authority: impl Into<String>,
330        purpose: Option<String>,
331    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
332        self.insert(range, authority, MemoryManagerRangeMode::Allowed, purpose)
333    }
334
335    /// Add an allowed authority range from inclusive ID bounds with a diagnostic purpose.
336    ///
337    /// Allowed is a policy authority mode. It does not allocate any ID in the
338    /// range and does not write to the allocation ledger.
339    pub fn allow_ids_with_purpose(
340        self,
341        start: u8,
342        end: u8,
343        authority: impl Into<String>,
344        purpose: Option<String>,
345    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
346        self.allow_with_purpose(MemoryManagerIdRange::new(start, end)?, authority, purpose)
347    }
348
349    /// Validate that `slot` belongs to `expected_authority`.
350    pub fn validate_slot_authority(
351        &self,
352        slot: &AllocationSlotDescriptor,
353        expected_authority: &str,
354    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
355        let id = slot
356            .memory_manager_id()
357            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
358        self.validate_id_authority(id, expected_authority)
359    }
360
361    /// Validate that `slot` belongs to `expected_authority` with `expected_mode`.
362    pub fn validate_slot_authority_mode(
363        &self,
364        slot: &AllocationSlotDescriptor,
365        expected_authority: &str,
366        expected_mode: MemoryManagerRangeMode,
367    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
368        let id = slot
369            .memory_manager_id()
370            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
371        self.validate_id_authority_mode(id, expected_authority, expected_mode)
372    }
373
374    /// Validate that `id` belongs to `expected_authority`.
375    pub fn validate_id_authority(
376        &self,
377        id: u8,
378        expected_authority: &str,
379    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
380        validate_diagnostic_string("expected_authority", expected_authority)?;
381        let record = self.covering_record(id)?;
382
383        if record.authority != expected_authority {
384            return Err(MemoryManagerRangeAuthorityError::AuthorityMismatch {
385                id,
386                expected_authority: expected_authority.to_string(),
387                actual_authority: record.authority.clone(),
388            });
389        }
390
391        Ok(record)
392    }
393
394    /// Validate that `id` belongs to `expected_authority` with `expected_mode`.
395    pub fn validate_id_authority_mode(
396        &self,
397        id: u8,
398        expected_authority: &str,
399        expected_mode: MemoryManagerRangeMode,
400    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
401        let record = self.validate_id_authority(id, expected_authority)?;
402        if record.mode != expected_mode {
403            return Err(MemoryManagerRangeAuthorityError::ModeMismatch {
404                id,
405                authority: record.authority.clone(),
406                expected_mode,
407                actual_mode: record.mode,
408            });
409        }
410        Ok(record)
411    }
412
413    /// Return the authority record that governs `id`, if any.
414    pub fn authority_for_id(
415        &self,
416        id: u8,
417    ) -> Result<Option<&MemoryManagerAuthorityRecord>, MemoryManagerRangeAuthorityError> {
418        validate_memory_manager_id(id).map_err(MemoryManagerRangeAuthorityError::Slot)?;
419        Ok(self
420            .authorities
421            .iter()
422            .find(|record| record.range.contains(id)))
423    }
424
425    /// Ordered non-overlapping authority records.
426    ///
427    /// This is the stable diagnostic/export surface for the authority table.
428    /// Records are returned in ascending range order and do not imply ledger
429    /// allocation state.
430    #[must_use]
431    pub fn authorities(&self) -> &[MemoryManagerAuthorityRecord] {
432        &self.authorities
433    }
434
435    /// Validate that authority records exactly and contiguously cover `target`.
436    ///
437    /// All records must be inside `target`, and together they must form a
438    /// gap-free partition. This checks policy table coverage only and never
439    /// changes allocation ledger state.
440    pub fn validate_complete_coverage(
441        &self,
442        target: MemoryManagerIdRange,
443    ) -> Result<(), MemoryManagerRangeAuthorityError> {
444        if self.authorities.is_empty() {
445            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
446                start: target.start(),
447                end: target.end(),
448            });
449        }
450
451        for record in &self.authorities {
452            if record.range.start() < target.start() || record.range.end() > target.end() {
453                return Err(
454                    MemoryManagerRangeAuthorityError::RangeOutsideCoverageTarget {
455                        start: record.range.start(),
456                        end: record.range.end(),
457                        target_start: target.start(),
458                        target_end: target.end(),
459                    },
460                );
461            }
462        }
463
464        let mut next_uncovered = u16::from(target.start());
465        let target_end = u16::from(target.end());
466        for record in &self.authorities {
467            let record_start = u16::from(record.range.start());
468            let record_end = u16::from(record.range.end());
469
470            if record_start > next_uncovered {
471                let start = u8::try_from(next_uncovered).map_err(|_| {
472                    MemoryManagerRangeAuthorityError::MissingCoverage {
473                        start: target.start(),
474                        end: target.end(),
475                    }
476                })?;
477                return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
478                    start,
479                    end: record.range.start() - 1,
480                });
481            }
482
483            if record_end >= next_uncovered {
484                next_uncovered = record_end + 1;
485            }
486        }
487
488        if next_uncovered <= target_end {
489            let start = u8::try_from(next_uncovered).map_err(|_| {
490                MemoryManagerRangeAuthorityError::MissingCoverage {
491                    start: target.start(),
492                    end: target.end(),
493                }
494            })?;
495            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
496                start,
497                end: target.end(),
498            });
499        }
500
501        Ok(())
502    }
503
504    fn insert(
505        self,
506        range: MemoryManagerIdRange,
507        authority: impl Into<String>,
508        mode: MemoryManagerRangeMode,
509        purpose: Option<String>,
510    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
511        let record = MemoryManagerAuthorityRecord {
512            range,
513            authority: authority.into(),
514            mode,
515            purpose,
516        };
517        self.insert_record(record)
518    }
519
520    fn insert_record(
521        mut self,
522        record: MemoryManagerAuthorityRecord,
523    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
524        validate_authority_record(&record)?;
525
526        for existing in &self.authorities {
527            if ranges_overlap(existing.range, record.range) {
528                return Err(MemoryManagerRangeAuthorityError::OverlappingRanges {
529                    existing_start: existing.range.start(),
530                    existing_end: existing.range.end(),
531                    candidate_start: record.range.start(),
532                    candidate_end: record.range.end(),
533                });
534            }
535        }
536
537        self.authorities.push(record);
538        self.authorities.sort_by_key(|record| record.range.start());
539        Ok(self)
540    }
541
542    fn covering_record(
543        &self,
544        id: u8,
545    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
546        let Some(record) = self.authority_for_id(id)? else {
547            return Err(MemoryManagerRangeAuthorityError::UnclaimedId { id });
548        };
549        Ok(record)
550    }
551}
552
553fn validate_authority_record(
554    record: &MemoryManagerAuthorityRecord,
555) -> Result<(), MemoryManagerRangeAuthorityError> {
556    record.range.validate()?;
557    validate_diagnostic_string("authority", &record.authority)?;
558    if let Some(purpose) = &record.purpose {
559        validate_diagnostic_string("purpose", purpose)?;
560    }
561    Ok(())
562}
563
564///
565/// MemoryManagerRangeAuthorityError
566///
567/// Invalid `MemoryManager` range authority policy.
568#[non_exhaustive]
569#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
570pub enum MemoryManagerRangeAuthorityError {
571    /// Authority range bounds are invalid.
572    #[error(transparent)]
573    Range(#[from] MemoryManagerRangeError),
574    /// Slot descriptor is not a usable `MemoryManager` ID slot.
575    #[error("{0}")]
576    Slot(#[from] MemoryManagerSlotError),
577    /// Authority range overlaps an existing range.
578    #[error(
579        "MemoryManager authority range {candidate_start}-{candidate_end} overlaps existing range {existing_start}-{existing_end}"
580    )]
581    OverlappingRanges {
582        /// Existing range start.
583        existing_start: u8,
584        /// Existing range end.
585        existing_end: u8,
586        /// Candidate range start.
587        candidate_start: u8,
588        /// Candidate range end.
589        candidate_end: u8,
590    },
591    /// Authority or purpose text failed diagnostic string validation.
592    #[error("{field} {reason}")]
593    InvalidDiagnosticString {
594        /// Diagnostic field name.
595        field: &'static str,
596        /// Validation failure.
597        reason: &'static str,
598    },
599    /// No authority range covers the requested ID.
600    #[error("MemoryManager ID {id} is not covered by an authority range")]
601    UnclaimedId {
602        /// Unclaimed MemoryManager ID.
603        id: u8,
604    },
605    /// Slot is governed by a different authority.
606    #[error(
607        "MemoryManager ID {id} belongs to authority '{actual_authority}', not '{expected_authority}'"
608    )]
609    AuthorityMismatch {
610        /// MemoryManager ID.
611        id: u8,
612        /// Expected authority identifier.
613        expected_authority: String,
614        /// Actual authority identifier.
615        actual_authority: String,
616    },
617    /// Slot is governed by the expected authority with a different mode.
618    #[error(
619        "MemoryManager ID {id} belongs to authority '{authority}' with mode {actual_mode:?}, not {expected_mode:?}"
620    )]
621    ModeMismatch {
622        /// MemoryManager ID.
623        id: u8,
624        /// Authority identifier.
625        authority: String,
626        /// Expected authority mode.
627        expected_mode: MemoryManagerRangeMode,
628        /// Actual authority mode.
629        actual_mode: MemoryManagerRangeMode,
630    },
631    /// A complete coverage target has no authority records for part of it.
632    #[error("MemoryManager authority coverage is missing range {start}-{end}")]
633    MissingCoverage {
634        /// First missing ID.
635        start: u8,
636        /// Last missing ID.
637        end: u8,
638    },
639    /// An authority record lies outside the complete coverage target.
640    #[error(
641        "MemoryManager authority range {start}-{end} is outside coverage target {target_start}-{target_end}"
642    )]
643    RangeOutsideCoverageTarget {
644        /// Authority range start.
645        start: u8,
646        /// Authority range end.
647        end: u8,
648        /// Coverage target start.
649        target_start: u8,
650        /// Coverage target end.
651        target_end: u8,
652    },
653}
654
655const fn ranges_overlap(left: MemoryManagerIdRange, right: MemoryManagerIdRange) -> bool {
656    left.start() <= right.end() && right.start() <= left.end()
657}
658
659fn validate_diagnostic_string(
660    field: &'static str,
661    value: &str,
662) -> Result<(), MemoryManagerRangeAuthorityError> {
663    validate_diagnostic_text(value).map_err(|error| {
664        MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
665            field,
666            reason: error.reason(),
667        }
668    })
669}