1//! Wrappers around `Box<[u8; LEN]>` and similar types with a custom `bincode::Decode` 2//! implementation that deserializes directly into heap memory. 3//! 4//! This exists because the implementation that `#[derive(Decode)]` generates for `Box<[u8; LEN]>` 5//! deserializes into stack memory and then moves to the heap, which is problematic when deserializing 6//! large arrays (particularly on Windows). 7//! 8//! For types other than `u8`, these implementations also feature significantly more efficient 9//! `bincode::Encode` and `bincode::Decode` implementations than what bincode's derive macros 10//! would produce. 11 12use crate::frontend::Color; 13use bincode::de::read::Reader; 14use bincode::de::{BorrowDecoder, Decoder}; 15use bincode::enc::Encoder; 16use bincode::enc::write::Writer; 17use bincode::error::{DecodeError, EncodeError}; 18use bincode::{BorrowDecode, Decode, Encode}; 19use bytemuck::Pod; 20use rand::distr::StandardUniform; 21use rand::prelude::Distribution; 22use std::fmt::Debug; 23use std::ops::{Deref, DerefMut}; 24 25#[derive(Debug, Clone)] 26pub struct BoxedArray<T, const LEN: usize>(Box<[T; LEN]>); 27 28impl<T: Debug + Default + Copy, const LEN: usize> Default for BoxedArray<T, LEN> { 29 fn default() -> Self { 30 Self::new() 31 } 32} 33 34impl<T: Debug + Default + Copy, const LEN: usize> BoxedArray<T, LEN> { 35 #[must_use] 36 #[allow(clippy::missing_panics_doc)] 37 pub fn new() -> Self { 38 Self(vec![T::default(); LEN].into_boxed_slice().try_into().unwrap()) 39 } 40} 41 42impl<T: Debug + Default + Copy, const LEN: usize> BoxedArray<T, LEN> 43where 44 StandardUniform: Distribution<T>, 45{ 46 #[must_use] 47 pub fn new_random() -> Self { 48 let mut array = Self::new(); 49 array.fill_with(rand::random); 50 array 51 } 52} 53 54impl<T, const LEN: usize> From<Box<[T; LEN]>> for BoxedArray<T, LEN> { 55 fn from(value: Box<[T; LEN]>) -> Self { 56 Self(value) 57 } 58} 59 60impl<T, const LEN: usize> From<BoxedArray<T, LEN>> for Box<[T; LEN]> { 61 fn from(value: BoxedArray<T, LEN>) -> Self { 62 value.0 63 } 64} 65 66impl<T, const LEN: usize> Deref for BoxedArray<T, LEN> { 67 type Target = Box<[T; LEN]>; 68 69 fn deref(&self) -> &Self::Target { 70 &self.0 71 } 72} 73 74impl<T, const LEN: usize> DerefMut for BoxedArray<T, LEN> { 75 fn deref_mut(&mut self) -> &mut Self::Target { 76 &mut self.0 77 } 78} 79 80impl<T: Pod, const LEN: usize> Encode for BoxedArray<T, LEN> { 81 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> { 82 // This is _significantly_ faster than the #[derive(Encode)] implementation for non-u8 types 83 let bytes = bytemuck::cast_slice(self.0.as_slice()); 84 encoder.writer().write(bytes) 85 } 86} 87 88impl<T: Debug + Default + Copy + Pod, Context, const LEN: usize> Decode<Context> 89 for BoxedArray<T, LEN> 90{ 91 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> { 92 // Similarly, this is _significantly_ faster than the #[derive(Decode)] implementation for non-u8 types 93 let mut array = vec![T::default(); LEN]; 94 decoder.reader().read(bytemuck::cast_slice_mut(&mut array))?; 95 96 Ok(Self(array.into_boxed_slice().try_into().unwrap())) 97 } 98} 99 100impl<'de, T: Debug + Default + Copy + Pod, Context, const LEN: usize> BorrowDecode<'de, Context> 101 for BoxedArray<T, LEN> 102{ 103 fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>( 104 decoder: &mut D, 105 ) -> Result<Self, DecodeError> { 106 Self::decode(decoder) 107 } 108} 109 110pub type BoxedByteArray<const LEN: usize> = BoxedArray<u8, LEN>; 111pub type BoxedWordArray<const LEN: usize> = BoxedArray<u16, LEN>; 112pub type BoxedColorArray<const LEN: usize> = BoxedArray<Color, LEN>; 113 114#[derive(Debug, Clone)] 115pub struct Boxed2DArray<T, const ROWS: usize, const COLS: usize>(Box<[[T; COLS]; ROWS]>); 116 117impl<T: Debug + Default + Copy, const ROWS: usize, const COLS: usize> Default 118 for Boxed2DArray<T, ROWS, COLS> 119{ 120 fn default() -> Self { 121 Self::new() 122 } 123} 124 125impl<T: Debug + Default + Copy, const ROWS: usize, const COLS: usize> Boxed2DArray<T, ROWS, COLS> { 126 #[must_use] 127 pub fn new() -> Self { 128 // SAFETY: Memory is fully filled with T::default() before calling assume_init() 129 // Total allocation length is (ROWS * COLS * size_of::<T>()), and it is only accessed through 130 // a *mut T with offset strictly less than (ROWS * COLS) 131 unsafe { 132 let mut array = Box::<[[T; COLS]; ROWS]>::new_uninit(); 133 let ptr = array.as_mut_ptr().cast::<T>(); 134 for i in 0..ROWS * COLS { 135 ptr.add(i).write(T::default()); 136 } 137 Self(array.assume_init()) 138 } 139 } 140} 141 142impl<T, const ROWS: usize, const COLS: usize> From<Box<[[T; COLS]; ROWS]>> 143 for Boxed2DArray<T, ROWS, COLS> 144{ 145 fn from(value: Box<[[T; COLS]; ROWS]>) -> Self { 146 Self(value) 147 } 148} 149 150impl<T, const ROWS: usize, const COLS: usize> From<Boxed2DArray<T, ROWS, COLS>> 151 for Box<[[T; COLS]; ROWS]> 152{ 153 fn from(value: Boxed2DArray<T, ROWS, COLS>) -> Self { 154 value.0 155 } 156} 157 158impl<T, const ROWS: usize, const COLS: usize> Deref for Boxed2DArray<T, ROWS, COLS> { 159 type Target = Box<[[T; COLS]; ROWS]>; 160 161 fn deref(&self) -> &Self::Target { 162 &self.0 163 } 164} 165 166impl<T, const ROWS: usize, const COLS: usize> DerefMut for Boxed2DArray<T, ROWS, COLS> { 167 fn deref_mut(&mut self) -> &mut Self::Target { 168 &mut self.0 169 } 170} 171 172impl<T: Pod, const ROWS: usize, const COLS: usize> Encode for Boxed2DArray<T, ROWS, COLS> { 173 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> { 174 let writer = encoder.writer(); 175 for row in self.as_slice() { 176 writer.write(bytemuck::cast_slice(row))?; 177 } 178 179 Ok(()) 180 } 181} 182 183impl<T: Debug + Default + Copy + Pod, Context, const ROWS: usize, const COLS: usize> Decode<Context> 184 for Boxed2DArray<T, ROWS, COLS> 185{ 186 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> { 187 let mut array = Self::new(); 188 let reader = decoder.reader(); 189 for row in array.as_mut_slice() { 190 reader.read(bytemuck::cast_slice_mut(row))?; 191 } 192 193 Ok(array) 194 } 195} 196 197impl<'de, T: Debug + Default + Copy + Pod, Context, const ROWS: usize, const COLS: usize> 198 BorrowDecode<'de, Context> for Boxed2DArray<T, ROWS, COLS> 199{ 200 fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>( 201 decoder: &mut D, 202 ) -> Result<Self, DecodeError> { 203 Self::decode(decoder) 204 } 205} 206 207pub type Boxed2DWordArray<const ROWS: usize, const COLS: usize> = Boxed2DArray<u16, ROWS, COLS>; 208 209#[cfg(test)] 210mod tests { 211 use super::*; 212 213 // Test should be run with miri: 214 // $ cargo +nightly miri test -p jgenesis-common 215 #[test] 216 fn new_boxed_2d_array() { 217 let array: Boxed2DArray<Color, 10, 10> = Boxed2DArray::new(); 218 219 for row in array.as_slice() { 220 for &color in row { 221 assert_eq!(color, Color::default()); 222 } 223 } 224 } 225}