#![allow(clippy::needless_borrow, clippy::borrow_deref_ref)]
use crate::{
Discriminant, Field, MaybeUninhabited, TypeLayout, TypeLayoutGraph, TypeLayoutInfo,
TypeStructure, Variant,
};
pub enum Serialiser<'a> {
Hasher { state: u64 },
Writer { buffer: &'a mut [u8], cursor: usize },
Counter { cursor: usize },
}
impl<'a> Serialiser<'a> {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
#[must_use]
pub const fn hasher(seed: u64) -> Self {
let mut hasher = Self::Hasher {
state: Self::FNV_OFFSET,
};
hasher.write_bytes(&seed.to_le_bytes());
hasher
}
#[must_use]
pub const fn hash(self) -> u64 {
let Self::Hasher { state } = self else {
panic!("not a hasher");
};
state
}
#[must_use]
pub const fn writer(buffer: &'a mut [u8], cursor: usize) -> Self {
Self::Writer { buffer, cursor }
}
#[must_use]
pub const fn counter(cursor: usize) -> Self {
Self::Counter { cursor }
}
#[must_use]
pub const fn cursor(self) -> usize {
let (Self::Counter { cursor } | Self::Writer { buffer: _, cursor }) = self else {
panic!("not a writer or counter");
};
cursor
}
}
impl<'a> Serialiser<'a> {
#[inline]
pub const fn write_bytes(&mut self, bytes: &[u8]) {
match self {
Self::Hasher { state } => {
let mut i = 0;
while i < bytes.len() {
*state ^= bytes[i] as u64;
*state = state.wrapping_mul(Self::FNV_PRIME);
i += 1;
}
},
Self::Writer { buffer, cursor } => {
assert!(
(*cursor + bytes.len()) <= buffer.len(),
"writer buffer exceeded"
);
let mut i = 0;
while i < bytes.len() {
buffer[*cursor] = bytes[i];
*cursor += 1;
i += 1;
}
},
Self::Counter { cursor: len } => *len += bytes.len(),
}
}
#[inline]
pub const fn write_byte(&mut self, byte: u8) {
match self {
Self::Hasher { state } => {
*state ^= byte as u64;
*state = state.wrapping_mul(Self::FNV_PRIME);
},
Self::Writer { buffer, cursor } => {
assert!((*cursor + 1) <= buffer.len(), "writer buffer exceeded");
buffer[*cursor] = byte;
*cursor += 1;
},
Self::Counter { cursor: len } => *len += 1,
}
}
}
impl<'a> Serialiser<'a> {
pub const fn serialise_str(&mut self, value: &str) {
self.serialise_usize(value.len());
self.write_bytes(value.as_bytes());
}
#[allow(clippy::cast_possible_truncation)]
pub const fn serialise_usize(&mut self, value: usize) {
let mut rem = value;
while rem > 0b0111_1111_usize {
self.write_byte(((rem & 0b0111_1111_usize) as u8) | 0b1000_0000_u8);
rem >>= 7_u8;
}
self.write_byte((rem & 0b0111_1111_usize) as u8);
}
pub const fn serialise_byte(&mut self, value: u8) {
self.write_byte(value);
}
pub const fn serialise_maybe_uninhabited(&mut self, value: MaybeUninhabited<()>) {
self.write_byte(match value {
MaybeUninhabited::Inhabited(()) => b'h',
MaybeUninhabited::Uninhabited => b'n',
});
}
const fn serialise_discriminant_bytes(&mut self, value_bytes: &[u8]) {
let mut leading_zeroes = 0;
while leading_zeroes < value_bytes.len() {
if value_bytes[leading_zeroes] != 0_u8 {
break;
}
leading_zeroes += 1;
}
self.serialise_usize(value_bytes.len() - leading_zeroes);
let mut i = leading_zeroes;
while i < value_bytes.len() {
self.write_byte(value_bytes[i]);
i += 1;
}
}
pub const fn serialise_discriminant(&mut self, value: &Discriminant) {
match value {
Discriminant::I8(_) => self.serialise_str(<i8 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::I16(_) => self.serialise_str(<i16 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::I32(_) => self.serialise_str(<i32 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::I64(_) => self.serialise_str(<i64 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::I128(_) => self.serialise_str(<i128 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::Isize(_) => self.serialise_str(<isize as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::U8(_) => self.serialise_str(<u8 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::U16(_) => self.serialise_str(<u16 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::U32(_) => self.serialise_str(<u32 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::U64(_) => self.serialise_str(<u64 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::U128(_) => self.serialise_str(<u128 as TypeLayout>::TYPE_LAYOUT.name),
Discriminant::Usize(_) => self.serialise_str(<usize as TypeLayout>::TYPE_LAYOUT.name),
};
match value {
Discriminant::I8(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::I16(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::I32(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::I64(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::I128(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::Isize(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::U8(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::U16(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::U32(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::U64(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::U128(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
Discriminant::Usize(v) => self.serialise_discriminant_bytes(&v.to_be_bytes()),
};
}
pub const fn serialise_field(&mut self, value: &Field) {
self.serialise_str(value.name);
self.serialise_maybe_uninhabited(match value.offset {
MaybeUninhabited::Inhabited(_) => MaybeUninhabited::Inhabited(()),
MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited,
});
match value.offset {
MaybeUninhabited::Inhabited(offset) => self.serialise_usize(offset),
MaybeUninhabited::Uninhabited => (),
};
self.serialise_str(value.ty);
}
pub const fn serialise_fields(&mut self, value: &[Field]) {
self.serialise_usize(value.len());
let mut i = 0;
while i < value.len() {
self.serialise_field(&value[i]);
i += 1;
}
}
pub const fn serialise_variant(&mut self, value: &Variant) {
self.serialise_str(value.name);
self.serialise_maybe_uninhabited(match value.discriminant {
MaybeUninhabited::Inhabited(_) => MaybeUninhabited::Inhabited(()),
MaybeUninhabited::Uninhabited => MaybeUninhabited::Uninhabited,
});
match &value.discriminant {
MaybeUninhabited::Inhabited(discriminant) => {
self.serialise_discriminant(discriminant);
},
MaybeUninhabited::Uninhabited => (),
};
self.serialise_fields(&value.fields);
}
pub const fn serialise_variants(&mut self, value: &[Variant]) {
self.serialise_usize(value.len());
let mut i = 0;
while i < value.len() {
self.serialise_variant(&value[i]);
i += 1;
}
}
pub const fn serialise_parameters(&mut self, value: &[&str]) {
self.serialise_usize(value.len());
let mut i = 0;
while i < value.len() {
self.serialise_str(value[i]);
i += 1;
}
}
pub const fn serialise_type_structure(&mut self, value: &TypeStructure) {
match value {
TypeStructure::Primitive => self.serialise_byte(b'p'),
TypeStructure::Struct { repr, fields } => {
self.serialise_byte(b's');
self.serialise_str(repr);
self.serialise_fields(fields);
},
TypeStructure::Union { repr, fields } => {
self.serialise_byte(b'u');
self.serialise_str(repr);
self.serialise_fields(fields);
},
TypeStructure::Enum { repr, variants } => {
self.serialise_byte(b'e');
self.serialise_str(repr);
self.serialise_variants(variants);
},
}
}
pub const fn serialise_type_layout_info(&mut self, value: &TypeLayoutInfo) {
self.serialise_str(value.name);
self.serialise_usize(value.size);
self.serialise_usize(value.alignment);
self.serialise_type_structure(&value.structure);
}
pub const fn serialise_type_layout_graph(&mut self, value: &TypeLayoutGraph) {
self.serialise_str(env!("CARGO_PKG_VERSION"));
self.serialise_str(value.ty);
self.serialise_usize(value.tys.len());
let mut i = 0;
while i < value.tys.len() {
self.serialise_type_layout_info(&*value.tys[i]);
i += 1;
}
}
}