#![no_std]
#[cfg(any(feature = "alloc", kani))]
extern crate alloc;
#[cfg(kani)]
extern crate kani;
use core::{fmt, hash::Hash, iter::FusedIterator, marker::PhantomData};
use zerocopy::{
FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned,
byteorder::{LE, U16, U32, U64},
};
pub mod build;
pub mod integrity;
const CRC32C_TABLE: [u32; 256] = build_crc32c_table();
const fn build_crc32c_table() -> [u32; 256] {
let mut table = [0u32; 256];
let mut index: usize = 0;
let mut seed: u32 = 0;
while index < 256 {
let mut crc = seed;
let mut bit = 0;
while bit < 8 {
let mask = (crc & 1).wrapping_neg();
crc = (crc >> 1) ^ (0x82F6_3B78 & mask);
bit += 1;
}
table[index] = crc;
index += 1;
seed += 1;
}
table
}
#[must_use]
pub fn crc32c_append(crc: u32, bytes: &[u8]) -> u32 {
let mut state = !crc;
for &byte in bytes {
let index = usize::from(state.to_le_bytes()[0] ^ byte);
state = (state >> 8) ^ CRC32C_TABLE[index];
}
!state
}
mod sealed {
pub trait LayoutIndex {}
pub trait ZerocopyWord {}
pub trait LayoutSnapshotWord {}
pub trait SnapshotWidth {}
pub trait Axis {}
}
pub trait LayoutIndex:
sealed::LayoutIndex + Copy + Eq + Ord + fmt::Debug + fmt::Display + Hash + Sized
{
const ZERO: Self;
fn to_usize(self) -> Option<usize>;
fn from_usize(value: usize) -> Option<Self>;
}
macro_rules! impl_layout_index {
($index:ty) => {
impl sealed::LayoutIndex for $index {}
impl LayoutIndex for $index {
const ZERO: Self = 0;
fn to_usize(self) -> Option<usize> {
usize::try_from(self).ok()
}
fn from_usize(value: usize) -> Option<Self> {
Self::try_from(value).ok()
}
}
};
}
impl_layout_index!(u16);
impl_layout_index!(u32);
impl_layout_index!(u64);
impl sealed::LayoutIndex for usize {}
impl LayoutIndex for usize {
const ZERO: Self = 0;
fn to_usize(self) -> Option<usize> {
Some(self)
}
fn from_usize(value: usize) -> Option<Self> {
Some(value)
}
}
pub trait ZerocopyWord: sealed::ZerocopyWord + Copy {
fn read_as_usize(self) -> Option<usize>;
}
macro_rules! impl_native_zerocopy_word {
($word:ty) => {
impl sealed::ZerocopyWord for $word {}
impl ZerocopyWord for $word {
fn read_as_usize(self) -> Option<usize> {
usize::try_from(self).ok()
}
}
};
}
impl_native_zerocopy_word!(u16);
impl_native_zerocopy_word!(u32);
impl_native_zerocopy_word!(u64);
impl_native_zerocopy_word!(usize);
macro_rules! impl_le_zerocopy_word {
($word:ty) => {
impl sealed::ZerocopyWord for $word {}
impl ZerocopyWord for $word {
fn read_as_usize(self) -> Option<usize> {
usize::try_from(<$word>::get(self)).ok()
}
}
};
}
impl_le_zerocopy_word!(U16<LE>);
impl_le_zerocopy_word!(U32<LE>);
impl_le_zerocopy_word!(U64<LE>);
pub trait LayoutWord: Copy + ZerocopyWord {
type Index: LayoutIndex;
fn get(self) -> Self::Index;
}
macro_rules! impl_native_layout_word {
($word:ty) => {
impl LayoutWord for $word {
type Index = $word;
fn get(self) -> Self::Index {
self
}
}
};
}
impl_native_layout_word!(u16);
impl_native_layout_word!(u32);
impl_native_layout_word!(u64);
impl_native_layout_word!(usize);
macro_rules! impl_le_layout_word {
($word:ty, $index:ty) => {
impl LayoutWord for $word {
type Index = $index;
fn get(self) -> Self::Index {
<$word>::get(self)
}
}
};
}
impl_le_layout_word!(U16<LE>, u16);
impl_le_layout_word!(U32<LE>, u32);
impl_le_layout_word!(U64<LE>, u64);
pub trait LayoutSnapshotWord:
sealed::LayoutSnapshotWord
+ LayoutWord
+ FromBytes
+ Immutable
+ IntoBytes
+ KnownLayout
+ Unaligned
{
}
macro_rules! impl_layout_snapshot_word {
($word:ty) => {
impl sealed::LayoutSnapshotWord for $word {}
impl LayoutSnapshotWord for $word {}
};
}
impl_layout_snapshot_word!(U16<LE>);
impl_layout_snapshot_word!(U32<LE>);
impl_layout_snapshot_word!(U64<LE>);
pub trait SnapshotWidth: sealed::SnapshotWidth + LayoutIndex {
const WIDTH_CODE: u32;
type LittleEndianWord: LayoutSnapshotWord<Index = Self>;
fn to_le_word(self) -> Self::LittleEndianWord;
fn from_le_word(word: Self::LittleEndianWord) -> Self;
}
macro_rules! impl_snapshot_width {
($index:ty, $word:ty, $width_code:expr) => {
impl sealed::SnapshotWidth for $index {}
impl SnapshotWidth for $index {
const WIDTH_CODE: u32 = $width_code;
type LittleEndianWord = $word;
fn to_le_word(self) -> Self::LittleEndianWord {
<$word>::new(self)
}
fn from_le_word(word: Self::LittleEndianWord) -> Self {
<$word>::get(word)
}
}
};
}
impl_snapshot_width!(u16, U16<LE>, 0b00);
impl_snapshot_width!(u32, U32<LE>, 0b01);
impl_snapshot_width!(u64, U64<LE>, 0b10);
pub trait Axis: sealed::Axis {}
macro_rules! define_axis {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct $name;
impl sealed::Axis for $name {}
impl Axis for $name {}
};
}
define_axis!(
NodeAxis
);
define_axis!(
EdgeAxis
);
define_axis!(
VertexAxis
);
define_axis!(
HyperedgeAxis
);
define_axis!(
IncidenceAxis
);
pub struct LocalId<A, Width> {
value: Width,
axis: PhantomData<fn() -> A>,
}
impl<A, Width> LocalId<A, Width> {
#[inline]
#[must_use]
pub const fn new(value: Width) -> Self {
Self {
value,
axis: PhantomData,
}
}
}
impl<A, Width: Copy> LocalId<A, Width> {
#[inline]
#[must_use]
pub const fn get(self) -> Width {
self.value
}
}
impl<A, Width> From<Width> for LocalId<A, Width> {
#[inline]
fn from(value: Width) -> Self {
Self::new(value)
}
}
impl<A, Width: Clone> Clone for LocalId<A, Width> {
#[inline]
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
axis: PhantomData,
}
}
}
impl<A, Width: Copy> Copy for LocalId<A, Width> {}
impl<A, Width: PartialEq> PartialEq for LocalId<A, Width> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<A, Width: Eq> Eq for LocalId<A, Width> {}
impl<A, Width: PartialOrd> PartialOrd for LocalId<A, Width> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl<A, Width: Ord> Ord for LocalId<A, Width> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl<A, Width: Hash> Hash for LocalId<A, Width> {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<A, Width: fmt::Debug> fmt::Debug for LocalId<A, Width> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_tuple("LocalId").field(&self.value).finish()
}
}
impl<A, Width: fmt::Display> fmt::Display for LocalId<A, Width> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(formatter)
}
}
#[derive(Clone, Debug)]
pub struct IdSlice<'view, W: LayoutWord, Id> {
inner: core::slice::Iter<'view, W>,
id: PhantomData<fn() -> Id>,
}
impl<'view, W: LayoutWord, Id> IdSlice<'view, W, Id> {
#[inline]
#[must_use]
pub fn new(slice: &'view [W]) -> Self {
Self {
inner: slice.iter(),
id: PhantomData,
}
}
}
impl<W: LayoutWord, Id> Iterator for IdSlice<'_, W, Id>
where
Id: From<W::Index>,
{
type Item = Id;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|word| Id::from(word.get()))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<W: LayoutWord, Id> ExactSizeIterator for IdSlice<'_, W, Id>
where
Id: From<W::Index>,
{
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
}
impl<W: LayoutWord, Id> DoubleEndedIterator for IdSlice<'_, W, Id>
where
Id: From<W::Index>,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|word| Id::from(word.get()))
}
}
impl<W: LayoutWord, Id> FusedIterator for IdSlice<'_, W, Id> where Id: From<W::Index> {}
#[cfg(kani)]
mod proofs;