#[derive(Debug, Default, Clone)] pub enum Layout { #[default] Unknown, Layout(KnownLayout), } impl Layout { pub const fn known(size: usize, align: usize, niches: u128) -> Self { Self::Layout(KnownLayout { size, align, niches, offsets: None }) } pub fn layout(self) -> Option { if let Self::Layout(layout) = self { Some(layout) } else { None } } } impl From for Layout { fn from(layout: KnownLayout) -> Self { Self::Layout(layout) } } #[derive(Debug, Default, Clone)] pub struct KnownLayout { size: usize, align: usize, /// number of available niches niches: u128, offsets: Option>, } impl KnownLayout { pub const fn new(size: usize, align: usize, niches: u128) -> Self { Self { size, align, niches, offsets: None } } #[inline] pub fn size(&self) -> usize { self.size } #[inline] pub fn align(&self) -> usize { self.align } /// number of available niches #[inline] pub fn niches(&self) -> u128 { self.niches } #[inline] pub fn offsets(&self) -> Option<&Vec> { self.offsets.as_ref() } pub unsafe fn set_size_unchecked(&mut self, size: usize) { self.size = size; } pub unsafe fn set_align_unchecked(&mut self, align: usize) { self.align = align; } pub unsafe fn set_niches_unchecked(&mut self, niches: u128) { self.niches = niches; } pub fn with_offsets(mut self, offsets: Vec) -> Self { self.offsets = Some(offsets); self } /// Panics /// if doesn't have enough viable space and `can_resize` is false pub fn consume_niches(&mut self, n: u128, can_resize: bool) { if self.niches() >= n { self.niches -= n; } else if can_resize { let align = self.align(); self.size += align; self.niches += max_val_of_bytes(align); self.consume_niches(n, can_resize); } else { panic!("`{}` called on a layout without enough space.", stringify!(consume_niches)); } } pub fn unpack(self) -> (/* size */ usize, /* align */ usize, /* offsets */ Option>) { let Self { size, align, offsets, .. } = self; (size, align, offsets) } } impl Layout { /// # Panics /// If `T` has more than 8 niches. pub const fn of() -> Self { // TODO: find a better way of calculating this. struct N1(Option); struct N2(N1>); struct N3(N1>); struct N4(N1>); struct N5(N1>); struct N6(N1>); struct N7(N1>); struct N8(N1>); let size = size_of::(); let align = align_of::(); let niches = if size_of::>() > size { 0 } else if size_of::>() > size { 1 } else if size_of::>() > size { 2 } else if size_of::>() > size { 3 } else if size_of::>() > size { 4 } else if size_of::>() > size { 5 } else if size_of::>() > size { 6 } else if size_of::>() > size { 7 } else if size_of::>() == size { 8 } else { panic!("`T` has more niches than what we can infer automatically"); }; Self::known(size, align, niches) } pub const fn zero() -> Self { #[repr(C)] struct Empty; Self::of::() } pub const fn ptr_32() -> Self { Layout::known(4, 4, 0) } pub const fn ptr_64() -> Self { Layout::known(8, 8, 0) } pub const fn wide_ptr_32() -> Self { Layout::known(8, 4, 1) } pub const fn wide_ptr_64() -> Self { Layout::of::<&str>() } pub fn is_unknown(&self) -> bool { matches!(self, Self::Unknown) } } /// Returns the max valid number in a primitive with the size of `n` bytes. /// Panics /// For `n` bigger than `16`, Or if it's not a power of 2 number fn max_val_of_bytes(n: usize) -> u128 { match n { 1 => u8::MAX as u128, 2 => u16::MAX as u128, 4 => u32::MAX as u128, 8 => u64::MAX as u128, 16 => u128::MAX, _ => panic!("We do not support `n` bigger than 16 bytes."), } }