1 use crate::error::{Error, OomOrDynError}; 2 use core::{fmt, mem, ptr::NonNull}; 3 4 /// Out-of-memory error. 5 /// 6 /// This error is the sentinel for allocation failure due to memory exhaustion. 7 /// 8 /// Constructing an [`Error`] from an `OutOfMemory` does not 9 /// allocate. 10 /// 11 /// Allocation failure inside any `Error` method that must allocate 12 /// (e.g. [`Error::context`]) will propagate an `OutOfMemory` error. 13 #[derive(Clone, Copy)] 14 // NB: `OutOfMemory`'s representation must be the same as `OomOrDynError` 15 // (and therefore also `Error`). 16 #[repr(transparent)] 17 pub struct OutOfMemory { 18 inner: NonNull<u8>, 19 } 20 21 // Safety: The `inner` pointer is not a real pointer, it is just bitpacked size 22 // data. 23 unsafe impl Send for OutOfMemory {} 24 25 // Safety: The `inner` pointer is not a real pointer, it is just bitpacked size 26 // data. 27 unsafe impl Sync for OutOfMemory {} 28 29 impl fmt::Debug for OutOfMemory { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 f.debug_struct("OutOfMemory") 32 .field( 33 "requested_allocation_size", 34 &self.requested_allocation_size(), 35 ) 36 .finish() 37 } 38 } 39 40 impl fmt::Display for OutOfMemory { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 42 write!( 43 f, 44 "out of memory (failed to allocate {} bytes)", 45 self.requested_allocation_size() 46 ) 47 } 48 } 49 50 impl core::error::Error for OutOfMemory { 51 #[inline] source(&self) -> Option<&(dyn core::error::Error + 'static)>52 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { 53 None 54 } 55 } 56 57 impl OutOfMemory { 58 // NB: `OutOfMemory`'s representation must be the same as `OomOrDynError` 59 // (and therefore also `Error`). 60 const _SAME_SIZE_AS_OOM_OR_DYN_ERROR: () = 61 assert!(mem::size_of::<OutOfMemory>() == mem::size_of::<OomOrDynError>()); 62 const _SAME_ALIGN_AS_OOM_OR_DYN_ERROR: () = 63 assert!(mem::align_of::<OutOfMemory>() == mem::align_of::<OomOrDynError>()); 64 const _SAME_SIZE_AS_ERROR: () = 65 assert!(mem::size_of::<OutOfMemory>() == mem::size_of::<Error>()); 66 const _SAME_ALIGN_AS_ERROR: () = 67 assert!(mem::align_of::<OutOfMemory>() == mem::align_of::<Error>()); 68 69 /// Construct a new `OutOfMemory` error. 70 /// 71 /// The `requested_allocation_size` argument should be the size (in bytes) 72 /// of the associated allocation that was attempted and failed. 73 /// 74 /// This operation does not allocate. 75 /// 76 /// # Example 77 /// 78 /// ```rust 79 /// # use wasmtime_internal_core::error::OutOfMemory; 80 /// # extern crate alloc; 81 /// use alloc::alloc::{Layout, alloc}; 82 /// use core::ptr::NonNull; 83 /// 84 /// /// Attempt to allocate a block of memory from the global allocator, 85 /// /// returning an `OutOfMemory` error on failure. 86 /// fn try_global_alloc(layout: Layout) -> Result<NonNull<u8>, OutOfMemory> { 87 /// if layout.size() == 0 { 88 /// return Ok(NonNull::dangling()); 89 /// } 90 /// 91 /// // Safety: the layout's size is non-zero. 92 /// let ptr = unsafe { alloc(layout) }; 93 /// 94 /// if let Some(ptr) = NonNull::new(ptr) { 95 /// Ok(ptr) 96 /// } else { 97 /// // The allocation failed, so return an `OutOfMemory` error, 98 /// // passing the attempted allocation's size into the `OutOfMemory` 99 /// // constructor. 100 /// Err(OutOfMemory::new(layout.size())) 101 /// } 102 /// } 103 /// ``` 104 #[inline] new(requested_allocation_size: usize) -> Self105 pub const fn new(requested_allocation_size: usize) -> Self { 106 Self { 107 inner: OomOrDynError::new_oom_ptr(requested_allocation_size), 108 } 109 } 110 111 /// Get the size (in bytes) of the associated allocation that was attempted 112 /// and which failed. 113 /// 114 /// Very large allocation sizes (near `isize::MAX` and larger) may be capped 115 /// to a maximum value. 116 /// 117 /// # Example 118 /// 119 /// ```rust 120 /// # use wasmtime_internal_core::error::OutOfMemory; 121 /// let oom = OutOfMemory::new(8192); 122 /// assert_eq!(oom.requested_allocation_size(), 8192); 123 /// ``` 124 #[inline] requested_allocation_size(&self) -> usize125 pub fn requested_allocation_size(&self) -> usize { 126 OomOrDynError::oom_size(self.inner) 127 } 128 } 129 130 impl From<OutOfMemory> for OomOrDynError { from(oom: OutOfMemory) -> Self131 fn from(oom: OutOfMemory) -> Self { 132 OomOrDynError::new_oom(oom.inner) 133 } 134 } 135