ic_memory/runtime/
backing.rs1use ic_stable_structures::{Memory, memory_manager::VirtualMemory};
2use std::{cell::RefCell, rc::Rc};
3
4pub(super) struct GrowthState<M: Memory> {
7 pub backing: Rc<M>,
8 pub bucket_size_pages: u16,
9 pub allocated_buckets: RefCell<u16>,
10}
11
12pub struct RuntimeMemory<M: Memory> {
28 pub(super) memory: VirtualMemory<Rc<M>>,
29 pub(super) growth: Rc<GrowthState<M>>,
30}
31
32impl<M: Memory> Clone for RuntimeMemory<M> {
33 fn clone(&self) -> Self {
34 Self {
35 memory: self.memory.clone(),
36 growth: Rc::clone(&self.growth),
37 }
38 }
39}
40
41impl<M: Memory> RuntimeMemory<M> {
42 pub fn grow(&self, pages: u64) -> Result<u64, super::RuntimeGrowError> {
49 use super::RuntimeGrowError;
50 let mut allocated = self
51 .growth
52 .allocated_buckets
53 .try_borrow_mut()
54 .map_err(|_| RuntimeGrowError::ReentrantAccess)?;
55 let old_pages = self.memory.size();
56 let new_pages = old_pages
57 .checked_add(pages)
58 .ok_or(RuntimeGrowError::ArithmeticOverflow)?;
59 let bucket_pages = u64::from(self.growth.bucket_size_pages);
60 let extra = new_pages.div_ceil(bucket_pages) - old_pages.div_ceil(bucket_pages);
61 let total = u64::from(*allocated)
62 .checked_add(extra)
63 .ok_or(RuntimeGrowError::ArithmeticOverflow)?;
64 if total > u64::from(super::layout::BUCKET_CAPACITY) {
65 return Err(RuntimeGrowError::BucketExhausted {
66 required_buckets: total,
67 capacity: super::layout::BUCKET_CAPACITY,
68 });
69 }
70 let total_buckets =
71 u16::try_from(total).map_err(|_| RuntimeGrowError::ArithmeticOverflow)?;
72 let required_pages = 1 + total * bucket_pages;
73 let physical_pages = self.growth.backing.size();
74 if required_pages > physical_pages
75 && self.growth.backing.grow(required_pages - physical_pages) < 0
76 {
77 return Err(RuntimeGrowError::BackingRefused {
78 additional_pages: required_pages - physical_pages,
79 });
80 }
81 let previous =
82 u64::try_from(self.memory.grow(pages)).map_err(|_| RuntimeGrowError::ManagerRefused)?;
83 *allocated = total_buckets;
84 Ok(previous)
85 }
86}
87
88impl<M: Memory> Memory for RuntimeMemory<M> {
89 fn size(&self) -> u64 {
90 self.memory.size()
91 }
92 fn grow(&self, pages: u64) -> i64 {
93 Self::grow(self, pages).map_or(-1, |previous| i64::try_from(previous).unwrap_or(-1))
96 }
97 fn read(&self, offset: u64, dst: &mut [u8]) {
98 self.memory.read(offset, dst);
99 }
100 #[expect(
101 unsafe_code,
102 reason = "delegate the upstream raw-read contract unchanged"
103 )]
104 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
105 unsafe { self.memory.read_unsafe(offset, dst, count) }
110 }
111 fn write(&self, offset: u64, src: &[u8]) {
112 self.memory.write(offset, src);
113 }
114}