1 //! Unchecked methods for working with the data inside GC objects. 2 3 use crate::V128; 4 use core::mem; 5 6 /// A plain-old-data type that can be stored in a `ValType` or a `StorageType`. 7 pub trait PodValType<const SIZE: usize>: Copy { 8 /// Read an instance of `Self` from the given native-endian bytes. read_le(le_bytes: &[u8; SIZE]) -> Self9 fn read_le(le_bytes: &[u8; SIZE]) -> Self; 10 11 /// Write `self` into the given memory location, as native-endian bytes. write_le(&self, into: &mut [u8; SIZE])12 fn write_le(&self, into: &mut [u8; SIZE]); 13 } 14 15 macro_rules! impl_pod_val_type { 16 ( $( $t:ty , )* ) => { 17 $( 18 impl PodValType<{mem::size_of::<$t>()}> for $t { 19 fn read_le(le_bytes: &[u8; mem::size_of::<$t>()]) -> Self { 20 <$t>::from_le_bytes(*le_bytes) 21 } 22 fn write_le(&self, into: &mut [u8; mem::size_of::<$t>()]) { 23 *into = self.to_le_bytes(); 24 } 25 } 26 )* 27 }; 28 } 29 30 impl_pod_val_type! { 31 u8, 32 u16, 33 u32, 34 u64, 35 i8, 36 i16, 37 i32, 38 i64, 39 } 40 41 impl PodValType<{ mem::size_of::<V128>() }> for V128 { read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self42 fn read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self { 43 u128::from_le_bytes(*le_bytes).into() 44 } write_le(&self, into: &mut [u8; mem::size_of::<V128>()])45 fn write_le(&self, into: &mut [u8; mem::size_of::<V128>()]) { 46 *into = self.as_u128().to_le_bytes(); 47 } 48 } 49 50 /// The backing storage for a GC-managed object. 51 /// 52 /// Methods on this type do not, generally, check against things like type 53 /// mismatches or that the given offset to read from even falls on a field 54 /// boundary. Omitting these checks is memory safe, due to our untrusted, 55 /// indexed GC heaps. Providing incorrect offsets will result in general 56 /// incorrectness, such as wrong answers or even panics, however. 57 /// 58 /// Finally, these methods *will* panic on out-of-bounds accesses, either out of 59 /// the GC heap's bounds or out of this object's bounds. The former is necessary 60 /// for preserving the memory safety of indexed GC heaps in the face of (for 61 /// example) collector bugs, but the latter is just a defensive technique to 62 /// catch bugs early and prevent action at a distance as much as possible. 63 #[repr(transparent)] 64 pub struct VMGcObjectData { 65 data: [u8], 66 } 67 68 macro_rules! impl_pod_methods { 69 ( $( $T:ty, $read:ident, $write:ident; )* ) => { 70 $( 71 /// Read a ` 72 #[doc = stringify!($T)] 73 /// ` field this object. 74 /// 75 /// Panics on out-of-bounds accesses. 76 #[inline] 77 pub fn $read(&self, offset: u32) -> $T 78 { 79 self.read_pod::<{ mem::size_of::<$T>() }, $T>(offset) 80 } 81 82 /// Write a ` 83 #[doc = stringify!($T)] 84 /// ` into this object. 85 /// 86 /// Panics on out-of-bounds accesses. 87 #[inline] 88 pub fn $write(&mut self, offset: u32, val: $T) 89 { 90 self.write_pod::<{ mem::size_of::<$T>() }, $T>(offset, val); 91 } 92 )* 93 }; 94 } 95 96 impl<'a> From<&'a [u8]> for &'a VMGcObjectData { 97 #[inline] from(data: &'a [u8]) -> Self98 fn from(data: &'a [u8]) -> Self { 99 &VMGcObjectData::from_slice(data) 100 } 101 } 102 103 impl<'a> From<&'a mut [u8]> for &'a mut VMGcObjectData { 104 #[inline] from(data: &'a mut [u8]) -> Self105 fn from(data: &'a mut [u8]) -> Self { 106 VMGcObjectData::from_slice_mut(data) 107 } 108 } 109 110 impl VMGcObjectData { 111 /// Construct a `VMGcObjectData` from the given slice of bytes. 112 #[inline] from_slice(data: &[u8]) -> &Self113 pub fn from_slice(data: &[u8]) -> &Self { 114 unsafe { mem::transmute(data) } 115 } 116 117 /// Construct a `VMGcObjectData` from the given slice of bytes. 118 #[inline] from_slice_mut(data: &mut [u8]) -> &mut Self119 pub fn from_slice_mut(data: &mut [u8]) -> &mut Self { 120 unsafe { mem::transmute(data) } 121 } 122 123 /// Read a POD field out of this object. 124 /// 125 /// Panics on out-of-bounds accesses. 126 /// 127 /// Don't generally use this method, use `read_u8`, `read_i64`, 128 /// etc... instead. 129 #[inline] read_pod<const N: usize, T>(&self, offset: u32) -> T where T: PodValType<N>,130 fn read_pod<const N: usize, T>(&self, offset: u32) -> T 131 where 132 T: PodValType<N>, 133 { 134 assert_eq!(N, mem::size_of::<T>()); 135 let offset = usize::try_from(offset).unwrap(); 136 let end = offset.checked_add(N).unwrap(); 137 let bytes = self.data.get(offset..end).expect("out of bounds field"); 138 T::read_le(bytes.try_into().unwrap()) 139 } 140 141 /// Read a POD field out of this object. 142 /// 143 /// Panics on out-of-bounds accesses. 144 /// 145 /// Don't generally use this method, use `write_u8`, `write_i64`, 146 /// etc... instead. 147 #[inline] write_pod<const N: usize, T>(&mut self, offset: u32, val: T) where T: PodValType<N>,148 fn write_pod<const N: usize, T>(&mut self, offset: u32, val: T) 149 where 150 T: PodValType<N>, 151 { 152 assert_eq!(N, mem::size_of::<T>()); 153 let offset = usize::try_from(offset).unwrap(); 154 let end = offset.checked_add(N).unwrap(); 155 let into = match self.data.get_mut(offset..end) { 156 Some(into) => into, 157 None => panic!( 158 "out of bounds field! field range = {offset:#x}..{end:#x}; object len = {:#x}", 159 self.data.as_mut().len(), 160 ), 161 }; 162 val.write_le(into.try_into().unwrap()); 163 } 164 165 /// Get a slice of this object's data. 166 /// 167 /// Note that GC data is always stored in little-endian order, and this 168 /// method does not do any conversions to/from host endianness for you. 169 /// 170 /// Panics on out-of-bounds accesses. 171 #[inline] slice(&self, offset: u32, len: u32) -> &[u8]172 pub fn slice(&self, offset: u32, len: u32) -> &[u8] { 173 let start = usize::try_from(offset).unwrap(); 174 let len = usize::try_from(len).unwrap(); 175 let end = start.checked_add(len).unwrap(); 176 self.data.get(start..end).expect("out of bounds slice") 177 } 178 179 /// Get a mutable slice of this object's data. 180 /// 181 /// Note that GC data is always stored in little-endian order, and this 182 /// method does not do any conversions to/from host endianness for you. 183 /// 184 /// Panics on out-of-bounds accesses. 185 #[inline] slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8]186 pub fn slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8] { 187 let start = usize::try_from(offset).unwrap(); 188 let len = usize::try_from(len).unwrap(); 189 let end = start.checked_add(len).unwrap(); 190 self.data.get_mut(start..end).expect("out of bounds slice") 191 } 192 193 /// Copy the given slice into this object's data at the given offset. 194 /// 195 /// Note that GC data is always stored in little-endian order, and this 196 /// method does not do any conversions to/from host endianness for you. 197 /// 198 /// Panics on out-of-bounds accesses. 199 #[inline] copy_from_slice(&mut self, offset: u32, src: &[u8])200 pub fn copy_from_slice(&mut self, offset: u32, src: &[u8]) { 201 let offset = usize::try_from(offset).unwrap(); 202 let end = offset.checked_add(src.len()).unwrap(); 203 let into = self.data.get_mut(offset..end).expect("out of bounds copy"); 204 into.copy_from_slice(src); 205 } 206 207 impl_pod_methods! { 208 u8, read_u8, write_u8; 209 u16, read_u16, write_u16; 210 u32, read_u32, write_u32; 211 u64, read_u64, write_u64; 212 i8, read_i8, write_i8; 213 i16, read_i16, write_i16; 214 i32, read_i32, write_i32; 215 i64, read_i64, write_i64; 216 V128, read_v128, write_v128; 217 } 218 } 219