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