Skip to main content

ic_storage/adapter/
log.rs

1//! Governed log collection implementation.
2use super::{LogIter, LogWriteError, Memory, Storable};
3use crate::substrate::{self, DefaultMemoryImpl};
4use ic_memory::RuntimeMemory;
5
6/// Governed upstream `Log`; initialization and failures follow upstream contracts.
7pub struct StableLog<T: Storable, M: Memory = DefaultMemoryImpl> {
8    inner: substrate::Log<T, RuntimeMemory<M>, RuntimeMemory<M>>,
9}
10impl<T: Storable, M: Memory> StableLog<T, M> {
11    /// Create a log only when both admitted memories have zero pages.
12    ///
13    /// # Panics
14    /// Refuses either nonempty memory before writing. Growth failures retain
15    /// upstream behavior.
16    pub fn create(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
17        assert_eq!(index_memory.size(), 0, "log index is not empty");
18        assert_eq!(data_memory.size(), 0, "log data is not empty");
19        Self::reset(index_memory, data_memory)
20    }
21
22    /// Deliberately replace both admitted log allocations.
23    ///
24    /// # Panics
25    /// Growth and backing failures retain upstream behavior.
26    pub fn reset(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
27        Self {
28            inner: substrate::Log::new(index_memory, data_memory),
29        }
30    }
31
32    /// Load a retained log without initializing either allocation.
33    ///
34    /// # Panics
35    /// Refuses empty/foreign memory and incompatible layouts without writing.
36    /// Backing and collection corruption failures retain upstream behavior.
37    pub fn load(index_memory: RuntimeMemory<M>, data_memory: RuntimeMemory<M>) -> Self {
38        assert!(
39            index_memory.size() > 0 && data_memory.size() > 0,
40            "log memory is empty"
41        );
42        let mut magic = [0; 3];
43        data_memory.read(0, &mut magic);
44        assert_eq!(&magic, b"GLD", "foreign log data");
45        // With recognized data magic upstream refuses every other incompatible
46        // header before writes, including invalid/missing index magic.
47        Self {
48            inner: substrate::Log::init(index_memory, data_memory),
49        }
50    }
51    /// Delegates to upstream `Log::first` with the governed allocation.
52    pub fn first(&self) -> Option<T> {
53        self.inner.first()
54    }
55    /// Delegates to upstream `Log::last` with the governed allocation.
56    pub fn last(&self) -> Option<T> {
57        self.inner.last()
58    }
59    /// Delegates to upstream `Log::get` with the governed allocation.
60    pub fn get(&self, idx: u64) -> Option<T> {
61        self.inner.get(idx)
62    }
63    /// Delegates to upstream `Log::len` with the governed allocation.
64    pub fn len(&self) -> u64 {
65        self.inner.len()
66    }
67    /// Delegates to upstream `Log::is_empty` with the governed allocation.
68    pub fn is_empty(&self) -> bool {
69        self.inner.is_empty()
70    }
71    /// Delegates to upstream `Log::append` with the governed allocation.
72    /// # Errors
73    /// Returns the upstream write error if memory cannot grow.
74    pub fn append(&self, item: &T) -> Result<u64, LogWriteError> {
75        self.inner.append(item)
76    }
77    /// Delegates to upstream `Log::iter` with the governed allocation.
78    pub fn iter(&self) -> LogIter<'_, T, M> {
79        self.inner.iter()
80    }
81}
82
83impl<'a, T: Storable, M: Memory> IntoIterator for &'a StableLog<T, M> {
84    type Item = T;
85    type IntoIter = LogIter<'a, T, M>;
86    fn into_iter(self) -> Self::IntoIter {
87        self.iter()
88    }
89}