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                let start = u8::try_from(next_uncovered).map_err(|_| {
442                    MemoryManagerRangeAuthorityError::MissingCoverage {
443                        start: target.start(),
444                        end: target.end(),
445                    }
446                })?;
447                return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
448                    start,
449                    end: record.range.start() - 1,
450                });
451            }
452
453            if record_end >= next_uncovered {
454                next_uncovered = record_end + 1;
455            }
456        }
457
458        if next_uncovered <= target_end {
459            let start = u8::try_from(next_uncovered).map_err(|_| {
460                MemoryManagerRangeAuthorityError::MissingCoverage {
461                    start: target.start(),
462                    end: target.end(),
463                }
464            })?;
465            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
466                start,
467                end: target.end(),
468            });
469        }
470
471        Ok(())
472    }
473
474    fn insert(
475        self,
476        range: MemoryManagerIdRange,
477        authority: impl Into<String>,
478        mode: MemoryManagerRangeMode,
479        purpose: Option<String>,
480    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
481        let record = MemoryManagerAuthorityRecord {
482            range,
483            authority: authority.into(),
484            mode,
485            purpose,
486        };
487        self.insert_record(record)
488    }
489
490    fn insert_record(
491        mut self,
492        record: MemoryManagerAuthorityRecord,
493    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
494        validate_authority_record(&record)?;
495
496        for existing in &self.authorities {
497            if ranges_overlap(existing.range, record.range) {
498                return Err(MemoryManagerRangeAuthorityError::OverlappingRanges {
499                    existing_start: existing.range.start(),
500                    existing_end: existing.range.end(),
501                    candidate_start: record.range.start(),
502                    candidate_end: record.range.end(),
503                });
504            }
505        }
506
507        self.authorities.push(record);
508        self.authorities.sort_by_key(|record| record.range.start());
509        Ok(self)
510    }
511
512    fn covering_record(
513        &self,
514        id: u8,
515    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
516        let Some(record) = self.authority_for_id(id)? else {
517            return Err(MemoryManagerRangeAuthorityError::UnclaimedId { id });
518        };
519        Ok(record)
520    }
521}
522
523fn validate_authority_record(
524    record: &MemoryManagerAuthorityRecord,
525) -> Result<(), MemoryManagerRangeAuthorityError> {
526    record.range.validate()?;
527    validate_diagnostic_string("authority", &record.authority)?;
528    if let Some(purpose) = &record.purpose {
529        validate_diagnostic_string("purpose", purpose)?;
530    }
531    Ok(())
532}
533
534///
535/// MemoryManagerRangeAuthorityError
536///
537/// Invalid `MemoryManager` range authority policy.
538#[non_exhaustive]
539#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
540pub enum MemoryManagerRangeAuthorityError {
541    /// Authority range bounds are invalid.
542    #[error(transparent)]
543    Range(#[from] MemoryManagerRangeError),
544    /// Slot descriptor is not a usable `MemoryManager` ID slot.
545    #[error("{0}")]
546    Slot(#[from] MemoryManagerSlotError),
547    /// Authority range overlaps an existing range.
548    #[error(
549        "MemoryManager authority range {candidate_start}-{candidate_end} overlaps existing range {existing_start}-{existing_end}"
550    )]
551    OverlappingRanges {
552        /// Existing range start.
553        existing_start: u8,
554        /// Existing range end.
555        existing_end: u8,
556        /// Candidate range start.
557        candidate_start: u8,
558        /// Candidate range end.
559        candidate_end: u8,
560    },
561    /// Authority or purpose text failed diagnostic string validation.
562    #[error("{field} {reason}")]
563    InvalidDiagnosticString {
564        /// Diagnostic field name.
565        field: &'static str,
566        /// Validation failure.
567        reason: &'static str,
568    },
569    /// No authority range covers the requested ID.
570    #[error("MemoryManager ID {id} is not covered by an authority range")]
571    UnclaimedId {
572        /// Unclaimed MemoryManager ID.
573        id: u8,
574    },
575    /// Slot is governed by a different authority.
576    #[error(
577        "MemoryManager ID {id} belongs to authority '{actual_authority}', not '{expected_authority}'"
578    )]
579    AuthorityMismatch {
580        /// MemoryManager ID.
581        id: u8,
582        /// Expected authority identifier.
583        expected_authority: String,
584        /// Actual authority identifier.
585        actual_authority: String,
586    },
587    /// Slot is governed by the expected authority with a different mode.
588    #[error(
589        "MemoryManager ID {id} belongs to authority '{authority}' with mode {actual_mode:?}, not {expected_mode:?}"
590    )]
591    ModeMismatch {
592        /// MemoryManager ID.
593        id: u8,
594        /// Authority identifier.
595        authority: String,
596        /// Expected authority mode.
597        expected_mode: MemoryManagerRangeMode,
598        /// Actual authority mode.
599        actual_mode: MemoryManagerRangeMode,
600    },
601    /// A complete coverage target has no authority records for part of it.
602    #[error("MemoryManager authority coverage is missing range {start}-{end}")]
603    MissingCoverage {
604        /// First missing ID.
605        start: u8,
606        /// Last missing ID.
607        end: u8,
608    },
609    /// An authority record lies outside the complete coverage target.
610    #[error(
611        "MemoryManager authority range {start}-{end} is outside coverage target {target_start}-{target_end}"
612    )]
613    RangeOutsideCoverageTarget {
614        /// Authority range start.
615        start: u8,
616        /// Authority range end.
617        end: u8,
618        /// Coverage target start.
619        target_start: u8,
620        /// Coverage target end.
621        target_end: u8,
622    },
623}
624
625const fn ranges_overlap(left: MemoryManagerIdRange, right: MemoryManagerIdRange) -> bool {
626    left.start() <= right.end() && right.start() <= left.end()
627}
628
629fn validate_diagnostic_string(
630    field: &'static str,
631    value: &str,
632) -> Result<(), MemoryManagerRangeAuthorityError> {
633    if value.is_empty() {
634        return Err(MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
635            field,
636            reason: "must not be empty",
637        });
638    }
639    if value.len() > DIAGNOSTIC_STRING_MAX_BYTES {
640        return Err(MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
641            field,
642            reason: "must be at most 256 bytes",
643        });
644    }
645    if !value.is_ascii() {
646        return Err(MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
647            field,
648            reason: "must be ASCII",
649        });
650    }
651    if value.bytes().any(|byte| byte.is_ascii_control()) {
652        return Err(MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
653            field,
654            reason: "must not contain ASCII control characters",
655        });
656    }
657    Ok(())
658}