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