linera_witty/runtime/
memory.rs1use std::borrow::Cow;
7
8use frunk::{hlist, hlist_pat, HList};
9
10use super::{
11 traits::{CabiFreeAlias, CabiReallocAlias},
12 InstanceWithFunction, Runtime, RuntimeError,
13};
14use crate::{Layout, WitType};
15
16#[cfg(test)]
17#[path = "unit_tests/memory.rs"]
18mod tests;
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct GuestPointer(pub(crate) u32);
23
24impl GuestPointer {
25 pub const fn aligned_at(&self, alignment: u32) -> Self {
28 let padding = (-(self.0 as i32) & (alignment as i32 - 1)) as u32;
32
33 GuestPointer(self.0 + padding)
34 }
35
36 pub const fn after<T: WitType>(&self) -> Self {
38 GuestPointer(self.0 + T::SIZE)
39 }
40
41 pub const fn after_padding_for<T: WitType>(&self) -> Self {
44 self.aligned_at(<T::Layout as Layout>::ALIGNMENT)
45 }
46
47 pub const fn index<T: WitType>(&self, index: u32) -> Self {
49 let element_size = GuestPointer(T::SIZE).after_padding_for::<T>();
50
51 GuestPointer(self.0 + index * element_size.0)
52 }
53}
54
55pub trait RuntimeMemory<Instance> {
57 fn read<'instance>(
59 &self,
60 instance: &'instance Instance,
61 location: GuestPointer,
62 length: u32,
63 ) -> Result<Cow<'instance, [u8]>, RuntimeError>;
64
65 fn write(
67 &mut self,
68 instance: &mut Instance,
69 location: GuestPointer,
70 bytes: &[u8],
71 ) -> Result<(), RuntimeError>;
72}
73
74#[expect(clippy::type_complexity)]
76pub struct Memory<'runtime, Instance>
77where
78 Instance: CabiReallocAlias + CabiFreeAlias,
79{
80 instance: &'runtime mut Instance,
81 memory: <Instance::Runtime as Runtime>::Memory,
82 cabi_realloc: Option<
83 <Instance as InstanceWithFunction<HList![i32, i32, i32, i32], HList![i32]>>::Function,
84 >,
85 cabi_free: Option<<Instance as InstanceWithFunction<HList![i32], HList![]>>::Function>,
86}
87
88impl<'runtime, Instance> Memory<'runtime, Instance>
89where
90 Instance: CabiReallocAlias + CabiFreeAlias,
91{
92 pub(super) fn new(
94 instance: &'runtime mut Instance,
95 memory: <Instance::Runtime as Runtime>::Memory,
96 ) -> Self {
97 Memory {
98 instance,
99 memory,
100 cabi_realloc: None,
101 cabi_free: None,
102 }
103 }
104}
105
106impl<Instance> Memory<'_, Instance>
107where
108 Instance: CabiReallocAlias + CabiFreeAlias,
109 <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
110{
111 pub fn read(&self, location: GuestPointer, length: u32) -> Result<Cow<'_, [u8]>, RuntimeError> {
115 self.memory.read(&*self.instance, location, length)
116 }
117
118 pub fn write(&mut self, location: GuestPointer, bytes: &[u8]) -> Result<(), RuntimeError> {
120 self.memory.write(&mut *self.instance, location, bytes)
121 }
122
123 pub fn allocate(&mut self, size: u32) -> Result<GuestPointer, RuntimeError> {
128 if self.cabi_realloc.is_none() {
129 self.cabi_realloc = Some(<Instance as InstanceWithFunction<
130 HList![i32, i32, i32, i32],
131 HList![i32],
132 >>::load_function(self.instance, "cabi_realloc")?);
133 }
134
135 let size = i32::try_from(size).map_err(|_| RuntimeError::AllocationTooLarge)?;
136
137 let cabi_realloc = self
138 .cabi_realloc
139 .as_ref()
140 .expect("`cabi_realloc` function was not loaded before it was called");
141
142 let hlist_pat![allocation_address] =
143 self.instance.call(cabi_realloc, hlist![0, 0, 1, size])?;
144
145 Ok(GuestPointer(
146 allocation_address
147 .try_into()
148 .map_err(|_| RuntimeError::AllocationFailed)?,
149 ))
150 }
151
152 pub fn deallocate(&mut self, allocation: GuestPointer) -> Result<(), RuntimeError> {
154 if self.cabi_free.is_none() {
155 self.cabi_free = Some(
156 <Instance as InstanceWithFunction<HList![i32], HList![]>>::load_function(
157 self.instance,
158 "cabi_free",
159 )?,
160 );
161 }
162
163 let address = allocation
164 .0
165 .try_into()
166 .map_err(|_| RuntimeError::DeallocateInvalidAddress)?;
167
168 let cabi_free = self
169 .cabi_free
170 .as_ref()
171 .expect("`cabi_free` function was not loaded before it was called");
172
173 self.instance.call(cabi_free, hlist![address])?;
174
175 Ok(())
176 }
177}