Skip to main content

ic_memory/
declaration.rs

1use crate::{key::StableKey, slot::MemoryManagerSlot};
2use serde::Serialize;
3
4///
5/// AllocationDeclaration
6///
7/// Resolved key-to-slot binding borrowed from a committed runtime.
8///
9/// Production inputs are key-only [`crate::MemoryRequest`] values. The runtime
10/// resolves slots, validates recovered ownership and persists its generation
11/// before exposing these bindings. Numeric construction and decoding are
12/// available only with `test-support` for explicit fixtures.
13///
14
15#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
16#[cfg_attr(any(test, feature = "test-support"), derive(serde::Deserialize))]
17#[serde(deny_unknown_fields)]
18pub struct AllocationDeclaration {
19    /// Durable stable key.
20    pub(crate) stable_key: StableKey,
21    /// Claimed allocation slot.
22    pub(crate) slot: MemoryManagerSlot,
23}
24
25impl AllocationDeclaration {
26    /// Return the durable stable key claimed by this declaration.
27    #[must_use]
28    pub const fn stable_key(&self) -> &StableKey {
29        &self.stable_key
30    }
31
32    /// Return the allocation slot claimed by this declaration.
33    #[must_use]
34    pub const fn slot(&self) -> &MemoryManagerSlot {
35        &self.slot
36    }
37}
38
39// Borrowed runtime queries share
40// one lookup. Comparing text needs no temporary owned StableKey or second index.
41pub fn declaration_for_key<'a>(
42    declarations: &'a [AllocationDeclaration],
43    key: &str,
44) -> Option<&'a AllocationDeclaration> {
45    declarations
46        .iter()
47        .find(|declaration| declaration.stable_key.as_str() == key)
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::{slot::MemoryManagerSlot, testing::DeclarationSnapshotError};
54
55    fn declaration(key: &str, id: u8) -> AllocationDeclaration {
56        AllocationDeclaration::new(key, MemoryManagerSlot::new(id).expect("usable slot"))
57            .expect("declaration")
58    }
59
60    #[test]
61    fn memory_manager_declaration_constructor_builds_common_declaration() {
62        let declaration =
63            AllocationDeclaration::memory_manager("app.orders.v1", 100).expect("declaration");
64
65        assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
66        assert_eq!(
67            declaration.slot,
68            MemoryManagerSlot::new(100).expect("usable slot")
69        );
70    }
71
72    #[test]
73    fn memory_manager_declaration_constructor_rejects_invalid_slot() {
74        let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX)
75            .expect_err("sentinel must fail");
76
77        assert!(matches!(
78            err,
79            DeclarationSnapshotError::MemoryManagerSlot(_)
80        ));
81    }
82
83    #[test]
84    fn declaration_decode_rejects_unusable_memory_manager_slot() {
85        let mut value = serde_json::to_value(declaration("app.orders.v1", 100)).unwrap();
86        value["slot"] = serde_json::json!(255);
87        assert!(serde_json::from_value::<AllocationDeclaration>(value.clone()).is_err());
88        let bytes = crate::test_cbor::to_vec(&value).unwrap();
89        assert!(crate::test_cbor::from_slice::<AllocationDeclaration>(&bytes).is_err());
90    }
91}