1mod canister;
6
7pub use canister::CanisterStableMemory;
8use std::{error, fmt, io};
9
10pub const WASM_PAGE_SIZE_IN_BYTES: u64 = 64 * 1024; static CANISTER_STABLE_MEMORY: CanisterStableMemory = CanisterStableMemory {};
14
15pub trait StableMemory {
17 fn stable_size(&self) -> u64;
19
20 fn stable_grow(&self, new_pages: u64) -> Result<u64, StableMemoryError>;
27
28 fn stable_write(&self, offset: u64, buf: &[u8]);
33
34 fn stable_read(&self, offset: u64, buf: &mut [u8]);
36}
37
38#[derive(Debug)]
40pub enum StableMemoryError {
41 OutOfMemory,
43 OutOfBounds,
45}
46
47impl fmt::Display for StableMemoryError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::OutOfMemory => f.write_str("Out of memory"),
51 Self::OutOfBounds => f.write_str("Read exceeds allocated memory"),
52 }
53 }
54}
55
56impl error::Error for StableMemoryError {}
57
58pub fn stable_size() -> u64 {
60 CANISTER_STABLE_MEMORY.stable_size()
61}
62
63pub fn stable_grow(new_pages: u64) -> Result<u64, StableMemoryError> {
70 CANISTER_STABLE_MEMORY.stable_grow(new_pages)
71}
72
73pub fn stable_write(offset: u64, buf: &[u8]) {
78 CANISTER_STABLE_MEMORY.stable_write(offset, buf);
79}
80
81pub fn stable_read(offset: u64, buf: &mut [u8]) {
83 CANISTER_STABLE_MEMORY.stable_read(offset, buf);
84}
85
86pub fn stable_bytes() -> Vec<u8> {
94 let size = (stable_size() << 16)
95 .try_into()
96 .expect("overflow: stable memory too large to read in one go");
97 let mut vec = Vec::with_capacity(size);
98 ic0::stable64_read_uninit(&mut vec.spare_capacity_mut()[..size], 0);
99 unsafe {
101 vec.set_len(size);
102 }
103 vec
104}
105
106#[derive(Debug)]
115pub struct StableIO<M: StableMemory = CanisterStableMemory> {
116 offset: u64,
118
119 capacity: u64,
121
122 memory: M,
124}
125
126impl Default for StableIO {
127 fn default() -> Self {
128 Self::with_memory(CanisterStableMemory::default(), 0)
129 }
130}
131
132impl<M: StableMemory> StableIO<M> {
133 pub fn with_memory(memory: M, offset: u64) -> Self {
135 let capacity = memory.stable_size();
136 Self {
137 offset,
138 capacity,
139 memory,
140 }
141 }
142
143 pub fn offset(&self) -> u64 {
145 self.offset
146 }
147
148 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
150 let old_page_count = self.memory.stable_grow(new_pages)?;
151 self.capacity = old_page_count + new_pages;
152 Ok(())
153 }
154
155 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
161 let required_capacity_bytes = self.offset + buf.len() as u64;
162 let required_capacity_pages = required_capacity_bytes.div_ceil(WASM_PAGE_SIZE_IN_BYTES);
163 let current_pages = self.capacity;
164 let additional_pages_required = required_capacity_pages.saturating_sub(current_pages);
165
166 if additional_pages_required > 0 {
167 self.grow(additional_pages_required)?;
168 }
169
170 self.memory.stable_write(self.offset, buf);
171 self.offset += buf.len() as u64;
172 Ok(buf.len())
173 }
174
175 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
185 let capacity_bytes = self.capacity * WASM_PAGE_SIZE_IN_BYTES;
186 let read_buf = if buf.len() as u64 + self.offset > capacity_bytes {
187 if self.offset < capacity_bytes {
188 &mut buf[..(capacity_bytes - self.offset).try_into().unwrap()]
192 } else {
193 return Err(StableMemoryError::OutOfBounds);
194 }
195 } else {
196 buf
197 };
198 self.memory.stable_read(self.offset, read_buf);
199 self.offset += read_buf.len() as u64;
200 Ok(read_buf.len())
201 }
202
203 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
205 self.offset = match offset {
206 io::SeekFrom::Start(offset) => offset,
207 io::SeekFrom::End(offset) => {
208 ((self.capacity * WASM_PAGE_SIZE_IN_BYTES) as i64 + offset) as u64
209 }
210 io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64,
211 };
212
213 Ok(self.offset)
214 }
215}
216
217impl<M: StableMemory> io::Write for StableIO<M> {
218 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
219 self.write(buf)
220 .map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))
221 }
222
223 fn flush(&mut self) -> Result<(), io::Error> {
224 Ok(())
226 }
227}
228
229impl<M: StableMemory> io::Read for StableIO<M> {
230 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
231 Self::read(self, buf).or(Ok(0)) }
233}
234
235impl<M: StableMemory> io::Seek for StableIO<M> {
236 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
237 self.seek(offset)
238 }
239}
240
241#[derive(Debug)]
252pub struct StableWriter<M: StableMemory = CanisterStableMemory>(StableIO<M>);
253
254#[allow(clippy::derivable_impls)]
255impl Default for StableWriter {
256 #[inline]
257 fn default() -> Self {
258 Self(StableIO::default())
259 }
260}
261
262impl<M: StableMemory> StableWriter<M> {
263 #[inline]
265 pub fn with_memory(memory: M, offset: u64) -> Self {
266 Self(StableIO::<M>::with_memory(memory, offset))
267 }
268
269 #[inline]
271 pub fn offset(&self) -> u64 {
272 self.0.offset()
273 }
274
275 #[inline]
277 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
278 self.0.grow(new_pages)
279 }
280
281 #[inline]
286 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
287 self.0.write(buf)
288 }
289}
290
291impl<M: StableMemory> io::Write for StableWriter<M> {
292 #[inline]
293 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
294 io::Write::write(&mut self.0, buf)
295 }
296
297 #[inline]
298 fn flush(&mut self) -> Result<(), io::Error> {
299 io::Write::flush(&mut self.0)
300 }
301}
302
303impl<M: StableMemory> io::Seek for StableWriter<M> {
304 #[inline]
305 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
306 io::Seek::seek(&mut self.0, pos)
307 }
308}
309
310impl<M: StableMemory> From<StableIO<M>> for StableWriter<M> {
311 fn from(io: StableIO<M>) -> Self {
312 Self(io)
313 }
314}
315
316#[derive(Debug)]
325pub struct BufferedStableWriter<M: StableMemory = CanisterStableMemory> {
326 inner: io::BufWriter<StableWriter<M>>,
327}
328
329impl BufferedStableWriter {
330 pub fn new(buffer_size: usize) -> BufferedStableWriter {
332 BufferedStableWriter::with_writer(buffer_size, StableWriter::default())
333 }
334}
335
336impl<M: StableMemory> BufferedStableWriter<M> {
337 pub fn with_writer(buffer_size: usize, writer: StableWriter<M>) -> BufferedStableWriter<M> {
339 BufferedStableWriter {
340 inner: io::BufWriter::with_capacity(buffer_size, writer),
341 }
342 }
343
344 pub fn offset(&self) -> u64 {
346 self.inner.get_ref().offset()
347 }
348}
349
350impl<M: StableMemory> io::Write for BufferedStableWriter<M> {
351 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
352 self.inner.write(buf)
353 }
354
355 fn flush(&mut self) -> io::Result<()> {
356 self.inner.flush()
357 }
358}
359
360impl<M: StableMemory> io::Seek for BufferedStableWriter<M> {
361 #[inline]
362 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
363 io::Seek::seek(&mut self.inner, pos)
364 }
365}
366
367#[derive(Debug)]
371pub struct StableReader<M: StableMemory = CanisterStableMemory>(StableIO<M>);
372
373#[allow(clippy::derivable_impls)]
374impl Default for StableReader {
375 fn default() -> Self {
376 Self(StableIO::default())
377 }
378}
379
380impl<M: StableMemory> StableReader<M> {
381 #[inline]
383 pub fn with_memory(memory: M, offset: u64) -> Self {
384 Self(StableIO::<M>::with_memory(memory, offset))
385 }
386
387 #[inline]
389 pub fn offset(&self) -> u64 {
390 self.0.offset()
391 }
392
393 #[inline]
402 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
403 self.0.read(buf)
404 }
405}
406
407impl<M: StableMemory> io::Read for StableReader<M> {
408 #[inline]
409 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
410 io::Read::read(&mut self.0, buf)
411 }
412}
413
414impl<M: StableMemory> io::Seek for StableReader<M> {
415 #[inline]
416 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
417 io::Seek::seek(&mut self.0, pos)
418 }
419}
420
421impl<M: StableMemory> From<StableIO<M>> for StableReader<M> {
422 fn from(io: StableIO<M>) -> Self {
423 Self(io)
424 }
425}
426
427#[derive(Debug)]
429pub struct BufferedStableReader<M: StableMemory = CanisterStableMemory> {
430 inner: io::BufReader<StableReader<M>>,
431}
432
433impl BufferedStableReader {
434 pub fn new(buffer_size: usize) -> BufferedStableReader {
436 BufferedStableReader::with_reader(buffer_size, StableReader::default())
437 }
438}
439
440impl<M: StableMemory> BufferedStableReader<M> {
441 pub fn with_reader(buffer_size: usize, reader: StableReader<M>) -> BufferedStableReader<M> {
443 BufferedStableReader {
444 inner: io::BufReader::with_capacity(buffer_size, reader),
445 }
446 }
447
448 pub fn offset(&self) -> u64 {
450 self.inner.get_ref().offset()
451 }
452}
453
454impl<M: StableMemory> io::Read for BufferedStableReader<M> {
455 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
456 self.inner.read(buf)
457 }
458}
459
460impl<M: StableMemory> io::Seek for BufferedStableReader<M> {
461 #[inline]
462 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
463 io::Seek::seek(&mut self.inner, pos)
464 }
465}