nibblecode 0.1.0

A serialization format based on rkyv
Documentation
use core::cmp::Ordering;
use core::marker::{PhantomData, PhantomPinned};
use core::mem::MaybeUninit;
use core::ptr::{Alignment, copy_nonoverlapping};

use crate::boxed::ArchivedBox;
use crate::impls::lexicographical_partial_ord;
use crate::list::ArchivedList;
use crate::primitive::{ArchivedUsize, FixedUsize};
use crate::string::ArchivedString;
use crate::util::{align_offset, max_alignment, offset_archived};
use crate::{Serialize, SerializeError, VerifyError};

impl<T: Serialize> Serialize for Box<T> {
	type Archived = ArchivedBox<T::Archived>;
	const ALIGN: Alignment = max_alignment([Alignment::of::<ArchivedUsize>(), T::ALIGN]);

	unsafe fn serialize(
		&self,
		out: *mut MaybeUninit<ArchivedBox<T::Archived>>,
		heap: *mut MaybeUninit<u8>,
	) -> usize {
		let align_size = align_offset::<T::Archived>(heap as usize);

		unsafe {
			let heap = heap.add(align_size);

			*out = MaybeUninit::new(ArchivedBox {
				offset: ArchivedUsize::from_native(heap.offset_from_unsigned(out.cast()) as _),
				_phantom_data: PhantomData,
				_phantom_pinned: PhantomPinned,
			});

			if T::COPY_OPTIMIZATION {
				copy_nonoverlapping((&raw const **self).cast(), heap, size_of::<T>());

				align_size + size_of::<T::Archived>()
			} else {
				align_size
					+ size_of::<T::Archived>()
					+ (**self).serialize(heap.cast(), heap.add(size_of::<T::Archived>()))
			}
		}
	}

	fn serialized_size(&self, offset: usize) -> Result<usize, SerializeError> {
		if (FixedUsize::MAX as usize) < offset + align_offset::<T::Archived>(offset) {
			Err(SerializeError::OverflowedPointer)
		} else {
			let type_size = align_offset::<T::Archived>(offset) + size_of::<T::Archived>();
			(**self)
				.serialized_size(offset + type_size)
				.map(|size| size + type_size)
		}
	}

	#[inline]
	unsafe fn verify(
		this: *const ArchivedBox<T::Archived>,
		buffer_end: *const u8,
	) -> Result<(), VerifyError> {
		unsafe {
			offset_archived(
				this.cast(),
				(*this).offset.to_native() as usize,
				size_of::<T::Archived>(),
				buffer_end,
				Alignment::of::<T::Archived>(),
			)
			.and_then(|ptr| T::verify(ptr.cast(), buffer_end))
		}
	}
}

impl<T: Serialize> Serialize for Box<[T]> {
	type Archived = ArchivedList<T::Archived>;
	const ALIGN: Alignment = max_alignment([T::ALIGN, Alignment::of::<ArchivedUsize>()]);

	unsafe fn serialize(
		&self,
		out: *mut MaybeUninit<ArchivedList<T::Archived>>,
		heap: *mut MaybeUninit<u8>,
	) -> usize {
		unsafe { (**self).serialize(out, heap) }
	}

	fn serialized_size(&self, offset: usize) -> Result<usize, SerializeError> {
		(**self).serialized_size(offset)
	}

	#[inline]
	unsafe fn verify(
		this: *const ArchivedList<T::Archived>,
		buffer_end: *const u8,
	) -> Result<(), VerifyError> {
		unsafe { <[T]>::verify(this, buffer_end) }
	}
}

impl Serialize for Box<str> {
	type Archived = ArchivedString;
	const ALIGN: Alignment = Alignment::of::<ArchivedUsize>();

	unsafe fn serialize(
		&self,
		out: *mut MaybeUninit<ArchivedString>,
		heap: *mut MaybeUninit<u8>,
	) -> usize {
		unsafe { (**self).serialize(out, heap) }
	}

	fn serialized_size(&self, offset: usize) -> Result<usize, SerializeError> {
		(**self).serialized_size(offset)
	}

	#[inline]
	unsafe fn verify(
		this: *const ArchivedString,
		buffer_end: *const u8,
	) -> Result<(), VerifyError> {
		unsafe { str::verify(this, buffer_end) }
	}
}

impl<T: PartialEq<U>, U> PartialEq<Box<U>> for ArchivedBox<T> {
	fn eq(&self, other: &Box<U>) -> bool {
		self.get().eq(other.as_ref())
	}
}

impl<T: PartialOrd<U>, U> PartialOrd<Box<U>> for ArchivedBox<T> {
	fn partial_cmp(&self, other: &Box<U>) -> Option<Ordering> {
		self.get().partial_cmp(other.as_ref())
	}
}

impl<T: PartialEq<U>, U> PartialEq<Box<[U]>> for ArchivedList<T> {
	fn eq(&self, other: &Box<[U]>) -> bool {
		self.as_slice().eq(&**other)
	}
}

impl<T: PartialOrd<U>, U> PartialOrd<Box<[U]>> for ArchivedList<T> {
	fn partial_cmp(&self, other: &Box<[U]>) -> Option<Ordering> {
		lexicographical_partial_ord(self.as_slice(), other)
	}
}

impl<T: PartialEq<U>, U> PartialEq<ArchivedList<U>> for Box<[T]> {
	fn eq(&self, other: &ArchivedList<U>) -> bool {
		(**self).eq(other.as_slice())
	}
}

impl<T: PartialOrd<U>, U> PartialOrd<ArchivedList<U>> for Box<[T]> {
	fn partial_cmp(&self, other: &ArchivedList<U>) -> Option<Ordering> {
		lexicographical_partial_ord(self, other.as_slice())
	}
}

#[cfg(test)]
mod tests {
	use crate::test::roundtrip;

	#[test]
	fn roundtrip_box() {
		roundtrip(&Box::new(42));
		roundtrip(&Box::new([1, 2, 3, 4, 5, 6]));
	}

	#[test]
	fn roundtrip_boxed_str() {
		roundtrip(&"".to_string().into_boxed_str());
		roundtrip(&"hello world".to_string().into_boxed_str());
	}

	#[test]
	fn roundtrip_boxed_slice() {
		roundtrip(&Vec::<i32>::new().into_boxed_slice());
		roundtrip(&vec![1, 2, 3, 4].into_boxed_slice());
	}

	#[test]
	fn roundtrip_box_zsts() {
		roundtrip(&Box::new(()));
		roundtrip(&Vec::<()>::new().into_boxed_slice());
		roundtrip(&vec![(), (), (), ()].into_boxed_slice());
	}

	#[test]
	fn roundtrip_option_box() {
		roundtrip(&Some(Box::new(42)));
		roundtrip(&Some(Box::new([1, 2, 3, 4, 5, 6])));
	}

	#[test]
	fn roundtrip_option_box_str() {
		roundtrip(&Some("".to_string().into_boxed_str()));
		roundtrip(&Some("hello world".to_string().into_boxed_str()));
	}

	#[test]
	fn roundtrip_option_box_slice() {
		roundtrip(&Some(Vec::<i32>::new().into_boxed_slice()));
		roundtrip(&Some(vec![1, 2, 3, 4].into_boxed_slice()));
	}

	#[test]
	fn roundtrip_result_box() {
		roundtrip(&Ok::<_, ()>(Box::new(42)));
		roundtrip(&Ok::<_, ()>(Box::new([1, 2, 3, 4, 5, 6])));

		roundtrip(&Err::<(), _>(Box::new(42)));
		roundtrip(&Err::<(), _>(Box::new([1, 2, 3, 4, 5, 6])));
	}

	#[test]
	fn roundtrip_result_box_str() {
		roundtrip(&Ok::<_, ()>("".to_string().into_boxed_str()));
		roundtrip(&Ok::<_, ()>("hello world".to_string().into_boxed_str()));

		roundtrip(&Err::<(), _>("".to_string().into_boxed_str()));
		roundtrip(&Err::<(), _>("hello world".to_string().into_boxed_str()));
	}

	#[test]
	fn roundtrip_result_box_slice() {
		roundtrip(&Ok::<_, ()>(Vec::<i32>::new().into_boxed_slice()));
		roundtrip(&Ok::<_, ()>(vec![1, 2, 3, 4].into_boxed_slice()));

		roundtrip(&Err::<(), _>(Vec::<i32>::new().into_boxed_slice()));
		roundtrip(&Err::<(), _>(vec![1, 2, 3, 4].into_boxed_slice()));
	}
}