1mod canister;
6mod private;
7#[cfg(test)]
8mod tests;
9
10pub use canister::CanisterStableMemory;
11use std::{error, fmt, io};
12
13pub const WASM_PAGE_SIZE_IN_BYTES: usize = 64 * 1024; static CANISTER_STABLE_MEMORY: CanisterStableMemory = CanisterStableMemory {};
17
18pub trait StableMemory {
20 fn stable_size(&self) -> u32;
22
23 fn stable64_size(&self) -> u64;
25
26 fn stable_grow(&self, new_pages: u32) -> Result<u32, StableMemoryError>;
33
34 fn stable64_grow(&self, new_pages: u64) -> Result<u64, StableMemoryError>;
36
37 fn stable_write(&self, offset: u32, buf: &[u8]);
42
43 fn stable64_write(&self, offset: u64, buf: &[u8]);
45
46 fn stable_read(&self, offset: u32, buf: &mut [u8]);
48
49 fn stable64_read(&self, offset: u64, buf: &mut [u8]);
51}
52
53pub fn stable_size() -> u32 {
55 CANISTER_STABLE_MEMORY.stable_size()
56}
57
58pub fn stable64_size() -> u64 {
60 CANISTER_STABLE_MEMORY.stable64_size()
61}
62
63#[derive(Debug)]
65pub enum StableMemoryError {
66 OutOfMemory,
68 OutOfBounds,
70}
71
72impl fmt::Display for StableMemoryError {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 Self::OutOfMemory => f.write_str("Out of memory"),
76 Self::OutOfBounds => f.write_str("Read exceeds allocated memory"),
77 }
78 }
79}
80
81impl error::Error for StableMemoryError {}
82
83pub fn stable_grow(new_pages: u32) -> Result<u32, StableMemoryError> {
90 CANISTER_STABLE_MEMORY.stable_grow(new_pages)
91}
92
93pub fn stable64_grow(new_pages: u64) -> Result<u64, StableMemoryError> {
95 CANISTER_STABLE_MEMORY.stable64_grow(new_pages)
96}
97
98pub fn stable_write(offset: u32, buf: &[u8]) {
103 CANISTER_STABLE_MEMORY.stable_write(offset, buf)
104}
105
106pub fn stable64_write(offset: u64, buf: &[u8]) {
108 CANISTER_STABLE_MEMORY.stable64_write(offset, buf)
109}
110
111pub fn stable_read(offset: u32, buf: &mut [u8]) {
113 CANISTER_STABLE_MEMORY.stable_read(offset, buf)
114}
115
116pub fn stable64_read(offset: u64, buf: &mut [u8]) {
118 CANISTER_STABLE_MEMORY.stable64_read(offset, buf)
119}
120
121pub fn stable_bytes() -> Vec<u8> {
125 let size = (stable_size() as usize) << 16;
126 let mut vec = Vec::with_capacity(size);
127 unsafe {
131 ic0::stable_read(vec.as_ptr() as i32, 0, size as i32);
132 vec.set_len(size);
133 }
134 vec
135}
136
137#[derive(Debug)]
146pub struct StableIO<M: StableMemory = CanisterStableMemory, A: private::AddressSize = u32> {
147 offset: A,
149
150 capacity: A,
152
153 memory: M,
155}
156
157impl Default for StableIO {
158 fn default() -> Self {
159 Self::with_memory(CanisterStableMemory::default(), 0)
160 }
161}
162
163macro_rules! impl_stable_io {
168 ($address:ty) => {
169 impl<M: private::StableMemory_<$address> + StableMemory> StableIO<M, $address> {
170 pub fn with_memory(memory: M, offset: $address) -> Self {
172 let capacity = memory.stable_size_();
173
174 Self {
175 offset,
176 capacity,
177 memory,
178 }
179 }
180
181 pub fn offset(&self) -> $address {
183 self.offset
184 }
185
186 pub fn grow(&mut self, new_pages: $address) -> Result<(), StableMemoryError> {
188 let old_page_count = self.memory.stable_grow_(new_pages)?;
189 self.capacity = old_page_count + new_pages;
190 Ok(())
191 }
192
193 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
198 let required_capacity_bytes = self.offset + buf.len() as $address;
199 let required_capacity_pages =
200 ((required_capacity_bytes + WASM_PAGE_SIZE_IN_BYTES as $address - 1)
201 / WASM_PAGE_SIZE_IN_BYTES as $address);
202 let current_pages = self.capacity;
203 let additional_pages_required =
204 required_capacity_pages.saturating_sub(current_pages);
205
206 if additional_pages_required > 0 {
207 self.grow(additional_pages_required)?;
208 }
209
210 self.memory.stable_write_(self.offset, buf);
211 self.offset += buf.len() as $address;
212 Ok(buf.len())
213 }
214
215 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
224 let capacity_bytes = self.capacity * WASM_PAGE_SIZE_IN_BYTES as $address;
225 let read_buf = if buf.len() as $address + self.offset > capacity_bytes {
226 if self.offset < capacity_bytes {
227 &mut buf[..(capacity_bytes - self.offset) as usize]
228 } else {
229 return Err(StableMemoryError::OutOfBounds);
230 }
231 } else {
232 buf
233 };
234 self.memory.stable_read_(self.offset, read_buf);
235 self.offset += read_buf.len() as $address;
236 Ok(read_buf.len())
237 }
238
239 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
241 self.offset = match offset {
242 io::SeekFrom::Start(offset) => offset as $address,
243 io::SeekFrom::End(offset) => {
244 ((self.capacity * WASM_PAGE_SIZE_IN_BYTES as $address) as i64 + offset)
245 as $address
246 }
247 io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as $address,
248 };
249
250 Ok(self.offset as u64)
251 }
252 }
253
254 impl<M: private::StableMemory_<$address> + StableMemory> io::Write
255 for StableIO<M, $address>
256 {
257 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
258 self.write(buf)
259 .map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))
260 }
261
262 fn flush(&mut self) -> Result<(), io::Error> {
263 Ok(())
265 }
266 }
267
268 impl<M: private::StableMemory_<$address> + StableMemory> io::Read
269 for StableIO<M, $address>
270 {
271 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
272 Self::read(self, buf).or(Ok(0)) }
274 }
275
276 impl<M: private::StableMemory_<$address> + StableMemory> io::Seek
277 for StableIO<M, $address>
278 {
279 fn seek(&mut self, offset: io::SeekFrom) -> io::Result<u64> {
280 self.seek(offset)
281 }
282 }
283 };
284}
285
286impl_stable_io!(u32);
287impl_stable_io!(u64);
288
289#[derive(Debug)]
297pub struct StableWriter<M: StableMemory = CanisterStableMemory>(StableIO<M, u32>);
298
299#[allow(clippy::derivable_impls)]
300impl Default for StableWriter {
301 #[inline]
302 fn default() -> Self {
303 Self(StableIO::default())
304 }
305}
306
307impl<M: StableMemory> StableWriter<M> {
308 #[inline]
310 pub fn with_memory(memory: M, offset: usize) -> Self {
311 Self(StableIO::<M, u32>::with_memory(memory, offset as u32))
312 }
313
314 #[inline]
316 pub fn offset(&self) -> usize {
317 self.0.offset() as usize
318 }
319
320 #[inline]
322 pub fn grow(&mut self, new_pages: u32) -> Result<(), StableMemoryError> {
323 self.0.grow(new_pages)
324 }
325
326 #[inline]
331 pub fn write(&mut self, buf: &[u8]) -> Result<usize, StableMemoryError> {
332 self.0.write(buf)
333 }
334}
335
336impl<M: StableMemory> io::Write for StableWriter<M> {
337 #[inline]
338 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
339 io::Write::write(&mut self.0, buf)
340 }
341
342 #[inline]
343 fn flush(&mut self) -> Result<(), io::Error> {
344 io::Write::flush(&mut self.0)
345 }
346}
347
348impl<M: StableMemory> io::Seek for StableWriter<M> {
349 #[inline]
350 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
351 io::Seek::seek(&mut self.0, pos)
352 }
353}
354
355impl<M: StableMemory> From<StableIO<M>> for StableWriter<M> {
356 fn from(io: StableIO<M>) -> Self {
357 Self(io)
358 }
359}
360
361#[derive(Debug)]
370pub struct BufferedStableWriter<M: StableMemory = CanisterStableMemory> {
371 inner: io::BufWriter<StableWriter<M>>,
372}
373
374impl BufferedStableWriter {
375 pub fn new(buffer_size: usize) -> BufferedStableWriter {
377 BufferedStableWriter::with_writer(buffer_size, StableWriter::default())
378 }
379}
380
381impl<M: StableMemory> BufferedStableWriter<M> {
382 pub fn with_writer(buffer_size: usize, writer: StableWriter<M>) -> BufferedStableWriter<M> {
384 BufferedStableWriter {
385 inner: io::BufWriter::with_capacity(buffer_size, writer),
386 }
387 }
388
389 pub fn offset(&self) -> usize {
391 self.inner.get_ref().offset()
392 }
393}
394
395impl<M: StableMemory> io::Write for BufferedStableWriter<M> {
396 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
397 self.inner.write(buf)
398 }
399
400 fn flush(&mut self) -> io::Result<()> {
401 self.inner.flush()
402 }
403}
404
405impl<M: StableMemory> io::Seek for BufferedStableWriter<M> {
406 #[inline]
407 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
408 io::Seek::seek(&mut self.inner, pos)
409 }
410}
411
412#[derive(Debug)]
416pub struct StableReader<M: StableMemory = CanisterStableMemory>(StableIO<M, u32>);
417
418#[allow(clippy::derivable_impls)]
419impl Default for StableReader {
420 fn default() -> Self {
421 Self(StableIO::default())
422 }
423}
424
425impl<M: StableMemory> StableReader<M> {
426 #[inline]
428 pub fn with_memory(memory: M, offset: usize) -> Self {
429 Self(StableIO::<M, u32>::with_memory(memory, offset as u32))
430 }
431
432 #[inline]
434 pub fn offset(&self) -> usize {
435 self.0.offset() as usize
436 }
437
438 #[inline]
447 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, StableMemoryError> {
448 self.0.read(buf)
449 }
450}
451
452impl<M: StableMemory> io::Read for StableReader<M> {
453 #[inline]
454 fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
455 io::Read::read(&mut self.0, buf)
456 }
457}
458
459impl<M: StableMemory> io::Seek for StableReader<M> {
460 #[inline]
461 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
462 io::Seek::seek(&mut self.0, pos)
463 }
464}
465
466impl<M: StableMemory> From<StableIO<M>> for StableReader<M> {
467 fn from(io: StableIO<M>) -> Self {
468 Self(io)
469 }
470}
471
472#[derive(Debug)]
474pub struct BufferedStableReader<M: StableMemory = CanisterStableMemory> {
475 inner: io::BufReader<StableReader<M>>,
476}
477
478impl BufferedStableReader {
479 pub fn new(buffer_size: usize) -> BufferedStableReader {
481 BufferedStableReader::with_reader(buffer_size, StableReader::default())
482 }
483}
484
485impl<M: StableMemory> BufferedStableReader<M> {
486 pub fn with_reader(buffer_size: usize, reader: StableReader<M>) -> BufferedStableReader<M> {
488 BufferedStableReader {
489 inner: io::BufReader::with_capacity(buffer_size, reader),
490 }
491 }
492
493 pub fn offset(&self) -> usize {
495 self.inner.get_ref().offset()
496 }
497}
498
499impl<M: StableMemory> io::Read for BufferedStableReader<M> {
500 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
501 self.inner.read(buf)
502 }
503}
504
505impl<M: StableMemory> io::Seek for BufferedStableReader<M> {
506 #[inline]
507 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
508 io::Seek::seek(&mut self.inner, pos)
509 }
510}