use std::num::TryFromIntError;
use bincode::{Decode, Encode};
macro_rules! id_newtype {
($(#[$meta:meta])* $name:ident($inner:ty)) => {
$(#[$meta])*
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Encode, Decode)]
pub struct $name(pub $inner);
impl $name {
#[inline]
#[must_use]
pub const fn get(self) -> $inner {
self.0
}
}
impl From<$name> for $inner {
#[inline]
fn from(id: $name) -> $inner {
id.0
}
}
impl TryFrom<usize> for $name {
type Error = TryFromIntError;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
<$inner>::try_from(value).map(Self)
}
}
};
}
id_newtype!(
StateId(u32)
);
impl StateId {
#[inline]
#[must_use]
pub fn cast_slice(raw: &[u32]) -> &[StateId] {
unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<StateId>(), raw.len()) }
}
}
id_newtype!(
TokenId(u32)
);
id_newtype!(
MintermId(u32)
);
id_newtype!(
ByteClassId(u16)
);
id_newtype!(
NodeId(u32)
);
id_newtype!(
TrieNodeId(u32)
);
#[cfg(test)]
mod tests {
use std::mem::{align_of, size_of};
use super::*;
#[test]
fn newtypes_are_zero_cost() {
assert_eq!(size_of::<StateId>(), size_of::<u32>());
assert_eq!(align_of::<StateId>(), align_of::<u32>());
assert_eq!(size_of::<TokenId>(), size_of::<u32>());
assert_eq!(size_of::<MintermId>(), size_of::<u32>());
assert_eq!(size_of::<NodeId>(), size_of::<u32>());
assert_eq!(align_of::<NodeId>(), align_of::<u32>());
assert_eq!(size_of::<ByteClassId>(), size_of::<u16>());
assert_eq!(align_of::<ByteClassId>(), align_of::<u16>());
}
#[test]
fn cast_slice_matches_element_by_element_construction_with_no_reallocation() {
let raw = [0u32, 7, 42, u32::MAX];
let cast = StateId::cast_slice(&raw);
let built: Vec<StateId> = raw.iter().copied().map(StateId).collect();
assert_eq!(cast, &built[..]);
assert_eq!(
cast.as_ptr().cast::<u32>(),
raw.as_ptr(),
"cast_slice must reinterpret raw's own allocation, not build a new one"
);
}
#[test]
fn cast_slice_of_empty_is_empty() {
let raw: [u32; 0] = [];
assert!(StateId::cast_slice(&raw).is_empty());
}
#[test]
fn checked_conversion_fails_out_of_range() {
assert_eq!(TokenId::try_from(7usize).map(TokenId::get), Ok(7));
if let Some(too_big) = usize::try_from(u32::MAX)
.ok()
.and_then(|m| m.checked_add(1))
{
assert!(TokenId::try_from(too_big).is_err());
}
assert!(ByteClassId::try_from(usize::from(u16::MAX) + 1).is_err());
if let Some(too_big) = usize::try_from(u32::MAX)
.ok()
.and_then(|m| m.checked_add(1))
{
assert!(NodeId::try_from(too_big).is_err());
}
}
}