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 before insertion, overlaps are rejected, and
230    /// accepted records are stored in ascending range order. Decoded records
231    /// pass the same checks as records built with their checked constructor.
232    pub fn from_records(
233        records: Vec<MemoryManagerAuthorityRecord>,
234    ) -> Result<Self, MemoryManagerRangeAuthorityError> {
235        let mut authorities: Vec<MemoryManagerAuthorityRecord> = Vec::new();
236        for record in records {
237            validate_authority_record(&record)?;
238            let insertion = authorities
239                .partition_point(|existing| existing.range.start() < record.range.start());
240            // Accepted ranges are ordered and disjoint. Only the predecessor
241            // and successor can be the first overlap; check them in range order.
242            let neighbours = insertion.saturating_sub(1)..(insertion + 1).min(authorities.len());
243            for existing in &authorities[neighbours] {
244                if ranges_overlap(existing.range, record.range) {
245                    return Err(MemoryManagerRangeAuthorityError::OverlappingRanges {
246                        existing_start: existing.range.start(),
247                        existing_end: existing.range.end(),
248                        candidate_start: record.range.start(),
249                        candidate_end: record.range.end(),
250                    });
251                }
252            }
253            authorities.insert(insertion, record);
254        }
255        Ok(Self { authorities })
256    }
257
258    /// Validate that `slot` belongs to `expected_authority`.
259    pub fn validate_slot_authority(
260        &self,
261        slot: &MemoryManagerSlot,
262        expected_authority: &str,
263    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
264        let id = slot.id();
265        self.validate_id_authority(id, expected_authority)
266    }
267
268    /// Validate that `slot` belongs to `expected_authority` with `expected_mode`.
269    pub fn validate_slot_authority_mode(
270        &self,
271        slot: &MemoryManagerSlot,
272        expected_authority: &str,
273        expected_mode: MemoryManagerRangeMode,
274    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
275        let id = slot.id();
276        self.validate_id_authority_mode(id, expected_authority, expected_mode)
277    }
278
279    /// Validate that `id` belongs to `expected_authority`.
280    pub fn validate_id_authority(
281        &self,
282        id: u8,
283        expected_authority: &str,
284    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
285        validate_diagnostic_string("expected_authority", expected_authority)?;
286        let record = self
287            .authority_for_id(id)?
288            .ok_or(MemoryManagerRangeAuthorityError::UnclaimedId { id })?;
289
290        if record.authority != expected_authority {
291            return Err(MemoryManagerRangeAuthorityError::AuthorityMismatch {
292                id,
293                expected_authority: expected_authority.to_string(),
294                actual_authority: record.authority.clone(),
295            });
296        }
297
298        Ok(record)
299    }
300
301    /// Validate that `id` belongs to `expected_authority` with `expected_mode`.
302    pub fn validate_id_authority_mode(
303        &self,
304        id: u8,
305        expected_authority: &str,
306        expected_mode: MemoryManagerRangeMode,
307    ) -> Result<&MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError> {
308        let record = self.validate_id_authority(id, expected_authority)?;
309        if record.mode != expected_mode {
310            return Err(MemoryManagerRangeAuthorityError::ModeMismatch {
311                id,
312                authority: record.authority.clone(),
313                expected_mode,
314                actual_mode: record.mode,
315            });
316        }
317        Ok(record)
318    }
319
320    /// Return the authority record that governs `id`, if any.
321    pub fn authority_for_id(
322        &self,
323        id: u8,
324    ) -> Result<Option<&MemoryManagerAuthorityRecord>, MemoryManagerRangeAuthorityError> {
325        validate_memory_manager_id(id).map_err(MemoryManagerRangeAuthorityError::Slot)?;
326        // Construction and decoding establish ordered, disjoint ranges, so
327        // their ends are increasing. Only the first end at or above this ID
328        // can cover it; its start distinguishes coverage from a gap.
329        let index = self
330            .authorities
331            .partition_point(|record| record.range.end() < id);
332        Ok(self
333            .authorities
334            .get(index)
335            .filter(|record| record.range.start() <= id))
336    }
337
338    /// Ordered non-overlapping authority records.
339    ///
340    /// This is the stable diagnostic/export surface for the authority table.
341    /// Records are returned in ascending range order and do not imply ledger
342    /// allocation state.
343    #[must_use]
344    pub fn authorities(&self) -> &[MemoryManagerAuthorityRecord] {
345        &self.authorities
346    }
347
348    /// Validate that authority records exactly and contiguously cover `target`.
349    ///
350    /// All records must be inside `target`, and together they must form a
351    /// gap-free partition. This checks policy table coverage only and never
352    /// changes allocation ledger state.
353    pub fn validate_complete_coverage(
354        &self,
355        target: MemoryManagerIdRange,
356    ) -> Result<(), MemoryManagerRangeAuthorityError> {
357        if self.authorities.is_empty() {
358            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
359                start: target.start(),
360                end: target.end(),
361            });
362        }
363
364        for record in &self.authorities {
365            if record.range.start() < target.start() || record.range.end() > target.end() {
366                return Err(
367                    MemoryManagerRangeAuthorityError::RangeOutsideCoverageTarget {
368                        start: record.range.start(),
369                        end: record.range.end(),
370                        target_start: target.start(),
371                        target_end: target.end(),
372                    },
373                );
374            }
375        }
376
377        let mut next_uncovered = target.start();
378        for record in &self.authorities {
379            if record.range.start() > next_uncovered {
380                return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
381                    start: next_uncovered,
382                    end: record.range.start() - 1,
383                });
384            }
385            // Ranges are ordered and disjoint, with usable ends at most 254.
386            // The next position therefore advances and fits through 255.
387            next_uncovered = record.range.end() + 1;
388        }
389
390        if next_uncovered <= target.end() {
391            return Err(MemoryManagerRangeAuthorityError::MissingCoverage {
392                start: next_uncovered,
393                end: target.end(),
394            });
395        }
396
397        Ok(())
398    }
399}
400
401fn validate_authority_record(
402    record: &MemoryManagerAuthorityRecord,
403) -> Result<(), MemoryManagerRangeAuthorityError> {
404    record.range.validate()?;
405    validate_diagnostic_string("authority", &record.authority)?;
406    if let Some(purpose) = &record.purpose {
407        validate_diagnostic_string("purpose", purpose)?;
408    }
409    Ok(())
410}
411
412///
413/// MemoryManagerRangeAuthorityError
414///
415/// Invalid `MemoryManager` range authority policy.
416#[non_exhaustive]
417#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
418pub enum MemoryManagerRangeAuthorityError {
419    /// Authority range bounds are invalid.
420    #[error(transparent)]
421    Range(#[from] MemoryManagerRangeError),
422    /// A raw numeric ID is not a usable `MemoryManager` slot.
423    #[error("{0}")]
424    Slot(#[from] MemoryManagerSlotError),
425    /// Authority range overlaps an existing range.
426    #[error(
427        "MemoryManager authority range {candidate_start}-{candidate_end} overlaps existing range {existing_start}-{existing_end}"
428    )]
429    OverlappingRanges {
430        /// Existing range start.
431        existing_start: u8,
432        /// Existing range end.
433        existing_end: u8,
434        /// Candidate range start.
435        candidate_start: u8,
436        /// Candidate range end.
437        candidate_end: u8,
438    },
439    /// Authority or purpose text failed diagnostic string validation.
440    #[error("{field} {reason}")]
441    InvalidDiagnosticString {
442        /// Diagnostic field name.
443        field: &'static str,
444        /// Validation failure.
445        reason: &'static str,
446    },
447    /// No authority range covers the requested ID.
448    #[error("MemoryManager ID {id} is not covered by an authority range")]
449    UnclaimedId {
450        /// Unclaimed MemoryManager ID.
451        id: u8,
452    },
453    /// Slot is governed by a different authority.
454    #[error(
455        "MemoryManager ID {id} belongs to authority '{actual_authority}', not '{expected_authority}'"
456    )]
457    AuthorityMismatch {
458        /// MemoryManager ID.
459        id: u8,
460        /// Expected authority identifier.
461        expected_authority: String,
462        /// Actual authority identifier.
463        actual_authority: String,
464    },
465    /// Slot is governed by the expected authority with a different mode.
466    #[error(
467        "MemoryManager ID {id} belongs to authority '{authority}' with mode {actual_mode:?}, not {expected_mode:?}"
468    )]
469    ModeMismatch {
470        /// MemoryManager ID.
471        id: u8,
472        /// Authority identifier.
473        authority: String,
474        /// Expected authority mode.
475        expected_mode: MemoryManagerRangeMode,
476        /// Actual authority mode.
477        actual_mode: MemoryManagerRangeMode,
478    },
479    /// A complete coverage target has no authority records for part of it.
480    #[error("MemoryManager authority coverage is missing range {start}-{end}")]
481    MissingCoverage {
482        /// First missing ID.
483        start: u8,
484        /// Last missing ID.
485        end: u8,
486    },
487    /// An authority record lies outside the complete coverage target.
488    #[error(
489        "MemoryManager authority range {start}-{end} is outside coverage target {target_start}-{target_end}"
490    )]
491    RangeOutsideCoverageTarget {
492        /// Authority range start.
493        start: u8,
494        /// Authority range end.
495        end: u8,
496        /// Coverage target start.
497        target_start: u8,
498        /// Coverage target end.
499        target_end: u8,
500    },
501}
502
503const fn ranges_overlap(left: MemoryManagerIdRange, right: MemoryManagerIdRange) -> bool {
504    left.start() <= right.end() && right.start() <= left.end()
505}
506
507fn validate_diagnostic_string(
508    field: &'static str,
509    value: &str,
510) -> Result<(), MemoryManagerRangeAuthorityError> {
511    validate_diagnostic_text(value).map_err(|error| {
512        MemoryManagerRangeAuthorityError::InvalidDiagnosticString {
513            field,
514            reason: error.reason(),
515        }
516    })
517}