nibblecode 0.1.0

A serialization format based on rkyv
Documentation
use std::ptr::{Alignment, Thin};

use crate::VerifyError;

/// Like [`Ord::max`] for [`Alignment`] that can be used in `const`.
#[must_use]
pub const fn max_alignment<const N: usize>(alignments: [Alignment; N]) -> Alignment {
	let mut max = Alignment::MIN;
	let mut i = 0;
	while i < N {
		if max.as_usize() < alignments[i].as_usize() {
			max = alignments[i];
		}
		i += 1;
	}
	max
}

/// Like `pointer::align_offset` but for `usize`.
#[must_use]
pub fn align_offset<T>(ptr: usize) -> usize {
	// Compiles to a `neg` and an `and (align - 1)`
	(!ptr + 1) & (align_of::<T>() - 1)
}

pub fn check_alignment(address: *const impl Thin, align: Alignment) -> Result<(), VerifyError> {
	let address = address as usize;
	if address & (align.as_usize() - 1) == 0 {
		Ok(())
	} else {
		Err(VerifyError::UnalignedPointer { address, align })
	}
}

pub fn offset_archived(
	base: *const u8,
	offset: usize,
	size: usize,
	buffer_end: *const u8,
	align: Alignment,
) -> Result<*const u8, VerifyError> {
	let data = base.wrapping_add(offset);

	check_alignment(data, align)?;

	let data_end = data.wrapping_add(size);

	// Check if either addition overflowed or went past the end of the buffer
	if data <= base || data_end < data || buffer_end < data_end {
		Err(VerifyError::InvalidPointer {
			address: data,
			size,
			range: base..buffer_end,
		})
	} else {
		Ok(data)
	}
}