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