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