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::text::validate_diagnostic_text;
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/// Allocation policy mode for a `MemoryManager` authority range.
98///
99/// These modes describe policy authority, not durable allocation state.
100/// Only `Allowed` ranges supply fresh logical placements; neither mode allocates
101/// memory by itself. [`crate::ic_memory_range!`] defaults to `Reserved` when its
102/// `mode` argument is omitted.
103///
104
105#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
106pub enum MemoryManagerRangeMode {
107    /// Range is reserved for authority-owned framework or infrastructure use.
108    ///
109    /// Reserved does not mean every ID in the range has been allocated.
110    Reserved,
111    /// Range is allowed for authority-governed application allocation use.
112    ///
113    /// Allowed does not allocate any ID in the range.
114    Allowed,
115}
116
117///
118/// MemoryManagerAuthorityRecord
119///
120/// Ordered diagnostic authority record for a `MemoryManager` ID range.
121///
122
123#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
124#[serde(deny_unknown_fields)]
125pub struct MemoryManagerAuthorityRecord {
126    /// Inclusive range governed by this authority.
127    pub(crate) range: MemoryManagerIdRange,
128    /// Stable printable ASCII authority identifier.
129    pub(crate) authority: String,
130    /// Policy mode for this authority range.
131    pub(crate) mode: MemoryManagerRangeMode,
132    /// Optional stable printable ASCII diagnostic purpose.
133    #[serde(deserialize_with = "crate::cbor::deserialize_present_option")]
134    pub(crate) purpose: Option<String>,
135}
136
137impl MemoryManagerAuthorityRecord {
138    /// Build a diagnostic authority record after validating printable metadata.
139    pub fn new(
140        range: MemoryManagerIdRange,
141        authority: impl Into<String>,
142        mode: MemoryManagerRangeMode,
143        purpose: Option<String>,
144    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
145        let record = Self {
146            range,
147            authority: authority.into(),
148            mode,
149            purpose,
150        };
151        validate_authority_record(&record)?;
152        Ok(record)
153    }
154
155    /// Return the inclusive range governed by this authority.
156    #[must_use]
157    pub const fn range(&self) -> MemoryManagerIdRange {
158        self.range
159    }
160
161    /// Return the stable printable ASCII authority identifier.
162    #[must_use]
163    pub fn authority(&self) -> &str {
164        &self.authority
165    }
166
167    /// Return the policy mode for this authority range.
168    #[must_use]
169    pub const fn mode(&self) -> MemoryManagerRangeMode {
170        self.mode
171    }
172
173    /// Return the optional stable printable ASCII diagnostic purpose.
174    #[must_use]
175    pub fn purpose(&self) -> Option<&str> {
176        self.purpose.as_deref()
177    }
178
179    /// Validate constructor invariants after decode or manual assembly.
180    pub fn validate(&self) -> Result<(), MemoryManagerRangeAuthorityError> {
181        validate_authority_record(self)
182    }
183}
184
185///
186/// MemoryManagerRangeAuthority
187///
188/// Substrate-specific range authority policy helper for `MemoryManager` IDs.
189///
190/// This helper records policy and diagnostic authority ranges only. It never
191/// mutates the allocation ledger, and it does not allocate or reserve durable
192/// stable-memory slots. Durable allocation remains the generic ledger mapping
193/// from stable key to allocation slot.
194///
195/// When used through the default runtime registry, registered ranges are
196/// authoritative generic policy and are checked before caller-supplied
197/// [`crate::AllocationPolicy`]. When no user ranges are registered, frameworks
198/// can enforce fixed application claims through their own policy. Logical
199/// placement and historical selection always require explicit grants; fresh
200/// placements use only `Allowed` ranges.
201///
202
203#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
204pub struct MemoryManagerRangeAuthority {
205    authorities: Vec<MemoryManagerAuthorityRecord>,
206}
207
208#[derive(Deserialize)]
209#[serde(deny_unknown_fields)]
210struct MemoryManagerRangeAuthorityDto {
211    authorities: Vec<MemoryManagerAuthorityRecord>,
212}
213
214impl<'de> Deserialize<'de> for MemoryManagerRangeAuthority {
215    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
216        let dto = MemoryManagerRangeAuthorityDto::deserialize(deserializer)?;
217        Self::from_records(dto.authorities).map_err(D::Error::custom)
218    }
219}
220
221impl MemoryManagerRangeAuthority {
222    /// Create an empty `MemoryManager` range authority policy.
223    #[must_use]
224    pub const fn new() -> Self {
225        Self {
226            authorities: Vec::new(),
227        }
228    }
229
230    /// Build a range authority from diagnostic records.
231    ///
232    /// Each record is validated before insertion, overlaps are rejected, and
233    /// accepted records are stored in ascending range order. Decoded records
234    /// pass the same checks as records built with their checked constructor.
235    pub fn from_records(
236        records: Vec<MemoryManagerAuthorityRecord>,
237    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
238        let mut authorities: Vec<MemoryManagerAuthorityRecord> = Vec::new();
239        for record in records {
240            validate_authority_record(&record)?;
241            let insertion = authorities
242                .partition_point(|existing| existing.range.start() < record.range.start());
243            // Accepted ranges are ordered and disjoint. Only the predecessor
244            // and successor can be the first overlap; check them in range order.
245            let neighbours = insertion.saturating_sub(1)..(insertion + 1).min(authorities.len());
246            for existing in &authorities[neighbours] {
247                if ranges_overlap(existing.range, record.range) {
248                    return Err(MemoryManagerRangeAuthorityError::OverlappingRanges {
249                        existing_start: existing.range.start(),
250                        existing_end: existing.range.end(),
251                        candidate_start: record.range.start(),
252                        candidate_end: record.range.end(),
253                    });
254                }
255            }
256            authorities.insert(insertion, record);
257        }
258        Ok(Self { authorities })
259    }
260
261    /// Validate that `slot` belongs to `expected_authority`.
262    pub fn validate_slot_authority(
263        &self,
264        slot: &AllocationSlotDescriptor,
265        expected_authority: &str,
266    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
267        let id = slot
268            .memory_manager_id()
269            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
270        self.validate_id_authority(id, expected_authority)
271    }
272
273    /// Validate that `slot` belongs to `expected_authority` with `expected_mode`.
274    pub fn validate_slot_authority_mode(
275        &self,
276        slot: &AllocationSlotDescriptor,
277        expected_authority: &str,
278        expected_mode: MemoryManagerRangeMode,
279    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
280        let id = slot
281            .memory_manager_id()
282            .map_err(MemoryManagerRangeAuthorityError::Slot)?;
283        self.validate_id_authority_mode(id, expected_authority, expected_mode)
284    }
285
286    /// Validate that `id` belongs to `expected_authority`.
287    pub fn validate_id_authority(
288        &self,
289        id: u8,
290        expected_authority: &str,
291    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
292        validate_diagnostic_string("expected_authority", expected_authority)?;
293        let record = self.covering_record(id)?;
294
295        if record.authority != expected_authority {
296            return Err(MemoryManagerRangeAuthorityError::AuthorityMismatch {
297                id,
298                expected_authority: expected_authority.to_string(),
299                actual_authority: record.authority.clone(),
300            });
301        }
302
303        Ok(record)
304    }
305
306    /// Validate that `id` belongs to `expected_authority` with `expected_mode`.
307    pub fn validate_id_authority_mode(
308        &self,
309        id: u8,
310        expected_authority: &str,
311        expected_mode: MemoryManagerRangeMode,
312    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
313        let record = self.validate_id_authority(id, expected_authority)?;
314        if record.mode != expected_mode {
315            return Err(MemoryManagerRangeAuthorityError::ModeMismatch {
316                id,
317                authority: record.authority.clone(),
318                expected_mode,
319                actual_mode: record.mode,
320            });
321        }
322        Ok(record)
323    }
324
325    /// Return the authority record that governs `id`, if any.
326    pub fn authority_for_id(
327        &self,
328        id: u8,
329    ) -> Result<Option<&MemoryManagerAuthorityRecord>, MemoryManagerRangeAuthorityError> {
330        validate_memory_manager_id(id).map_err(MemoryManagerRangeAuthorityError::Slot)?;
331        Ok(self
332            .authorities
333            .iter()
334            .find(|record| record.range.contains(id)))
335    }
336
337    /// Ordered non-overlapping authority records.
338    ///
339    /// This is the stable diagnostic/export surface for the authority table.
340    /// Records are returned in ascending range order and do not imply ledger
341    /// allocation state.
342    #[must_use]
343    pub fn authorities(&self) -> &[MemoryManagerAuthorityRecord] {
344        &self.authorities
345    }
346
347    /// Validate that authority records exactly and contiguously cover `target`.
348    ///
349    /// All records must be inside `target`, and together they must form a
350    /// gap-free partition. This checks policy table coverage only and never
351    /// changes allocation ledger state.
352    pub fn validate_complete_coverage(
353        &self,
354        target: MemoryManagerIdRange,
355    ) -> Result<(), MemoryManagerRangeAuthorityError> {
356        if self.authorities.is_empty() {
357            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
358                start: target.start(),
359                end: target.end(),
360            });
361        }
362
363        for record in &self.authorities {
364            if record.range.start() < target.start() || record.range.end() > target.end() {
365                return Err(
366                    MemoryManagerRangeAuthorityError::RangeOutsideCoverageTarget {
367                        start: record.range.start(),
368                        end: record.range.end(),
369                        target_start: target.start(),
370                        target_end: target.end(),
371                    },
372                );
373            }
374        }
375
376        let mut next_uncovered = target.start();
377        for record in &self.authorities {
378            if record.range.start() > next_uncovered {
379                return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
380                    start: next_uncovered,
381                    end: record.range.start() - 1,
382                });
383            }
384            // Ranges are ordered and disjoint, with usable ends at most 254.
385            // The next position therefore advances and fits through 255.
386            next_uncovered = record.range.end() + 1;
387        }
388
389        if next_uncovered <= target.end() {
390            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
391                start: next_uncovered,
392                end: target.end(),
393            });
394        }
395
396        Ok(())
397    }
398
399    fn covering_record(
400        &self,
401        id: u8,
402    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
403        let Some(record) = self.authority_for_id(id)? else {
404            return Err(MemoryManagerRangeAuthorityError::UnclaimedId { id });
405        };
406        Ok(record)
407    }
408}
409
410fn validate_authority_record(
411    record: &MemoryManagerAuthorityRecord,
412) -> Result<(), MemoryManagerRangeAuthorityError> {
413    record.range.validate()?;
414    validate_diagnostic_string("authority", &record.authority)?;
415    if let Some(purpose) = &record.purpose {
416        validate_diagnostic_string("purpose", purpose)?;
417    }
418    Ok(())
419}
420
421///
422/// MemoryManagerRangeAuthorityError
423///
424/// Invalid `MemoryManager` range authority policy.
425#[non_exhaustive]
426#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
427pub enum MemoryManagerRangeAuthorityError {
428    /// Authority range bounds are invalid.
429    #[error(transparent)]
430    Range(#[from] MemoryManagerRangeError),
431    /// Slot descriptor is not a usable `MemoryManager` ID slot.
432    #[error("{0}")]
433    Slot(#[from] MemoryManagerSlotError),
434    /// Authority range overlaps an existing range.
435    #[error(
436        "MemoryManager authority range {candidate_start}-{candidate_end} overlaps existing range {existing_start}-{existing_end}"
437    )]
438    OverlappingRanges {
439        /// Existing range start.
440        existing_start: u8,
441        /// Existing range end.
442        existing_end: u8,
443        /// Candidate range start.
444        candidate_start: u8,
445        /// Candidate range end.
446        candidate_end: u8,
447    },
448    /// Authority or purpose text failed diagnostic string validation.
449    #[error("{field} {reason}")]
450    InvalidDiagnosticString {
451        /// Diagnostic field name.
452        field: &'static str,
453        /// Validation failure.
454        reason: &'static str,
455    },
456    /// No authority range covers the requested ID.
457    #[error("MemoryManager ID {id} is not covered by an authority range")]
458    UnclaimedId {
459        /// Unclaimed MemoryManager ID.
460        id: u8,
461    },
462    /// Slot is governed by a different authority.
463    #[error(
464        "MemoryManager ID {id} belongs to authority '{actual_authority}', not '{expected_authority}'"
465    )]
466    AuthorityMismatch {
467        /// MemoryManager ID.
468        id: u8,
469        /// Expected authority identifier.
470        expected_authority: String,
471        /// Actual authority identifier.
472        actual_authority: String,
473    },
474    /// Slot is governed by the expected authority with a different mode.
475    #[error(
476        "MemoryManager ID {id} belongs to authority '{authority}' with mode {actual_mode:?}, not {expected_mode:?}"
477    )]
478    ModeMismatch {
479        /// MemoryManager ID.
480        id: u8,
481        /// Authority identifier.
482        authority: String,
483        /// Expected authority mode.
484        expected_mode: MemoryManagerRangeMode,
485        /// Actual authority mode.
486        actual_mode: MemoryManagerRangeMode,
487    },
488    /// A complete coverage target has no authority records for part of it.
489    #[error("MemoryManager authority coverage is missing range {start}-{end}")]
490    MissingCoverage {
491        /// First missing ID.
492        start: u8,
493        /// Last missing ID.
494        end: u8,
495    },
496    /// An authority record lies outside the complete coverage target.
497    #[error(
498        "MemoryManager authority range {start}-{end} is outside coverage target {target_start}-{target_end}"
499    )]
500    RangeOutsideCoverageTarget {
501        /// Authority range start.
502        start: u8,
503        /// Authority range end.
504        end: u8,
505        /// Coverage target start.
506        target_start: u8,
507        /// Coverage target end.
508        target_end: u8,
509    },
510}
511
512const fn ranges_overlap(left: MemoryManagerIdRange, right: MemoryManagerIdRange) -> bool {
513    left.start() <= right.end() && right.start() <= left.end()
514}
515
516fn validate_diagnostic_string(
517    field: &'static str,
518    value: &str,
519) -> Result<(), MemoryManagerRangeAuthorityError> {
520    validate_diagnostic_text(value).map_err(|error| {
521        MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
522            field,
523            reason: error.reason(),
524        }
525    })
526}