Make 'alloc' and 'std' default features; Make serialisations variably sized again; Refactor derive implementations; Completely rework streams; Fix tuple deserialisation; Encode 'FixedString' in UTF-8; Remove methods 'from_chars' and 'set_len' from 'FixedString'; Rename 'as_slice' and 'as_mut_slice' methods in 'FixedString' to 'as_st' and 'as_mut_str'; Add methods 'as_bytes', 'push_str', 'chars', 'capacity', and 'char_indices' to 'FixedString'; Rework 'FixedString' traits; Remove 'FixedIter'; Update lints; Add methods 'set_len' and 'set_len_unchecked' to 'Buffer'; Elaborate docs; Update readme; Do not require 'Serialise' for 'Deserialise'; Rename 'SERIALISED_SIZE' in 'Serialise' to 'MAX_SERIALISED_SIZE'; Use streams in 'Serialise' and 'Deserialise'; Drop 'Serialise' requirement for 'Buffer'; Add methods 'with_capacity' and 'capacity' to 'Buffer';

This commit is contained in:
Gabriel Bjørnager Jensen 2024-08-31 12:55:15 +02:00
parent 3b29e72624
commit a872a5c245
24 changed files with 1215 additions and 1040 deletions

View file

@ -3,6 +3,29 @@
This is the changelog of bzipper.
See `"README.md"` for more information.
## 0.7.0
* Make `alloc` and `std` default features
* Make serialisations variably sized again
* Refactor derive implementations
* Completely rework streams
* Fix tuple deserialisation
* Encode `FixedString` in UTF-8
* Remove methods `from_chars` and `set_len` from `FixedString`
* Rename `as_slice` and `as_mut_slice` methods in `FixedString` to `as_st` and `as_mut_str`
* Add methods `as_bytes`, `push_str`, `chars`, `capacity`, and `char_indices` to `FixedString`
* Rework `FixedString` traits
* Remove `FixedIter`
* Update lints
* Add methods `set_len` and `set_len_unchecked` to `Buffer`
* Elaborate docs
* Update readme
* Do not require `Serialise` for `Deserialise`
* Rename `SERIALISED_SIZE` in `Serialise` to `MAX_SERIALISED_SIZE`
* Use streams in `Serialise` and `Deserialise`
* Drop `Serialise` requirement for `Buffer`
* Add methods `with_capacity` and `capacity` to `Buffer`
## 0.6.2
* Fix `Deserialise` derive for unit variants

View file

@ -101,7 +101,6 @@ option_as_ref_cloned = "warn"
option_if_let_else = "warn"
option_option = "deny"
or_fun_call = "deny"
panic_in_result_fn = "deny"
path_buf_push_overwrite = "deny"
pattern_type_mismatch = "deny"
ptr_as_ptr = "forbid"
@ -122,7 +121,6 @@ return_self_not_must_use = "deny"
same_functions_in_if_condition = "deny"
same_name_method = "deny"
self_named_module_files = "deny"
semicolon_outside_block = "warn"
single_char_pattern = "warn"
str_split_at_newline = "warn"
string_lit_as_bytes = "deny"

View file

@ -2,7 +2,7 @@
[bzipper](https://crates.io/crates/bzipper/) is a binary (de)serialiser for the Rust language.
Contrary to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the goal of bzipper is to serialise with a known size constraint.
In contrast to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the primary goal of bzipper is to serialise with a known size constraint.
Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.
Keep in mind that this project is still work-in-progress.
@ -20,15 +20,15 @@ For strings, the `FixedString` type is also provided.
## Usage
This crate revolves around the `Serialise` and `Deserialise` traits, both of which are commonly used in conjunction with streams (more specifically, s-streams and d-streams).
This crate revolves around the `Serialise` and `Deserialise` traits, both of which use *streams* – or more specifically – s-streams and d-streams.
Many core types come implemented with bzipper, including primitives as well as some standard library types such as `Option` and `Result`.
It is recommended in most cases to just derive these traits for custom types (enumerations and structures only).
Here, each field is chained in declaration order:
It is recommended in most cases to just derive these two traits for custom types (although this is only supported with enumerations and structures).
Here, each field is *chained* according to declaration order:
```rs
use bzipper::{Deserialise, Serialise};
```rust
use bzipper::{Buffer, Deserialise, Serialise};
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct IoRegister {
@ -36,45 +36,55 @@ struct IoRegister {
value: u16,
}
let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
let mut buf = Buffer::new();
buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
assert_eq!(buf.len(), 0x6);
assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
```
### Serialisation
To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation.
The required size of any given serialisation is specified by the `SERIALISED_SIZE` constant:
To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation and wrap it in an s-stream (*serialisation stream*) with the `Sstream` type.
```rs
use bzipper::Serialise;
```rust
use bzipper::{Serialise, Sstream};
let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
'Ж'.serialise(&mut buf).unwrap();
let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE];
let mut stream = Sstream::new(&mut buf);
assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
'Ж'.serialise(&mut stream).unwrap();
assert_eq!(stream, [0x00, 0x00, 0x04, 0x16]);
```
The only special requirement of the `serialise` method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
The maximum size of any given serialisation is specified by the `MAX_SERIALISED_SIZE` constant.
We can also use streams to *chain* multiple elements together:
We can also use streams to chain multiple elements together:
```rs
use bzipper::Serialise;
```rust
use bzipper::{Serialise, Sstream};
let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
let mut stream = bzipper::Sstream::new(&mut buf);
let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE * 0x5];
let mut stream = Sstream::new(&mut buf);
stream.append(&'ل');
stream.append(&'ا');
stream.append(&'م');
stream.append(&'د');
stream.append(&'ا');
// Note: For serialising multiple characters, the
// `FixedString` type is usually preferred.
assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);
'ل'.serialise(&mut stream).unwrap();
'ا'.serialise(&mut stream).unwrap();
'م'.serialise(&mut stream).unwrap();
'د'.serialise(&mut stream).unwrap();
'ا'.serialise(&mut stream).unwrap();
assert_eq!(buf, [
0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27,
0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F,
0x00, 0x00, 0x06, 0x27
]);
```
When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
@ -82,25 +92,33 @@ It is recommended for implementors to adhere to this convention as well.
### Deserialisation
Deserialisation works with an almost identical syntax to serialisation.
Deserialisation works with a similar syntax to serialisation.
To deserialise a buffer, simply call the `deserialise` method:
D-streams (*deserialisation streams*) use the `Dstream` type and are constructed in a manner similar to s-streams.
To deserialise a buffer, simply call the `deserialise` method with the strema:
```rs
use bzipper::Deserialise;
```rust
use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
let stream = Dstream::new(&data);
assert_eq!(u16::deserialise(&stream).unwrap(), 0x4554);
```
Just like with serialisations, the `Dstream` can be used to deserialise chained elements:
And just like s-streams, d-streams can also be used to handle chaining:
```rs
use bzipper::Deserialise;
```rust
use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
let stream = bzipper::Dstream::new(&data);
let stream = Dstream::new(&data);
assert_eq!(stream.take::<u8>().unwrap(), 0x45);
assert_eq!(stream.take::<u8>().unwrap(), 0x54);
assert_eq!(u8::deserialise(&stream).unwrap(), 0x45);
assert_eq!(u8::deserialise(&stream).unwrap(), 0x54);
// The data can also be deserialised as a tuple (up
// to twelve elements).
let stream = Dstream::new(&data);
assert_eq!(<(u8, u8)>::deserialise(&stream).unwrap(), (0x45, 0x54));
```

View file

@ -1,6 +1,6 @@
[package]
name = "bzipper"
version = "0.6.2"
version = "0.7.0"
edition = "2021"
rust-version = "1.81"
documentation = "https://docs.rs/bzipper/"
@ -16,11 +16,13 @@ license.workspace = true
all-features = true
[features]
default = ["alloc", "std"]
alloc = []
std = []
[dependencies]
bzipper_macros = { path = "../bzipper_macros", version = "0.6.2"}
bzipper_macros = { path = "../bzipper_macros", version = "0.7.0"}
[lints]
workspace = true

View file

@ -22,112 +22,201 @@
#[cfg(test)]
mod test;
use crate::{Deserialise, Result, Serialise};
use crate::{Deserialise, Dstream, Result, Serialise, Sstream};
use alloc::vec;
use alloc::boxed::Box;
use core::borrow::Borrow;
use core::fmt::{Debug, Formatter};
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
// We cannot use arrays for the `Buffer` type as
// that would require `generic_const_exprs`.
/// Typed (de)serialisation buffer.
///
/// This structure is intended as a lightweight wrapper around byte buffers for specific (de)serialisations of specific types.
///
/// The methods [`write`](Self::write) and [`read`](Self::read) can be used to <interpreting> the internal buffer.
/// The methods [`write`](Self::write) and [`read`](Self::read) can be used to handle the internal buffer.
/// Other methods exist for accessing the internal buffer directly.
///
/// # Examples
///
/// Create a buffer for holding a `Request` enumeration:
///
/// ```
/// ```rust
/// use bzipper::{Buffer, FixedString, Serialise};
///
/// #[derive(Serialise)]
/// enum Request {
/// Join { username: FixedString<0x10> },
/// Join { username: FixedString<0x40> },
///
/// Quit { username: FixedString<0x10> },
/// Quit { username: FixedString<0x40> },
///
/// SendMessage { message: FixedString<0x20> },
/// SendMessage { message: FixedString<0x80> },
/// }
///
/// use Request::*;
///
/// let join_request = Join { username: FixedString::try_from("epsiloneridani").unwrap() };
///
/// let mut buf = Buffer::<Request>::new();
/// buf.write(&join_request);
/// let mut buf = Buffer::new();
/// buf.write(join_request);
///
/// // Do something with the buffer...
/// ```
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
#[derive(Clone, Eq, PartialEq)]
pub struct Buffer<T: Serialise> {
pub struct Buffer<T> {
buf: Box<[u8]>,
len: usize,
_phanton: PhantomData<T>
}
impl<T: Serialise> Buffer<T> {
/// Allocates a new buffer suitable for (de)serialisation.
impl<T> Buffer<T> {
/// Allocates a new buffer suitable for serialisation.
///
/// The given capacity should be large enough to hold any expected serialisation of `T`.
/// Therefore, if `T` implements [`Serialise`], it is recommended to use [`new`](Self::new) instead, which is equivalent to passing [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) to this function:
#[inline]
#[must_use]
pub fn new() -> Self { Self { buf: vec![0x00; T::SERIALISED_SIZE].into(), _phanton: PhantomData } }
pub fn with_capacity(len: usize) -> Self {
Self {
buf: vec![0x00; len].into(),
len: 0x0,
/// Serialises into the contained buffer.
_phanton: PhantomData,
}
}
/// Sets the length of the used buffer.
///
/// The provided size is checked before being written.
/// For the same operation *without* checks, see [`set_len_unchecked`](Self::set_len_unchecked).
///
/// # Panics
///
/// The provided size must not be greater than the buffer's capacity.
/// If this is the case, however, this method will panic.
#[inline(always)]
pub fn write(&mut self, value: &T) -> Result<()> { value.serialise(&mut self.buf) }
pub fn set_len(&mut self, len: usize) {
assert!(len <= self.capacity(), "cannot extend buffer beyond capacity");
/// Retrieves a pointer to the first byte.
self.len = len;
}
/// Sets the length of the used buffer without checks.
///
/// The validity of the provided size is **not** checked before being written.
/// For the same operation *with* checks, see [`set_len`](Self::set_len).
///
/// # Safety
///
/// If the value of `len` is greater than the buffer's capacity, behaviour is undefined.
#[inline(always)]
pub unsafe fn set_len_unchecked(&mut self, len: usize) { self.len = len }
/// Retrieves a pointer to the first byte of the internal buffer.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() }
/// Retrieves a mutable pointer to the first byte.
/// Retrieves a mutable pointer to the first byte of the internal buffer.
#[inline(always)]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut u8 { self.buf.as_mut_ptr() }
/// Gets a slice of the internal buffer.
///
/// The returned slice will only include the used part of the buffer (as specified by [`len`](Self::len)).
#[inline(always)]
#[must_use]
pub const fn as_slice(&self) -> &[u8] { unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) } }
/// Gets a mutable slice of the internal buffer.
///
/// In contrast to [`as_slice`](Self::as_slice), this method returns a slice of the **entire** internal buffer.
///
/// If the returned reference is written through, the new buffer length -- if different -- should be set using [`set_len`](Self::set_len).
#[inline(always)]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u8] { &mut self.buf }
/// Gets the length of the buffer.
///
/// This is defined as (and therefore always equal to) the value of [SERIALISED_SIZE](Serialise::SERIALISED_SIZE) as specified by `T`.
#[allow(clippy::len_without_is_empty)]
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { T::SERIALISED_SIZE }
pub const fn len(&self) -> usize { self.len }
/// Gets the capacity of the buffer.
///
/// If the buffer was constructed using [`new`](Self::new), this value is exactly the same as [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE).
#[inline(always)]
#[must_use]
pub const fn capacity(&self) -> usize { self.buf.len() }
}
impl<T: Serialise> Buffer<T> {
/// Allocates a new buffer suitable for serialisation.
///
/// The capacity of the internal buffer is set so that any serialisation of `T` may be stored.
///
/// This is equivalent to calling [`with_capacity`](Self::with_capacity) with [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE).
#[inline(always)]
#[must_use]
pub fn new() -> Self { Self::with_capacity(T::MAX_SERIALISED_SIZE) }
/// Serialises into the contained buffer.
///
/// # Errors
///
/// Any error that occurs during serialisation is passed on and returned from this method.
///
/// # Panics
///
/// If the amount of bytes read by [`serialise`](Serialise::serialise) is greater than that specified by [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE), this method panics.
///
/// In reality, however, this error can only be detected if the buffer's capacity is set to a value greater than `MAX_SERIALISED_SIZE` to begin with (e.g. using [`with_capacity`](Self::with_capacity)).
#[inline(always)]
pub fn write<U: Borrow<T>>(&mut self, value: U) -> Result<()> {
let mut stream = Sstream::new(&mut self.buf);
value.borrow().serialise(&mut stream)?;
assert!(stream.len() <= T::MAX_SERIALISED_SIZE);
self.len = stream.len();
Ok(())
}
}
impl<T: Deserialise> Buffer<T> {
/// Deserialises from the contained buffer.
///
/// # Errors
///
/// Any error that occurs during deserialisation is passed on and returned from this method.
#[inline(always)]
pub fn read(&self) -> Result<T> { T::deserialise(&self.buf) }
pub fn read(&self) -> Result<T> {
// We should only pass the used part of the buffer
// to `deserialise`.
let stream = Dstream::new(&self.buf[0x0..self.len()]);
let value = Deserialise::deserialise(&stream)?;
Ok(value)
}
}
impl<T: Serialise> AsMut<[u8]> for Buffer<T> {
impl<T> AsMut<[u8]> for Buffer<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_slice() }
}
impl<T: Serialise> AsRef<[u8]> for Buffer<T> {
impl<T> AsRef<[u8]> for Buffer<T> {
#[inline(always)]
fn as_ref(&self) -> &[u8] { self.as_slice() }
}
impl<T: Serialise> Debug for Buffer<T> {
impl<T> Debug for Buffer<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "{:?}", self.as_slice()) }
}
@ -137,19 +226,24 @@ impl<T: Serialise> Default for Buffer<T> {
fn default() -> Self { Self::new() }
}
impl<T: Serialise> Deref for Buffer<T> {
impl<T> Deref for Buffer<T> {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &Self::Target { self.as_slice() }
}
impl<T: Serialise> DerefMut for Buffer<T> {
impl<T> DerefMut for Buffer<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_slice() }
}
impl<T: Serialise> PartialEq<&[u8]> for Buffer<T> {
impl<T> PartialEq<&[u8]> for Buffer<T> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
}
impl<T, const N: usize> PartialEq<[u8; N]> for Buffer<T> {
#[inline(always)]
fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
}

View file

@ -25,11 +25,11 @@ use crate::{Buffer, Error};
fn test_buffer() {
let mut buf = Buffer::<char>::new();
buf.write(&'\u{1F44D}').unwrap();
buf.write('\u{1F44D}').unwrap();
assert_eq!(buf, [0x00, 0x01, 0xF4, 0x4D].as_slice());
buf.as_mut_slice().copy_from_slice(&[0x00, 0x00, 0xD8, 0x00]);
assert!(matches!(buf.read(), Err(Error::InvalidCodePoint { value: 0xD800 })));
assert!(matches!(buf.read(), Err(Error::InvalidCodePoint(0xD800))));
buf.as_mut_slice().copy_from_slice(&[0x00, 0x00, 0xFF, 0x3A]);
assert_eq!(buf.read().unwrap(), '\u{FF3A}');

View file

@ -31,15 +31,12 @@ use core::num::NonZero;
mod tuple;
/// Types capable of being deserialised.
///
/// This trait requires [`Serialise`] also being implemented as it relies on the [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE) constant.
pub trait Deserialise: Serialise + Sized {
/// Deserialises a slice into an object.
/// Denotes a type capable of deserialisation.
pub trait Deserialise: Sized {
/// Deserialises an object from the given d-stream.
///
/// This function must **never** take more bytes than specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE).
/// This method must **never** read more bytes than specified by [`MAX_SERIALISED_SIZE`](crate::Serialise::MAX_SERIALISED_SIZE) (if [`Serialise`] is defined, that is).
/// Doing so is considered a logic error.
/// Likewise, providing more than this amount is also disfavoured.
///
/// # Errors
///
@ -47,22 +44,20 @@ pub trait Deserialise: Serialise + Sized {
///
/// # Panics
///
/// This method will usually panic if the provided slice has a length *less* than the value of `SERIALISED_SIZE`.
/// This method will usually panic if the provided slice has a length *less* than the value of `MAX_SERIALISED_SIZE`.
/// Official implementations of this trait (including those that are derived) always panic in debug mode if the provided slice has a length that is different at all.
fn deserialise(data: &[u8]) -> Result<Self>;
fn deserialise(stream: &Dstream) -> Result<Self>;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Deserialise for $ty {
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
const SIZE: usize = ::core::mem::size_of::<$ty>();
let data = data
.get(0x0..SIZE)
.ok_or(::bzipper::Error::EndOfStream { req: SIZE, rem: data.len() })?
#[inline]
fn deserialise(stream: &Dstream) -> ::bzipper::Result<Self> {
let data = stream
.read(Self::MAX_SERIALISED_SIZE)
.unwrap()
//.ok_or(::bzipper::Error::EndOfStream { req: Self::MAX_SERIALISED_SIZE, rem: data.len() })?
.try_into()
.unwrap();
@ -75,34 +70,29 @@ macro_rules! impl_numeric {
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Deserialise for NonZero<$ty> {
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
#[inline]
fn deserialise(stream: &Dstream) -> ::bzipper::Result<Self> {
let value = <$ty as ::bzipper::Deserialise>::deserialise(stream)?;
let value = <$ty as ::bzipper::Deserialise>::deserialise(data)?;
let value = NonZero::new(value)
.ok_or(Error::NullInteger)?;
NonZero::new(value)
.ok_or(Error::NullInteger)
Ok(value)
}
}
};
}
impl<T, const N: usize> Deserialise for [T; N]
where
T: Deserialise {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
impl<T: Deserialise, const N: usize> Deserialise for [T; N] {
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
// Initialise the array incrementally.
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
let mut pos = 0x0;
for item in &mut buf {
let range = pos..pos + T::SERIALISED_SIZE;
pos = range.end;
item.write(Deserialise::deserialise(&data[range])?);
let value = T::deserialise(stream)?;
item.write(value);
}
// This should be safe as `MaybeUninit<T>` is
@ -118,83 +108,80 @@ where
}
impl Deserialise for bool {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u8::deserialise(data)?;
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = u8::deserialise(stream)?;
match value {
0x00 => Ok(false),
0x01 => Ok(true),
_ => Err(Error::InvalidBoolean { value })
_ => Err(Error::InvalidBoolean(value))
}
}
}
impl Deserialise for char {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = u32::deserialise(stream)?;
let value = u32::deserialise(data)?;
let value = value
.try_into()
.map_err(|_| Error::InvalidCodePoint(value))?;
Self::from_u32(value)
.ok_or(Error::InvalidCodePoint { value })
Ok(value)
}
}
impl Deserialise for Infallible {
#[allow(clippy::panic_in_result_fn)]
#[inline(always)]
fn deserialise(_data: &[u8]) -> Result<Self> { panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with") }
fn deserialise(_stream: &Dstream) -> Result<Self> { panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with") }
}
impl Deserialise for isize {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = i32::deserialise(stream)?;
let value = i32::deserialise(data)?
.try_into().expect("unable to convert from `i32` to `isize`");
let value = value
.try_into()
.expect("unable to convert from `i32` to `isize`");
Ok(value)
}
}
impl<T: Deserialise> Deserialise for Option<T> {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
#[allow(clippy::if_then_some_else_none)]
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let sign = bool::deserialise(stream)?;
let stream = Dstream::new(data);
let sign = stream.take::<bool>()?;
if sign {
Ok(Some(stream.take::<T>()?))
let value = if sign {
Some(T::deserialise(stream)?)
} else {
Ok(None)
}
None
};
Ok(value)
}
}
impl<T> Deserialise for PhantomData<T> {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok(Self)
}
#[inline(always)]
fn deserialise(_stream: &Dstream) -> Result<Self> { Ok(Self) }
}
impl<T: Deserialise, E: Deserialise> Deserialise for core::result::Result<T, E> {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let stream = Dstream::new(data);
let sign = stream.take::<bool>()?;
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let sign = bool::deserialise(stream)?;
let value = if sign {
Err(stream.take::<E>()?)
Err(E::deserialise(stream)?)
} else {
Ok(stream.take::<T>()?)
Ok(T::deserialise(stream)?)
};
Ok(value)
@ -202,15 +189,18 @@ impl<T: Deserialise, E: Deserialise> Deserialise for core::result::Result<T, E>
}
impl Deserialise for () {
fn deserialise(_data: &[u8]) -> Result<Self> { Ok(()) }
#[inline(always)]
fn deserialise(_stream: &Dstream) -> Result<Self> { Ok(()) }
}
impl Deserialise for usize {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = u32::deserialise(stream)?;
let value = u32::deserialise(data)?
.try_into().expect("unable to convert from `u32` to `usize`");
let value = value
.try_into()
.expect("must be able to convert from `u32` to `usize`");
Ok(value)
}

View file

@ -19,7 +19,9 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use crate::{Deserialise, Serialise};
use core::char;
use crate::{Deserialise, Dstream, Serialise};
#[test]
fn test() {
@ -46,9 +48,10 @@ fn test() {
($ty:ty: $data:expr => $value:expr) => {{
use ::bzipper::{Deserialise, Serialise};
let buf: [u8; <$ty as Serialise>::SERIALISED_SIZE] = $data;
let mut buf: [u8; <$ty as Serialise>::MAX_SERIALISED_SIZE] = $data;
let stream = Dstream::new(&mut buf);
let left = <$ty as Deserialise>::deserialise(&buf).unwrap();
let left = <$ty as Deserialise>::deserialise(&stream).unwrap();
let right = $value;
assert_eq!(left, right);
@ -80,6 +83,8 @@ fn test() {
0xBF, 0x4F, 0xAF, 0x5F, 0x9F, 0x6F, 0x8F, 0x7F,
] => 0xFF_0F_EF_1F_DF_2F_CF_3F_BF_4F_AF_5F_9F_6F_8F_7F);
test!(char: [0x00, 0x00, 0xFF, 0xFD] => char::REPLACEMENT_CHARACTER);
test!([char; 0x5]: [
0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, 0x91,
0x00, 0x00, 0x03, 0xBC, 0x00, 0x00, 0x03, 0x94,

View file

@ -19,17 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use crate::{Deserialise, Result, Serialise};
use crate::{Deserialise, Dstream, Result};
impl<T0> Deserialise for (T0, )
where
T0: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -37,13 +37,13 @@ impl<T0, T1> Deserialise for (T0, T1)
where
T0: Deserialise,
T1: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -52,14 +52,14 @@ where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -69,15 +69,15 @@ where
T1: Deserialise,
T2: Deserialise,
T3: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -88,16 +88,16 @@ where
T2: Deserialise,
T3: Deserialise,
T4: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -109,17 +109,17 @@ where
T3: Deserialise,
T4: Deserialise,
T5: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -132,18 +132,18 @@ where
T4: Deserialise,
T5: Deserialise,
T6: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -157,19 +157,19 @@ where
T5: Deserialise,
T6: Deserialise,
T7: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -184,20 +184,20 @@ where
T6: Deserialise,
T7: Deserialise,
T8: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -213,21 +213,21 @@ where
T7: Deserialise,
T8: Deserialise,
T9: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -244,22 +244,22 @@ where
T8: Deserialise,
T9: Deserialise,
T10: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}
@ -277,22 +277,22 @@ where
T9: Deserialise,
T10: Deserialise,
T11: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
fn deserialise(stream: &Dstream) -> Result<Self> {
let value = (
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
Ok(value)
}
}

View file

@ -19,16 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use crate::{Deserialise, Error, Result};
use crate::{Error, Result};
use core::cell::Cell;
use core::fmt::{Debug, Formatter};
/// Byte stream for deserialisation.
/// Byte stream suitable for deserialisation.
///
/// This type borrows a slice, keeping track internally of the used bytes.
/// This type borrows a buffer, keeping track internally of the used bytes.
pub struct Dstream<'a> {
data: &'a [u8],
pos: Cell<usize>,
pub(in crate) data: &'a [u8],
pub(in crate) pos: Cell<usize>,
}
impl<'a> Dstream<'a> {
@ -37,22 +38,84 @@ impl<'a> Dstream<'a> {
#[must_use]
pub const fn new(data: &'a [u8]) -> Self { Self { data, pos: Cell::new(0x0) } }
/// Deserialises an object from the stream.
///
/// # Errors
///
/// If the stream doesn't hold at least the amount of bytes specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE), an [`EndOfStream`](Error::EndOfStream) error is returned.
/// Takes (borrows) raw bytes from the stream.
#[inline]
pub fn take<T: Deserialise>(&self) -> Result<T> {
pub fn read(&self, count: usize) -> Result<&[u8]> {
let rem = self.data.len() - self.pos.get();
let req = T::SERIALISED_SIZE;
let req = count;
if rem < req { return Err(Error::EndOfStream { req, rem }) };
if rem < req { return Err(Error::EndOfStream { req, rem }) }
let start = self.pos.get();
let stop = start + req;
self.pos.set(stop);
T::deserialise(&self.data[start..stop])
let data = &self.data[start..stop];
Ok(data)
}
/// Gets a pointer to the first byte in the stream.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 { self.data.as_ptr() }
/// Gets a slice of the stream.
#[inline(always)]
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
let ptr = self.as_ptr();
let len = self.len();
unsafe { core::slice::from_raw_parts(ptr, len) }
}
/// Gets the length of the stream.
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { unsafe { self.pos.as_ptr().read() } }
/// Tests if the stream is empty.
///
/// If no deserialisations have been made at the time of calling, this method returns `false`.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
/// Tests if the stream is full.
///
/// Note that zero-sized types such as [`()`](unit) can still be deserialised from this stream.
#[inline(always)]
#[must_use]
pub const fn is_full(&self) -> bool { self.len() == self.data.len() }
}
impl Debug for Dstream<'_> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_slice(), f) }
}
impl<'a> From<&'a [u8]> for Dstream<'a> {
#[inline(always)]
fn from(value: &'a [u8]) -> Self { Self::new(value) }
}
impl<'a> From<&'a mut [u8]> for Dstream<'a> {
#[inline(always)]
fn from(value: &'a mut [u8]) -> Self { Self::new(value) }
}
impl PartialEq for Dstream<'_> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
}
impl PartialEq<&[u8]> for Dstream<'_> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
}
impl<const N: usize> PartialEq<[u8; N]> for Dstream<'_> {
#[inline(always)]
fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
}

View file

@ -28,47 +28,53 @@ use alloc::boxed::Box;
/// Mapping of [`core::result::Result`].
pub type Result<T> = core::result::Result<T, Error>;
/// (De)serialisation failures.
/// bzipper errors.
///
/// These variants are used when deserialisation fails.
/// Serialisations are assumed infallible.
#[derive(Debug)]
pub enum Error {
/// An array could not hold the requested amount of elements.
ArrayTooShort { req: usize, len: usize },
ArrayTooShort {
/// The required amount of bytes.
req: usize,
/// The remaining amount of bytes.
len: usize,
},
/// A string encountered an invalid UTF-8 sequence.
BadString { source: Utf8Error },
/// An implementor-defined error.
/// An unspecified (de)serialisation error.
///
/// This is mainly useful if none of the predefined errors are appropriate.
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
CustomError { source: Box<dyn core::error::Error> },
CustomError(Box<dyn core::error::Error>),
/// Bytes were requested on an empty stream.
EndOfStream { req: usize, rem: usize },
/// A boolean encountered a value outside `0` and `1`.
InvalidBoolean { value: u8 },
InvalidBoolean(u8),
/// An invalid code point was encountered.
///
/// This includes surrogate points in the inclusive range `U+D800` to `U+DFFF`, as well as values larger than `U+10FFFF`.
InvalidCodePoint { value: u32 },
InvalidCodePoint(u32),
/// An invalid enumeration descriminant was provided.
InvalidDiscriminant { value: u32 },
InvalidDiscriminant(u32),
/// An `isize` value couldn't fit into `16` bits.
IsizeOutOfRange { value: isize },
/// An `isize` value couldn't fit into `32` bits.
IsizeOutOfRange(isize),
/// A non-zero integer encountered the value `0`.
NullInteger,
/// A `usize` value couldn't fit into `16` bits.
UsizeOutOfRange { value: usize },
/// A `usize` value couldn't fit into `32` bits.
UsizeOutOfRange(usize),
}
impl Display for Error {
@ -83,28 +89,28 @@ impl Display for Error {
=> write!(f, "unable to parse utf8: \"{source}\""),
#[cfg(feature = "alloc")]
CustomError { ref source }
CustomError(ref source)
=> write!(f, "{source}"),
EndOfStream { req, rem }
=> write!(f, "({req}) byte(s) were requested but only ({rem}) byte(s) were left"),
InvalidBoolean { value }
InvalidBoolean(value)
=> write!(f, "expected boolean but got {value:#02X}"),
InvalidCodePoint { value }
InvalidCodePoint(value)
=> write!(f, "code point U+{value:04X} is not valid"),
InvalidDiscriminant { value }
InvalidDiscriminant(value)
=> write!(f, "discriminant ({value}) is not valid for the given enumeration"),
IsizeOutOfRange { value }
IsizeOutOfRange(value)
=> write!(f, "signed size value ({value}) cannot be serialised: must be in the range ({}) to ({})", i16::MIN, i16::MAX),
NullInteger
=> write!(f, "expected non-zero integer but got (0)"),
UsizeOutOfRange { value }
UsizeOutOfRange(value)
=> write!(f, "unsigned size value ({value}) cannot be serialised: must be at most ({})", u16::MAX),
}
}
@ -118,7 +124,7 @@ impl core::error::Error for Error {
BadString { ref source } => Some(source),
#[cfg(feature = "alloc")]
CustomError { ref source } => Some(source.as_ref()),
CustomError(ref source) => Some(source.as_ref()),
_ => None,
}

View file

@ -1,46 +0,0 @@
// Copyright 2024 Gabriel Bjørnager Jensen.
//
// This file is part of bzipper.
//
// bzipper is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Lesser General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// bzipper is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Less-
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use core::mem::MaybeUninit;
/// Iterator to a fixed vector.
///
/// This type is used by the [`FixedString`](crate::FixedString) type for iterating over an owned string.
#[must_use]
pub struct FixedIter<T, const N: usize> {
pub(in crate) buf: [MaybeUninit<T>; N],
pub(in crate) pos: usize,
pub(in crate) len: usize,
}
impl<T, const N: usize> Iterator for FixedIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.len { return None };
let item = unsafe { self.buf[self.pos].assume_init_read() };
self.pos += 0x1;
Some(item)
}
}

View file

@ -22,39 +22,55 @@
#[cfg(test)]
mod test;
use crate::{Deserialise, Error, FixedIter, Serialise};
use crate::{
Deserialise,
Dstream,
Error,
Serialise,
Sstream,
};
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::fmt::{Debug, Display, Formatter};
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut, Index, IndexMut};
use core::hash::{Hash, Hasher};
use core::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut};
use core::slice::SliceIndex;
use core::str::FromStr;
use core::str::{Chars, CharIndices, FromStr};
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
use alloc::string::String;
/// Owned string with maximum size.
#[cfg(feature = "std")]
use std::ffi::OsStr;
#[cfg(feature = "std")]
use std::net::ToSocketAddrs;
#[cfg(feature = "std")]
use std::path::Path;
/// Heap-allocated string with maximum size.
///
/// This is in contrast to [String] -- which has no size limit in practice -- and [str], which is unsized.
///
/// The string itself is encoded in UTF-8 for interoperability wtih Rust's standard string facilities, as well as for memory concerns.
///
/// Keep in mind that the size limit specified by `N` denotes *bytes* and not *characters* -- i.e. a value of `8` may translate to between two and eight characters, depending on their codepoints.
///
/// # Examples
///
/// All instances of this type have the same size if the value of `N` is also the same.
/// This size can be found through
///
/// `size_of::<char>() * N + size_of::<usize>()`.
///
/// Therefore, the following four strings have -- despite their different contents -- the same total size.
///
/// ```
/// ```rust
/// use bzipper::FixedString;
/// use std::str::FromStr;
///
/// let str0 = FixedString::<0xF>::new(); // Empty string.
/// let str1 = FixedString::<0xF>::from_str("Hello there!");
/// let str2 = FixedString::<0xF>::from_str("أنا من أوروپا");
/// let str3 = FixedString::<0xF>::from_str("COGITO ERGO SUM");
/// let str0 = FixedString::<0x40>::new(); // Empty string.
/// let str1 = FixedString::<0x40>::from_str("Hello there!").unwrap();
/// let str2 = FixedString::<0x40>::from_str("أنا من أوروپا").unwrap();
/// let str3 = FixedString::<0x40>::from_str("COGITO ERGO SUM").unwrap();
///
/// assert_eq!(size_of_val(&str0), size_of_val(&str1));
/// assert_eq!(size_of_val(&str0), size_of_val(&str2));
@ -64,10 +80,10 @@ use alloc::string::{String, ToString};
/// assert_eq!(size_of_val(&str2), size_of_val(&str3));
/// ```
///
/// These three strings can---by extend in theory---also interchange their contents between each other.
#[derive(Clone, Deserialise, Serialise)]
/// These three strings can -- by extend in theory -- also interchange their contents between each other.
#[derive(Clone)]
pub struct FixedString<const N: usize> {
buf: [char; N],
buf: [u8; N],
len: usize,
}
@ -81,62 +97,78 @@ impl<const N: usize> FixedString<N> {
/// The constructed string will have a null length.
/// All characters inside the internal buffer are instanced as `U+0000 NULL`.
///
/// For constructing a string with an already defined buffer, see [`from_chars`](Self::from_chars) and [`from_raw_parts`](Self::from_raw_parts).
/// For constructing a string with an already defined buffer, see [`from_raw_parts`](Self::from_raw_parts) and [`from_str`](Self::from_str).
#[inline(always)]
#[must_use]
pub const fn new() -> Self { Self { buf: ['\0'; N], len: 0x0 } }
pub const fn new() -> Self { Self { buf: [0x00; N], len: 0x0 } }
/// Consumes the buffer into a fixed string.
/// Constructs a new, fixed-size string from raw parts.
///
/// The internal length is to `N`.
/// For a similar function but with an explicit size, see [`from_raw_parts`](Self::from_raw_parts).
#[inline(always)]
#[must_use]
pub const fn from_chars(buf: [char; N]) -> Self { Self { buf, len: N } }
/// Constructs a fixed string from raw parts.
#[inline(always)]
#[must_use]
pub const fn from_raw_parts(buf: [char; N], len: usize) -> Self { Self { buf, len } }
/// Deconstructs a fixed string into its raw parts.
#[inline(always)]
#[must_use]
pub const fn into_raw_parts(self) -> ([char; N], usize) { (self.buf, self.len) }
/// Gets a pointer to the first character.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const char { self.buf.as_ptr() }
/// Gets a mutable pointer to the first character.
/// The provided parts are not tested in any way.
///
/// This function can only be marked as `const` when `const_mut_refs` is implemented.
/// See tracking issue [`#57349`](https://github.com/rust-lang/rust/issues/57349/) for more information.
/// # Safety
///
/// The value of `len` may not exceed that of `N`.
/// Additionally, the octets in `buf` (from index zero up to the value of `len`) must be valid UTF-8 codepoints.
///
/// If any of these requirements are violated, behaviour is undefined.
#[inline(always)]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut char { self.buf.as_mut_ptr() }
pub const unsafe fn from_raw_parts(buf: [u8; N], len: usize) -> Self { Self { buf, len } }
/// Borrows the string as a character slice.
/// Destructs the provided string into its raw parts.
///
/// The returned values are valid to pass on to [`from_raw_parts`](Self::from_raw_parts).
///
/// The returned byte array is guaranteed to be fully initialised.
/// However, only octets up to an index of [`len`](Self::len) are also guaranteed to be valid UTF-8 codepoints.
#[inline(always)]
#[must_use]
pub const fn into_raw_parts(self) -> ([u8; N], usize) { (self.buf, self.len) }
/// Gets a pointer to the first octet.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() }
// This function can only be marked as `const` when
// `const_mut_refs` is implemented. See tracking
// issue #57349 for more information.
/// Gets a mutable pointer to the first octet.
///
#[inline(always)]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut u8 { self.buf.as_mut_ptr() }
/// Borrows the string as a byte slice.
///
/// The range of the returned slice only includes characters that are "used."
/// For borrowing the entire internal buffer, see [`as_mut_slice`](Self::as_mut_slice).
#[inline(always)]
#[must_use]
pub const fn as_slice(&self) -> &[char] {
pub const fn as_bytes(&self) -> &[u8] {
// We need to use `from_raw_parts` to mark this
// function `const`.
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
/// Mutably borrows the string as a character slice.
/// Borrows the string as a string slice.
///
/// The range of the returned slice includes the entire internal buffer.
/// For borrowing only the "used" characters, see [`as_slice`](Self::as_slice).
/// The range of the returned slice only includes characters that are "used."
#[inline(always)]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [char] { &mut self.buf[0x0..self.len] }
pub const fn as_str(&self) -> &str { unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } }
/// Mutably borrows the string as a string slice.
///
/// The range of the returned slice only includes characters that are "used."
#[inline(always)]
#[must_use]
pub fn as_mut_str(&mut self) -> &mut str {
let range = 0x0..self.len();
unsafe { core::str::from_utf8_unchecked_mut(&mut self.buf[range]) }
}
/// Returns the length of the string.
///
@ -145,28 +177,32 @@ impl<const N: usize> FixedString<N> {
#[must_use]
pub const fn len(&self) -> usize { self.len }
/// Checks if the string is empty, i.e. `self.len() == 0x0`.
/// Checks if the string is empty, i.e. no characters are contained.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
/// Checks if the string is full, i.e. `self.len() == N`.
/// Checks if the string is full, i.e. it cannot hold any more characters.
#[inline(always)]
#[must_use]
pub const fn is_full(&self) -> bool { self.len() == N }
/// Sets the internal length.
/// Returns the total capacity of the string.
///
/// The length is compared with `N` to guarantee that bounds are honoured.
///
/// # Panics
///
/// This method panics if the value of `len` is greater than that of `N`.
/// This is defined as being exactly the value of `N`.
#[inline(always)]
pub fn set_len(&mut self, len: usize) {
assert!(self.len <= N, "cannot set length longer than the fixed size");
self.len = len;
}
#[must_use]
pub const fn capacity(&self) -> usize { N }
/// Gets a substring of the string.
#[inline(always)]
#[must_use]
pub fn get<I: SliceIndex<str>>(&self, index: I) -> Option<&I::Output> { self.as_str().get(index) }
/// Gets a mutable substring of the string.
#[inline(always)]
#[must_use]
pub fn get_mut<I: SliceIndex<str>>(&mut self, index: I) -> Option<&mut I::Output> { self.as_mut_str().get_mut(index) }
/// Pushes a character into the string.
///
@ -174,13 +210,34 @@ impl<const N: usize> FixedString<N> {
///
/// # Panics
///
/// If the string cannot hold any more character (i.e. it is full), this method will panic.
/// If the string cannot hold the provided character *after* encoding, this method will panic.
#[inline(always)]
pub fn push(&mut self, c: char) {
assert!(!self.is_full(), "cannot push character to full string");
let mut buf = [0x00; 0x4];
let s = c.encode_utf8(&mut buf);
self.buf[self.len] = c;
self.len += 0x1;
self.push_str(s);
}
/// Pushes a string slice into the string.
///
/// The internal length is updated accordingly.
///
/// # Panics
///
/// If the string cannot hold the provided slice, this method will panic.
#[inline(always)]
pub fn push_str(&mut self, s: &str) {
let rem = self.buf.len() - self.len;
let req = s.len();
assert!(rem >= req, "cannot push string beyond fixed length");
let start = self.len;
let stop = start + req;
let buf = &mut self.buf[start..stop];
buf.copy_from_slice(s.as_bytes());
}
/// Pops a character from the string.
@ -188,38 +245,76 @@ impl<const N: usize> FixedString<N> {
/// The internal length is updated accordingly.
///
/// If no characters are left (i.e. the string is empty), an instance of [`None`] is returned.
///
/// **Note that this method is currently unimplemented.**
#[deprecated = "temporarily unimplemented"]
#[inline(always)]
pub fn pop(&mut self) -> Option<char> {
self.len
.checked_sub(0x1)
.map(|len| {
let c = self.buf[self.len];
self.len = len;
pub fn pop(&mut self) -> Option<char> { todo!() }
c
})
/// Returns an iterator of the string's characters.
#[inline(always)]
pub fn chars(&self) -> Chars { self.as_str().chars() }
/// Returns an iterator of the string's characters along with their positions.
#[inline(always)]
pub fn char_indices(&self) -> CharIndices { self.as_str().char_indices() }
}
impl<const N: usize> Add<&str> for FixedString<N> {
type Output = Self;
fn add(mut self, rhs: &str) -> Self::Output {
self.push_str(rhs);
self
}
}
impl<const N: usize> AsMut<[char]> for FixedString<N> {
#[inline(always)]
fn as_mut(&mut self) -> &mut [char] { self.as_mut_slice() }
impl<const N: usize> AddAssign<&str> for FixedString<N> {
fn add_assign(&mut self, rhs: &str) { self.push_str(rhs) }
}
impl<const N: usize> AsRef<[char]> for FixedString<N> {
impl<const N: usize> AsMut<str> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &[char] { self.as_slice() }
fn as_mut(&mut self) -> &mut str { self.as_mut_str() }
}
#[cfg(feature = "std")]
#[cfg_attr(doc, doc(cfg(feature = "std")))]
impl<const N: usize> AsRef<OsStr> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &OsStr { self.as_str().as_ref() }
}
#[cfg(feature = "std")]
#[cfg_attr(doc, doc(cfg(feature = "std")))]
impl<const N: usize> AsRef<Path> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &Path { self.as_str().as_ref() }
}
impl<const N: usize> AsRef<str> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &str { self.as_str() }
}
impl<const N: usize> AsRef<[u8]> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &[u8] { self.as_bytes() }
}
impl<const N: usize> Borrow<str> for FixedString<N> {
#[inline(always)]
fn borrow(&self) -> &str { self.as_str() }
}
impl<const N: usize> BorrowMut<str> for FixedString<N> {
#[inline(always)]
fn borrow_mut(&mut self) -> &mut str { self.as_mut_str() }
}
impl<const N: usize> Debug for FixedString<N> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "\"")?;
for c in self { write!(f, "{}", c.escape_debug())? }
write!(f, "\"")?;
Ok(())
}
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_str(), f) }
}
impl<const N: usize> Default for FixedString<N> {
@ -227,159 +322,129 @@ impl<const N: usize> Default for FixedString<N> {
fn default() -> Self { Self { buf: [Default::default(); N], len: 0x0 } }
}
/// See [`as_slice`](Self::as_slice).
impl<const N: usize> Deref for FixedString<N> {
type Target = [char];
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target { self.as_slice() }
fn deref(&self) -> &Self::Target { self.as_str() }
}
/// See [`as_mut_slice`](Self::as_mut_slice).
impl<const N: usize> DerefMut for FixedString<N> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_slice() }
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_str() }
}
impl<const N: usize> Deserialise for FixedString<N> {
#[inline]
fn deserialise(stream: &Dstream) -> Result<Self, Error> {
let len = Deserialise::deserialise(stream)?;
if len > N { return Err(Error::ArrayTooShort { req: len, len: N }) };
let bytes = stream.read(len)?;
let s = core::str::from_utf8(bytes)
.map_err(|e| Error::BadString { source: e })?;
Self::from_str(s)
}
}
impl<const N: usize> Display for FixedString<N> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
for c in self { write!(f, "{c}")? }
Ok(())
}
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Display::fmt(self.as_str(), f) }
}
impl<const N: usize> Eq for FixedString<N> { }
impl<const N: usize> From<[char; N]> for FixedString<N> {
#[inline(always)]
fn from(value: [char; N]) -> Self { Self::from_chars(value) }
}
impl<const N: usize> FromStr for FixedString<N> {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut buf = [Default::default(); N];
let len = s.chars().count();
let len = s.len();
if len > N { return Err(Error::ArrayTooShort { req: len, len: N }) };
for (i, c) in s.chars().enumerate() {
if i >= N { return Err(Error::ArrayTooShort { req: len, len: N }) }
let mut buf = [0x00; N];
unsafe { core::ptr::copy_nonoverlapping(s.as_ptr(), buf.as_mut_ptr(), len) };
buf[i] = c;
}
// The remaining bytes are already initialised to
// null.
Ok(Self { buf, len })
}
}
impl<I: SliceIndex<[char]>, const N: usize> Index<I> for FixedString<N> {
type Output = I::Output;
impl<const N: usize> Hash for FixedString<N> {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
}
impl<I: SliceIndex<str>, const N: usize> Index<I> for FixedString<N> {
type Output = I::Output;
#[inline(always)]
fn index(&self, index: I) -> &Self::Output { self.get(index).unwrap() }
}
impl<I: SliceIndex<[char]>, const N: usize> IndexMut<I> for FixedString<N> {
impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for FixedString<N> {
#[inline(always)]
fn index_mut(&mut self, index: I) -> &mut Self::Output { self.get_mut(index).unwrap() }
}
impl<const N: usize> IntoIterator for FixedString<N> {
type Item = char;
type IntoIter = FixedIter<char, N>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
FixedIter {
buf: unsafe { self.buf.as_ptr().cast::<[MaybeUninit<char>; N]>().read() },
pos: 0x0,
len: self.len,
}
}
}
impl<'a, const N: usize> IntoIterator for &'a FixedString<N> {
type Item = &'a char;
type IntoIter = core::slice::Iter<'a, char>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
impl<'a, const N: usize> IntoIterator for &'a mut FixedString<N> {
type Item = &'a mut char;
type IntoIter = core::slice::IterMut<'a, char>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
}
impl<const N: usize> Ord for FixedString<N> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() }
fn cmp(&self, other: &Self) -> Ordering { self.as_str().cmp(other.as_str()) }
}
impl<const N: usize, const M: usize> PartialEq<FixedString<M>> for FixedString<N> {
#[inline(always)]
fn eq(&self, other: &FixedString<M>) -> bool { self.as_slice() == other.as_slice() }
}
impl<const N: usize> PartialEq<&[char]> for FixedString<N> {
#[inline(always)]
fn eq(&self, other: &&[char]) -> bool { self.as_slice() == *other }
fn eq(&self, other: &FixedString<M>) -> bool { self.as_str() == other.as_str() }
}
impl<const N: usize> PartialEq<&str> for FixedString<N> {
#[inline]
fn eq(&self, other: &&str) -> bool {
for (i, c) in other.chars().enumerate() {
if self.get(i) != Some(&c) { return false };
}
true
}
#[inline(always)]
fn eq(&self, other: &&str) -> bool { self.as_str() == *other }
}
impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<N> {
#[inline(always)]
fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> { self.partial_cmp(&other.as_slice()) }
}
impl<const N: usize> PartialOrd<&[char]> for FixedString<N> {
#[inline(always)]
fn partial_cmp(&self, other: &&[char]) -> Option<Ordering> { self.as_slice().partial_cmp(other) }
fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> { self.as_str().partial_cmp(other.as_str()) }
}
impl<const N: usize> PartialOrd<&str> for FixedString<N> {
#[inline]
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
let llen = self.len();
let rlen = other.chars().count();
#[inline(always)]
fn partial_cmp(&self, other: &&str) -> Option<Ordering> { self.as_str().partial_cmp(*other) }
}
match llen.cmp(&rlen) {
Ordering::Equal => {},
impl<const N: usize> Serialise for FixedString<N> {
const MAX_SERIALISED_SIZE: usize = N + usize::MAX_SERIALISED_SIZE;
ordering => return Some(ordering),
};
fn serialise(&self, stream: &mut Sstream) -> Result<(), Error> {
self.len().serialise(stream)?;
stream.write(self.as_bytes())?;
for (i, rc) in other.chars().enumerate() {
let lc = self[i];
Ok(())
}
}
match lc.cmp(&rc) {
Ordering::Equal => {},
#[cfg(feature = "std")]
#[cfg_attr(doc, doc(cfg(feature = "std")))]
impl<const N: usize> ToSocketAddrs for FixedString<N> {
type Iter = <str as ToSocketAddrs>::Iter;
ordering => return Some(ordering),
}
}
#[inline(always)]
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> { self.as_str().to_socket_addrs() }
}
Some(Ordering::Equal)
impl<const N: usize> TryFrom<char> for FixedString<N> {
type Error = <Self as FromStr>::Err;
#[inline(always)]
fn try_from(value: char) -> Result<Self, Self::Error> {
let mut buf = [0x00; 0x4];
let s = value.encode_utf8(&mut buf);
s.parse()
}
}
@ -391,6 +456,7 @@ impl<const N: usize> TryFrom<&str> for FixedString<N> {
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<const N: usize> TryFrom<String> for FixedString<N> {
type Error = <Self as FromStr>::Err;
@ -398,8 +464,17 @@ impl<const N: usize> TryFrom<String> for FixedString<N> {
fn try_from(value: String) -> Result<Self, Self::Error> { Self::from_str(&value) }
}
/// Converts the fixed-size string into a dynamic string.
///
/// The capacity of the resulting [`String`] object is equal to the value of `N`.
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<const N: usize> From<FixedString<N>> for String {
#[inline(always)]
fn from(value: FixedString<N>) -> Self { value.to_string() }
fn from(value: FixedString<N>) -> Self {
let mut s = Self::with_capacity(N);
s.push_str(value.as_str());
s
}
}

View file

@ -25,9 +25,9 @@ use core::cmp::Ordering;
#[test]
fn test_fixed_string() {
let str0 = FixedString::<0xC>::try_from("Hello there!").unwrap();
let str1 = FixedString::<0xE>::try_from("MEIN_GRO\u{1E9E}_GOTT").unwrap();
let str2 = FixedString::<0x5>::try_from("Hello").unwrap();
let str0 = FixedString::<0x0C>::try_from("Hello there!").unwrap();
let str1 = FixedString::<0x12>::try_from("MEIN_GRO\u{1E9E}_GOTT").unwrap();
let str2 = FixedString::<0x05>::try_from("Hello").unwrap();
assert_eq!(str0.partial_cmp(&str0), Some(Ordering::Equal));
assert_eq!(str0.partial_cmp(&str1), Some(Ordering::Less));

View file

@ -23,7 +23,7 @@
//! Binary (de)serialisation.
//!
//! Contrary to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the goal of bzipper is to serialise with a known size constraint.
//! In contrast to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the primary goal of bzipper is to serialise with a known size constraint.
//! Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.
//!
//! Keep in mind that this project is still work-in-progress.
@ -41,15 +41,15 @@
//!
//! # Usage
//!
//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which are commonly used in conjunction with streams (more specifically, [s-streams](Sstream) and [d-streams](Dstream)).
//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which use *streams* -- or more specifically -- [s-streams](Sstream) and [d-streams](Dstream).
//!
//! Many core types come implemented with bzipper, including primitives as well as some standard library types such as [`Option`] and [`Result`](core::result::Result).
//!
//! It is recommended in most cases to just derive these traits for custom types (enumerations and structures only).
//! Here, each field is chained in declaration order:
//! It is recommended in most cases to just derive these two traits for custom types (although this is only supported with enumerations and structures).
//! Here, each field is *chained* according to declaration order:
//!
//! ```
//! use bzipper::{Deserialise, Serialise};
//! use bzipper::{Buffer, Deserialise, Serialise};
//!
//! #[derive(Debug, Deserialise, PartialEq, Serialise)]
//! struct IoRegister {
@ -57,45 +57,55 @@
//! value: u16,
//! }
//!
//! let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
//! IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
//! let mut buf = Buffer::new();
//!
//! buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
//!
//! assert_eq!(buf.len(), 0x6);
//! assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
//!
//! assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
//! assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
//! ```
//!
//! ## Serialisation
//!
//! To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation.
//! The required size of any given serialisation is specified by the [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) constant:
//! To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation and wrap it in an s-stream (*serialisation stream*) with the [`Sstream`] type.
//!
//! ```
//! use bzipper::Serialise;
//! use bzipper::{Serialise, Sstream};
//!
//! let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
//! 'Ж'.serialise(&mut buf).unwrap();
//! let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE];
//! let mut stream = Sstream::new(&mut buf);
//!
//! assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
//! 'Ж'.serialise(&mut stream).unwrap();
//!
//! assert_eq!(stream, [0x00, 0x00, 0x04, 0x16]);
//! ```
//!
//! The only special requirement of the [`serialise`](Serialise::serialise) method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
//! The maximum size of any given serialisation is specified by the [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) constant.
//!
//! We can also use streams to *chain* multiple elements together.
//! We can also use streams to chain multiple elements together:
//!
//! ```
//! use bzipper::Serialise;
//! use bzipper::{Serialise, Sstream};
//!
//! let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
//! let mut stream = bzipper::Sstream::new(&mut buf);
//! let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE * 0x5];
//! let mut stream = Sstream::new(&mut buf);
//!
//! stream.append(&'ل');
//! stream.append(&'ا');
//! stream.append(&'م');
//! stream.append(&'د');
//! stream.append(&'ا');
//! // Note: For serialising multiple characters, the
//! // `FixedString` type is usually preferred.
//!
//! assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);
//! 'ل'.serialise(&mut stream).unwrap();
//! 'ا'.serialise(&mut stream).unwrap();
//! 'م'.serialise(&mut stream).unwrap();
//! 'د'.serialise(&mut stream).unwrap();
//! 'ا'.serialise(&mut stream).unwrap();
//!
//! assert_eq!(buf, [
//! 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27,
//! 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F,
//! 0x00, 0x00, 0x06, 0x27
//! ]);
//! ```
//!
//! When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
@ -103,27 +113,35 @@
//!
//! ## Deserialisation
//!
//! Deserialisation works with an almost identical syntax to serialisation.
//! Deserialisation works with a similar syntax to serialisation.
//!
//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method:
//! D-streams (*deserialisation streams*) use the [`Dstream`] type and are constructed in a manner similar to s-streams.
//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method with the strema:
//!
//! ```
//! use bzipper::Deserialise;
//! use bzipper::{Deserialise, Dstream};
//!
//! let data = [0x45, 0x54];
//! assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
//! let stream = Dstream::new(&data);
//! assert_eq!(u16::deserialise(&stream).unwrap(), 0x4554);
//! ```
//!
//! Just like with serialisations, the [`Dstream`] can be used to deserialise chained elements:
//! And just like s-streams, d-streams can also be used to handle chaining:
//!
//! ```
//! use bzipper::Deserialise;
//! use bzipper::{Deserialise, Dstream};
//!
//! let data = [0x45, 0x54];
//! let stream = bzipper::Dstream::new(&data);
//! let stream = Dstream::new(&data);
//!
//! assert_eq!(stream.take::<u8>().unwrap(), 0x45);
//! assert_eq!(stream.take::<u8>().unwrap(), 0x54);
//! assert_eq!(u8::deserialise(&stream).unwrap(), 0x45);
//! assert_eq!(u8::deserialise(&stream).unwrap(), 0x54);
//!
//! // The data can also be deserialised as a tuple (up
//! // to twelve elements).
//!
//! let stream = Dstream::new(&data);
//! assert_eq!(<(u8, u8)>::deserialise(&stream).unwrap(), (0x45, 0x54));
//! ```
#![no_std]
@ -139,8 +157,6 @@ extern crate alloc;
extern crate std;
/// Implements [`Deserialise`] for the provided type.
///
/// This macro assumes that `Serialise` was also derived, although this is not strictly required as it is unenforceable.
#[doc(inline)]
pub use bzipper_macros::Deserialise;
@ -151,7 +167,7 @@ pub use bzipper_macros::Deserialise;
/// For structures, each element is chained in **order of declaration.**
/// For example, the following struct will serialise its field `foo` before `bar`:
///
/// ```
/// ```rust
/// use bzipper::Serialise;
///
/// #[derive(Serialise)]
@ -161,9 +177,9 @@ pub use bzipper_macros::Deserialise;
/// }
/// ```
///
/// Should the order of declaration change, then most of---if not all---previous dervied serialisations become void.
/// Should the structure's declaration change, then all previous derived serialisations be considered void.
///
/// The value of [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) is set to the combined value of all fields.
/// The value of [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) is set to the combined value of all fields.
///
/// If the structure is a unit structure (i.e. it has *no* fields), it is serialised equivalently to the [unit] type.
///
@ -176,12 +192,12 @@ pub use bzipper_macros::Deserialise;
/// Variants with fields are serialised exactly like structures.
/// That is, each field is chained in order of declaration.
///
/// Each variant has its own serialised size, and the largest of these values is chosen as the serialised size of the enumeration type.
/// Each variant has its own value of `MAX_SERIALISED_SIZE`, and the largest of these values is chosen as the value of the enumeration's own `MAX_SERIALISED_SIZE`.
///
/// # Unions
///
/// Unions cannot derive `Serialise` due to the uncertainty of their contents.
/// The trait should therefore be implemented manually.
/// The trait should therefore be implemented manually for such types.
#[doc(inline)]
pub use bzipper_macros::Serialise;
@ -196,7 +212,6 @@ pub(in crate) use use_mod;
use_mod!(pub deserialise);
use_mod!(pub dstream);
use_mod!(pub error);
use_mod!(pub fixed_iter);
use_mod!(pub fixed_string);
use_mod!(pub serialise);
use_mod!(pub sstream);

View file

@ -24,16 +24,20 @@ mod test;
use crate::{Error, Result, Sstream};
use core::{convert::Infallible, marker::PhantomData};
use core::{convert::Infallible, hint::unreachable_unchecked, marker::PhantomData};
mod tuple;
/// Denotes a type capable of being serialised.
/// Denotes a type capable of serialisation.
///
/// It is recommended to simply derive this trait for custom types.
/// It can, however, be manually implemented:
/// It can, however, also be manually implemented:
///
/// ```rust
/// // Manual implementation of custom type. This im-
/// // plementation is equivalent to what would have
/// // been derived.
///
/// ```
/// use bzipper::{Result, Serialise, Sstream};
///
/// struct Foo {
@ -42,60 +46,46 @@ mod tuple;
/// }
///
/// impl Serialise for Foo {
/// const SERIALISED_SIZE: usize = u16::SERIALISED_SIZE + f32::SERIALISED_SIZE;
///
/// fn serialise(&self, buf: &mut [u8]) -> Result<()> {
/// debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
/// const MAX_SERIALISED_SIZE: usize = u16::MAX_SERIALISED_SIZE + f32::MAX_SERIALISED_SIZE;
///
/// fn serialise(&self, stream: &mut Sstream) -> Result<()> {
/// // Serialise fields using chaining.
///
/// let mut stream = Sstream::new(buf);
///
/// stream.append(&self.bar)?;
/// stream.append(&self.baz)?;
/// self.bar.serialise(stream)?;
/// self.baz.serialise(stream)?;
///
/// Ok(())
/// }
/// }
/// ```
///
/// Implementors of this trait should make sure that [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) is properly defined.
/// This value indicates the definitive size of any serialisation of the `Self` type.
/// Implementors of this trait should make sure that [`MAX_SERIALISED_SIZE`](Self::MAX_SERIALISED_SIZE) is properly defined.
/// This value indicates the definitively largest size of any serialisation of `Self`.
pub trait Serialise: Sized {
/// The amount of bytes that result from a serialisation.
/// The maximum amount of bytes that can result from a serialisation.
///
/// Implementors of this trait should make sure that no serialisation (or deserialisation) uses more than the amount specified by this constant.
/// When using these traits, always assume that exactly this amount has or will be used.
const SERIALISED_SIZE: usize;
const MAX_SERIALISED_SIZE: usize;
/// Serialises `self` into a slice.
/// Serialises `self` into the given s-stream.
///
/// In most cases it is wiser to chain serialisations using [`Sstream`] instead of using this method directly.
/// This method must **never** write more bytes than specified by [`MAX_SERIALISED_SIZE`](Self::MAX_SERIALISED_SIZE).
/// Doing so is considered a logic error.
///
/// # Errors
///
/// If serialisation failed, e.g. by an unencodable value being provided, an error is returned.
///
/// # Panics
///
/// This method will usually panic if the provided slice has a length *less* than the value of `SERIALISED_SIZE`.
/// Official implementations of this trait (including those that are derived) always panic in debug mode if the provided slice has a length that is different at all.
fn serialise(&self, buf: &mut [u8]) -> Result<()>;
/// If serialisation fails, e.g. by an unencodable value being provided, an error is returned.
fn serialise(&self, stream: &mut Sstream) -> Result<()>;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Serialise for $ty {
const SERIALISED_SIZE: usize = size_of::<$ty>();
const MAX_SERIALISED_SIZE: usize = size_of::<$ty>();
#[inline]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let data = self.to_be_bytes();
buf.copy_from_slice(&data);
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
stream.write(&self.to_be_bytes())?;
Ok(())
}
@ -106,101 +96,82 @@ macro_rules! impl_numeric {
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Serialise for ::core::num::NonZero<$ty> {
const SERIALISED_SIZE: usize = ::core::mem::size_of::<$ty>();
const MAX_SERIALISED_SIZE: usize = ::core::mem::size_of::<$ty>();
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
self.get().serialise(buf)
}
fn serialise(&self, stream: &mut Sstream) -> Result<()> { self.get().serialise(stream) }
}
};
}
impl<T: Serialise, const N: usize> Serialise for [T; N] {
const SERIALISED_SIZE: usize = T::SERIALISED_SIZE * N;
const MAX_SERIALISED_SIZE: usize = T::MAX_SERIALISED_SIZE * N;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
for v in self { stream.append(v)? }
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
for v in self { v.serialise(stream)? }
Ok(())
}
}
impl Serialise for bool {
const SERIALISED_SIZE: usize = u8::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize = u8::MAX_SERIALISED_SIZE;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
u8::from(*self).serialise(buf)
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
u8::from(*self).serialise(stream)
}
}
impl Serialise for char {
const SERIALISED_SIZE: usize = u32::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize = u32::MAX_SERIALISED_SIZE;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
u32::from(*self).serialise(buf)
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
u32::from(*self).serialise(stream)
}
}
// Especially useful for `Result<T, Infallible>`.
// *If* that is needed, of course.
// *If* that is even needed, of course.
impl Serialise for Infallible {
const SERIALISED_SIZE: usize = 0x0;
const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
fn serialise(&self, _buf: &mut [u8]) -> Result<()> { unreachable!() }
fn serialise(&self, _stream: &mut Sstream) -> Result<()> { unsafe { unreachable_unchecked() } }
}
impl Serialise for isize {
const SERIALISED_SIZE: usize = i32::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize = i32::MAX_SERIALISED_SIZE;
#[inline]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
let value = i32::try_from(*self)
.map_err(|_| Error::IsizeOutOfRange { value: *self })?;
.map_err(|_| Error::IsizeOutOfRange(*self))?;
value.serialise(buf)
value.serialise(stream)
}
}
impl<T: Serialise> Serialise for Option<T> {
const SERIALISED_SIZE: usize = bool::SERIALISED_SIZE + T::SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
const MAX_SERIALISED_SIZE: usize = bool::MAX_SERIALISED_SIZE + T::MAX_SERIALISED_SIZE;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
// The first element is of type `bool` and is
// called the "sign." It signifies whether there is
// a following element or not. The remaining bytes
// are preserved if `self` is `None`.
let mut stream = Sstream::new(buf);
// a following element or not.
match *self {
None => {
stream.append(&false)?;
false.serialise(stream)?;
// No need to zero-fill.
},
Some(ref v) => {
stream.append(&true)?;
stream.append(v)?;
true.serialise(stream)?;
v.serialise(stream)?;
},
};
@ -209,37 +180,30 @@ impl<T: Serialise> Serialise for Option<T> {
}
impl<T> Serialise for PhantomData<T> {
const SERIALISED_SIZE: usize = size_of::<Self>();
const MAX_SERIALISED_SIZE: usize = size_of::<Self>();
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
Ok(())
}
fn serialise(&self, _stream: &mut Sstream) -> Result<()> { Ok(()) }
}
impl<T, E> Serialise for core::result::Result<T, E>
where
T: Serialise,
E: Serialise, {
const SERIALISED_SIZE: usize = bool::SERIALISED_SIZE + if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
const MAX_SERIALISED_SIZE: usize = bool::MAX_SERIALISED_SIZE + if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
// Remember the descriminant.
match *self {
Ok(ref v) => {
stream.append(&false)?;
stream.append(v)?;
false.serialise(stream)?;
v.serialise(stream)?;
},
Err(ref e) => {
stream.append(&true)?;
stream.append(e)?;
true.serialise(stream)?;
e.serialise(stream)?;
},
};
@ -248,26 +212,20 @@ where
}
impl Serialise for () {
const SERIALISED_SIZE: usize = size_of::<Self>();
const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
Ok(())
}
fn serialise(&self, _stream: &mut Sstream) -> Result<()> { Ok(()) }
}
impl Serialise for usize {
const SERIALISED_SIZE: Self = u32::SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
const MAX_SERIALISED_SIZE: Self = u32::MAX_SERIALISED_SIZE;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
let value = u32::try_from(*self)
.map_err(|_| Error::UsizeOutOfRange { value: *self })?;
.map_err(|_| Error::UsizeOutOfRange(*self))?;
value.serialise(buf)
value.serialise(stream)
}
}

View file

@ -18,7 +18,7 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use crate::{FixedString, Serialise};
use crate::{FixedString, Serialise, Sstream};
#[test]
fn test_serialise() {
@ -32,19 +32,19 @@ fn test_serialise() {
Teacher { initials: [char; 0x3] },
}
assert_eq!(Foo::SERIALISED_SIZE, 0x4);
assert_eq!(Bar::SERIALISED_SIZE, 0x10);
assert_eq!(Foo::MAX_SERIALISED_SIZE, 0x4);
assert_eq!(Bar::MAX_SERIALISED_SIZE, 0x10);
macro_rules! test {
($ty:ty: $value:expr => $data:expr) => {{
use ::bzipper::Serialise;
let data: [u8; <$ty as Serialise>::SERIALISED_SIZE] = $data;
let mut buf = [0x00; <$ty as Serialise>::MAX_SERIALISED_SIZE];
let mut buf = [0x00; <$ty as Serialise>::SERIALISED_SIZE];
<$ty as Serialise>::serialise(&mut $value, &mut buf).unwrap();
let mut stream = Sstream::new(&mut buf);
<$ty as Serialise>::serialise(&mut $value, &mut stream).unwrap();
assert_eq!(buf, data);
assert_eq!(stream, $data);
}};
}
@ -63,14 +63,11 @@ fn test_serialise() {
0x83, 0x2E, 0x3C, 0x2C, 0x84, 0x10, 0x58, 0x1A,
]);
test!(FixedString::<0x1>: FixedString::try_from("A").unwrap() => [0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01]);
test!(FixedString::<0x1>: FixedString::try_from("A").unwrap() => [0x00, 0x00, 0x00, 0x01, 0x41]);
test!(FixedString::<0x9>: FixedString::try_from("l\u{00F8}gma\u{00F0}ur").unwrap() => [
0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0xF8,
0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x6D,
0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xF0,
0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x72,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
test!(FixedString::<0x24>: FixedString::try_from("l\u{00F8}gma\u{00F0}ur").unwrap() => [
0x00, 0x00, 0x00, 0x0A, 0x6C, 0xC3, 0xB8, 0x67,
0x6D, 0x61, 0xC3, 0xB0, 0x75, 0x72,
]);
test!([char; 0x5]: ['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'] => [
@ -79,7 +76,7 @@ fn test_serialise() {
0x00, 0x00, 0x03, 0xB1,
]);
test!(Result::<u16, char>: Ok(0x45_45) => [0x00, 0x45, 0x45, 0x00, 0x00]);
test!(Result::<u16, char>: Ok(0x45_45) => [0x00, 0x45, 0x45]);
test!(Result::<u16, char>: Err(char::REPLACEMENT_CHARACTER) => [0x01, 0x00, 0x00, 0xFF, 0xFD]);
test!(Option<()>: None => [0x00]);
@ -87,15 +84,9 @@ fn test_serialise() {
test!(Foo: Foo('\u{FDF2}') => [0x00, 0x00, 0xFD, 0xF2]);
test!(Bar: Bar::Unit => [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
test!(Bar: Bar::Unit => [0x00, 0x00, 0x00, 0x00]);
test!(Bar: Bar::Pretty(true) => [
0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
test!(Bar: Bar::Pretty(true) => [0x00, 0x00, 0x00, 0x01, 0x01]);
test!(Bar: Bar::Teacher { initials: ['T', 'L', '\0'] } => [
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54,

View file

@ -24,15 +24,11 @@ use crate::{Result, Serialise, Sstream};
impl<T0> Serialise for (T0, )
where
T0: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
Ok(())
}
@ -42,17 +38,13 @@ impl<T0, T1> Serialise for (T0, T1)
where
T0: Serialise,
T1: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
Ok(())
}
@ -63,19 +55,15 @@ where
T0: Serialise,
T1: Serialise,
T2: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
Ok(())
}
@ -87,21 +75,17 @@ where
T1: Serialise,
T2: Serialise,
T3: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
Ok(())
}
@ -114,23 +98,19 @@ where
T2: Serialise,
T3: Serialise,
T4: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
Ok(())
}
@ -144,25 +124,21 @@ where
T3: Serialise,
T4: Serialise,
T5: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
Ok(())
}
@ -177,27 +153,23 @@ where
T4: Serialise,
T5: Serialise,
T6: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
Ok(())
}
@ -213,29 +185,25 @@ where
T5: Serialise,
T6: Serialise,
T7: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE
+ T7::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE
+ T7::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
stream.append(&self.7)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
self.7.serialise(stream)?;
Ok(())
}
@ -252,31 +220,27 @@ where
T6: Serialise,
T7: Serialise,
T8: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE
+ T7::SERIALISED_SIZE
+ T8::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE
+ T7::MAX_SERIALISED_SIZE
+ T8::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
stream.append(&self.7)?;
stream.append(&self.8)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
self.7.serialise(stream)?;
self.8.serialise(stream)?;
Ok(())
}
@ -294,33 +258,29 @@ where
T7: Serialise,
T8: Serialise,
T9: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE
+ T7::SERIALISED_SIZE
+ T8::SERIALISED_SIZE
+ T9::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE
+ T7::MAX_SERIALISED_SIZE
+ T8::MAX_SERIALISED_SIZE
+ T9::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
stream.append(&self.7)?;
stream.append(&self.8)?;
stream.append(&self.9)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
self.7.serialise(stream)?;
self.8.serialise(stream)?;
self.9.serialise(stream)?;
Ok(())
}
@ -339,35 +299,31 @@ where
T8: Serialise,
T9: Serialise,
T10: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE
+ T7::SERIALISED_SIZE
+ T8::SERIALISED_SIZE
+ T9::SERIALISED_SIZE
+ T10::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE
+ T7::MAX_SERIALISED_SIZE
+ T8::MAX_SERIALISED_SIZE
+ T9::MAX_SERIALISED_SIZE
+ T10::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
stream.append(&self.7)?;
stream.append(&self.8)?;
stream.append(&self.9)?;
stream.append(&self.10)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
self.7.serialise(stream)?;
self.8.serialise(stream)?;
self.9.serialise(stream)?;
self.10.serialise(stream)?;
Ok(())
}
@ -387,37 +343,33 @@ where
T9: Serialise,
T10: Serialise,
T11: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::SERIALISED_SIZE
+ T4::SERIALISED_SIZE
+ T5::SERIALISED_SIZE
+ T6::SERIALISED_SIZE
+ T7::SERIALISED_SIZE
+ T8::SERIALISED_SIZE
+ T9::SERIALISED_SIZE
+ T10::SERIALISED_SIZE
+ T11::SERIALISED_SIZE;
const MAX_SERIALISED_SIZE: usize =
T0::MAX_SERIALISED_SIZE
+ T1::MAX_SERIALISED_SIZE
+ T2::MAX_SERIALISED_SIZE
+ T3::MAX_SERIALISED_SIZE
+ T4::MAX_SERIALISED_SIZE
+ T5::MAX_SERIALISED_SIZE
+ T6::MAX_SERIALISED_SIZE
+ T7::MAX_SERIALISED_SIZE
+ T8::MAX_SERIALISED_SIZE
+ T9::MAX_SERIALISED_SIZE
+ T10::MAX_SERIALISED_SIZE
+ T11::MAX_SERIALISED_SIZE;
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = Sstream::new(buf);
stream.append(&self.0)?;
stream.append(&self.1)?;
stream.append(&self.2)?;
stream.append(&self.3)?;
stream.append(&self.4)?;
stream.append(&self.5)?;
stream.append(&self.6)?;
stream.append(&self.7)?;
stream.append(&self.8)?;
stream.append(&self.9)?;
stream.append(&self.10)?;
stream.append(&self.11)?;
fn serialise(&self, stream: &mut Sstream) -> Result<()> {
self.0.serialise(stream)?;
self.1.serialise(stream)?;
self.2.serialise(stream)?;
self.3.serialise(stream)?;
self.4.serialise(stream)?;
self.5.serialise(stream)?;
self.6.serialise(stream)?;
self.7.serialise(stream)?;
self.8.serialise(stream)?;
self.9.serialise(stream)?;
self.10.serialise(stream)?;
self.11.serialise(stream)?;
Ok(())
}

View file

@ -19,16 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use crate::{Error, Result, Serialise};
use crate::{Dstream, Error, Result};
use core::cell::Cell;
use core::fmt::{Debug, Formatter};
/// Byte stream for deserialisation.
/// Byte stream suitable for serialisation.
///
/// This type borrows a slice, keeping track internally of the used bytes.
/// This type mutably borrows a buffer, keeping track internally of the used bytes.
pub struct Sstream<'a> {
buf: &'a mut [u8],
pos: Cell<usize>,
pub(in crate) buf: &'a mut [u8],
pub(in crate) pos: Cell<usize>,
}
impl<'a> Sstream<'a> {
@ -37,22 +38,86 @@ impl<'a> Sstream<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self { Self { buf, pos: Cell::new(0x0) } }
/// Extends the stream by appending a new serialisation.
///
/// # Errors
///
/// If the stream cannot hold any arbitrary serialisation of `T`, an [`EndOfStream`](Error::EndOfStream) instance is returned.
/// Appends raw bytes to the stream.
#[inline]
pub fn append<T: Serialise>(&mut self, value: &T) -> Result<()> {
pub fn write(&mut self, bytes: &[u8]) -> Result<()> {
let rem = self.buf.len() - self.pos.get();
let req = T::SERIALISED_SIZE;
let req = bytes.len();
if rem < req { return Err(Error::EndOfStream { req, rem }) };
if rem < req { return Err(Error::EndOfStream { req, rem }) }
let start = self.pos.get();
let stop = start + req;
self.pos.set(stop);
value.serialise(&mut self.buf[start..stop])
let buf = &mut self.buf[start..stop];
buf.copy_from_slice(bytes);
Ok(())
}
/// Gets a pointer to the first byte in the stream.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() }
/// Gets an immutable slice of the stream.
#[inline(always)]
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
let ptr = self.as_ptr();
let len = self.len();
unsafe { core::slice::from_raw_parts(ptr, len) }
}
/// Gets the length of the stream.
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { unsafe { self.pos.as_ptr().read() } }
/// Tests if the stream is empty.
///
/// If no serialisations have been made so far, this method returns `false`.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
/// Tests if the stream is full.
///
/// Note that zero-sized types such as [`()`](unit) can still be serialised into this stream.
#[inline(always)]
#[must_use]
pub const fn is_full(&self) -> bool { self.len() == self.buf.len() }
}
impl Debug for Sstream<'_> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_slice(), f) }
}
impl<'a> From<&'a mut [u8]> for Sstream<'a> {
#[inline(always)]
fn from(value: &'a mut [u8]) -> Self { Self::new(value) }
}
impl PartialEq for Sstream<'_> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
}
impl PartialEq<&[u8]> for Sstream<'_> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
}
impl<const N: usize> PartialEq<[u8; N]> for Sstream<'_> {
#[inline(always)]
fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
}
impl<'a> From<Sstream<'a>> for Dstream<'a> {
#[inline(always)]
fn from(value: Sstream<'a>) -> Self { Self { data: value.buf, pos: value.pos } }
}

View file

@ -1,6 +1,6 @@
[package]
name = "bzipper_macros"
version = "0.6.2"
version = "0.7.0"
edition = "2021"
documentation = "https://docs.rs/bzipper_macros/"

View file

@ -38,36 +38,30 @@ pub fn deserialise_enum(data: &DataEnum) -> TokenStream {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
for field in &variant.fields {
let field_ty = &field.ty;
let command = field.ident
.as_ref()
.map_or_else(
|| quote! { stream.take::<#field_ty>()? },
|field_name| quote! { #field_name: stream.take::<#field_ty>()? }
|| quote! { Deserialise::deserialise(stream)? },
|field_name| quote! { #field_name: Deserialise::deserialise(stream)? }
);
chain_commands.push(command);
}
let block = match variant.fields {
let value = match variant.fields {
Fields::Named( ..) => quote! { Self::#variant_name { #chain_commands } },
Fields::Unnamed(..) => quote! { Self::#variant_name(#chain_commands) },
Fields::Unit => quote! { Self::#variant_name },
};
match_arms.push(quote! { #discriminant => #block });
match_arms.push(quote! { #discriminant => #value });
}
match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant { value }) });
match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant(value)) });
quote! {
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
let mut stream = ::bzipper::Dstream::new(data);
let value = match (stream.take::<u32>()?) { #match_arms };
fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> {
let value = match (<u32 as ::bzipper::Deserialise>::deserialise(stream)?) { #match_arms };
Ok(value)
}
}

View file

@ -26,52 +26,35 @@ use syn::punctuated::Punctuated;
#[must_use]
pub fn deserialise_struct(data: &DataStruct) -> TokenStream {
if let Fields::Named(..) = data.fields {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
for field in &data.fields {
let name = field.ident.as_ref().unwrap();
let ty = &field.ty;
chain_commands.push(quote! { #name: stream.take::<#ty>()? });
}
quote! {
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
let stream = ::bzipper::Dstream::new(data);
Ok(Self { #chain_commands })
}
}
} else if let Fields::Unnamed(..) = data.fields {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
for field in &data.fields {
let ty = &field.ty;
chain_commands.push(quote! { stream.take::<#ty>()? });
}
quote! {
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
let stream = ::bzipper::Dstream::new(data);
Ok(Self(#chain_commands))
}
}
} else {
// Fields::Unit
if matches!(data.fields, Fields::Unit) {
quote! {
#[inline(always)]
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
fn deserialise(_stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> { Ok(Self) }
}
} else {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
Ok(Self)
for field in &data.fields {
let command = field.ident
.as_ref()
.map_or_else(
|| quote! { Deserialise::deserialise(stream)? },
|field_name| quote! { #field_name: Deserialise::deserialise(stream)? }
);
chain_commands.push(command);
}
let value = if let Fields::Named(..) = data.fields {
quote! { Self { #chain_commands } }
} else {
quote! { Self(#chain_commands) }
};
quote! {
fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> {
let value = #value;
Ok(value)
}
}
}

View file

@ -38,15 +38,15 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
let variant_name = &variant.ident;
let discriminant = u32::try_from(index)
.expect("enumeration discriminants must be representable in `u32`");
.expect("enumeration discriminants must be representable as `u32`");
// Discriminant size:
serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::SERIALISED_SIZE });
serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
let mut captures = Punctuated::<Capture, Token![,]>::new();
let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
chain_commands.push(quote! { stream.append(&#discriminant)? });
chain_commands.push(quote! { #discriminant.serialise(stream)? });
for (index, field) in variant.fields.iter().enumerate() {
let field_ty = &field.ty;
@ -55,14 +55,14 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
.as_ref()
.map_or_else(|| Ident::new(&format!("v{index}"), Span::call_site()), Clone::clone);
serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::SERIALISED_SIZE });
serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
captures.push(Capture {
ref_token: Token![ref](Span::call_site()),
ident: field_name.clone(),
});
chain_commands.push(quote! { stream.append(#field_name)? });
chain_commands.push(quote! { #field_name.serialise(stream)? });
}
chain_commands.push_punct(Token![;](Span::call_site()));
@ -90,14 +90,11 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
size_tests.push(quote! { { core::unreachable!(); } });
quote! {
const SERIALISED_SIZE: usize = const { #size_tests };
fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = ::bzipper::Sstream::new(buf);
const MAX_SERIALISED_SIZE: usize = const { #size_tests };
fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> {
match (*self) { #match_arms }
Ok(())
}
}

View file

@ -33,14 +33,10 @@ use syn::{
pub fn serialise_struct(data: &DataStruct) -> TokenStream {
if matches!(data.fields, Fields::Unit) {
quote! {
const SERIALISED_SIZE: usize = 0x0;
const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
Ok(())
}
fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> { Ok(()) }
}
} else {
let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
@ -53,21 +49,17 @@ pub fn serialise_struct(data: &DataStruct) -> TokenStream {
.as_ref()
.map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream);
serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::SERIALISED_SIZE });
serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
chain_commands.push(quote! { stream.append(&self.#name)? });
chain_commands.push(quote! { self.#name.serialise(stream)? });
}
chain_commands.push_punct(Token![;](Span::call_site()));
quote! {
const SERIALISED_SIZE: usize = #serialised_size;
fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let mut stream = ::bzipper::Sstream::new(buf);
const MAX_SERIALISED_SIZE: usize = #serialised_size;
fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> {
#chain_commands
Ok(())