use core::{fmt, marker::PhantomData, ops};
use crate::{
buffer::{Buffer, BufferExhausted, CheckedFixedBuffer, DryBuffer, MaybeFixedBuffer},
formula::{unwrap_size, BareFormula, Formula},
size::{FixedUsize, SIZE_STACK},
};
#[cfg(feature = "alloc")]
use crate::buffer::VecBuffer;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Sizes {
pub heap: usize,
pub stack: usize,
}
impl Sizes {
pub const ZERO: Self = Sizes { heap: 0, stack: 0 };
#[must_use]
#[inline(always)]
pub const fn with_heap(heap: usize) -> Self {
Sizes { heap, stack: 0 }
}
#[must_use]
#[inline(always)]
pub const fn with_stack(stack: usize) -> Self {
Sizes { heap: 0, stack }
}
#[inline(always)]
pub fn add_heap(&mut self, heap: usize) {
self.heap += heap;
}
#[inline(always)]
pub fn add_stack(&mut self, stack: usize) {
self.stack += stack;
}
#[inline(always)]
pub fn to_heap(&mut self, until: usize) -> usize {
let len = self.stack - until;
self.heap += len;
self.stack = until;
len
}
#[inline(always)]
pub fn total(&self) -> usize {
self.heap + self.stack
}
}
impl ops::Add for Sizes {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self {
Self {
heap: self.heap + rhs.heap,
stack: self.stack + rhs.stack,
}
}
}
impl ops::AddAssign for Sizes {
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.heap += rhs.heap;
self.stack += rhs.stack;
}
}
#[cfg_attr(
feature = "derive",
doc = r#"
When "derive" feature is enabled, `derive(Serialize)` is also available.
```
# use alkahest::*;
/// Self-serializable empty formula.
#[alkahest(Formula, Serialize)]
struct EmptyFormula {}
/// Another type serializable with `EmptyFormula`.
#[alkahest(Serialize<EmptyFormula>)]
struct EmptySerialize;
/// Formula for serializing tuple structures with fields
/// that are serializable with `u8` and `[u16]` formulas.
/// Slice formulas are serialized from some `IntoIterator`s and `SerIter` wrapper over any `Iterator`
/// with serializable item type.
#[alkahest(Formula)]
struct TupleFormula(u8, [u16]);
#[alkahest(Serialize<TupleFormula>)]
struct TupleSerialize(u8, std::iter::Once<u16>);
/// Formula for serializing structures with fields
/// that are serializable with `u8` and `str` formulas.
#[alkahest(Formula)]
struct StructFormula {
a: u8,
b: str,
}
# #[cfg(feature = "alloc")]
/// `String` can be serialized with `str` formula.
#[alkahest(Serialize<StructFormula>)]
struct StructSerialize {
a: u8,
b: String,
}
# #[cfg(feature = "alloc")]
/// Formula for serializing enums.
#[alkahest(Formula, Serialize)]
enum EnumFormula {
A,
B(u8),
C { y: String },
}
# #[cfg(feature = "alloc")]
/// `&str` can be serialized with `String` formula.
#[alkahest(Serialize<EnumFormula>)]
# // While `Formula` derive macro makes all variants and fields used,
# // this is not the case for `Serialize` derive macro.
# #[allow(dead_code)]
enum EnumSerialize<'a> {
A,
B(u8),
C { y: &'a str },
}
# #[cfg(feature = "alloc")]
/// `&str` can be serialized with `String` formula.
#[alkahest(Serialize<EnumFormula @C>)]
struct CVariantSerialize {
y: String,
}
```
Names of the formula variants and fields are important for `Serialize` and `Deserialize` derive macros.
"#
)]
pub trait Serialize<F: Formula + ?Sized> {
fn serialize<B>(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error>
where
Self: Sized,
B: Buffer;
fn size_hint(&self) -> Option<Sizes>;
}
pub trait SerializeRef<F: Formula + ?Sized> {
fn serialize<B>(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error>
where
B: Buffer;
fn size_hint(&self) -> Option<Sizes>;
}
impl<F, T> SerializeRef<F> for &T
where
F: Formula + ?Sized,
T: ?Sized,
for<'a> &'a T: Serialize<F>,
{
#[inline(always)]
fn serialize<B>(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error>
where
Self: Sized,
B: Buffer,
{
<&T as Serialize<F>>::serialize(self, sizes, buffer)
}
#[inline(always)]
fn size_hint(&self) -> Option<Sizes> {
<&T as Serialize<F>>::size_hint(self)
}
}
impl<F, T> Serialize<F> for &T
where
F: BareFormula + ?Sized,
T: SerializeRef<F> + ?Sized,
{
#[inline(always)]
fn serialize<B>(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error>
where
Self: Sized,
B: Buffer,
{
<T as SerializeRef<F>>::serialize(self, sizes, buffer)
}
#[inline(always)]
fn size_hint(&self) -> Option<Sizes> {
<T as SerializeRef<F>>::size_hint(self)
}
}
#[inline(always)]
pub fn serialize_into<F, T, B>(value: T, buffer: B) -> Result<(usize, usize), B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
let mut sizes = Sizes { heap: 0, stack: 0 };
let size = write_ref(value, &mut sizes, buffer)?;
Ok((sizes.heap, size))
}
#[inline(always)]
pub fn serialize<F, T>(value: T, output: &mut [u8]) -> Result<(usize, usize), BufferExhausted>
where
F: Formula + ?Sized,
T: Serialize<F>,
{
serialize_into::<F, T, _>(value, CheckedFixedBuffer::new(output))
}
#[inline(always)]
pub fn serialize_unchecked<F, T>(value: T, output: &mut [u8]) -> (usize, usize)
where
F: Formula + ?Sized,
T: Serialize<F>,
{
match serialize_into::<F, T, _>(value, output) {
Ok(sizes) => sizes,
Err(never) => match never {},
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct BufferSizeRequired {
pub required: usize,
}
impl fmt::Display for BufferSizeRequired {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "buffer size required: {}", self.required)
}
}
#[inline(always)]
pub fn serialize_or_size<F, T>(
value: T,
output: &mut [u8],
) -> Result<(usize, usize), BufferSizeRequired>
where
F: Formula + ?Sized,
T: Serialize<F>,
{
let mut exhausted = false;
let result = serialize_into::<F, T, _>(value, MaybeFixedBuffer::new(output, &mut exhausted));
let sizes = match result {
Ok(sizes) => sizes,
Err(never) => match never {},
};
if exhausted {
Err(BufferSizeRequired { required: sizes.0 })
} else {
Ok(sizes)
}
}
#[cfg(feature = "alloc")]
#[inline(always)]
pub fn serialize_to_vec<F, T>(value: T, output: &mut alloc::vec::Vec<u8>) -> (usize, usize)
where
F: Formula + ?Sized,
T: Serialize<F>,
{
match serialize_into::<F, T, _>(value, VecBuffer::new(output)) {
Ok(sizes) => sizes,
Err(never) => match never {},
}
}
#[inline(always)]
pub fn serialized_size<F, T>(value: T) -> (usize, usize)
where
F: Formula + ?Sized,
T: Serialize<F>,
{
let mut sizes = Sizes::ZERO;
match Serialize::<F>::serialize(value, &mut sizes, DryBuffer) {
Ok(()) => (sizes.total(), sizes.stack),
Err(never) => match never {},
}
}
#[inline(always)]
pub fn field_size_hint<F: Formula + ?Sized>(
value: &impl Serialize<F>,
last: bool,
) -> Option<Sizes> {
match (last, F::MAX_STACK_SIZE) {
(false, None) => None,
(true, _) => {
let sizes = value.size_hint()?;
Some(sizes)
}
(false, Some(max_stack)) => {
let sizes = value.size_hint()?;
debug_assert!(sizes.stack <= max_stack);
Some(Sizes {
heap: sizes.heap,
stack: max_stack,
})
}
}
}
#[inline(always)]
pub fn write_reference<F, B>(
size: usize,
address: usize,
heap: usize,
stack: usize,
mut buffer: B,
) -> Result<(), B::Error>
where
F: Formula + ?Sized,
B: Buffer,
{
let address = FixedUsize::truncate_unchecked(address);
let size = FixedUsize::truncate_unchecked(size);
if F::EXACT_SIZE {
buffer.write_stack(heap, stack, &address.to_le_bytes())?;
} else {
buffer.write_stack(heap, stack, &size.to_le_bytes())?;
buffer.write_stack(heap, stack + SIZE_STACK, &address.to_le_bytes())?;
}
Ok(())
}
#[inline(always)]
pub fn write_field<F, T, B>(
value: T,
sizes: &mut Sizes,
mut buffer: B,
last: bool,
) -> Result<(), B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
if !last && F::MAX_STACK_SIZE.is_none() {
buffer.write_stack(sizes.heap, sizes.stack, &[0; SIZE_STACK])?;
sizes.stack += SIZE_STACK;
}
let old_stack = sizes.stack;
<T as Serialize<F>>::serialize(value, sizes, buffer.reborrow())?;
match (F::MAX_STACK_SIZE, F::EXACT_SIZE, last) {
(None, _, false) => {
let size = FixedUsize::truncate_unchecked(sizes.stack - old_stack);
let res = buffer.write_stack(sizes.heap, old_stack - SIZE_STACK, &size.to_le_bytes());
if res.is_err() {
unreachable!("Successfully written before");
};
}
(None, _, true) => {}
(Some(max_stack), false, false) => {
debug_assert!(sizes.stack - old_stack <= max_stack);
buffer.pad_stack(sizes.heap, sizes.stack, old_stack + max_stack - sizes.stack)?;
sizes.stack = old_stack + max_stack;
}
(Some(max_stack), false, true) => {
debug_assert!(sizes.stack - old_stack <= max_stack);
}
(Some(max_stack), true, _) => {
debug_assert_eq!(sizes.stack - old_stack, max_stack);
}
}
Ok(())
}
#[inline(always)]
pub fn write_exact_size_field<F, T, B>(
value: T,
sizes: &mut Sizes,
buffer: B,
) -> Result<(), B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
debug_assert!(F::EXACT_SIZE);
let old_stack = sizes.stack;
<T as Serialize<F>>::serialize(value, sizes, buffer)?;
debug_assert_eq!(old_stack + unwrap_size(F::MAX_STACK_SIZE), sizes.stack);
Ok(())
}
#[inline(always)]
pub fn write_bytes<B>(bytes: &[u8], sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error>
where
B: Buffer,
{
buffer.write_stack(sizes.heap, sizes.stack, bytes)?;
sizes.stack += bytes.len();
Ok(())
}
#[cold]
#[inline(always)]
fn write_ref_slow<F, T, B>(value: T, sizes: &mut Sizes, mut buffer: B) -> Result<usize, B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
let old_stack = sizes.stack;
write_field(value, sizes, buffer.reborrow(), true)?;
let len = sizes.to_heap(old_stack);
buffer.move_to_heap(sizes.heap - len, sizes.stack + len, len);
Ok(len)
}
#[must_use]
#[inline(always)]
pub fn write_ref<F, T, B>(value: T, sizes: &mut Sizes, mut buffer: B) -> Result<usize, B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
let promised = <T as Serialize<F>>::size_hint(&value);
let stack = match promised {
None => write_ref_slow(value, sizes, buffer.reborrow())?,
Some(promised) => match buffer.reserve_heap(sizes.heap, sizes.stack, promised.total())? {
[] => match write_ref_slow(value, sizes, DryBuffer) {
Ok(stack) => stack,
Err(never) => match never {},
},
reserved => {
let mut reserved_sizes = Sizes {
heap: sizes.heap,
stack: 0,
};
<T as Serialize<F>>::serialize(value, &mut reserved_sizes, reserved)
.expect("Reserved enough space");
debug_assert_eq!(reserved_sizes.heap, sizes.heap + promised.heap);
debug_assert_eq!(reserved_sizes.stack, promised.stack);
sizes.heap = reserved_sizes.total();
reserved_sizes.stack
}
},
};
Ok(stack)
}
#[must_use]
pub struct SliceWriter<'a, F: Formula + ?Sized, B: Buffer + ?Sized> {
buffer: &'a mut B,
sizes: &'a mut Sizes,
count: usize,
marker: PhantomData<fn(&F)>,
}
impl<'a, F, B> SliceWriter<'a, F, B>
where
F: Formula + ?Sized,
B: Buffer + ?Sized,
{
#[inline(always)]
pub fn write_elem<T>(&mut self, value: T) -> Result<(), B::Error>
where
T: Serialize<F>,
{
if let Some(0) = <F as Formula>::MAX_STACK_SIZE {
debug_assert!(<F as Formula>::HEAPLESS);
debug_assert!(serialize::<F, T>(value, &mut []).is_ok());
self.count += 1;
Ok(())
} else {
write_field::<F, _, _>(value, self.sizes, self.buffer.reborrow(), false)
}
}
#[inline(always)]
pub fn finish(self) -> Result<(), B::Error> {
if let Some(0) = <F as Formula>::MAX_STACK_SIZE {
debug_assert!(<F as Formula>::HEAPLESS);
write_field::<FixedUsize, _, _>(self.count, self.sizes, self.buffer.reborrow(), true)?;
}
Ok(())
}
}
#[inline(always)]
pub fn slice_writer<'a, F, B>(sizes: &'a mut Sizes, buffer: &'a mut B) -> SliceWriter<'a, F, B>
where
F: Formula + ?Sized,
B: Buffer,
{
SliceWriter {
buffer,
sizes,
count: 0,
marker: PhantomData,
}
}
#[inline(always)]
pub fn write_slice<F, T, B>(
mut iter: impl Iterator<Item = T>,
sizes: &mut Sizes,
mut buffer: B,
) -> Result<(), B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
if let Some(0) = <F as Formula>::MAX_STACK_SIZE {
debug_assert!(<F as Formula>::HEAPLESS);
let count = if cfg!(debug_assertions) {
iter.fold(0, |acc, item| {
let r = serialize::<F, T>(item, &mut []);
assert!(r.is_ok());
acc + 1
})
} else {
iter.count()
};
write_field::<FixedUsize, _, _>(count, sizes, buffer, true)
} else {
iter.try_fold((), |(), elem| {
write_field::<F, _, _>(elem, sizes, buffer.reborrow(), false)
})
}
}
#[inline(always)]
pub fn write_array<F, T, B>(
mut iter: impl Iterator<Item = T>,
sizes: &mut Sizes,
mut buffer: B,
) -> Result<(), B::Error>
where
F: Formula + ?Sized,
T: Serialize<F>,
B: Buffer,
{
iter.try_fold((), |(), elem| {
write_field::<F, _, _>(elem, sizes, buffer.reborrow(), false)
})
}
#[must_use]
#[inline(always)]
pub const fn formula_fast_sizes<F>() -> Option<Sizes>
where
F: Formula + ?Sized,
{
match (F::EXACT_SIZE, F::HEAPLESS, F::MAX_STACK_SIZE) {
(true, true, Some(max_stack_size)) => Some(Sizes::with_stack(max_stack_size)),
_ => None,
}
}