nibblecode 0.1.0

A serialization format based on rkyv
Documentation
use std::any::type_name;
use std::mem::{MaybeUninit, offset_of};
use std::ptr::Alignment;

use crate::result::ArchivedResult;
use crate::util::max_alignment;
use crate::{Serialize, SerializeError, VerifyError};

impl<T: Serialize, U: Serialize> Serialize for Result<T, U> {
	type Archived = ArchivedResult<T::Archived, U::Archived>;
	const ALIGN: Alignment = max_alignment([T::ALIGN, U::ALIGN]);
	const COPY_OPTIMIZATION: bool = T::COPY_OPTIMIZATION
		&& U::COPY_OPTIMIZATION
		&& offset_of!(Result<T, U>, Ok.0)
			== offset_of!(ArchivedResult<T::Archived, U::Archived>, Ok.0)
		&& offset_of!(Result<T, U>, Err.0)
			== offset_of!(ArchivedResult<T::Archived, U::Archived>, Err.0);

	unsafe fn serialize(
		&self,
		out: *mut MaybeUninit<ArchivedResult<T::Archived, U::Archived>>,
		heap: *mut MaybeUninit<u8>,
	) -> usize {
		unsafe {
			let (discriminant, size) = match self {
				Ok(value) => (
					0,
					value.serialize(
						out.cast::<u8>()
							.add(offset_of!(ArchivedResult<T::Archived, U::Archived>, Ok.0))
							.cast(),
						heap,
					),
				),
				Err(error) => (
					1,
					error.serialize(
						out.cast::<u8>()
							.add(offset_of!(ArchivedResult<T::Archived, U::Archived>, Err.0))
							.cast(),
						heap,
					),
				),
			};

			*out.cast() = discriminant;

			size
		}
	}

	fn serialized_size(&self, offset: usize) -> Result<usize, SerializeError> {
		match self {
			Ok(value) => value.serialized_size(offset),
			Err(error) => error.serialized_size(offset),
		}
	}

	#[inline]
	unsafe fn verify(
		this: *const ArchivedResult<T::Archived, U::Archived>,
		buffer_end: *const u8,
	) -> Result<(), VerifyError> {
		unsafe {
			let tag = *this.cast::<u8>();
			match tag {
				0 => T::verify(
					this.cast::<u8>()
						.add(offset_of!(ArchivedResult<T::Archived, U::Archived>, Ok.0))
						.cast(),
					buffer_end,
				),
				1 => U::verify(
					this.cast::<u8>()
						.add(offset_of!(ArchivedResult<T::Archived, U::Archived>, Err.0))
						.cast(),
					buffer_end,
				),
				_ => Err(VerifyError::InvalidEnumDiscriminantError {
					enum_name: type_name::<ArchivedResult<T::Archived, U::Archived>>(),
					invalid_discriminant: tag,
				}),
			}
		}
	}
}

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

	#[test]
	fn roundtrip_result() {
		roundtrip(&Result::<i32, u32>::Ok(12345i32));
		roundtrip(&Result::<i32, u32>::Err(12345u32));
	}
}