From 314d56b6f5368e012327f1761fb58fedc117c10b Mon Sep 17 00:00:00 2001 From: Zakarum Date: Thu, 28 Dec 2023 11:54:05 +0100 Subject: [PATCH] Remove FixedUsize and FixedIsize wrappers Use usize and isize directly. --- Cargo.toml | 10 +- README.md | 15 +- src/bincoded.rs | 81 +++++++++- src/deserialize.rs | 29 ++-- src/iter.rs | 8 +- src/lib.rs | 3 +- src/packet.rs | 10 +- src/serialize.rs | 17 +- src/size.rs | 395 ++++++++++++--------------------------------- 9 files changed, 225 insertions(+), 343 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b676e9..0e9af67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,10 @@ inline-more = [] ## TODO: Control on value or type level? ## Keep features for defaults? -fixed8 = [] # sets size of `FixedUsize` and `FixedIsize` to 8 bits. -fixed16 = [] # sets size of `FixedUsize` and `FixedIsize` to 16 bits. -fixed32 = [] # sets size of `FixedUsize` and `FixedIsize` to 32 bits. Default. -fixed64 = [] # sets size of `FixedUsize` and `FixedIsize` to 64 bits. +fixed8 = [] # sets size of `usize` and `isize` to 8 bits. +fixed16 = [] # sets size of `usize` and `isize` to 16 bits. +fixed32 = [] # sets size of `usize` and `isize` to 32 bits. Default. +fixed64 = [] # sets size of `usize` and `isize` to 64 bits. default = ["alloc", "fixed32", "inline-more"] @@ -29,11 +29,13 @@ bincoded = ["dep:bincode", "dep:serde", "std"] [dependencies] alkahest-proc = { version = "=0.3.0", path = "proc", optional = true } +cfg-if = { version = "1.0" } bincode = { version = "1.3", optional = true } serde = { version = "1.0", optional = true } [dev-dependencies] rand = { version = "0.8", features = ["small_rng"] } +serde = { version = "1.0", features = ["derive"] } [[example]] name = "test" diff --git a/README.md b/README.md index 6816163..26eabd2 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ custom high-performance network protocols. This benchmark that mimics some game networking protocol. -| | `alkahest` | `bincode` | `rkyv` | `speedy` | -|:----------------|:-------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | -| **`serialize`** | `10.69 us` (✅ **1.00x**) | `11.08 us` (✅ **1.04x slower**) | `12.43 us` (❌ *1.16x slower*) | `11.13 us` (✅ **1.04x slower**) | -| **`read`** | `1.19 us` (✅ **1.00x**) | `9.19 us` (❌ *7.74x slower*) | `2.10 us` (❌ *1.77x slower*) | `1.54 us` (❌ *1.30x slower*) | +| | `alkahest` | `bincode` | `rkyv` | `speedy` | +| :-------------- | :----------------------- | :------------------------------ | :---------------------------- | :------------------------------ | +| **`serialize`** | `10.69 us` (✅ **1.00x**) | `11.08 us` (✅ **1.04x slower**) | `12.43 us` (❌ *1.16x slower*) | `11.13 us` (✅ **1.04x slower**) | +| **`read`** | `1.19 us` (✅ **1.00x**) | `9.19 us` (❌ *7.74x slower*) | `2.10 us` (❌ *1.77x slower*) | `1.54 us` (❌ *1.30x slower*) | --- Made with [criterion-table](https://github.com/nu11ptr/criterion-table) @@ -140,14 +140,11 @@ opening possibility for cross-language communication. `Formula` is implemented for a number of types out-of-the-box. Primitive types like `bool`, integers and floating point types all implement `Formula`. -This excludes `isize` and `usize`. -In their place there's `FixedUsize` and `FixedIsize` types provided, -whose size is controlled by a feature-flag. *!Caveat!*: - Sizes and addresses are serialized as `FixedUsize`. + Serialized size of `isize` and `usize` is controlled by a feature-flag. + Sizes and addresses are serialized as `usize`. Truncating `usize` value if it was too large. This may result in broken data generated and panic in debug. - Increase size of the `FixedUsize` if you encounter this. It is also implemented for tuples, array and slice, `Option` and `Vec` (the later requires `"alloc"` feature). The easiest way to define a new formula is to derive `Formula` trait for a struct or an enum. diff --git a/src/bincoded.rs b/src/bincoded.rs index 16cb1e2..6dbd71e 100644 --- a/src/bincoded.rs +++ b/src/bincoded.rs @@ -6,7 +6,7 @@ use crate::{ deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{reference_size, Formula}, serialize::{write_reference, Serialize, Sizes}, - size::{FixedUsize, FixedUsizeType}, + size::FixedUsizeType, }; /// A formula that can be used to serialize and deserialize data @@ -40,12 +40,9 @@ where }; let Ok(size) = FixedUsizeType::try_from(size) else { - panic!("Bincode serialization uses more that `FixedUsize::MAX` bytes"); + panic!("Bincode serialization uses more that `FixedUsizeType::MAX` bytes"); }; - let Ok(size) = FixedUsize::try_from(size) else { - panic!("Bincode serialization uses more that `usize::MAX` bytes"); - }; let size: usize = size.into(); match buffer.reserve_heap(sizes.heap, sizes.stack, size) { @@ -175,3 +172,77 @@ where >::deserialize_in_place(self, de) } } + +#[test] +fn roundtrip() { + use alkahest::{alkahest, Bincoded}; + + #[derive(Clone, Default, serde::Serialize, serde::Deserialize)] + pub struct BincodedStruct { + bytes: Box<[u8]>, + } + + #[derive(Clone)] + #[alkahest(Serialize, Deserialize<'_, BincodedWrapperFormula>)] + pub struct BincodedWrapperStruct { + wrapped: BincodedStruct, + } + + #[alkahest(Formula)] + pub struct BincodedWrapperFormula { + wrapped: Bincoded, + } + + #[derive(Clone)] + #[alkahest(Serialize)] + pub(crate) struct VariableHeaderV1Construction { + // A filter covering all keys within the table. + pub(crate) bincoded: BincodedWrapperStruct, + pub(crate) bytes: Vec, + } + + #[alkahest(Deserialize<'de, VariableHeaderV1Formula>)] + pub(crate) struct VariableHeaderV1Access<'de> { + // A filter covering all keys within the table. + pub(crate) bincoded: BincodedWrapperStruct, + pub(crate) bytes: &'de [u8], + } + + #[alkahest(Formula)] + pub(crate) struct VariableHeaderV1Formula { + bincoded: BincodedWrapperFormula, + bytes: alkahest::Bytes, + } + + let bincoded = BincodedWrapperStruct { + wrapped: BincodedStruct { + bytes: Box::new([4, 5, 6, 7]), + }, + }; + let header = VariableHeaderV1Construction { + bincoded, + bytes: vec![1, 2, 3, 4], + }; + let mut output = vec![0u8; 4096]; + let (serialized_len, size) = + alkahest::serialize::(header, &mut output).unwrap(); + output.truncate(serialized_len); + + let deserialized = alkahest::deserialize_with_size::< + VariableHeaderV1Formula, + VariableHeaderV1Access, + >(&output, size) + .unwrap(); + assert_eq!( + &*deserialized.bincoded.wrapped.bytes, + &[4, 5, 6, 7], + "Full serialized {:?}", + &output + ); + assert_eq!( + deserialized.bytes, + &[1, 2, 3, 4], + "Full serialized {:?}", + &output + ); +} diff --git a/src/deserialize.rs b/src/deserialize.rs index 67ff21b..0de1a91 100644 --- a/src/deserialize.rs +++ b/src/deserialize.rs @@ -2,7 +2,7 @@ use core::{any::type_name, iter::FusedIterator, marker::PhantomData, str::Utf8Er use crate::{ formula::{reference_size, unwrap_size, Formula}, - size::{FixedIsizeType, FixedUsize, FixedUsizeType, SIZE_STACK}, + size::{deserialize_usize, FixedIsizeType, FixedUsizeType, SIZE_STACK}, }; #[inline(always)] @@ -197,6 +197,17 @@ impl<'de> Deserializer<'de> { &self.input[at..] } + /// Reads and deserializes usize from the input buffer. + /// Advances the input buffer. + /// + /// # Errors + /// + /// Returns `DeserializeError` if deserialization fails. + #[inline(always)] + pub fn read_usize(&mut self) -> Result { + deserialize_usize(self.sub(SIZE_STACK)?) + } + /// Reads and deserializes field from the input buffer. /// Advances the input buffer. /// @@ -210,7 +221,7 @@ impl<'de> Deserializer<'de> { T: Deserialize<'de, F>, { let stack = match (F::MAX_STACK_SIZE, F::EXACT_SIZE, last) { - (None, _, false) => self.read_value::(false)?, + (None, _, false) => self.read_usize()?, (None, _, true) => self.stack, (Some(max_stack), false, true) => max_stack.min(self.stack), (Some(max_stack), _, _) => max_stack, @@ -259,7 +270,7 @@ impl<'de> Deserializer<'de> { let stack = match (last, F::MAX_STACK_SIZE) { (true, _) => self.stack, (false, Some(max_stack)) => max_stack, - (false, None) => self.read_value::(false)?, + (false, None) => self.read_usize()?, }; >::deserialize_in_place(place, self.sub(stack)?) @@ -308,7 +319,7 @@ impl<'de> Deserializer<'de> { { let upper = match F::MAX_STACK_SIZE { None => panic!("Formula must be sized"), - Some(0) => self.read_value::(true).unwrap_or(0), + Some(0) => self.read_usize().unwrap_or(0), Some(max_stack) => self.stack / max_stack, }; @@ -331,7 +342,7 @@ impl<'de> Deserializer<'de> { { let upper = match F::MAX_STACK_SIZE { None => self.stack / SIZE_STACK, - Some(0) => self.read_value::(true).unwrap_or(0), + Some(0) => self.read_usize().unwrap_or(0), Some(max_stack) => self.stack / max_stack, }; @@ -406,7 +417,7 @@ impl<'de> Deserializer<'de> { match F::MAX_STACK_SIZE { None => { for _ in 0..n { - let skip_bytes = self.read_value::(false)?; + let skip_bytes = self.read_usize()?; self.read_bytes(skip_bytes)?; } } @@ -536,7 +547,7 @@ where let sub = Deserializer::new_unchecked(SIZE_STACK, self.de.input); self.de.input = &self.de.input[..self.de.input.len() - SIZE_STACK]; - let stack = match >::deserialize(sub) { + let stack = match deserialize_usize(sub) { Ok(stack) => stack, Err(err) => { self.de.stack = 0; @@ -779,13 +790,13 @@ where if F::EXACT_SIZE { let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); - let Ok(address) = de.read_value::(true) else { + let Ok(address) = de.read_usize() else { unreachable!(); }; (address, unwrap_size(F::MAX_STACK_SIZE).min(len)) } else { let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); - let Ok([size, address]) = de.read_value::<[FixedUsize; 2], [usize; 2]>(true) else { + let Ok([size, address]) = de.read_value::<[usize; 2], [usize; 2]>(true) else { unreachable!(); }; (address, size) diff --git a/src/iter.rs b/src/iter.rs index 7317cc2..4c30bf9 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -3,7 +3,7 @@ use crate::{ deserialize::DeserializeError, formula::Formula, serialize::{field_size_hint, write_slice, Serialize, Sizes}, - size::{FixedUsize, SIZE_STACK}, + size::SIZE_STACK, }; const ITER_UPPER: usize = 4; @@ -258,7 +258,7 @@ where // Typically `usize` is not serializable. // But lib makes exception for `usize`s that are derived from actual sizes. -impl Serialize<[(FixedUsize, F)]> for core::iter::Enumerate +impl Serialize<[(usize, F)]> for core::iter::Enumerate where F: Formula, I: Iterator, @@ -269,12 +269,12 @@ where where B: Buffer, { - serialize_iter_to_slice!((FixedUsize, F) : self => sizes, buffer) + serialize_iter_to_slice!((usize, F) : self => sizes, buffer) } #[inline(always)] fn size_hint(&self) -> Option { - default_iter_fast_sizes::<(FixedUsize, F), _>(self) + default_iter_fast_sizes::<(usize, F), _>(self) } } diff --git a/src/lib.rs b/src/lib.rs index 3cceac0..8f71e39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,6 @@ pub use crate::{ serialize, serialize_or_size, serialize_unchecked, serialized_size, BufferSizeRequired, Serialize, SerializeRef, }, - size::{FixedIsize, FixedUsize}, skip::Skip, vlq::Vlq, }; @@ -97,7 +96,7 @@ pub mod advanced { write_exact_size_field, write_field, write_ref, write_reference, write_slice, Sizes, SliceWriter, }, - size::{FixedIsize, FixedIsizeType}, + size::{FixedIsizeType, FixedUsizeType}, }; #[cfg(feature = "alloc")] diff --git a/src/packet.rs b/src/packet.rs index 2c5d25e..b3e514d 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,9 +1,10 @@ use crate::{ + advanced::FixedUsizeType, buffer::{Buffer, BufferExhausted, CheckedFixedBuffer, DryBuffer, VecBuffer}, deserialize::{read_reference, Deserialize, DeserializeError, Deserializer}, formula::{reference_size, Formula}, serialize::{write_ref, write_reference, Serialize, Sizes}, - size::{FixedUsize, SIZE_STACK}, + size::SIZE_STACK, }; /// Returns the number of bytes required to write packet with the value. @@ -129,9 +130,10 @@ where } else { let mut bytes = [0u8; SIZE_STACK]; bytes.copy_from_slice(&input[..SIZE_STACK]); - let address = - FixedUsize::from_le_bytes(bytes).expect("Value size can't fit `usize`"); - Some(address.into()) + let address = FixedUsizeType::from_le_bytes(bytes) + .try_into() + .expect("Value size can't fit `usize`"); + Some(address) } } } diff --git a/src/serialize.rs b/src/serialize.rs index 4501891..cff7e44 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -3,7 +3,7 @@ use core::{fmt, marker::PhantomData, ops}; use crate::{ buffer::{Buffer, BufferExhausted, CheckedFixedBuffer, DryBuffer, MaybeFixedBuffer}, formula::{unwrap_size, BareFormula, Formula}, - size::{FixedUsize, SIZE_STACK}, + size::{usize_truncate_unchecked, SIZE_STACK}, }; #[cfg(feature = "alloc")] @@ -492,14 +492,11 @@ where F: Formula + ?Sized, B: Buffer, { - let address = FixedUsize::truncate_unchecked(address); - let size = FixedUsize::truncate_unchecked(size); + let address = usize_truncate_unchecked(address); + let size = usize_truncate_unchecked(size); if F::EXACT_SIZE { - debug_assert_eq!( - size, - FixedUsize::truncate_unchecked(F::MAX_STACK_SIZE.unwrap()) - ); + debug_assert_eq!(size, usize_truncate_unchecked(F::MAX_STACK_SIZE.unwrap())); buffer.write_stack(heap, stack, &address.to_le_bytes())?; } else { buffer.write_stack(heap, stack, &size.to_le_bytes())?; @@ -537,7 +534,7 @@ where match (F::MAX_STACK_SIZE, F::EXACT_SIZE, last) { (None, _, false) => { - let size = FixedUsize::truncate_unchecked(sizes.stack - old_stack); + let size = usize_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"); @@ -711,7 +708,7 @@ where pub fn finish(self) -> Result<(), B::Error> { if let Some(0) = ::MAX_STACK_SIZE { debug_assert!(::HEAPLESS); - write_field::(self.count, self.sizes, self.buffer.reborrow(), true)?; + write_field::(self.count, self.sizes, self.buffer.reborrow(), true)?; } Ok(()) } @@ -768,7 +765,7 @@ where } else { iter.count() }; - write_field::(count, sizes, buffer, true) + write_field::(count, sizes, buffer, true) } else { iter.try_fold((), |(), elem| { write_field::(elem, sizes, buffer.reborrow(), false) diff --git a/src/size.rs b/src/size.rs index e9fa7b9..fb31532 100644 --- a/src/size.rs +++ b/src/size.rs @@ -1,160 +1,75 @@ -use core::{mem::size_of, num::TryFromIntError}; +use core::mem::size_of; use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{BareFormula, Formula}, - serialize::{Serialize, Sizes}, + serialize::{write_bytes, Serialize, Sizes}, }; -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed8")] -pub type FixedUsizeType = u8; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed16")] -pub type FixedUsizeType = u16; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed32")] -pub type FixedUsizeType = u32; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed64")] -pub type FixedUsizeType = u64; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed8")] -pub type FixedIsizeType = i8; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed16")] -pub type FixedIsizeType = i16; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed32")] -pub type FixedIsizeType = i32; - -/// Type used to represent sizes and offsets in serialized data. -#[cfg(feature = "fixed64")] -pub type FixedIsizeType = i64; - -/// Type used to represent sizes and offsets in serialized data. -/// This places limitation on sequence sizes which practically is never hit. -/// `usize` itself is not portable and cannot be written into alkahest package. -#[repr(transparent)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FixedUsize(FixedUsizeType); - -impl FixedUsize { - /// Truncates `usize` to `FixedUsizeType` without checking. - #[must_use] - #[inline(always)] - pub fn truncate_unchecked(value: usize) -> Self { - debug_assert!(FixedUsize::try_from(value).is_ok()); - FixedUsize(value as FixedUsizeType) - } - - /// Converts to byte array in little endian. - #[must_use] - #[inline(always)] - pub fn to_le_bytes(self) -> [u8; size_of::()] { - self.0.to_le_bytes() - } - - /// Converts from byte array in little endian. - /// - /// # Errors - /// - /// Returns `Err` if the byte array does not represent a valid `usize`. - #[inline(always)] - pub fn from_le_bytes(bytes: [u8; size_of::()]) -> Result { - FixedUsizeType::from_le_bytes(bytes).try_into() +cfg_if::cfg_if! { + if #[cfg(feature = "fixed8")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedUsizeType = u8; + } else if #[cfg(feature = "fixed16")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedUsizeType = u16; + } else if #[cfg(feature = "fixed32")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedUsizeType = u32; + } else if #[cfg(feature = "fixed64")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedUsizeType = u64; + } else { + compile_error!("No fixed size integer feature enabled"); } } -impl TryFrom for FixedUsize { - type Error = TryFromIntError; - - #[inline(always)] - fn try_from(value: usize) -> Result { - FixedUsizeType::try_from(value).map(FixedUsize) +cfg_if::cfg_if! { + if #[cfg(feature = "fixed8")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedIsizeType = i8; + } else if #[cfg(feature = "fixed16")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedIsizeType = i16; + } else if #[cfg(feature = "fixed32")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedIsizeType = i32; + } else if #[cfg(feature = "fixed64")] { + /// Type used to represent sizes and offsets in serialized data. + pub type FixedIsizeType = i64; + } else { + compile_error!("No fixed size integer feature enabled"); } } -impl TryFrom for FixedUsize { - type Error = TryFromIntError; - - #[inline(always)] - fn try_from(value: FixedUsizeType) -> Result { - usize::try_from(value)?; - Ok(FixedUsize(value)) - } -} +pub const SIZE_STACK: usize = size_of::(); -impl From for usize { - #[inline(always)] - fn from(value: FixedUsize) -> Self { - value.0 as usize - } +pub fn usize_truncate_unchecked(value: usize) -> FixedUsizeType { + debug_assert!(FixedUsizeType::try_from(value).is_ok()); + value as FixedUsizeType } -impl From for FixedUsizeType { - #[inline(always)] - fn from(value: FixedUsize) -> Self { - value.0 - } +pub fn isize_truncate_unchecked(value: isize) -> FixedIsizeType { + debug_assert!(FixedIsizeType::try_from(value).is_ok()); + value as FixedIsizeType } -impl Formula for FixedUsize { +impl Formula for usize { const MAX_STACK_SIZE: Option = Some(size_of::()); const EXACT_SIZE: bool = true; const HEAPLESS: bool = true; } -impl BareFormula for FixedUsize {} - -impl Serialize for FixedUsize { - #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> - where - B: Buffer, - { - >::serialize(self.0, sizes, buffer) - } - - #[inline(always)] - fn size_hint(&self) -> Option { - Some(Sizes::with_stack(size_of::())) - } -} - -impl Serialize for &FixedUsize { - #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> - where - B: Buffer, - { - >::serialize(self.0, sizes, buffer) - } - - #[inline(always)] - fn size_hint(&self) -> Option { - Some(Sizes::with_stack(size_of::())) - } -} +impl BareFormula for usize {} -impl Serialize for usize { +impl Serialize for usize { #[inline(always)] fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { - Serialize::::serialize( - FixedUsize::truncate_unchecked(self).0, - sizes, - buffer, - ) + serialize_usize(self, sizes, buffer) } #[inline(always)] @@ -163,17 +78,13 @@ impl Serialize for usize { } } -impl Serialize for &usize { +impl Serialize for &usize { #[inline(always)] fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { - Serialize::::serialize( - FixedUsize::truncate_unchecked(*self).0, - sizes, - buffer, - ) + serialize_usize(*self, sizes, buffer) } #[inline(always)] @@ -182,127 +93,34 @@ impl Serialize for &usize { } } -impl Deserialize<'_, FixedUsize> for FixedUsize { - #[inline(always)] - fn deserialize(de: Deserializer) -> Result { - let value = >::deserialize(de)?; - - #[cfg(debug_assertions)] - if usize::try_from(value).is_err() { - return Err(DeserializeError::InvalidUsize(value)); - } - - Ok(FixedUsize(value)) - } - - #[inline(always)] - fn deserialize_in_place(&mut self, de: Deserializer) -> Result<(), DeserializeError> { - >::deserialize_in_place(&mut self.0, de) - } -} - -impl Deserialize<'_, FixedUsize> for usize { +impl Deserialize<'_, usize> for usize { #[inline(always)] fn deserialize(de: Deserializer) -> Result { - let value = >::deserialize(de)?; - - #[cfg(debug_assertions)] - if usize::try_from(value).is_err() { - return Err(DeserializeError::InvalidUsize(value)); - } - - Ok(value as usize) + deserialize_usize(de) } #[inline(always)] fn deserialize_in_place(&mut self, de: Deserializer) -> Result<(), DeserializeError> { - *self = >::deserialize(de)?; + *self = deserialize_usize(de)?; Ok(()) } } -/// Type used to represent sizes and offsets in serialized data. -/// This places limitation on sequence sizes which practically is never hit. -/// `usize` itself is not portable and cannot be written into alkahest package. -#[repr(transparent)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct FixedIsize(FixedIsizeType); - -impl FixedIsize { - /// Truncates `isize` to `FixedIsizeType` without checking. - #[must_use] - #[inline(always)] - pub fn truncate_unchecked(value: isize) -> Self { - debug_assert!(FixedIsize::try_from(value).is_ok()); - FixedIsize(value as FixedIsizeType) - } - - /// Converts to byte array in little endian. - #[must_use] - #[inline(always)] - pub fn to_le_bytes(self) -> [u8; size_of::()] { - self.0.to_le_bytes() - } - - /// Converts from byte array in little endian. - /// - /// # Errors - /// - /// Returns `Err` if the byte array does not represent a valid `isize`. - #[inline(always)] - pub fn from_le_bytes(bytes: [u8; size_of::()]) -> Result { - FixedIsizeType::from_le_bytes(bytes).try_into() - } -} - -impl TryFrom for FixedIsize { - type Error = TryFromIntError; - - #[inline(always)] - fn try_from(value: isize) -> Result { - FixedIsizeType::try_from(value).map(FixedIsize) - } -} - -impl TryFrom for FixedIsize { - type Error = TryFromIntError; - - #[inline(always)] - fn try_from(value: FixedIsizeType) -> Result { - isize::try_from(value)?; - Ok(FixedIsize(value)) - } -} - -impl From for isize { - #[inline(always)] - fn from(value: FixedIsize) -> Self { - value.0 as isize - } -} - -impl From for FixedIsizeType { - #[inline(always)] - fn from(value: FixedIsize) -> Self { - value.0 - } -} - -impl Formula for FixedIsize { +impl Formula for isize { const MAX_STACK_SIZE: Option = Some(size_of::()); const EXACT_SIZE: bool = true; const HEAPLESS: bool = true; } -impl BareFormula for FixedIsize {} +impl BareFormula for isize {} -impl Serialize for FixedIsize { +impl Serialize for isize { #[inline(always)] fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { - >::serialize(self.0, sizes, buffer) + serialize_isize(self, sizes, buffer) } #[inline(always)] @@ -311,13 +129,13 @@ impl Serialize for FixedIsize { } } -impl Serialize for &FixedIsize { +impl Serialize for &isize { #[inline(always)] fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { - >::serialize(self.0, sizes, buffer) + serialize_isize(*self, sizes, buffer) } #[inline(always)] @@ -326,82 +144,67 @@ impl Serialize for &FixedIsize { } } -impl Serialize for isize { +impl Deserialize<'_, isize> for isize { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> - where - B: Buffer, - { - Serialize::::serialize( - FixedIsize::truncate_unchecked(self).0, - sizes, - buffer, - ) + fn deserialize(de: Deserializer) -> Result { + deserialize_isize(de) } #[inline(always)] - fn size_hint(&self) -> Option { - Some(Sizes::with_stack(size_of::())) + fn deserialize_in_place(&mut self, de: Deserializer) -> Result<(), DeserializeError> { + *self = deserialize_isize(de)?; + Ok(()) } } -impl Serialize for &isize { - #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> - where - B: Buffer, - { - Serialize::::serialize( - FixedIsize::truncate_unchecked(*self).0, - sizes, - buffer, - ) - } - - #[inline(always)] - fn size_hint(&self) -> Option { - Some(Sizes::with_stack(size_of::())) - } +#[inline(always)] +pub fn serialize_usize(value: usize, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> +where + B: Buffer, +{ + write_bytes( + &usize_truncate_unchecked(value).to_le_bytes(), + sizes, + buffer, + ) } -impl Deserialize<'_, FixedIsize> for FixedIsize { - #[inline(always)] - fn deserialize(de: Deserializer) -> Result { - let value = >::deserialize(de)?; +#[inline(always)] +pub fn serialize_isize(value: isize, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> +where + B: Buffer, +{ + write_bytes( + &isize_truncate_unchecked(value).to_le_bytes(), + sizes, + buffer, + ) +} - #[cfg(debug_assertions)] - if isize::try_from(value).is_err() { - return Err(DeserializeError::InvalidIsize(value)); - } +#[inline(always)] +pub fn deserialize_usize(mut de: Deserializer) -> Result { + let input = de.read_byte_array::<{ size_of::() }>()?; + // de.finish()?; + let value = ::from_le_bytes(input); - Ok(FixedIsize(value)) + #[cfg(debug_assertions)] + if usize::try_from(value).is_err() { + return Err(DeserializeError::InvalidUsize(value)); } - #[inline(always)] - fn deserialize_in_place(&mut self, de: Deserializer) -> Result<(), DeserializeError> { - >::deserialize_in_place(&mut self.0, de) - } + Ok(value as usize) } -impl Deserialize<'_, FixedIsize> for isize { - #[inline(always)] - fn deserialize(de: Deserializer) -> Result { - let value = >::deserialize(de)?; +#[inline(always)] +pub fn deserialize_isize(mut de: Deserializer) -> Result { + let input = de.read_byte_array::<{ size_of::() }>()?; + // de.finish()?; + let value = ::from_le_bytes(input); - #[cfg(debug_assertions)] - if isize::try_from(value).is_err() { - return Err(DeserializeError::InvalidIsize(value)); - } - - Ok(value as isize) + #[cfg(debug_assertions)] + if usize::try_from(value).is_err() { + return Err(DeserializeError::InvalidIsize(value)); } - #[inline(always)] - fn deserialize_in_place(&mut self, de: Deserializer) -> Result<(), DeserializeError> { - *self = >::deserialize(de)?; - Ok(()) - } + Ok(value as isize) } - -/// Stack space occupied by sizes and addresses. -pub const SIZE_STACK: usize = size_of::();