Skip to main content

ic_memory/slot/
range_authority.rs

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