ic_stable_structures/
cell.rs1use crate::storable::Storable;
3use crate::{read_to_vec, Memory, WASM_PAGE_SIZE};
4use std::borrow::{Borrow, Cow};
5use std::fmt;
6
7#[cfg(test)]
8mod tests;
9
10const MAGIC: &[u8; 3] = b"SCL"; const HEADER_V1_SIZE: u64 = 8;
12const LAYOUT_VERSION: u8 = 1;
13
14#[derive(Debug)]
30struct HeaderV1 {
31 magic: [u8; 3],
32 version: u8,
33 value_length: u32,
34}
35
36#[derive(Debug, PartialEq, Eq)]
38pub enum InitError {
39 IncompatibleVersion {
42 last_supported_version: u8,
43 decoded_version: u8,
44 },
45 ValueTooLarge { value_size: u64 },
47}
48
49impl fmt::Display for InitError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 InitError::IncompatibleVersion {
53 last_supported_version,
54 decoded_version,
55 } => write!(
56 f,
57 "Incompatible version: last supported version is {}, but the memory contains version {}",
58 last_supported_version, decoded_version
59 ),
60 InitError::ValueTooLarge { value_size } => write!(
61 f,
62 "The initial value is too large to fit into the memory: {} bytes",
63 value_size
64 ),
65 }
66 }
67}
68
69#[derive(Debug, PartialEq, Eq)]
71pub enum ValueError {
72 ValueTooLarge { value_size: u64 },
74}
75
76impl From<ValueError> for InitError {
77 fn from(e: ValueError) -> InitError {
78 match e {
79 ValueError::ValueTooLarge { value_size } => InitError::ValueTooLarge { value_size },
80 }
81 }
82}
83
84pub struct Cell<T: Storable, M: Memory> {
93 memory: M,
94 value: T,
95}
96
97impl<T: Storable, M: Memory> Cell<T, M> {
98 pub fn new(memory: M, value: T) -> Result<Self, ValueError> {
100 Self::flush_value(&memory, &value)?;
101 Ok(Self { memory, value })
102 }
103
104 pub fn init(memory: M, default_value: T) -> Result<Self, InitError> {
108 if memory.size() == 0 {
109 return Ok(Self::new(memory, default_value)?);
110 }
111
112 let header = Self::read_header(&memory);
113
114 if &header.magic != MAGIC {
115 return Ok(Self::new(memory, default_value)?);
116 }
117
118 if header.version != LAYOUT_VERSION {
119 return Err(InitError::IncompatibleVersion {
120 last_supported_version: LAYOUT_VERSION,
121 decoded_version: header.version,
122 });
123 }
124
125 Ok(Self {
126 value: Self::read_value(&memory, header.value_length),
127 memory,
128 })
129 }
130
131 fn read_value(memory: &M, len: u32) -> T {
135 let mut buf = vec![];
136 read_to_vec(memory, HEADER_V1_SIZE.into(), &mut buf, len as usize);
137 T::from_bytes(Cow::Owned(buf))
138 }
139
140 fn read_header(memory: &M) -> HeaderV1 {
144 let mut magic: [u8; 3] = [0; 3];
145 let mut version: [u8; 1] = [0; 1];
146 let mut len: [u8; 4] = [0; 4];
147
148 memory.read(0, &mut magic);
149 memory.read(3, &mut version);
150 memory.read(4, &mut len);
151
152 HeaderV1 {
153 magic,
154 version: version[0],
155 value_length: u32::from_le_bytes(len),
156 }
157 }
158
159 pub fn get(&self) -> &T {
161 &self.value
162 }
163
164 pub fn into_memory(self) -> M {
166 self.memory
167 }
168
169 pub fn set(&mut self, value: T) -> Result<T, ValueError> {
173 Self::flush_value(&self.memory, &value)?;
174 Ok(std::mem::replace(&mut self.value, value))
175 }
176
177 fn flush_value(memory: &M, value: &T) -> Result<(), ValueError> {
179 let encoded = value.to_bytes();
180 let bytes: &[u8] = encoded.borrow();
181 let len = bytes.len();
182 if len > u32::MAX as usize {
183 return Err(ValueError::ValueTooLarge {
184 value_size: len as u64,
185 });
186 }
187 let size = memory.size();
188 let available_space = size * WASM_PAGE_SIZE;
189 if len as u64 > available_space.saturating_sub(HEADER_V1_SIZE) || size == 0 {
190 let grow_by =
191 (len as u64 + HEADER_V1_SIZE + WASM_PAGE_SIZE - size * WASM_PAGE_SIZE - 1)
192 / WASM_PAGE_SIZE;
193 if memory.grow(grow_by) < 0 {
194 return Err(ValueError::ValueTooLarge {
195 value_size: len as u64,
196 });
197 }
198 }
199
200 debug_assert!(memory.size() * WASM_PAGE_SIZE >= len as u64 + HEADER_V1_SIZE);
201
202 let version = [LAYOUT_VERSION; 1];
203 memory.write(0, MAGIC);
204 memory.write(3, &version);
205 memory.write(4, &(len as u32).to_le_bytes());
206 memory.write(HEADER_V1_SIZE, bytes);
207 Ok(())
208 }
209}