solana_program_runtime/
mem_pool.rs1use {
2 solana_compute_budget::{
3 compute_budget::{MAX_CALL_DEPTH, MAX_INSTRUCTION_STACK_DEPTH, STACK_FRAME_SIZE},
4 compute_budget_limits::{MAX_HEAP_FRAME_BYTES, MIN_HEAP_FRAME_BYTES},
5 },
6 solana_sbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN},
7 std::array,
8};
9
10trait Reset {
11 fn reset(&mut self);
12}
13
14struct Pool<T: Reset, const SIZE: usize> {
15 items: [Option<T>; SIZE],
16 next_empty: usize,
17}
18
19impl<T: Reset, const SIZE: usize> Pool<T, SIZE> {
20 fn new(items: [T; SIZE]) -> Self {
21 Self {
22 items: items.map(|i| Some(i)),
23 next_empty: SIZE,
24 }
25 }
26
27 fn len(&self) -> usize {
28 SIZE
29 }
30
31 fn get(&mut self) -> Option<T> {
32 if self.next_empty == 0 {
33 return None;
34 }
35 self.next_empty = self.next_empty.saturating_sub(1);
36 self.items
37 .get_mut(self.next_empty)
38 .and_then(|item| item.take())
39 }
40
41 fn put(&mut self, mut value: T) -> bool {
42 self.items
43 .get_mut(self.next_empty)
44 .map(|item| {
45 value.reset();
46 item.replace(value);
47 self.next_empty = self.next_empty.saturating_add(1);
48 true
49 })
50 .unwrap_or(false)
51 }
52}
53
54impl Reset for AlignedMemory<{ HOST_ALIGN }> {
55 fn reset(&mut self) {
56 self.as_slice_mut().fill(0)
57 }
58}
59
60pub struct VmMemoryPool {
61 stack: Pool<AlignedMemory<{ HOST_ALIGN }>, MAX_INSTRUCTION_STACK_DEPTH>,
62 heap: Pool<AlignedMemory<{ HOST_ALIGN }>, MAX_INSTRUCTION_STACK_DEPTH>,
63}
64
65impl VmMemoryPool {
66 pub fn new() -> Self {
67 Self {
68 stack: Pool::new(array::from_fn(|_| {
69 AlignedMemory::zero_filled(STACK_FRAME_SIZE * MAX_CALL_DEPTH)
70 })),
71 heap: Pool::new(array::from_fn(|_| {
72 AlignedMemory::zero_filled(MAX_HEAP_FRAME_BYTES as usize)
73 })),
74 }
75 }
76
77 pub fn stack_len(&self) -> usize {
78 self.stack.len()
79 }
80
81 pub fn heap_len(&self) -> usize {
82 self.heap.len()
83 }
84
85 pub fn get_stack(&mut self, size: usize) -> AlignedMemory<{ HOST_ALIGN }> {
86 debug_assert!(size == STACK_FRAME_SIZE * MAX_CALL_DEPTH);
87 self.stack
88 .get()
89 .unwrap_or_else(|| AlignedMemory::zero_filled(size))
90 }
91
92 pub fn put_stack(&mut self, stack: AlignedMemory<{ HOST_ALIGN }>) -> bool {
93 self.stack.put(stack)
94 }
95
96 pub fn get_heap(&mut self, heap_size: u32) -> AlignedMemory<{ HOST_ALIGN }> {
97 debug_assert!((MIN_HEAP_FRAME_BYTES..=MAX_HEAP_FRAME_BYTES).contains(&heap_size));
98 self.heap
99 .get()
100 .unwrap_or_else(|| AlignedMemory::zero_filled(MAX_HEAP_FRAME_BYTES as usize))
101 }
102
103 pub fn put_heap(&mut self, heap: AlignedMemory<{ HOST_ALIGN }>) -> bool {
104 let heap_size = heap.len();
105 debug_assert!(
106 heap_size >= MIN_HEAP_FRAME_BYTES as usize
107 && heap_size <= MAX_HEAP_FRAME_BYTES as usize
108 );
109 self.heap.put(heap)
110 }
111}
112
113impl Default for VmMemoryPool {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122
123 #[derive(Debug, Eq, PartialEq)]
124 struct Item(u8, u8);
125 impl Reset for Item {
126 fn reset(&mut self) {
127 self.1 = 0;
128 }
129 }
130
131 #[test]
132 fn test_pool() {
133 let mut pool = Pool::<Item, 2>::new([Item(0, 1), Item(1, 1)]);
134 assert_eq!(pool.get(), Some(Item(1, 1)));
135 assert_eq!(pool.get(), Some(Item(0, 1)));
136 assert_eq!(pool.get(), None);
137 pool.put(Item(1, 1));
138 assert_eq!(pool.get(), Some(Item(1, 0)));
139 pool.put(Item(2, 2));
140 pool.put(Item(3, 3));
141 assert!(!pool.put(Item(4, 4)));
142 assert_eq!(pool.get(), Some(Item(3, 0)));
143 assert_eq!(pool.get(), Some(Item(2, 0)));
144 assert_eq!(pool.get(), None);
145 }
146}