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