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