1mod canister;
6#[cfg(test)]
7mod tests;
8
9pub use canister::CanisterStableMemory;
10use std::{error, fmt, io};
11
12pub const WASM_PAGE_SIZE_IN_BYTES: u64 = 64 * 1024; static CANISTER_STABLE_MEMORY: CanisterStableMemory = CanisterStableMemory {};
16
17pub trait StableMemory {
19 fn stable_size(&self) -> u64;
21
22 fn stable_grow(&self, new_pages: u64) -> Result<u64, StableMemoryError>;
29
30 fn stable_write(&self, offset: u64, buf: &[u8]);
35
36 fn stable_read(&self, offset: u64, buf: &mut [u8]);
38}
39
40pub fn stable_size() -> u64 {
42 CANISTER_STABLE_MEMORY.stable_size()
43}
44
45#[derive(Debug)]
47pub enum StableMemoryError {
48 OutOfMemory,
50 OutOfBounds,
52}
53
54impl fmt::Display for StableMemoryError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::OutOfMemory => f.write_str("Out of memory"),
58 Self::OutOfBounds => f.write_str("Read exceeds allocated memory"),
59 }
60 }
61}
62
63impl error::Error for StableMemoryError {}
64
65pub fn stable_grow(new_pages: u64) -> Result<u64, StableMemoryError> {
72 CANISTER_STABLE_MEMORY.stable_grow(new_pages)
73}
74
75pub fn stable_write(offset: u64, buf: &[u8]) {
80 CANISTER_STABLE_MEMORY.stable_write(offset, buf)
81}
82
83pub fn stable_read(offset: u64, buf: &mut [u8]) {
85 CANISTER_STABLE_MEMORY.stable_read(offset, buf)
86}
87
88pub fn stable_bytes() -> Vec<u8> {
96 let size = (stable_size() << 16)
97 .try_into()
98 .expect("overflow: stable memory too large to read in one go");
99 let mut vec = Vec::with_capacity(size);
100 unsafe {
104 ic0::stable64_read(vec.as_ptr() as i64, 0, size as i64);
105 vec.set_len(size);
106 }
107 vec
108}
109
110#[derive(Debug)]
119pub struct StableIO<M: StableMemory = CanisterStableMemory> {
120 offset: u64,
122
123 capacity: u64,
125
126 memory: M,
128}
129
130impl Default for StableIO {
131 fn default() -> Self {
132 Self::with_memory(CanisterStableMemory::default(), 0)
133 }
134}
135
136impl<M: StableMemory> StableIO<M> {
142 pub fn with_memory(memory: M, offset: u64) -> Self {
144 let capacity = memory.stable_size();
145 Self {
146 offset,
147 capacity,
148 memory,
149 }
150 }
151
152 pub fn offset(&self) -> u64 {
154 self.offset
155 }
156
157 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
159 let old_page_count = self.memory.stable_grow(new_pages)?;
160 self.capacity = old_page_count + new_pages;
161 Ok(())
162 }
163
164 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
170 let required_capacity_bytes = self.offset + buf.len() as u64;
171 let required_capacity_pages =
172 (required_capacity_bytes + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES;
173 let current_pages = self.capacity;
174 let additional_pages_required = required_capacity_pages.saturating_sub(current_pages);
175
176 if additional_pages_required > 0 {
177 self.grow(additional_pages_required)?;
178 }
179
180 self.memory.stable_write(self.offset, buf);
181 self.offset += buf.len() as u64;
182 Ok(buf.len())
183 }
184
185 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
195 let capacity_bytes = self.capacity * WASM_PAGE_SIZE_IN_BYTES;
196 let read_buf = if buf.len() as u64 + self.offset > capacity_bytes {
197 if self.offset < capacity_bytes {
198 &mut buf[..(capacity_bytes - self.offset).try_into().unwrap()]
202 } else {
203 return Err(StableMemoryError::OutOfBounds);
204 }
205 } else {
206 buf
207 };
208 self.memory.stable_read(self.offset, read_buf);
209 self.offset += read_buf.len() as u64;
210 Ok(read_buf.len())
211 }
212
213 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
215 self.offset = match offset {
216 io::SeekFrom::Start(offset) => offset,
217 io::SeekFrom::End(offset) => {
218 ((self.capacity * WASM_PAGE_SIZE_IN_BYTES) as i64 + offset) as u64
219 }
220 io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64,
221 };
222
223 Ok(self.offset)
224 }
225}
226
227impl<M: StableMemory> io::Write for StableIO<M> {
228 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
229 self.write(buf)
230 .map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))
231 }
232
233 fn flush(&mut self) -> Result<(), io::Error> {
234 Ok(())
236 }
237}
238
239impl<M: StableMemory> io::Read for StableIO<M> {
240 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
241 Self::read(self, buf).or(Ok(0)) }
243}
244
245impl<M: StableMemory> io::Seek for StableIO<M> {
246 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
247 self.seek(offset)
248 }
249}
250
251#[derive(Debug)]
262pub struct StableWriter<M: StableMemory = CanisterStableMemory>(StableIO<M>);
263
264#[allow(clippy::derivable_impls)]
265impl Default for StableWriter {
266 #[inline]
267 fn default() -> Self {
268 Self(StableIO::default())
269 }
270}
271
272impl<M: StableMemory> StableWriter<M> {
273 #[inline]
275 pub fn with_memory(memory: M, offset: u64) -> Self {
276 Self(StableIO::<M>::with_memory(memory, offset))
277 }
278
279 #[inline]
281 pub fn offset(&self) -> u64 {
282 self.0.offset()
283 }
284
285 #[inline]
287 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
288 self.0.grow(new_pages)
289 }
290
291 #[inline]
296 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
297 self.0.write(buf)
298 }
299}
300
301impl<M: StableMemory> io::Write for StableWriter<M> {
302 #[inline]
303 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
304 io::Write::write(&mut self.0, buf)
305 }
306
307 #[inline]
308 fn flush(&mut self) -> Result<(), io::Error> {
309 io::Write::flush(&mut self.0)
310 }
311}
312
313impl<M: StableMemory> io::Seek for StableWriter<M> {
314 #[inline]
315 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
316 io::Seek::seek(&mut self.0, pos)
317 }
318}
319
320impl<M: StableMemory> From<StableIO<M>> for StableWriter<M> {
321 fn from(io: StableIO<M>) -> Self {
322 Self(io)
323 }
324}
325
326#[derive(Debug)]
335pub struct BufferedStableWriter<M: StableMemory = CanisterStableMemory> {
336 inner: io::BufWriter<StableWriter<M>>,
337}
338
339impl BufferedStableWriter {
340 pub fn new(buffer_size: usize) -> BufferedStableWriter {
342 BufferedStableWriter::with_writer(buffer_size, StableWriter::default())
343 }
344}
345
346impl<M: StableMemory> BufferedStableWriter<M> {
347 pub fn with_writer(buffer_size: usize, writer: StableWriter<M>) -> BufferedStableWriter<M> {
349 BufferedStableWriter {
350 inner: io::BufWriter::with_capacity(buffer_size, writer),
351 }
352 }
353
354 pub fn offset(&self) -> u64 {
356 self.inner.get_ref().offset()
357 }
358}
359
360impl<M: StableMemory> io::Write for BufferedStableWriter<M> {
361 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
362 self.inner.write(buf)
363 }
364
365 fn flush(&mut self) -> io::Result<()> {
366 self.inner.flush()
367 }
368}
369
370impl<M: StableMemory> io::Seek for BufferedStableWriter<M> {
371 #[inline]
372 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
373 io::Seek::seek(&mut self.inner, pos)
374 }
375}
376
377#[derive(Debug)]
381pub struct StableReader<M: StableMemory = CanisterStableMemory>(StableIO<M>);
382
383#[allow(clippy::derivable_impls)]
384impl Default for StableReader {
385 fn default() -> Self {
386 Self(StableIO::default())
387 }
388}
389
390impl<M: StableMemory> StableReader<M> {
391 #[inline]
393 pub fn with_memory(memory: M, offset: u64) -> Self {
394 Self(StableIO::<M>::with_memory(memory, offset))
395 }
396
397 #[inline]
399 pub fn offset(&self) -> u64 {
400 self.0.offset()
401 }
402
403 #[inline]
412 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
413 self.0.read(buf)
414 }
415}
416
417impl<M: StableMemory> io::Read for StableReader<M> {
418 #[inline]
419 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
420 io::Read::read(&mut self.0, buf)
421 }
422}
423
424impl<M: StableMemory> io::Seek for StableReader<M> {
425 #[inline]
426 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
427 io::Seek::seek(&mut self.0, pos)
428 }
429}
430
431impl<M: StableMemory> From<StableIO<M>> for StableReader<M> {
432 fn from(io: StableIO<M>) -> Self {
433 Self(io)
434 }
435}
436
437#[derive(Debug)]
439pub struct BufferedStableReader<M: StableMemory = CanisterStableMemory> {
440 inner: io::BufReader<StableReader<M>>,
441}
442
443impl BufferedStableReader {
444 pub fn new(buffer_size: usize) -> BufferedStableReader {
446 BufferedStableReader::with_reader(buffer_size, StableReader::default())
447 }
448}
449
450impl<M: StableMemory> BufferedStableReader<M> {
451 pub fn with_reader(buffer_size: usize, reader: StableReader<M>) -> BufferedStableReader<M> {
453 BufferedStableReader {
454 inner: io::BufReader::with_capacity(buffer_size, reader),
455 }
456 }
457
458 pub fn offset(&self) -> u64 {
460 self.inner.get_ref().offset()
461 }
462}
463
464impl<M: StableMemory> io::Read for BufferedStableReader<M> {
465 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
466 self.inner.read(buf)
467 }
468}
469
470impl<M: StableMemory> io::Seek for BufferedStableReader<M> {
471 #[inline]
472 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
473 io::Seek::seek(&mut self.inner, pos)
474 }
475}