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