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