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