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