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 unsafe {
102 ic0::stable64_read(vec.as_ptr() as u64, 0, size as u64);
103 vec.set_len(size);
104 }
105 vec
106}
107
108#[derive(Debug)]
117pub struct StableIO<M: StableMemory = CanisterStableMemory> {
118 offset: u64,
120
121 capacity: u64,
123
124 memory: M,
126}
127
128impl Default for StableIO {
129 fn default() -> Self {
130 Self::with_memory(CanisterStableMemory::default(), 0)
131 }
132}
133
134impl<M: StableMemory> StableIO<M> {
135 pub fn with_memory(memory: M, offset: u64) -> Self {
137 let capacity = memory.stable_size();
138 Self {
139 offset,
140 capacity,
141 memory,
142 }
143 }
144
145 pub fn offset(&self) -> u64 {
147 self.offset
148 }
149
150 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
152 let old_page_count = self.memory.stable_grow(new_pages)?;
153 self.capacity = old_page_count + new_pages;
154 Ok(())
155 }
156
157 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
163 let required_capacity_bytes = self.offset + buf.len() as u64;
164 let required_capacity_pages = required_capacity_bytes.div_ceil(WASM_PAGE_SIZE_IN_BYTES);
165 let current_pages = self.capacity;
166 let additional_pages_required = required_capacity_pages.saturating_sub(current_pages);
167
168 if additional_pages_required > 0 {
169 self.grow(additional_pages_required)?;
170 }
171
172 self.memory.stable_write(self.offset, buf);
173 self.offset += buf.len() as u64;
174 Ok(buf.len())
175 }
176
177 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
187 let capacity_bytes = self.capacity * WASM_PAGE_SIZE_IN_BYTES;
188 let read_buf = if buf.len() as u64 + self.offset > capacity_bytes {
189 if self.offset < capacity_bytes {
190 &mut buf[..(capacity_bytes - self.offset).try_into().unwrap()]
194 } else {
195 return Err(StableMemoryError::OutOfBounds);
196 }
197 } else {
198 buf
199 };
200 self.memory.stable_read(self.offset, read_buf);
201 self.offset += read_buf.len() as u64;
202 Ok(read_buf.len())
203 }
204
205 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
207 self.offset = match offset {
208 io::SeekFrom::Start(offset) => offset,
209 io::SeekFrom::End(offset) => {
210 ((self.capacity * WASM_PAGE_SIZE_IN_BYTES) as i64 + offset) as u64
211 }
212 io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64,
213 };
214
215 Ok(self.offset)
216 }
217}
218
219impl<M: StableMemory> io::Write for StableIO<M> {
220 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
221 self.write(buf)
222 .map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))
223 }
224
225 fn flush(&mut self) -> Result<(), io::Error> {
226 Ok(())
228 }
229}
230
231impl<M: StableMemory> io::Read for StableIO<M> {
232 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
233 Self::read(self, buf).or(Ok(0)) }
235}
236
237impl<M: StableMemory> io::Seek for StableIO<M> {
238 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
239 self.seek(offset)
240 }
241}
242
243#[derive(Debug)]
254pub struct StableWriter<M: StableMemory = CanisterStableMemory>(StableIO<M>);
255
256#[allow(clippy::derivable_impls)]
257impl Default for StableWriter {
258 #[inline]
259 fn default() -> Self {
260 Self(StableIO::default())
261 }
262}
263
264impl<M: StableMemory> StableWriter<M> {
265 #[inline]
267 pub fn with_memory(memory: M, offset: u64) -> Self {
268 Self(StableIO::<M>::with_memory(memory, offset))
269 }
270
271 #[inline]
273 pub fn offset(&self) -> u64 {
274 self.0.offset()
275 }
276
277 #[inline]
279 pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> {
280 self.0.grow(new_pages)
281 }
282
283 #[inline]
288 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
289 self.0.write(buf)
290 }
291}
292
293impl<M: StableMemory> io::Write for StableWriter<M> {
294 #[inline]
295 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
296 io::Write::write(&mut self.0, buf)
297 }
298
299 #[inline]
300 fn flush(&mut self) -> Result<(), io::Error> {
301 io::Write::flush(&mut self.0)
302 }
303}
304
305impl<M: StableMemory> io::Seek for StableWriter<M> {
306 #[inline]
307 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
308 io::Seek::seek(&mut self.0, pos)
309 }
310}
311
312impl<M: StableMemory> From<StableIO<M>> for StableWriter<M> {
313 fn from(io: StableIO<M>) -> Self {
314 Self(io)
315 }
316}
317
318#[derive(Debug)]
327pub struct BufferedStableWriter<M: StableMemory = CanisterStableMemory> {
328 inner: io::BufWriter<StableWriter<M>>,
329}
330
331impl BufferedStableWriter {
332 pub fn new(buffer_size: usize) -> BufferedStableWriter {
334 BufferedStableWriter::with_writer(buffer_size, StableWriter::default())
335 }
336}
337
338impl<M: StableMemory> BufferedStableWriter<M> {
339 pub fn with_writer(buffer_size: usize, writer: StableWriter<M>) -> BufferedStableWriter<M> {
341 BufferedStableWriter {
342 inner: io::BufWriter::with_capacity(buffer_size, writer),
343 }
344 }
345
346 pub fn offset(&self) -> u64 {
348 self.inner.get_ref().offset()
349 }
350}
351
352impl<M: StableMemory> io::Write for BufferedStableWriter<M> {
353 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
354 self.inner.write(buf)
355 }
356
357 fn flush(&mut self) -> io::Result<()> {
358 self.inner.flush()
359 }
360}
361
362impl<M: StableMemory> io::Seek for BufferedStableWriter<M> {
363 #[inline]
364 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
365 io::Seek::seek(&mut self.inner, pos)
366 }
367}
368
369#[derive(Debug)]
373pub struct StableReader<M: StableMemory = CanisterStableMemory>(StableIO<M>);
374
375#[allow(clippy::derivable_impls)]
376impl Default for StableReader {
377 fn default() -> Self {
378 Self(StableIO::default())
379 }
380}
381
382impl<M: StableMemory> StableReader<M> {
383 #[inline]
385 pub fn with_memory(memory: M, offset: u64) -> Self {
386 Self(StableIO::<M>::with_memory(memory, offset))
387 }
388
389 #[inline]
391 pub fn offset(&self) -> u64 {
392 self.0.offset()
393 }
394
395 #[inline]
404 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
405 self.0.read(buf)
406 }
407}
408
409impl<M: StableMemory> io::Read for StableReader<M> {
410 #[inline]
411 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
412 io::Read::read(&mut self.0, buf)
413 }
414}
415
416impl<M: StableMemory> io::Seek for StableReader<M> {
417 #[inline]
418 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
419 io::Seek::seek(&mut self.0, pos)
420 }
421}
422
423impl<M: StableMemory> From<StableIO<M>> for StableReader<M> {
424 fn from(io: StableIO<M>) -> Self {
425 Self(io)
426 }
427}
428
429#[derive(Debug)]
431pub struct BufferedStableReader<M: StableMemory = CanisterStableMemory> {
432 inner: io::BufReader<StableReader<M>>,
433}
434
435impl BufferedStableReader {
436 pub fn new(buffer_size: usize) -> BufferedStableReader {
438 BufferedStableReader::with_reader(buffer_size, StableReader::default())
439 }
440}
441
442impl<M: StableMemory> BufferedStableReader<M> {
443 pub fn with_reader(buffer_size: usize, reader: StableReader<M>) -> BufferedStableReader<M> {
445 BufferedStableReader {
446 inner: io::BufReader::with_capacity(buffer_size, reader),
447 }
448 }
449
450 pub fn offset(&self) -> u64 {
452 self.inner.get_ref().offset()
453 }
454}
455
456impl<M: StableMemory> io::Read for BufferedStableReader<M> {
457 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
458 self.inner.read(buf)
459 }
460}
461
462impl<M: StableMemory> io::Seek for BufferedStableReader<M> {
463 #[inline]
464 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
465 io::Seek::seek(&mut self.inner, pos)
466 }
467}