thinarray 0.2.0

A thin alternative to Box<[T]>
Documentation
use std::{alloc::Layout, fmt::Debug, hash::Hash, marker::PhantomData, ops::{Index, IndexMut}, ptr::NonNull, slice::SliceIndex};

pub struct ThinArray<T> {
	data: Option<NonNull<u32>>,
	phantom: PhantomData<T>
}

unsafe impl<T: Send> Send for ThinArray<T> {}
unsafe impl<T: Sync> Sync for ThinArray<T> {}

static_assertions::assert_eq_size!(ThinArray<usize>, usize);

impl<T: Debug> Debug for ThinArray<T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "{:?}", &self.as_slice())
	}
}

impl<T: Hash> Hash for ThinArray<T> {
	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
		for item in self.iter() {
			item.hash(state);
		}
	}
}

impl<T> ThinArray<T> {
	fn layout(len: usize) -> Layout {
		Layout::new::<u32>().extend(Layout::array::<T>(len).unwrap()).unwrap().0
	}

	pub fn new() -> Self {
		ThinArray {data: None, phantom: PhantomData::default()}
	}

	// short circuits if an Err is found
	pub fn from_results<E, U: IntoIterator<Item = Result<T, E>>>(obj: U) -> Result<Self, E> where U::IntoIter: ExactSizeIterator {
		let iter = obj.into_iter();
		if iter.len() == 0 {
			Ok(ThinArray {data: None, phantom: PhantomData::default()})
		} else {
			unsafe {
				let len = iter.len();
				let ptr = NonNull::new(std::alloc::alloc(Self::layout(len))).unwrap().cast();
				std::ptr::write(ptr.as_ptr(), len as u32);
				let items_ptr = Self::get_items_ptr(ptr);
				for (i, item) in iter.enumerate() {
					match item {
						Ok(item) => {
							std::ptr::write(items_ptr.add(i), item);
						}
						Err(err) => {
							for j in 0..i {
								std::ptr::drop_in_place(items_ptr.add(j));
							}
							std::alloc::dealloc(ptr.cast().as_ptr(), Self::layout(len));
							return Err(err);
						}
					}
				}
				Ok(ThinArray {data: Some(ptr), phantom: PhantomData::default()})
			}
		}
	}

	pub fn is_empty(&self) -> bool {
		self.data.is_none()
	}

	unsafe fn get_items_ptr(ptr: NonNull<u32>) -> *mut T {
		ptr.as_ptr().byte_add(std::mem::align_of::<T>().max(std::mem::size_of::<u32>())).cast::<T>()
	}

	pub fn get<I>(&self, idx: I) -> Option<&<I as SliceIndex<[T]>>::Output> where I: SliceIndex<[T]> {
		self.as_slice().get(idx)
	}

	pub fn get_mut<I>(&mut self, idx: I) -> Option<&mut <I as SliceIndex<[T]>>::Output> where I: SliceIndex<[T]> {
		self.as_slice_mut().get_mut(idx)
	}

	pub unsafe fn get_unchecked<I>(&self, idx: I) -> &<I as SliceIndex<[T]>>::Output where I: SliceIndex<[T]> {
		self.as_slice().get_unchecked(idx)
	}

	pub unsafe fn get_unchecked_mut<I>(&mut self, idx: I) -> &mut <I as SliceIndex<[T]>>::Output where I: SliceIndex<[T]> {
		self.as_slice_mut().get_unchecked_mut(idx)
	}

	pub fn as_slice(&self) -> &[T] {
		self.data.map_or(&[], |ptr| unsafe {NonNull::new(
			std::ptr::slice_from_raw_parts_mut(Self::get_items_ptr(ptr), self.len())
		).unwrap().as_ref()})
	}

	pub fn as_slice_mut(&mut self) -> &mut [T] {
		self.data.map_or(&mut [], |ptr| unsafe {NonNull::new(
			std::ptr::slice_from_raw_parts_mut(Self::get_items_ptr(ptr), self.len())
		).unwrap().as_mut()})
	}

	pub fn last(&self) -> Option<&T> {
		self.as_slice().last()
	}

	pub fn len(&self) -> usize {
		self.data.map_or(0, |p| *unsafe {p.as_ref()} as usize)
	}

	pub fn into_iter(self) -> ThinArrayIntoIter<T> {
		ThinArrayIntoIter {arr: self, pos: 0}
	}

	pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, T> {
		self.as_slice().iter()
	}

	pub fn iter_mut<'a>(&'a mut self) -> std::slice::IterMut<'a, T> {
		self.as_slice_mut().iter_mut()
	}

	pub fn from_two<U: IntoIterator<Item = T>, V: IntoIterator<Item = T>>(obj1: U, obj2: V) -> Self where U::IntoIter: ExactSizeIterator, V::IntoIter: ExactSizeIterator {
		let iter1 = obj1.into_iter();
		let iter1_len = iter1.len();
		let iter2 = obj2.into_iter();
		if iter1.len() + iter2.len() == 0 {
			ThinArray {data: None, phantom: PhantomData::default()}
		} else {
			unsafe {
				let len = iter1_len + iter2.len();
				let ptr = NonNull::new(std::alloc::alloc(Self::layout(len))).unwrap().cast();
				std::ptr::write(ptr.as_ptr(), len as u32);
				let items_ptr = Self::get_items_ptr(ptr);
				for (i, item) in iter1.enumerate() {
					std::ptr::write(items_ptr.add(i), item);
				}
				for (i, item) in iter2.enumerate() {
					std::ptr::write(items_ptr.add(iter1_len + i), item);
				}
				ThinArray {data: Some(ptr), phantom: PhantomData::default()}
			}
		}
	}
}

impl<T> Drop for ThinArray<T> {
	fn drop(&mut self) {
		if let Some(ptr) = self.data.take() {
			unsafe {
				let items_ptr = Self::get_items_ptr(ptr);
				let len = self.len();
				for i in 0..len {
					std::ptr::drop_in_place(items_ptr.add(i));
				}
				std::alloc::dealloc(ptr.cast().as_ptr(), Self::layout(len));
			}
		}
	}
}

impl<T: PartialEq> PartialEq for ThinArray<T> {
	fn eq(&self, other: &Self) -> bool {
		self.len() == other.len() || self.iter().zip(other.iter()).all(|(x, y)| x == y)
	}
}

impl<T: Eq> Eq for ThinArray<T> {}

impl<T: Clone> Clone for ThinArray<T> {
	fn clone(&self) -> Self {
		self.iter().cloned().into()
	}
}

impl<T> Default for ThinArray<T> {
	fn default() -> Self {
		Self::new()
	}
}

impl<T, R: SliceIndex<[T]>> Index<R> for ThinArray<T> {
	type Output = R::Output;
	fn index(&self, index: R) -> &Self::Output {
		&self.as_slice()[index]
	}
}

impl<T, R: SliceIndex<[T]>> IndexMut<R> for ThinArray<T> {
	fn index_mut(&mut self, index: R) -> &mut Self::Output {
		&mut self.as_slice_mut()[index]
	}
}

impl<T, U: IntoIterator<Item = T>> From<U> for ThinArray<T> where U::IntoIter: ExactSizeIterator {
	fn from(obj: U) -> Self where U::IntoIter: ExactSizeIterator {
		let iter = obj.into_iter();
		if iter.len() == 0 {
			ThinArray {data: None, phantom: PhantomData::default()}
		} else {
			unsafe {
				let len = iter.len();
				let ptr = NonNull::new(std::alloc::alloc(Self::layout(len))).unwrap().cast();
				std::ptr::write(ptr.as_ptr(), len as u32);
				let items_ptr = Self::get_items_ptr(ptr);
				for (i, item) in iter.enumerate() {
					std::ptr::write(items_ptr.add(i), item);
				}
				ThinArray {data: Some(ptr), phantom: PhantomData::default()}
			}
		}
	}
}

impl<'a, T> IntoIterator for &'a ThinArray<T> {
	type Item = &'a T;
	type IntoIter = std::slice::Iter<'a, T>;

	fn into_iter(self) -> Self::IntoIter {
		self.iter()
	}
}

impl<'a, T> IntoIterator for &'a mut ThinArray<T> {
	type Item = &'a mut T;
	type IntoIter = std::slice::IterMut<'a, T>;

	fn into_iter(self) -> Self::IntoIter {
		self.iter_mut()
	}
}

pub struct ThinArrayIntoIter<T> {
	arr: ThinArray<T>,
	pos: usize
}

impl<T> Iterator for ThinArrayIntoIter<T> {
	type Item = T;

	fn next(&mut self) -> Option<Self::Item> {
		if self.pos >= self.arr.len() {
			None
		} else {
			self.pos += 1;
			Some(unsafe {std::ptr::read(&self.arr[self.pos - 1])})
		}
	}

	fn size_hint(&self) -> (usize, Option<usize>) {
		let len = self.arr.len() - self.pos;
		(len, Some(len))
	}
}

impl<T> ExactSizeIterator for ThinArrayIntoIter<T> {
	fn len(&self) -> usize {
		self.arr.len() - self.pos
	}
}

impl<T> Drop for ThinArrayIntoIter<T> {
	fn drop(&mut self) {
		while self.next().is_some() {}
		unsafe {std::ptr::write(&mut self.arr, ThinArray::new())}
		self.arr.as_slice();
	}
}

#[test]
fn test() {
	let arr: ThinArray<_> = [1, 3].into();
	assert_eq!(arr.len(), 2);
	assert_eq!(arr[0], 1);
	assert_eq!(arr[1], 3);
}