1mod buffer;
2mod data;
3mod error;
4
5#[cfg(test)]
6mod tests;
7
8use self::buffer::ByteBuffer;
9pub use self::{
10 data::{DataSegment, DataSegmentEntity, DataSegmentIdx},
11 error::MemoryError,
12};
13use super::{AsContext, AsContextMut, StoreContext, StoreContextMut, Stored};
14use crate::{
15 collections::arena::ArenaIndex,
16 core::{Pages, TrapCode},
17 error::EntityGrowError,
18 store::{Fuel, ResourceLimiterRef},
19};
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
23pub struct MemoryIdx(u32);
24
25impl ArenaIndex for MemoryIdx {
26 fn into_usize(self) -> usize {
27 self.0 as usize
28 }
29
30 fn from_usize(value: usize) -> Self {
31 let value = value.try_into().unwrap_or_else(|error| {
32 panic!("index {value} is out of bounds as memory index: {error}")
33 });
34 Self(value)
35 }
36}
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq)]
40pub struct MemoryType {
41 initial_pages: Pages,
42 maximum_pages: Option<Pages>,
43}
44
45impl MemoryType {
46 pub fn new(initial: u32, maximum: Option<u32>) -> Result<Self, MemoryError> {
53 let initial_pages = Pages::new(initial).ok_or(MemoryError::InvalidMemoryType)?;
54 let maximum_pages = match maximum {
55 Some(maximum) => Pages::new(maximum)
56 .ok_or(MemoryError::InvalidMemoryType)?
57 .into(),
58 None => None,
59 };
60 Ok(Self {
61 initial_pages,
62 maximum_pages,
63 })
64 }
65
66 pub fn initial_pages(self) -> Pages {
68 self.initial_pages
69 }
70
71 pub fn maximum_pages(self) -> Option<Pages> {
78 self.maximum_pages
79 }
80
81 pub(crate) fn is_subtype_or_err(&self, other: &MemoryType) -> Result<(), MemoryError> {
95 match self.is_subtype_of(other) {
96 true => Ok(()),
97 false => Err(MemoryError::InvalidSubtype {
98 ty: *self,
99 other: *other,
100 }),
101 }
102 }
103
104 pub(crate) fn is_subtype_of(&self, other: &MemoryType) -> bool {
113 if self.initial_pages() < other.initial_pages() {
114 return false;
115 }
116 match (self.maximum_pages(), other.maximum_pages()) {
117 (_, None) => true,
118 (Some(max), Some(other_max)) => max <= other_max,
119 _ => false,
120 }
121 }
122}
123
124#[derive(Debug)]
126pub struct MemoryEntity {
127 bytes: ByteBuffer,
128 memory_type: MemoryType,
129 current_pages: Pages,
130}
131
132impl MemoryEntity {
133 pub fn new(
135 memory_type: MemoryType,
136 limiter: &mut ResourceLimiterRef<'_>,
137 ) -> Result<Self, MemoryError> {
138 let initial_pages = memory_type.initial_pages();
139 let initial_len = initial_pages.to_bytes();
140 let maximum_pages = memory_type.maximum_pages().unwrap_or_else(Pages::max);
141 let maximum_len = maximum_pages.to_bytes();
142
143 if let Some(limiter) = limiter.as_resource_limiter() {
144 if !limiter.memory_growing(0, initial_len.unwrap_or(usize::MAX), maximum_len)? {
145 return Err(MemoryError::OutOfBoundsAllocation);
149 }
150 }
151
152 if let Some(initial_len) = initial_len {
153 let memory = Self {
154 bytes: ByteBuffer::new(initial_len),
155 memory_type,
156 current_pages: initial_pages,
157 };
158 Ok(memory)
159 } else {
160 let err = MemoryError::OutOfBoundsAllocation;
161 if let Some(limiter) = limiter.as_resource_limiter() {
162 limiter.memory_grow_failed(&err)
163 }
164 Err(err)
165 }
166 }
167
168 pub fn new_static(
170 memory_type: MemoryType,
171 limiter: &mut ResourceLimiterRef<'_>,
172 buf: &'static mut [u8],
173 ) -> Result<Self, MemoryError> {
174 let initial_pages = memory_type.initial_pages();
175 let initial_len = initial_pages.to_bytes();
176 let maximum_pages = memory_type.maximum_pages().unwrap_or_else(Pages::max);
177 let maximum_len = maximum_pages.to_bytes();
178
179 if let Some(limiter) = limiter.as_resource_limiter() {
180 if !limiter.memory_growing(0, initial_len.unwrap_or(usize::MAX), maximum_len)? {
181 return Err(MemoryError::OutOfBoundsAllocation);
185 }
186 }
187
188 if let Some(initial_len) = initial_len {
189 if buf.len() < initial_len {
190 return Err(MemoryError::InvalidStaticBufferSize);
191 }
192 let memory = Self {
193 bytes: ByteBuffer::new_static(buf, initial_len),
194 memory_type,
195 current_pages: initial_pages,
196 };
197 Ok(memory)
198 } else {
199 let err = MemoryError::OutOfBoundsAllocation;
200 if let Some(limiter) = limiter.as_resource_limiter() {
201 limiter.memory_grow_failed(&err)
202 }
203 Err(err)
204 }
205 }
206
207 pub fn ty(&self) -> MemoryType {
209 self.memory_type
210 }
211
212 pub fn dynamic_ty(&self) -> MemoryType {
219 let current_pages = self.current_pages().into();
220 let maximum_pages = self.ty().maximum_pages().map(Into::into);
221 MemoryType::new(current_pages, maximum_pages)
222 .unwrap_or_else(|_| panic!("must result in valid memory type due to invariants"))
223 }
224
225 pub fn current_pages(&self) -> Pages {
227 self.current_pages
228 }
229
230 pub fn grow(
239 &mut self,
240 additional: Pages,
241 fuel: Option<&mut Fuel>,
242 limiter: &mut ResourceLimiterRef<'_>,
243 ) -> Result<Pages, EntityGrowError> {
244 fn notify_limiter(
245 limiter: &mut ResourceLimiterRef<'_>,
246 err: EntityGrowError,
247 ) -> Result<Pages, EntityGrowError> {
248 if let Some(limiter) = limiter.as_resource_limiter() {
249 limiter.memory_grow_failed(&MemoryError::OutOfBoundsGrowth)
250 }
251 Err(err)
252 }
253
254 let current_pages = self.current_pages();
255 if additional == Pages::from(0) {
256 return Ok(current_pages);
258 }
259
260 let maximum_pages = self.ty().maximum_pages().unwrap_or_else(Pages::max);
261 let desired_pages = current_pages.checked_add(additional);
262
263 if let Some(limiter) = limiter.as_resource_limiter() {
265 let current_size = current_pages.to_bytes().unwrap_or(usize::MAX);
266 let desired_size = desired_pages
267 .unwrap_or_else(Pages::max)
268 .to_bytes()
269 .unwrap_or(usize::MAX);
270 let maximum_size = maximum_pages.to_bytes();
271 match limiter.memory_growing(current_size, desired_size, maximum_size) {
272 Ok(true) => (),
273 Ok(false) => return Err(EntityGrowError::InvalidGrow),
274 Err(_) => return Err(EntityGrowError::TrapCode(TrapCode::GrowthOperationLimited)),
275 }
276 }
277
278 let Some(new_pages) = desired_pages else {
279 return notify_limiter(limiter, EntityGrowError::InvalidGrow);
280 };
281 if new_pages > maximum_pages {
282 return notify_limiter(limiter, EntityGrowError::InvalidGrow);
283 }
284 let Some(new_size) = new_pages.to_bytes() else {
285 return notify_limiter(limiter, EntityGrowError::InvalidGrow);
286 };
287 if let Some(fuel) = fuel {
288 let additional_bytes = additional.to_bytes().unwrap_or(usize::MAX) as u64;
289 if fuel
290 .consume_fuel_if(|costs| costs.fuel_for_bytes(additional_bytes))
291 .is_err()
292 {
293 return notify_limiter(limiter, EntityGrowError::TrapCode(TrapCode::OutOfFuel));
294 }
295 }
296 self.bytes.grow(new_size);
302 self.current_pages = new_pages;
303 Ok(current_pages)
304 }
305
306 pub fn data(&self) -> &[u8] {
308 self.bytes.data()
309 }
310
311 pub fn data_mut(&mut self) -> &mut [u8] {
313 self.bytes.data_mut()
314 }
315
316 pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), MemoryError> {
323 let len_buffer = buffer.len();
324 let slice = self
325 .data()
326 .get(offset..(offset + len_buffer))
327 .ok_or(MemoryError::OutOfBoundsAccess)?;
328 buffer.copy_from_slice(slice);
329 Ok(())
330 }
331
332 pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), MemoryError> {
339 let len_buffer = buffer.len();
340 let slice = self
341 .data_mut()
342 .get_mut(offset..(offset + len_buffer))
343 .ok_or(MemoryError::OutOfBoundsAccess)?;
344 slice.copy_from_slice(buffer);
345 Ok(())
346 }
347}
348
349#[derive(Debug, Copy, Clone)]
351#[repr(transparent)]
352pub struct Memory(Stored<MemoryIdx>);
353
354impl Memory {
355 pub(super) fn from_inner(stored: Stored<MemoryIdx>) -> Self {
357 Self(stored)
358 }
359
360 pub(super) fn as_inner(&self) -> &Stored<MemoryIdx> {
362 &self.0
363 }
364
365 pub fn new(mut ctx: impl AsContextMut, ty: MemoryType) -> Result<Self, MemoryError> {
371 let (inner, mut resource_limiter) = ctx
372 .as_context_mut()
373 .store
374 .store_inner_and_resource_limiter_ref();
375
376 let entity = MemoryEntity::new(ty, &mut resource_limiter)?;
377 let memory = inner.alloc_memory(entity);
378 Ok(memory)
379 }
380
381 pub fn new_static(
388 mut ctx: impl AsContextMut,
389 ty: MemoryType,
390 buf: &'static mut [u8],
391 ) -> Result<Self, MemoryError> {
392 let (inner, mut resource_limiter) = ctx
393 .as_context_mut()
394 .store
395 .store_inner_and_resource_limiter_ref();
396
397 let entity = MemoryEntity::new_static(ty, &mut resource_limiter, buf)?;
398 let memory = inner.alloc_memory(entity);
399 Ok(memory)
400 }
401
402 pub fn ty(&self, ctx: impl AsContext) -> MemoryType {
408 ctx.as_context().store.inner.resolve_memory(self).ty()
409 }
410
411 pub(crate) fn dynamic_ty(&self, ctx: impl AsContext) -> MemoryType {
422 ctx.as_context()
423 .store
424 .inner
425 .resolve_memory(self)
426 .dynamic_ty()
427 }
428
429 pub fn current_pages(&self, ctx: impl AsContext) -> Pages {
435 ctx.as_context()
436 .store
437 .inner
438 .resolve_memory(self)
439 .current_pages()
440 }
441
442 pub fn grow(
455 &self,
456 mut ctx: impl AsContextMut,
457 additional: Pages,
458 ) -> Result<Pages, MemoryError> {
459 let (inner, mut limiter) = ctx
460 .as_context_mut()
461 .store
462 .store_inner_and_resource_limiter_ref();
463 inner
464 .resolve_memory_mut(self)
465 .grow(additional, None, &mut limiter)
466 .map_err(|_| MemoryError::OutOfBoundsGrowth)
467 }
468
469 pub fn data<'a, T: 'a>(&self, ctx: impl Into<StoreContext<'a, T>>) -> &'a [u8] {
475 ctx.into().store.inner.resolve_memory(self).data()
476 }
477
478 pub fn data_mut<'a, T: 'a>(&self, ctx: impl Into<StoreContextMut<'a, T>>) -> &'a mut [u8] {
484 ctx.into().store.inner.resolve_memory_mut(self).data_mut()
485 }
486
487 pub fn data_and_store_mut<'a, T: 'a>(
494 &self,
495 ctx: impl Into<StoreContextMut<'a, T>>,
496 ) -> (&'a mut [u8], &'a mut T) {
497 let (memory, store) = ctx.into().store.resolve_memory_and_state_mut(self);
498 (memory.data_mut(), store)
499 }
500
501 pub fn read(
512 &self,
513 ctx: impl AsContext,
514 offset: usize,
515 buffer: &mut [u8],
516 ) -> Result<(), MemoryError> {
517 ctx.as_context()
518 .store
519 .inner
520 .resolve_memory(self)
521 .read(offset, buffer)
522 }
523
524 pub fn write(
535 &self,
536 mut ctx: impl AsContextMut,
537 offset: usize,
538 buffer: &[u8],
539 ) -> Result<(), MemoryError> {
540 ctx.as_context_mut()
541 .store
542 .inner
543 .resolve_memory_mut(self)
544 .write(offset, buffer)
545 }
546}