Bump minor version; Add macros crate; Add derive macros; Update package metadata; Update readme; Expand docs; Require fixed size (de)serialisations; Add more error variants; Require 'bzipper::Error' for (de)serialisation; Reworks streams; Remove 'Buffer'; Rework 'FixedString'; Serialise 'usize' and 'isize' as 'u32' and 'i32', respectively; Rework arrays (de)serialisation; Fix 'Result' serialisations; Add new logo; Add features 'alloc' and 'std'; Specify rustc version; Rename 'FixedStringIter' to 'FixedIter'; Implement 'Serialise' and 'Deserialise' for single tuples and 'PhantomData';

This commit is contained in:
Gabriel Bjørnager Jensen 2024-08-21 13:05:44 +02:00
parent 1573a33399
commit 0c64e6a7a0
38 changed files with 3173 additions and 2245 deletions

View file

@ -3,6 +3,29 @@
This is the changelog of `bzipper`.
See `"README.md"` for more information.
## 0.5.0
* Bump minor version
* Add macros crate
* Add derive macros
* Update package metadata
* Update readme
* Expand docs
* Require fixed size (de)serialisations
* Add more error variants
* Require `bzipper::Error` for (de)serialisation
* Reworks streams
* Remove `Buffer`
* Rework `FixedString`
* Serialise `usize` and `isize` as `u32` and `i32`, respectively
* Rework arrays (de)serialisation
* Fix `Result` serialisations
* Add new logo
* Add features `alloc` and `std`
* Specify rustc version
* Rename `FixedStringIter` to `FixedIter`
* Implement `Serialise` and `Deserialise` for single tuples and `PhantomData`
## 0.4.7
* Extensively elaborate docs

View file

@ -1,16 +1,17 @@
[package]
name = "bzipper"
version = "0.4.7"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Binary (de)serialiser."
documentation = "https://docs.rs/bzipper"
readme = "README.md"
homepage = "https://achernar.dk/html/bzipper.html"
repository = "https://mandelbrot.dk/bzipper"
license = "LGPL-3.0-or-later"
[workspace]
members = ["bzipper", "bzipper_macros"]
resolver = "2"
[lints.clippy]
[workspace.package]
version = "0.5.0"
authors = ["Gabriel Bjørnager Jensen"]
description = "Binary (de)serialiser."
readme = "README.md"
homepage = "https://achernar.dk/index.php?p=bzipper"
repository = "https://mandelbrot.dk/bzipper/"
license = "LGPL-3.0-or-later"
[workspace.lints.clippy]
as_ptr_cast_mut = "forbid"
as_underscore = "warn"
assertions_on_result_states = "warn"

107
README.md
View file

@ -2,92 +2,105 @@
[`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 this crate is to serialise data with a known size limit.
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.
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.
This crate does not require any dependencies at the moment.
It is also compatible with `no_std`.
This crate is compatible with `no_std`.
See [Docs.rs](https://docs.rs/bzipper/latest/bzipper/) for documentation.
## Data Model
## Data model
Most primitive types serialise losslessly, with the exception being `usize` and `isize`.
These serialise as `u16` and `i16`, respectively, for portability reasons.
These serialise as `u32` and `i32`, respectively, for portability reasons.
Unsized types, such as `str` and slices, are not supported.
Instead, array should be used.
Instead, arrays should be used.
For strings, the `FixedString` type is also provided.
## Usage
This crate revolves around the `Serialise` and `Deserialise` traits, both of which work around streams (more specifically, d-streams and s-streams).
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).
Many core types come implemented with `bzipper`, including primitives as well as some standard library types such as `Option` and `Result`.
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:
```rs
use bzipper::{Deserialise, Serialise};
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct IoRegister {
addr: u32,
value: u16,
}
let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
```
### Serialisation
To serialise an object implementing `Serialise`, simply allocate a so-called "s-stream" (short for *serialisation stream*) with the `Sstream` type:
```rs
let mut buf: [u8; 16] = Default::default();
let mut stream = bzipper::Sstream::new(&mut buf);
```
The resulting stream is immutable in the sense that it cannot grow its buffer, altough it does keep track of the buffer's state.
A byte sequence can be added to our new stream by passing the stream to a call to the `serialise` method:
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:
```rs
use bzipper::Serialise;
let mut buf: [u8; 2] = Default::default();
let mut stream = bzipper::Sstream::new(&mut buf);
let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
'Ж'.serialise(&mut buf).unwrap();
0x4554_u16.serialise(&mut stream).unwrap();
assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
```
The ammount of bytes used by the serialiser (that is, the ammount of bytes written to the stream) is indicated by its return value (i.e. it has the type `Result<usize, Serialise::Error>`).
The only special requirement of the `serialise` method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
Whilst the *maximum* ammount of bytes is specified by the `SERIALISE_LIMIT` constant, this can in cases be lower (for example with `None` variants which are always encoded as a single, null byte).
We can also use streams to *chain* multiple elements together.
```rs
use bzipper::Serialise;
let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
let mut stream = bzipper::Sstream::new(&mut buf);
stream.append(&'ل');
stream.append(&'ا');
stream.append(&'م');
stream.append(&'د');
stream.append(&'ا');
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).
It is recommended for implementors to adhere to this convention as well.
After serialisation, the s-stream records the new write-to position of the buffer. This allows for *chaining* of serialisations, which can prove useful when implementing the trait for custom types.
### Deserialisation
As with serialisation, deserialisation uses streams (just with the `Dstream` type; short for *deserialisation stream*):
Deserialisation works with an almost identical syntax to serialisation.
```rs
let data = [0x45, 0x54];
let mut stream = bzipper::Dstream::new(&data);
```
Using these streams is also just as simple as with s-streams:
To deserialise a buffer, simply call the `deserialise` method:
```rs
use bzipper::Deserialise;
let data = [0x45, 0x54];
let mut stream = bzipper::Dstream::new(&data);
assert_eq!(u16::deserialise(&mut stream).unwrap(), 0x4554);
assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
```
When chaining serialisations, keep in mind that appropriate deserialisations should come in **reverse order** (streams function similarly to stacks in this sense).
Just like with serialisations, the `Dstream` can be used to deserialise chained elements:
## Copyright & Licensing
```rs
use bzipper::Deserialise;
Copyright 2024 Gabriel Bjørnager Jensen.
let data = [0x45, 0x54];
let stream = bzipper::Dstream::new(&data);
This program 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.
This program 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 Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
assert_eq!(stream.take::<u8>().unwrap(), 0x45);
assert_eq!(stream.take::<u8>().unwrap(), 0x54);
```

View file

@ -1,9 +1,15 @@
<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
<mask id="z">
<polyline fill="none" points="20,28 20,20 76,20 36.970562748,76" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" />
<polyline fill="none" points="76,68 76,76 20,76 59.029437252,20" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" />
<rect fill="white" height="24" width="32" x="16" y="24" />
<circle cx="48" cy="48" fill="black" r="16" />
<polygon fill="white" points="20,16 76,16 80,20 80,80 16,80 64,32 16,32 16,20" />
<circle cx="20" cy="20" fill="white" r="4" />
<circle cx="76" cy="20" fill="white" r="4" />
<circle cx="80" cy="80" fill="black" r="16" />
</mask>
<rect fill="#FFFFFF" height="100%" width="100%" x="0" y="0" />
<rect fill="#B4202D" height="100%" mask="url(#z)" width="100%" x="0" y="0" />
<rect fill="#526F03" height="100%" width="100%" x="0" y="0" />
<rect fill="#FFFFFF" height="100%" mask="url(#z)" width="100%" x="0" y="0" />
</svg>

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 580 B

Before After
Before After

26
bzipper/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "bzipper"
edition = "2021"
rust-version = "1.81"
documentation = "https://docs.rs/bzipper/"
version.workspace = true
authors.workspace = true
description.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
[package.metadata.docs.rs]
all-features = true
[features]
alloc = []
std = []
[dependencies]
bzipper_macros = { path = "../bzipper_macros" }
[lints]
workspace = true

View file

@ -0,0 +1,245 @@
// 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/>.
#[cfg(test)]
mod test;
use crate::{Dstream, Error, Result, Serialise};
use core::convert::Infallible;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
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.
///
/// This function must **never** take more bytes than specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE).
/// Doing so is considered a logic error.
/// Likewise, providing more than this amount is also disfavoured.
///
/// # Errors
///
/// If deserialisation failed, e.g. by an illegal byte being found, 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 deserialise(data: &[u8]) -> 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() })?
.try_into()
.unwrap();
Ok(Self::from_be_bytes(data))
}
}
};
}
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);
let value = <$ty as ::bzipper::Deserialise>::deserialise(data)?;
NonZero::new(value)
.ok_or(Error::NullInteger)
}
}
};
}
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);
// 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])?);
}
// This should be safe as `MaybeUninit<T>` is
// transparent to `T`, and we have initialised
// every element. The original buffer is NOT
// dropped automatically, so we can just forget
// about it from this point on. `transmute` cannot
// be used here, and `transmute_unchecked` is re-
// served for the greedy rustc devs.
let value: [T; N] = unsafe { buf.as_ptr().cast::<[T; N]>().read() };
Ok(value)
}
}
impl Deserialise for bool {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u8::deserialise(data)?;
match value {
0x00 => Ok(false),
0x01 => Ok(true),
_ => Err(Error::InvalidBoolean { value })
}
}
}
impl Deserialise for char {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u32::deserialise(data)?;
Self::from_u32(value)
.ok_or(Error::InvalidCodePoint { 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") }
}
impl Deserialise for isize {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = i32::deserialise(data)?
.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);
let stream = Dstream::new(data);
let sign = stream.take::<bool>()?;
if sign {
Ok(Some(stream.take::<T>()?))
} else {
Ok(None)
}
}
}
impl<T> Deserialise for PhantomData<T> {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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>()?;
let value = if sign {
Err(stream.take::<E>()?)
} else {
Ok(stream.take::<T>()?)
};
Ok(value)
}
}
impl Deserialise for () {
fn deserialise(_data: &[u8]) -> Result<Self> { Ok(()) }
}
impl Deserialise for usize {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u32::deserialise(data)?
.try_into().expect("unable to convert from `u32` to `usize`");
Ok(value)
}
}
//impl_numeric!(f128);
//impl_numeric!(f16);
impl_numeric!(f32);
impl_numeric!(f64);
impl_numeric!(i128);
impl_numeric!(i16);
impl_numeric!(i32);
impl_numeric!(i64);
impl_numeric!(i8);
impl_numeric!(u128);
impl_numeric!(u16);
impl_numeric!(u32);
impl_numeric!(u64);
impl_numeric!(u8);
impl_non_zero!(i128);
impl_non_zero!(i16);
impl_non_zero!(i32);
impl_non_zero!(i64);
impl_non_zero!(i8);
impl_non_zero!(isize);
impl_non_zero!(u128);
impl_non_zero!(u16);
impl_non_zero!(u32);
impl_non_zero!(u64);
impl_non_zero!(u8);
impl_non_zero!(usize);

View file

@ -0,0 +1,118 @@
// 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 crate::{Deserialise, Serialise};
#[test]
fn test() {
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct ProcExit {
exit_code: i32,
timestmap: u64,
}
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct NewByte(u8);
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct Unit;
#[derive(Debug, Deserialise, PartialEq, Serialise)]
enum UnitOrFields {
Unit(Unit),
Unnamed(i32),
Named { timestamp: u64 },
}
macro_rules! test {
($ty:ty: $data:expr => $value:expr) => {{
use ::bzipper::{Deserialise, Serialise};
let buf: [u8; <$ty as Serialise>::SERIALISED_SIZE] = $data;
let left = <$ty as Deserialise>::deserialise(&buf).unwrap();
let right = $value;
assert_eq!(left, right);
}};
}
test!(i8: [0x00] => 0x00);
test!(i8: [0x7F] => 0x7F);
test!(i8: [0x80] => -0x80);
test!(i8: [0xFF] => -0x01);
test!(i16: [0x00, 0x00] => 0x0000);
test!(i16: [0x7F, 0xFF] => 0x7FFF);
test!(i16: [0x80, 0x00] => -0x8000);
test!(i16: [0xFF, 0xFF] => -0x0001);
test!(i32: [0x00, 0x00, 0x00, 0x00] => 0x00000000);
test!(i32: [0x7F, 0xFF, 0xFF, 0xFF] => 0x7FFFFFFF);
test!(i32: [0x80, 0x00, 0x00, 0x00] => -0x80000000);
test!(i32: [0xFF, 0xFF, 0xFF, 0xFF] => -0x00000001);
test!(i64: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] => 0x0000000000000000);
test!(i64: [0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] => 0x7FFFFFFFFFFFFFFF);
test!(i64: [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] => -0x8000000000000000);
test!(i64: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] => -0x0000000000000001);
test!(u128: [
0xFF, 0x0F, 0xEF, 0x1F, 0xDF, 0x2F, 0xCF, 0x3F,
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; 0x5]: [
0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, 0x91,
0x00, 0x00, 0x03, 0xBC, 0x00, 0x00, 0x03, 0x94,
0x00, 0x00, 0x03, 0xB1
] => ['\u{03BB}', '\u{0391}', '\u{03BC}', '\u{0394}', '\u{03B1}']);
test!(Option<()>: [0x00] => None);
test!(Option<()>: [0x01] => Some(()));
test!(Result<(), i8>: [0x00, 0x00] => Ok(()));
test!(Result<(), i8>: [0x01, 0x7F] => Err(i8::MAX));
test!(ProcExit: [
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x5E, 0x0B, 0xE1, 0x00,
] => ProcExit { exit_code: 0x1, timestmap: 1577836800 });
test!(NewByte: [0x80] => NewByte(0x80));
test!(Unit: [] => Unit);
test!(UnitOrFields: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
] => UnitOrFields::Unit(Unit));
test!(UnitOrFields: [
0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00,
] => UnitOrFields::Unnamed(-0x1));
test!(UnitOrFields: [
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x66, 0xC5, 0xC8, 0x4C,
] => UnitOrFields::Named { timestamp: 1724237900 });
}

View file

@ -0,0 +1,298 @@
// 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 crate::{Deserialise, Result, Serialise};
impl<T0> Deserialise for (T0, )
where
T0: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
))
}
}
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);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2> Deserialise for (T0, T1, T2)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2, T3> Deserialise for (T0, T1, T2, T3)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2, T3, T4> Deserialise for (T0, T1, T2, T3, T4)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5> Deserialise for (T0, T1, T2, T3, T4, T5)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6> Deserialise for (T0, T1, T2, T3, T4, T5, T6)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok((
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
Deserialise::deserialise(data)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise,
T7: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise,
T7: Deserialise,
T8: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise,
T7: Deserialise,
T8: Deserialise,
T9: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise,
T7: Deserialise,
T8: Deserialise,
T9: Deserialise,
T10: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise,
T3: Deserialise,
T4: Deserialise,
T5: Deserialise,
T6: Deserialise,
T7: Deserialise,
T8: Deserialise,
T9: Deserialise,
T10: Deserialise,
T11: Deserialise, {
fn deserialise(data: &[u8]) -> Result<Self> {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
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)?,
))
}
}

View file

@ -0,0 +1,58 @@
// 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 crate::{Deserialise, Error, Result};
use core::cell::Cell;
/// Byte stream for deserialisation.
///
/// This type borrows a slice, keeping track internally of the used bytes.
pub struct Dstream<'a> {
data: &'a [u8],
pos: Cell<usize>,
}
impl<'a> Dstream<'a> {
/// Constructs a new byte stream.
#[inline(always)]
#[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.
#[inline]
pub fn take<T: Deserialise>(&self) -> Result<T> {
let rem = self.data.len() - self.pos.get();
let req = T::SERIALISED_SIZE;
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])
}
}

View file

@ -19,14 +19,16 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
use core::error::Error as StdError;
use core::fmt::{Display, Formatter};
use core::str::Utf8Error;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
/// Mapping of [`core::result::Result`].
pub type Result<T> = core::result::Result<T, Error>;
/// Denotes an error.
/// (De)serialisation failures.
///
/// These variants are used when deserialisation fails.
/// Serialisations are assumed infallible.
@ -38,10 +40,17 @@ pub enum Error {
/// A string encountered an invalid UTF-8 sequence.
BadString { source: Utf8Error },
/// An implementor-defined 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> },
/// Bytes were requested on an empty stream.
EndOfStream { req: usize, rem: usize },
/// A boolean encountered a value outside (0) and (1).
/// A boolean encountered a value outside `0` and `1`.
InvalidBoolean { value: u8 },
/// An invalid code point was encountered.
@ -49,13 +58,16 @@ pub enum Error {
/// This includes surrogate points in the inclusive range `U+D800` to `U+DFFF`, as well as values larger than `U+10FFFF`.
InvalidCodePoint { value: u32 },
/// An `isize` value couldn't fit into (16) bits.
/// An invalid enumeration descriminant was provided.
InvalidDiscriminant { value: u32 },
/// An `isize` value couldn't fit into `16` bits.
IsizeOutOfRange { value: isize },
/// A non-zero integer encountered the value (0).
/// A non-zero integer encountered the value `0`.
NullInteger,
/// A `usize` value couldn't fit into (16) bits.
/// A `usize` value couldn't fit into `16` bits.
UsizeOutOfRange { value: usize },
}
@ -64,48 +76,50 @@ impl Display for Error {
use Error::*;
match *self {
ArrayTooShort { req, len } => {
write!(f, "array of ({len}) element(s) cannot hold ({req})")
},
ArrayTooShort { req, len }
=> write!(f, "array of ({len}) element(s) cannot hold ({req})"),
BadString { ref source } =>{
write!(f, "unable to parse utf8: \"{source}\"")
},
BadString { ref source }
=> write!(f, "unable to parse utf8: \"{source}\""),
EndOfStream { req, rem } => {
write!(f, "({req}) byte(s) were requested but only ({rem}) byte(s) were left")
},
#[cfg(feature = "alloc")]
CustomError { ref source }
=> write!(f, "{source}"),
InvalidBoolean { value } => {
write!(f, "expected boolean but got {value:#02X}")
},
EndOfStream { req, rem }
=> write!(f, "({req}) byte(s) were requested but only ({rem}) byte(s) were left"),
InvalidCodePoint { value } => {
write!(f, "code point U+{value:04X} is not valid")
},
InvalidBoolean { value }
=> write!(f, "expected boolean but got {value:#02X}"),
IsizeOutOfRange { value } => {
write!(f, "signed size value ({value}) cannot be serialised: must be in the range ({}) to ({})", i16::MIN, i16::MAX)
},
InvalidCodePoint { value }
=> write!(f, "code point U+{value:04X} is not valid"),
NullInteger => {
write!(f, "expected non-zero integer but got (0)")
},
InvalidDiscriminant { value }
=> write!(f, "discriminant ({value}) is not valid for the given enumeration"),
UsizeOutOfRange { value } => {
write!(f, "unsigned size value ({value}) cannot be serialised: must be at most ({})", u16::MAX)
},
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 }
=> write!(f, "unsigned size value ({value}) cannot be serialised: must be at most ({})", u16::MAX),
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
use Error::*;
match *self {
BadString { ref source } => Some(source),
#[cfg(feature = "alloc")]
CustomError { ref source } => Some(source.as_ref()),
_ => None,
}
}

View file

@ -0,0 +1,46 @@
// 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

@ -0,0 +1,448 @@
// 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/>.
#[cfg(test)]
mod test;
use crate::{Deserialise, Error, FixedIter, Serialise};
use core::cmp::Ordering;
use core::fmt::{Debug, Display, Formatter};
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut, Index, IndexMut};
use core::slice::SliceIndex;
use core::str::FromStr;
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
/// Owned string with maximum size.
///
/// This is in contrast to [String] -- which has no size limit in practice -- and [str], which is unsized.
///
/// # 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.
///
/// ```
/// 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");
///
/// assert_eq!(size_of_val(&str0), size_of_val(&str1));
/// assert_eq!(size_of_val(&str0), size_of_val(&str2));
/// assert_eq!(size_of_val(&str0), size_of_val(&str3));
/// assert_eq!(size_of_val(&str1), size_of_val(&str2));
/// assert_eq!(size_of_val(&str1), size_of_val(&str3));
/// 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)]
pub struct FixedString<const N: usize> {
buf: [char; N],
len: usize,
}
impl<const N: usize> FixedString<N> {
/// Constructs a new, fixed-size string.
///
/// Note that it is only the internal buffer that is size-constrained.
/// The string internally keeps track of the amount of used characters and acts accordingly.
/// One must therefore only see the value of `N` as a size *limit*.
///
/// 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).
#[inline(always)]
#[must_use]
pub const fn new() -> Self { Self { buf: ['\0'; N], len: 0x0 } }
/// Consumes the buffer into a fixed string.
///
/// 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.
///
/// 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.
#[inline(always)]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut char { self.buf.as_mut_ptr() }
/// Borrows the string as a character 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] {
// 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.
///
/// The range of the returned slice includes the entire internal buffer.
/// For borrowing only the "used" characters, see [`as_slice`](Self::as_slice).
#[inline(always)]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [char] { &mut self.buf[0x0..self.len] }
/// Returns the length of the string.
///
/// This does not necessarily equate to the value of `N`, as the internal buffer may be used but partially.
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { self.len }
/// Checks if the string is empty, i.e. `self.len() == 0x0`.
#[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`.
#[inline(always)]
#[must_use]
pub const fn is_full(&self) -> bool { self.len() == N }
/// Sets the internal length.
///
/// 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`.
#[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;
}
/// Borrows characters at the specified index.
///
/// If no element can be retrieved using the given index, [`None`] is returned instead.
#[inline(always)]
#[must_use]
pub fn get<I: SliceIndex<[char]>>(&self, index: I) -> Option<&I::Output> { self.buf.get(index) }
/// Borrows characters at the specified index *without* checking bounds.
///
/// For performing a similar operation *with* bounds checks, see [`get`](Self::get).
///
/// # Safety
///
/// If the given index points out of the bounds of the string, behaviour is undefined.
#[inline(always)]
#[must_use]
pub unsafe fn get_unchecked<I: SliceIndex<[char]>>(&self, index: I) -> &I::Output { self.buf.get_unchecked(index) }
/// Mutably borrows characters at the specified index.
///
/// If no element can be retrieved using the given index, [`None`] is returned instead.
#[inline(always)]
#[must_use]
pub fn get_mut<I: SliceIndex<[char]>>(&mut self, index: I) -> Option<&mut I::Output> { self.buf.get_mut(index) }
/// Mutably borrows characters at the specified index *without* checking bounds.
///
/// For performing a similar operation *with* bounds checks, see [`get_mut`](Self::get_mut)
///
/// # Safety
///
/// If the given index points out of the bounds of the string, behaviour is undefined.
#[inline(always)]
#[must_use]
pub unsafe fn get_unchecked_mut<I: SliceIndex<[char]>>(&mut self, index: I) -> &I::Output { self.buf.get_unchecked_mut(index) }
/// Pushes a character into the string.
///
/// The internal length is updated accordingly.
///
/// # Panics
///
/// If the string cannot hold any more character (i.e. it is full), this method will panic.
#[inline(always)]
pub fn push(&mut self, c: char) {
assert!(!self.is_full(), "cannot push character to full string");
self.buf[self.len] = c;
self.len += 0x1;
}
/// Pops a character from the string.
///
/// The internal length is updated accordingly.
///
/// If no characters are left (i.e. the string is empty), an instance of [`None`] is returned.
#[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;
c
})
}
/// Returns an iterator to the contained characters.
///
/// This iterator only covers "used" character.
/// See [`iter_mut`](Self::iter_mut) for borrowing the entire buffer.
#[inline(always)]
pub fn iter(&self) -> core::slice::Iter<'_, char> { self.as_slice().iter() }
/// Returns a mutable iterator to the contained characters.
///
/// This iterator covers the entire internal buffer.
/// See [`iter`](Self::iter) for borrowing only "used" characters.
#[inline(always)]
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, char> { self.as_mut_slice().iter_mut() }
}
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> AsRef<[char]> for FixedString<N> {
#[inline(always)]
fn as_ref(&self) -> &[char] { self.as_slice() }
}
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(())
}
}
impl<const N: usize> Default for FixedString<N> {
#[inline(always)]
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];
#[inline(always)]
fn deref(&self) -> &Self::Target { self.as_slice() }
}
/// 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() }
}
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(())
}
}
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();
for (i, c) in s.chars().enumerate() {
if i >= N { return Err(Error::ArrayTooShort { req: len, len: N }) }
buf[i] = c;
}
Ok(Self { buf, len })
}
}
impl<I: SliceIndex<[char]>, 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> {
#[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() }
}
impl<const N: usize, const M: usize> PartialEq<FixedString<M>> for FixedString<N> {
#[inline]
fn eq(&self, other: &FixedString<M>) -> bool {
if self.len() != other.len() { return false };
for i in 0x0..self.len() {
if self.buf[i] != other.buf[i] { return false };
}
true
}
}
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
}
}
impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<N> {
fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> {
let len = self.len().max(other.len());
for i in 0x0..len {
let lc = self.get(i);
let rc = other.get(i);
match (lc, rc) {
(None, None) => return Some(Ordering::Equal),
(Some(_), None) => return Some(Ordering::Greater),
(None, Some(_)) => return Some(Ordering::Less),
(Some(lc), Some(rc)) => {
match lc.partial_cmp(rc) {
Some(Ordering::Equal) => {},
ordering => return ordering
}
}
}
}
Some(Ordering::Equal)
}
}
impl<const N: usize> TryFrom<&str> for FixedString<N> {
type Error = <Self as FromStr>::Err;
#[inline(always)]
fn try_from(value: &str) -> Result<Self, Self::Error> { Self::from_str(value) }
}
#[cfg(feature = "alloc")]
impl<const N: usize> TryFrom<String> for FixedString<N> {
type Error = <Self as FromStr>::Err;
#[inline(always)]
fn try_from(value: String) -> Result<Self, Self::Error> { Self::from_str(&value) }
}
#[cfg(feature = "alloc")]
impl<const N: usize> From<FixedString<N>> for String {
#[inline(always)]
fn from(value: FixedString<N>) -> Self { value.to_string() }
}

View file

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

202
bzipper/src/lib.rs Normal file
View file

@ -0,0 +1,202 @@
// 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/>.
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")]
//! 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.
//! 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.
//!
//! This crate is compatible with `no_std`.
//!
//! # Data model
//!
//! Most primitive types serialise losslessly, with the exception being [`usize`] and [`isize`].
//! These serialise as [`u32`] and [`i32`], respectively, for portability reasons.
//!
//! Unsized types, such as [`str`] and [slices](slice), are not supported.
//! Instead, [arrays](array) should be used.
//! 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](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:
//!
//! ```
//! use bzipper::{Deserialise, Serialise};
//!
//! #[derive(Debug, Deserialise, PartialEq, Serialise)]
//! struct IoRegister {
//! addr: u32,
//! value: u16,
//! }
//!
//! let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
//! IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
//!
//! assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
//!
//! assert_eq!(IoRegister::deserialise(&buf).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:
//!
//! ```
//! use bzipper::Serialise;
//!
//! let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
//! 'Ж'.serialise(&mut buf).unwrap();
//!
//! assert_eq!(buf, [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`.
//!
//! We can also use streams to *chain* multiple elements together.
//!
//! ```
//! use bzipper::Serialise;
//!
//! let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
//! let mut stream = bzipper::Sstream::new(&mut buf);
//!
//! stream.append(&'ل');
//! stream.append(&'ا');
//! stream.append(&'م');
//! stream.append(&'د');
//! stream.append(&'ا');
//!
//! 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).
//! It is recommended for implementors to adhere to this convention as well.
//!
//! ## Deserialisation
//!
//! Deserialisation works with an almost identical syntax to serialisation.
//!
//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method:
//!
//! ```
//! use bzipper::Deserialise;
//!
//! let data = [0x45, 0x54];
//! assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
//! ```
//!
//! Just like with serialisations, the [`Dstream`] can be used to deserialise chained elements:
//!
//! ```
//! use bzipper::Deserialise;
//!
//! let data = [0x45, 0x54];
//! let stream = bzipper::Dstream::new(&data);
//!
//! assert_eq!(stream.take::<u8>().unwrap(), 0x45);
//! assert_eq!(stream.take::<u8>().unwrap(), 0x54);
//! ```
#![no_std]
#![cfg_attr(doc, feature(doc_cfg))]
extern crate self as bzipper;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "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;
/// Implements [`Serialise`] for the provided type.
///
/// # Structs
///
/// For structures, each element is chained in **order of declaration.**
/// For example, the following struct will serialise its field `foo` before `bar`:
///
/// ```
/// use bzipper::Serialise;
///
/// #[derive(Serialise)]
/// pub struct FooBar {
/// pub foo: char,
/// pub bar: char,
/// }
/// ```
///
/// Should the order of declaration change, then most of---if not all---previous dervied serialisations become void.
///
/// The value of [`SERIALISED_SIZE`](Serialise::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.
///
/// # Enums
///
/// Enumerations are serialised by first assigning each variant its own discriminant.
/// By default, each discriminant is assigned from the range 0 to infinite, to the extend allowed by the `u32` type (as which the discriminant is encoded).
/// In the future, however, custom representations and assigned discriminants will be honoured.
///
/// 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.
///
/// # Unions
///
/// Unions cannot derive `Serialise` due to the uncertainty of their contents.
/// The trait should therefore be implemented manually.
#[doc(inline)]
pub use bzipper_macros::Serialise;
macro_rules! use_mod {
($vis:vis $name:ident) => {
mod $name;
$vis use $name::*;
};
}
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

@ -0,0 +1,300 @@
// 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/>.
#[cfg(test)]
mod test;
use crate::{Error, Result, Sstream};
use core::{convert::Infallible, marker::PhantomData};
mod tuple;
/// Denotes a type capable of being serialised.
///
/// It is recommended to simply derive this trait for custom types.
/// It can, however, be manually implemented:
///
/// ```
/// use bzipper::{Result, Serialise, Sstream};
///
/// struct Foo {
/// bar: u16,
/// baz: f32,
/// }
///
/// 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);
///
/// // Serialise fields using chaining.
///
/// let mut stream = Sstream::new(buf);
///
/// stream.append(&self.bar)?;
/// stream.append(&self.baz)?;
///
/// 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.
pub trait Serialise: Sized {
/// The amount of bytes that 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;
/// Serialises `self` into a slice.
///
/// In most cases it is wiser to chain serialisations using [`Sstream`] instead of using this method directly.
///
/// # 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<()>;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Serialise for $ty {
const 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);
Ok(())
}
}
};
}
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Serialise for ::core::num::NonZero<$ty> {
const 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)
}
}
};
}
impl<T: Serialise, const N: usize> Serialise for [T; N] {
const SERIALISED_SIZE: usize = T::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)? }
Ok(())
}
}
impl Serialise for bool {
const SERIALISED_SIZE: usize = u8::SERIALISED_SIZE;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
u8::from(*self).serialise(buf)
}
}
impl Serialise for char {
const SERIALISED_SIZE: usize = u32::SERIALISED_SIZE;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
u32::from(*self).serialise(buf)
}
}
// Especially useful for `Result<T, Infallible>`.
// *If* that is needed, of course.
impl Serialise for Infallible {
const SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
fn serialise(&self, _buf: &mut [u8]) -> Result<()> { unreachable!() }
}
impl Serialise for isize {
const SERIALISED_SIZE: usize = i32::SERIALISED_SIZE;
#[inline]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
let value = i32::try_from(*self)
.map_err(|_| Error::IsizeOutOfRange { value: *self })?;
value.serialise(buf)
}
}
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);
// 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);
match *self {
None => {
stream.append(&false)?;
// No need to zero-fill.
},
Some(ref v) => {
stream.append(&true)?;
stream.append(v)?;
},
};
Ok(())
}
}
impl<T> Serialise for PhantomData<T> {
const SERIALISED_SIZE: usize = size_of::<Self>();
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
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);
// Remember the descriminant.
match *self {
Ok(ref v) => {
stream.append(&false)?;
stream.append(v)?;
},
Err(ref e) => {
stream.append(&true)?;
stream.append(e)?;
},
};
Ok(())
}
}
impl Serialise for () {
const SERIALISED_SIZE: usize = size_of::<Self>();
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> Result<()> {
debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
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);
let value = u32::try_from(*self)
.map_err(|_| Error::UsizeOutOfRange { value: *self })?;
value.serialise(buf)
}
}
//impl_numeric!(f128);
//impl_numeric!(f16);
impl_numeric!(f32);
impl_numeric!(f64);
impl_numeric!(i128);
impl_numeric!(i16);
impl_numeric!(i32);
impl_numeric!(i64);
impl_numeric!(i8);
impl_numeric!(u128);
impl_numeric!(u16);
impl_numeric!(u32);
impl_numeric!(u64);
impl_numeric!(u8);
impl_non_zero!(i128);
impl_non_zero!(i16);
impl_non_zero!(i32);
impl_non_zero!(i64);
impl_non_zero!(i8);
impl_non_zero!(isize);
impl_non_zero!(u128);
impl_non_zero!(u16);
impl_non_zero!(u32);
impl_non_zero!(u64);
impl_non_zero!(u8);
impl_non_zero!(usize);

View file

@ -0,0 +1,104 @@
// Copyright 2024 Gabriel Bjørnager Jensen.
//
// This file is part of bzipper.
//test!(you can redistribut => []);
// 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 crate::{FixedString, Serialise};
#[test]
fn test_serialise() {
#[derive(Serialise)]
struct Foo(char);
#[derive(Serialise)]
enum Bar {
Unit,
Pretty(bool),
Teacher { initials: [char; 0x3] },
}
assert_eq!(Foo::SERIALISED_SIZE, 0x4);
assert_eq!(Bar::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>::SERIALISED_SIZE];
<$ty as Serialise>::serialise(&mut $value, &mut buf).unwrap();
assert_eq!(buf, data);
}};
}
test!(u8: 0x00 => [0x00]);
test!(u8: 0xFF => [0xFF]);
test!(u8: 0x7F => [0x7F]);
test!(u16: 0x0F_7E => [0x0F, 0x7E]);
test!(u32: 0x00_2F_87_E7 => [0x00, 0x2F, 0x87, 0xE7]);
test!(u64: 0xF3_37_CF_8B_DB_03_2B_39 => [0xF3, 0x37, 0xCF, 0x8B, 0xDB, 0x03, 0x2B, 0x39]);
test!(u128: 0x45_A0_15_6A_36_77_17_8A_83_2E_3C_2C_84_10_58_1A => [
0x45, 0xA0, 0x15, 0x6A, 0x36, 0x77, 0x17, 0x8A,
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::<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!([char; 0x5]: ['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'] => [
0x00, 0x00, 0x03, 0xB4, 0x00, 0x00, 0x01, 0x90,
0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, 0xA4,
0x00, 0x00, 0x03, 0xB1,
]);
test!(Result::<u16, char>: Ok(0x45_45) => [0x00, 0x45, 0x45, 0x00, 0x00]);
test!(Result::<u16, char>: Err(char::REPLACEMENT_CHARACTER) => [0x01, 0x00, 0x00, 0xFF, 0xFD]);
test!(Option<()>: None => [0x00]);
test!(Option<()>: Some(()) => [0x01]);
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::Pretty(true) => [
0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
test!(Bar: Bar::Teacher { initials: ['T', 'L', '\0'] } => [
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54,
0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00,
]);
}

View file

@ -0,0 +1,424 @@
// 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 crate::{Result, Serialise, Sstream};
impl<T0> Serialise for (T0, )
where
T0: Serialise, {
const SERIALISED_SIZE: usize =
T0::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)?;
Ok(())
}
}
impl<T0, T1> Serialise for (T0, T1)
where
T0: Serialise,
T1: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::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)?;
Ok(())
}
}
impl<T0, T1, T2> Serialise for (T0, T1, T2)
where
T0: Serialise,
T1: Serialise,
T2: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::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)?;
Ok(())
}
}
impl<T0, T1, T2, T3> Serialise for (T0, T1, T2, T3)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise, {
const SERIALISED_SIZE: usize =
T0::SERIALISED_SIZE
+ T1::SERIALISED_SIZE
+ T2::SERIALISED_SIZE
+ T3::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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4> Serialise for (T0, T1, T2, T3, T4)
where
T0: Serialise,
T1: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5> Serialise for (T0, T1, T2, T3, T4, T5)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6> Serialise for (T0, T1, T2, T3, T4, T5, T6)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
T4: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
T4: Serialise,
T5: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
T4: Serialise,
T5: Serialise,
T6: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
T4: Serialise,
T5: Serialise,
T6: Serialise,
T7: Serialise,
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;
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)?;
Ok(())
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
where
T0: Serialise,
T1: Serialise,
T2: Serialise,
T3: Serialise,
T4: Serialise,
T5: Serialise,
T6: Serialise,
T7: Serialise,
T8: Serialise,
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;
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)?;
Ok(())
}
}

View file

@ -0,0 +1,58 @@
// 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 crate::{Error, Result, Serialise};
use core::cell::Cell;
/// Byte stream for deserialisation.
///
/// This type borrows a slice, keeping track internally of the used bytes.
pub struct Sstream<'a> {
buf: &'a mut [u8],
pos: Cell<usize>,
}
impl<'a> Sstream<'a> {
/// Constructs a new byte stream.
#[inline(always)]
#[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.
#[inline]
pub fn append<T: Serialise>(&mut self, value: &T) -> Result<()> {
let rem = self.buf.len() - self.pos.get();
let req = T::SERIALISED_SIZE;
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])
}
}

23
bzipper_macros/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "bzipper_macros"
edition = "2021"
documentation = "https://docs.rs/bzipper_macros/"
version.workspace = true
authors.workspace = true
description.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.86"
quote = "1.0.36"
syn = "2.0.72"
[lints]
workspace = true

View file

@ -0,0 +1,41 @@
// 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 proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Ident, Token};
/// A field capture list.
///
/// This is used for capturing fields of structures or enumeration variants.
#[derive(Clone)]
pub struct Capture {
pub ref_token: Token![ref],
pub ident: Ident,
}
impl ToTokens for Capture {
#[inline(always)]
fn to_tokens(&self, tokens: &mut TokenStream) {
self.ref_token.to_tokens(tokens);
self.ident.to_tokens(tokens);
}
}

View file

@ -0,0 +1,98 @@
// 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 proc_macro2::TokenStream;
use quote::ToTokens;
/// An enumeration discriminant.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Discriminant(u32);
impl Discriminant {
/// Constructs a new discriminant.
#[inline(always)]
#[must_use]
pub const fn new(value: u32) -> Self { Self(value) }
/// Retrieves the raw discriminant value.
#[inline(always)]
#[must_use]
pub const fn get(self) -> u32 { self.0 }
/// Unwraps the given value as a discriminant.
///
/// # Panics
///
/// If the given value cannot be represented as an `u32`, this function will panic.
#[inline(always)]
#[must_use]
pub fn unwrap_from<T: TryInto<Self>>(value: T) -> Self {
value
.try_into()
.unwrap_or_else(|_| panic!("enumeration discriminants must be representable in `u32`"))
}
/// Unsafely unwraps the given value as a discriminant.
///
/// This function assumes that this conversion is infallible for the given value.
/// If this is a false guarantee, the [`unwrap_from`](Self::unwrap_from) function should be used instead.
///
/// # Safety
///
/// Behaviour is undefined if the given value cannot be represented as an object of `u32`.
#[inline(always)]
#[must_use]
pub unsafe fn unwrap_from_unchecked<T: TryInto<Self>>(value: T) -> Self {
value
.try_into()
.unwrap_unchecked()
}
}
impl ToTokens for Discriminant {
#[inline(always)]
fn to_tokens(&self, tokens: &mut TokenStream) { self.0.to_tokens(tokens) }
}
impl From<u32> for Discriminant {
#[inline(always)]
fn from(value: u32) -> Self { Self(value) }
}
impl TryFrom<usize> for Discriminant {
type Error = <u32 as TryFrom<usize>>::Error;
#[inline(always)]
fn try_from(value: usize) -> Result<Self, Self::Error> { value.try_into().map(Self) }
}
impl From<Discriminant> for u32 {
#[inline(always)]
fn from(value: Discriminant) -> Self { value.0 }
}
impl TryFrom<Discriminant> for usize {
type Error = <Self as TryFrom<u32>>::Error;
#[inline(always)]
fn try_from(value: Discriminant) -> Result<Self, Self::Error> { value.0.try_into() }
}

View file

@ -0,0 +1,74 @@
// 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 proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
GenericParam,
Generics,
Ident,
Lifetime,
Token,
punctuated::Punctuated,
};
/// A name of a genric.
#[derive(Clone)]
pub enum GenericName {
Const( Ident),
Lifetime(Lifetime),
Type( Ident),
}
impl GenericName {
/// Extracts the names of the given generics.
#[must_use]
pub fn extract_from(generics: &Generics) -> Punctuated<Self, Token![,]> {
let mut names = Punctuated::new();
for generic in &generics.params {
let name = match *generic {
GenericParam::Const( ref param) => Self::Const( param.ident.clone()),
GenericParam::Lifetime(ref param) => Self::Lifetime(param.lifetime.clone()),
GenericParam::Type( ref param) => Self::Type( param.ident.clone()),
};
names.push(name);
}
names
}
}
impl ToTokens for GenericName {
#[inline(always)]
fn to_tokens(&self, tokens: &mut TokenStream) {
use GenericName::*;
match *self {
| Const(ref ident)
| Type( ref ident)
=> ident.to_tokens(tokens),
Lifetime(ref lifetime) => lifetime.to_tokens(tokens),
}
}
}

View file

@ -0,0 +1,78 @@
// 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 crate::Discriminant;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DataEnum, Fields, Token};
use syn::punctuated::Punctuated;
#[must_use]
pub fn deserialise_enum(data: &DataEnum) -> TokenStream {
let mut match_arms = Punctuated::<TokenStream, Token![,]>::new();
for (index, variant) in data.variants.iter().enumerate() {
let variant_name = &variant.ident;
let discriminant = Discriminant::unwrap_from(index);
let block = if matches!(variant.fields, Fields::Unit) {
quote! { Self }
} else {
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>()? }
);
chain_commands.push(command);
}
match variant.fields {
Fields::Named( ..) => quote! { Self::#variant_name { #chain_commands } },
Fields::Unnamed(..) => quote! { Self::#variant_name(#chain_commands) },
Fields::Unit => unreachable!(),
}
};
match_arms.push(quote! { #discriminant => #block });
}
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 };
Ok(value)
}
}
}

View file

@ -0,0 +1,78 @@
// 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 proc_macro2::TokenStream;
use quote::quote;
use syn::{DataStruct, Fields, Token};
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
quote! {
#[inline(always)]
fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
Ok(Self)
}
}
}
}

View file

@ -19,25 +19,8 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
/// Iterator to a fixed string.
pub struct FixedStringIter<const N: usize> {
pub(in crate) buf: [char; N],
pub(in crate) len: usize,
pub(in crate) pos: Option<usize>,
}
impl<const N: usize> Iterator for FixedStringIter<N> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
let pos = self.pos.as_mut()?;
if *pos >= self.len { return None };
let item = self.buf[*pos];
*pos += 0x1;
Some(item)
}
}
use crate::use_mod;
use_mod!(pub deserialise_enum);
use_mod!(pub deserialise_struct);
use_mod!(pub serialise_enum);
use_mod!(pub serialise_struct);

View file

@ -0,0 +1,108 @@
// 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 crate::Capture;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{DataEnum, Fields, Ident, Token};
use syn::punctuated::Punctuated;
#[must_use]
pub fn serialise_enum(data: &DataEnum) -> TokenStream {
let mut sizes = Vec::new();
let mut match_arms = Punctuated::<TokenStream, Token![,]>::new();
for (index, variant) in data.variants.iter().enumerate() {
let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
let name = &variant.ident;
let discriminant = u32::try_from(index)
.expect("enumeration discriminants must be representable in `u32`");
// Discriminant size:
serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::SERIALISED_SIZE });
let arm = if matches!(variant.fields, Fields::Unit) {
quote! { Self::#name => stream.append(&#discriminant)? }
} else {
let mut captures = Punctuated::<Capture, Token![,]>::new();
let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
chain_commands.push(quote! { stream.append(&#discriminant)? });
for (index, field) in variant.fields.iter().enumerate() {
let field_ty = &field.ty;
let field_name = field.ident
.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 });
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_punct(Token![;](Span::call_site()));
match variant.fields {
Fields::Named( ..) => quote! { Self::#name { #captures } => { #chain_commands } },
Fields::Unnamed(..) => quote! { Self::#name(#captures) => { #chain_commands } },
Fields::Unit => unreachable!(),
}
};
sizes.push(serialised_size);
match_arms.push(arm);
}
let mut size_tests = Punctuated::<TokenStream, Token![else]>::new();
for size in &sizes {
let mut test = Punctuated::<TokenStream, Token![&&]>::new();
for other_size in &sizes { test.push(quote! { #size >= #other_size }) }
size_tests.push(quote! { if #test { #size } });
}
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);
match (*self) { #match_arms }
Ok(())
}
}
}

View file

@ -0,0 +1,77 @@
// 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 proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
DataStruct,
Fields,
Index,
Token,
punctuated::Punctuated
};
#[must_use]
pub fn serialise_struct(data: &DataStruct) -> TokenStream {
if matches!(data.fields, Fields::Unit) {
quote! {
const SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
Ok(())
}
}
} else {
let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
for (index, field) in data.fields.iter().enumerate() {
let ty = &field.ty;
let name = field.ident
.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 });
chain_commands.push(quote! { stream.append(&self.#name)? });
}
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);
#chain_commands
Ok(())
}
}
}
}

102
bzipper_macros/src/lib.rs Normal file
View file

@ -0,0 +1,102 @@
// 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/>.
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")]
//! Binary (de)serialisation.
//!
//! This crate implements macros for the [`bzipper`](https://crates.io/crates/bzipper/) crate.
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, parse_macro_input};
macro_rules! use_mod {
($vis:vis $name:ident) => {
mod $name;
$vis use $name::*;
};
}
pub(in crate) use use_mod;
use_mod!(closure);
use_mod!(discriminant);
use_mod!(generic_name);
mod impls;
#[proc_macro_derive(Deserialise)]
pub fn derive_deserialise(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let impl_body = match input.data {
Data::Enum( ref data) => impls::deserialise_enum( data),
Data::Struct(ref data) => impls::deserialise_struct(data),
Data::Union(..) => panic!("unions cannot derive `Deserialise`"),
};
let type_name = &input.ident;
let generic_params = &input.generics.params;
let generic_where = &input.generics.where_clause;
let generic_names = GenericName::extract_from(&input.generics);
let output = quote! {
impl<#generic_params> ::bzipper::Deserialise for #type_name<#generic_names>
#generic_where {
#impl_body
}
};
output.into()
}
#[proc_macro_derive(Serialise)]
pub fn derive_serialise(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let impl_body = match input.data {
Data::Enum( ref data) => impls::serialise_enum( data),
Data::Struct(ref data) => impls::serialise_struct(data),
Data::Union(..) => panic!("unions cannot derive `Serialise`"),
};
let type_name = &input.ident;
let generic_params = &input.generics.params;
let generic_where = &input.generics.where_clause;
let generic_names = GenericName::extract_from(&input.generics);
let output = quote! {
impl<#generic_params> ::bzipper::Serialise for #type_name<#generic_names>
#generic_where {
#impl_body
}
};
//if let Data::Enum(..) = input.data { panic!("{output}") };
output.into()
}

View file

@ -2,8 +2,13 @@
<mask id="z">
<rect fill="white" height="100%" rx="8" width="100%" x="0" y="0" />
<polyline fill="none" points="20,28 20,20 76,20 36.970562748,76" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" />
<polyline fill="none" points="76,68 76,76 20,76 59.029437252,20" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" />
<rect fill="black" height="24" width="32" x="16" y="24" />
<circle cx="48" cy="48" fill="white" r="16" />
<polygon fill="black" points="20,16 76,16 80,20 80,80 16,80 64,32 16,32 16,20" />
<circle cx="20" cy="20" fill="black" r="4" />
<circle cx="76" cy="20" fill="black" r="4" />
<circle cx="80" cy="80" fill="white" r="16" />
</mask>
<rect fill="#FFFFFF" height="100%" mask="url(#z)" width="100%" x="0" y="0" />

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 586 B

Before After
Before After

View file

@ -1,172 +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 impl<T: Serialise>ied warranty of MERCHANTABILITY<T> 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 crate::{Deserialise, Dstream, Serialise, Sstream};
use alloc::vec;
use alloc::boxed::Box;
use core::fmt::{Debug, Formatter};
use core::marker::PhantomData;
/// Container type for (de)serialisations.
///
/// The purpose of this type is to easily hold a buffer than can fit any serialisation of a given type (hence the generic).
///
/// Do note that the internal buffer does not guarantee the state of any padding bytes that occur as a result of different serialisation sizes.
/// Deserialisations, however, are not affected by these.
#[derive(Clone, Eq, PartialEq)]
pub struct Buffer<T> {
data: Box<[u8]>,
len: usize,
_phantom: PhantomData<T>,
}
impl<T> Buffer<T> {
/// Sets the internal length of the buffer without checks.
///
/// For a safe alternative, see [`set_len`](Self::set_len).
///
/// # Safety
///
/// The new length must **not** exceed [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
#[inline(always)]
pub unsafe fn set_len_unchecked(&mut self, len: usize) {
self.len = len;
}
/// Returns a slice of the internal buffer.
///
/// This only includes bytes written by the last serialisation, or as set by [`set_len`](Self::set_len).
#[inline(always)]
#[must_use]
pub fn as_slice(&self) -> &[u8] { &self.data[0x0..self.len] }
/// Returns a mutable slice of the entire internal buffer.
///
/// This is in contrast to [`as_slice`](Self::as_slice), which only yields the last serialisation.
///
/// The length of bytes written to this slice should be set using [`set_len`](Self::set_len).
#[inline(always)]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u8] { self.data.as_mut() }
}
impl<T: Serialise> Buffer<T> {
/// Constructs a new, empty buffer.
///
/// The internal buffer is allocated on the heap instantly with the size [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
#[inline(always)]
#[must_use]
pub fn new() -> Self { Self {
data: vec![Default::default(); T::SERIALISE_LIMIT].into(),
len: 0x0,
_phantom: PhantomData
} }
/// Sets the length of the current serialisation.
///
/// This is specifically meant for cases where the buffer is set externally, as is the case for networking:
///
/// ```
/// use bzipper::{Buffer, FixedString};
/// use std::net::{SocketAddr, UdpSocket};
/// use std::str::FromStr;
///
/// let destination = SocketAddr::from_str("127.0.0.1:37279")?;
///
/// let sender = UdpSocket::bind("0.0.0.0:0")?;
/// let reciever = UdpSocket::bind(destination)?;
///
/// // Create a buffer for holding a fixed string.
/// let mut buffer = Buffer::<FixedString<0x10>>::new();
///
/// // Serialise and write the string:
/// buffer.write(&FixedString::new("Hello there!")?);
/// sender.send_to(buffer.as_ref(), destination);
///
/// // Recieve and deserialise the string:
/// let (count, _source) = reciever.recv_from(buffer.as_mut_slice())?;
/// buffer.set_len(count);
///
/// assert_eq!(buffer.read()?, "Hello there!");
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// # Panics
///
/// Panics if `len` is larger than [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
/// See [`set_len_unchecked`](Self::set_len_unchecked).
#[inline]
pub fn set_len(&mut self, len: usize) {
assert!(len <= T::SERIALISE_LIMIT);
self.len = len;
}
}
impl<T: Serialise> Buffer<T> {
/// Serialises into the buffer.
///
/// The result of [`serialise`](Serialise::serialise) is used as the length.
///
/// # Panics
///
/// Panics if the amount of written bytes exceeds [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
/// This *should*, in theory, not occur, as the internal buffer can only fit up to this limit, making all writes past this limit fail.
#[allow(clippy::panic_in_result_fn)]
pub fn write(&mut self, value: &T) -> Result<(), <T as Serialise>::Error> {
let mut stream = Sstream::new(&mut self.data);
let count = value.serialise(&mut stream)?;
assert!(count <= T::SERIALISE_LIMIT);
self.len = count;
Ok(())
}
}
impl<T: Deserialise> Buffer<T> {
/// Deserialises the contained buffer.
///
/// Only bytes from the last serialisation, or as set by [`set_len`](Self::set_len), are used.
pub fn read(&self) -> Result<T, <T as Deserialise>::Error> {
let mut stream = Dstream::new(self.as_ref());
T::deserialise(&mut stream)
}
}
impl<T> AsRef<[u8]> for Buffer<T> {
#[inline(always)]
fn as_ref(&self) -> &[u8] { self.as_slice() }
}
impl<T> Debug for Buffer<T> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
self.data.fmt(f)
}
}
impl<T: Serialise> Default for Buffer<T> {
#[inline(always)]
fn default() -> Self { Self::new() }
}

View file

@ -1,512 +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/>.
#[cfg(test)]
mod test;
use crate::{Error, Dstream};
use alloc::boxed::Box;
use core::convert::Infallible;
use core::error::Error as StdError;
use core::mem::{MaybeUninit, size_of};
use core::num::NonZero;
use core::ptr::read;
/// Types capable of being deserialised.
pub trait Deserialise: Sized {
/// The error of deserialisation.
///
/// Use [`Infallible`] if **all** deserialisations are infallible, as is the case of zero-length types.
type Error;
/// Deserialises the byte stream to an object.
///
/// This function should **not** take more bytes than specified by [`T::SERIALISE_LIMIT`](crate::Serialise::SERIALISE_LIMIT).
/// Doing so is considered a logic error.
///
/// # Errors
///
/// If deserialisation failed, e.g. by an invalid value being found, an error is returned.
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error>;
}
macro_rules! impl_float {
($type:ty) => {
impl Deserialise for $type {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let data = stream
.take(size_of::<Self>())?
.try_into()
.unwrap();
Ok(Self::from_be_bytes(data))
}
}
};
}
macro_rules! impl_int {
($type:ty) => {
impl Deserialise for $type {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let data = stream
.take(size_of::<Self>())?
.try_into()
.unwrap();
Ok(Self::from_be_bytes(data))
}
}
};
}
macro_rules! impl_non_zero {
($type:ty) => {
impl Deserialise for NonZero<$type> {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let value = <$type>::deserialise(stream)?;
NonZero::new(value)
.ok_or(Error::NullInteger)
}
}
};
}
impl<T0, T1> Deserialise for (T0, T1)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2> Deserialise for (T0, T1, T2)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3> Deserialise for (T0, T1, T2, T3)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3, T4> Deserialise for (T0, T1, T2, T3, T4)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5> Deserialise for (T0, T1, T2, T3, T4, T5)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6> Deserialise for (T0, T1, T2, T3, T4, T5, T6)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>,
T7: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
Deserialise::deserialise(stream)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>,
T7: Deserialise<Error: StdError + 'static>,
T8: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>,
T7: Deserialise<Error: StdError + 'static>,
T8: Deserialise<Error: StdError + 'static>,
T9: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>,
T7: Deserialise<Error: StdError + 'static>,
T8: Deserialise<Error: StdError + 'static>,
T9: Deserialise<Error: StdError + 'static>,
T10: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
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)?,
))
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Deserialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
where
T0: Deserialise<Error: StdError + 'static>,
T1: Deserialise<Error: StdError + 'static>,
T2: Deserialise<Error: StdError + 'static>,
T3: Deserialise<Error: StdError + 'static>,
T4: Deserialise<Error: StdError + 'static>,
T5: Deserialise<Error: StdError + 'static>,
T6: Deserialise<Error: StdError + 'static>,
T7: Deserialise<Error: StdError + 'static>,
T8: Deserialise<Error: StdError + 'static>,
T9: Deserialise<Error: StdError + 'static>,
T10: Deserialise<Error: StdError + 'static>,
T11: Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
Ok((
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)?,
))
}
}
impl<T, const N: usize> Deserialise for [T; N]
where
T: Default + Deserialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let len = usize::deserialise(stream)?;
if len != N { return Err(Box::new(Error::ArrayTooShort { req: len, len: N })) };
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
// Deserialise t
for item in buf.iter_mut().take(len) {
item.write(Deserialise::deserialise(stream)?);
}
for item in buf.iter_mut().skip(len) {
item.write(Default::default());
}
// This should be safe as `MaybeUninit<T>` is
// transparent to `T`. The original buffer is
// NOT dropped automatically, so we can just
// forget about it from this point on.
let buf = unsafe { read(core::ptr::from_ref(&buf).cast::<[T; N]>()) };
Ok(buf)
}
}
impl Deserialise for () {
type Error = Infallible;
fn deserialise(_stream: &mut Dstream) -> Result<Self, Self::Error> { Ok(()) }
}
impl Deserialise for bool {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let value = u8::deserialise(stream)?;
match value {
0x00 => Ok(false),
0x01 => Ok(true),
_ => Err(Error::InvalidBoolean { value })
}
}
}
impl Deserialise for char {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let value = u32::deserialise(stream)?;
Self::from_u32(value)
.ok_or(Error::InvalidCodePoint { value })
}
}
impl Deserialise for Infallible {
type Error = Self;
fn deserialise(_stream: &mut Dstream) -> Result<Self, Self::Error> { unreachable!() }
}
impl Deserialise for isize {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let value = i16::deserialise(stream)?
.into();
Ok(value)
}
}
impl<T: Deserialise<Error: StdError + 'static>> Deserialise for Option<T> {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let sign = bool::deserialise(stream)?;
if sign {
Ok(Some(T::deserialise(stream)?))
} else {
Ok(None)
}
}
}
impl<T: Deserialise, E: Deserialise> Deserialise for Result<T, E>
where
<T as Deserialise>::Error: StdError + 'static,
<E as Deserialise>::Error: StdError + 'static, {
type Error = Box<dyn StdError>;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let sign = bool::deserialise(stream)?;
let value = if sign {
Err(E::deserialise(stream)?)
} else {
Ok(T::deserialise(stream)?)
};
Ok(value)
}
}
impl Deserialise for usize {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let value = u16::deserialise(stream)?
.into();
Ok(value)
}
}
impl_float!(f32);
impl_float!(f64);
impl_int!(i128);
impl_int!(i16);
impl_int!(i32);
impl_int!(i64);
impl_int!(i8);
impl_int!(u128);
impl_int!(u16);
impl_int!(u32);
impl_int!(u64);
impl_int!(u8);
impl_non_zero!(i128);
impl_non_zero!(i16);
impl_non_zero!(i32);
impl_non_zero!(i64);
impl_non_zero!(i8);
impl_non_zero!(isize);
impl_non_zero!(u128);
impl_non_zero!(u16);
impl_non_zero!(u32);
impl_non_zero!(u64);
impl_non_zero!(u8);
impl_non_zero!(usize);

View file

@ -1,81 +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 crate::{Deserialise, Dstream, FixedString};
#[test]
fn test_deserialise() {
let data = [
0x00, 0xFF, 0xFF, 0x0F, 0xEF, 0x1F, 0xDF, 0x2F,
0xCF, 0x3F, 0xBF, 0x4F, 0xAF, 0x5F, 0x9F, 0x6F,
0x8F, 0x7F, 0x00, 0x09, 0x6D, 0xC3, 0xA1, 0x6E,
0x61, 0xC3, 0xB0, 0x75, 0x72, 0x00, 0x05, 0x00,
0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, 0x91, 0x00,
0x00, 0x03, 0xBC, 0x00, 0x00, 0x03, 0x94, 0x00,
0x00, 0x03, 0xB1, 0x01, 0x00, 0x00, 0x01, 0x80,
];
let mut stream = Dstream::new(&data);
assert_eq!(
u8::deserialise(&mut stream).unwrap(),
0x00,
);
assert_eq!(
u8::deserialise(&mut stream).unwrap(),
0xFF,
);
assert_eq!(
u128::deserialise(&mut stream).unwrap(),
0xFF_0F_EF_1F_DF_2F_CF_3F_BF_4F_AF_5F_9F_6F_8F_7F,
);
assert_eq!(
FixedString::<0x10>::deserialise(&mut stream).unwrap(),
"m\u{00E1}na\u{00F0}ur",
);
assert_eq!(
<[char; 0x5]>::deserialise(&mut stream).unwrap(),
['\u{03BB}', '\u{0391}', '\u{03BC}', '\u{0394}', '\u{03B1}'],
);
assert_eq!(
Option::<()>::deserialise(&mut stream).unwrap(),
Some(()),
);
assert_eq!(
Option::<()>::deserialise(&mut stream).unwrap(),
None,
);
assert_eq!(
Result::<(), i8>::deserialise(&mut stream).unwrap(),
Ok(()),
);
assert_eq!(
Result::<(), i8>::deserialise(&mut stream).unwrap(),
Err(i8::MIN),
);
}

View file

@ -1,86 +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 crate::{Error, Result};
use core::fmt::{Debug, Formatter};
/// Byte stream for deserialisation.
///
/// This type borrows a byte slice (hence [`new`](Dstream::new)), keeping track internally of the used bytes.
#[derive(Clone)]
pub struct Dstream<'a> {
data: &'a [u8],
len: usize,
}
impl<'a> Dstream<'a> {
/// Constructs a new byte stream.
#[inline(always)]
#[must_use]
pub fn new<T: AsRef<[u8]> + ?Sized>(buf: &'a T) -> Self { Self {
data: buf.as_ref(),
len: buf.as_ref().len(),
} }
/// Takes bytes from the stream.
///
/// # Errors
///
/// If the internal buffer doesn't hold at least the requested amount of bytes, an [`EndOfStream`](Error::EndOfStream) error is returned.
pub fn take(&mut self, req: usize) -> Result<&[u8]> {
let rem = self.len;
if rem < req { return Err(Error::EndOfStream { req, rem } ) }
let start = self.data.len() - rem;
let stop = start + req;
self.len -= req;
Ok(&self.data[start..stop])
}
/// Takes a single byte from the stream.
///
/// # Errors
///
/// If the internal buffer doesn't hold at least the requested amount of bytes, an [`EndOfStream`](Error::EndOfStream) error is returned.
pub fn take_byte(&mut self) -> Result<u8> {
const LEN: usize = 0x1;
if self.len < LEN { return Err(Error::EndOfStream { req: LEN, rem: self.len } ) }
self.len -= LEN;
let index = self.data.len() - self.len;
Ok(self.data[index])
}
}
impl Debug for Dstream<'_> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
self.data.fmt(f)
}
}
impl<'a, T: AsRef<[u8]>> From<&'a T> for Dstream<'a> {
fn from(value: &'a T) -> Self { Self::new(value) }
}

View file

@ -1,298 +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/>.
#[cfg(test)]
mod test;
use crate::{
Deserialise,
Dstream,
Error,
FixedStringIter,
Serialise,
Sstream,
};
use alloc::string::String;
use core::cmp::Ordering;
use core::fmt::{Debug, Display, Formatter};
use core::ops::{Index, IndexMut};
use core::str::FromStr;
/// Owned string with maximum size.
///
/// This is in contrast to [String], which has no size limit is practice, and [str], which is unsized.
#[derive(Clone)]
pub struct FixedString<const N: usize> {
buf: [char; N],
len: usize,
}
impl<const N: usize> FixedString<N> {
/// Constructs a new fixed string.
///
/// The contents of the provided string are copied into the internal buffer.
/// All residual characters are instanced as U+0000 `NULL`.
///
/// # Errors
///
/// If the given string `s` cannot fit into `N` characters, an [`ArrayTooShort`](Error::ArrayTooShort) error is returned.
pub fn new(s: &str) -> Result<Self, Error> {
let mut buf = ['\0'; N];
let len = s.chars().count();
for (i, c) in s.chars().enumerate() {
if i >= N { return Err(Error::ArrayTooShort { req: len, len: N }) }
buf[i] = c;
}
Ok(Self { buf, len })
}
/// Returns the length of the string.
///
/// This does not necessarily equal the value of `N`, as the internal buffer is not required to be used fully.
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { self.len }
/// Checks if the string is empty, i.e. `self.len() == 0x0`.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len == 0x0 }
/// Borrows the character at the specified index.
///
/// If no element exists at that position, [`None`] is returned instead.
#[inline]
#[must_use]
pub const fn get(&self, index: usize) -> Option<&char> {
if index >= self.len {
None
} else {
Some(&self.buf[index])
}
}
/// Mutably borrows the character at the specified index.
///
/// If no element exists at that position, [`None`] is returned instead.
#[inline]
#[must_use]
pub fn get_mut(&mut self, index: usize) -> Option<&mut char> {
if index >= self.len {
None
} else {
Some(&mut self.buf[index])
}
}
/// Returns an iterator to the contained characters.
#[inline(always)]
pub fn iter(&self) -> core::slice::Iter<'_, char> { self.buf[0x0..self.len].iter() }
/// Returns a mutable iterator to the contained characters.
#[inline(always)]
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, char> { self.buf[0x0..self.len].iter_mut() }
}
impl<const N: usize> Debug for FixedString<N> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "\"")?;
for c in self { write!(f, "{}", c.escape_debug())? }
write!(f, "\"")?;
Ok(())
}
}
impl<const N: usize> Deserialise for FixedString<N> {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
let len = usize::deserialise(stream)?;
let data = stream.take(len)?;
let s = core::str::from_utf8(data)
.map_err(|e| Error::BadString { source: e })?;
let len = s.chars().count();
if len > N {
return Err(Error::ArrayTooShort { req: len, len: N });
}
let mut buf = ['\0'; N];
for (i, c) in s.chars().enumerate() {
buf[i] = c;
}
Ok(Self { buf, len })
}
}
impl<const N: usize> Default for FixedString<N> {
#[inline(always)]
fn default() -> Self { Self {
buf: ['\0'; N],
len: 0x0,
} }
}
impl<const N: usize> Display for FixedString<N> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
for c in self { write!(f, "{c}")? }
Ok(())
}
}
impl<const N: usize> Eq for FixedString<N> { }
impl<const N: usize> From<[char; N]> for FixedString<N> {
fn from(value: [char; N]) -> Self { Self {
buf: value,
len: N,
} }
}
impl<const N: usize> FromStr for FixedString<N> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> { Self::new(s) }
}
impl<const N: usize> Index<usize> for FixedString<N> {
type Output = char;
fn index(&self, index: usize) -> &Self::Output { self.get(index).unwrap() }
}
impl<const N: usize> IndexMut<usize> for FixedString<N> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.get_mut(index).unwrap() }
}
impl<const N: usize> IntoIterator for FixedString<N> {
type Item = char;
type IntoIter = FixedStringIter<N>;
fn into_iter(self) -> Self::IntoIter {
FixedStringIter {
buf: self.buf,
len: self.len,
pos: Some(0x0),
}
}
}
impl<'a, const N: usize> IntoIterator for &'a FixedString<N> {
type Item = &'a char;
type IntoIter = core::slice::Iter<'a, char>;
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>;
fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
}
impl<const N: usize> Ord for FixedString<N> {
fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() }
}
impl<const N: usize, const M: usize> PartialEq<FixedString<M>> for FixedString<N> {
fn eq(&self, other: &FixedString<M>) -> bool {
if self.len() != other.len() { return false };
for i in 0x0..self.len() {
if self.buf[i] != other.buf[i] { return false };
}
true
}
}
impl<const N: usize> PartialEq<&str> for FixedString<N> {
fn eq(&self, other: &&str) -> bool {
for (i, c) in other.chars().enumerate() {
if self.buf.get(i) != Some(&c) { return false };
}
true
}
}
impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<N> {
fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> {
let len = self.len().max(other.len());
for i in 0x0..len {
let lc = self.get(i);
let rc = other.get(i);
match (lc, rc) {
(None, None) => return Some(Ordering::Equal),
(Some(_), None) => return Some(Ordering::Greater),
(None, Some(_)) => return Some(Ordering::Less),
(Some(lc), Some(rc)) => {
let ordering = lc.cmp(rc);
if ordering != Ordering::Equal { return Some(ordering) };
}
}
}
Some(Ordering::Equal)
}
}
impl<const N: usize> Serialise for FixedString<N> {
type Error = Error;
const SERIALISE_LIMIT: usize = N * 0x4;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
let s: String = self.iter().collect();
count += s.len().serialise(stream)?;
count += stream.add(&s.into_bytes())?;
Ok(count)
}
}
impl<const N: usize> TryFrom<&str> for FixedString<N> {
type Error = Error;
#[inline(always)]
fn try_from(value: &str) -> Result<Self, Self::Error> { Self::new(value) }
}

View file

@ -1,123 +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/>.
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")]
//! 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.
//! 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.
//!
//! This crate does not require any dependencies at the moment.
//! It is also compatible with `no_std`.
//!
//! # Data model
//!
//! Most primitive types serialise losslessly, with the exception being [`usize`] and [`isize`].
//! These serialise as [`u16`] and [`i16`], respectively, for portability reasons.
//!
//! Unsized types, such as [`str`] and [slices](slice), are not supported.
//! Instead, [arrays](array) should be used.
//! For strings, the [`FixedString`] type is also provided.
//!
//! # Usage
//!
//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which work around streams (more specifically, [d-streams](Dstream) and [s-streams](Sstream)).
//!
//! Many core types come implemented with `bzipper`, including primitives as well as some standard library types such as [`Option`] and [`Result`](core::result::Result).
//!
//! ## Serialisation
//!
//! To serialise an object implementing `Serialise`, simply allocate a so-called "s-stream" (short for *serialisation stream*) with the [`Sstream`] type:
//!
//! ```
//! let mut buf: [u8; 16] = Default::default();
//!
//! let mut stream = bzipper::Sstream::new(&mut buf);
//! ```
//!
//! The resulting stream is immutable in the sense that it cannot grow its buffer, altough it does keep track of the buffer's state.
//!
//! A byte sequence can be added to our new stream by passing the stream to a call to the [`serialise`](Serialise::serialise) method:
//!
//! ```
//! use bzipper::Serialise;
//!
//! let mut buf: [u8; 2] = Default::default();
//! let mut stream = bzipper::Sstream::new(&mut buf);
//!
//! 0x4554_u16.serialise(&mut stream).unwrap();
//! ```
//!
//! The ammount of bytes used by the serialiser (that is, the ammount of bytes written to the stream) is indicated by its return value (i.e. it has the type `Result<usize, Serialise::Error>`).
//!
//! Whilst the *maximum* ammount of bytes is specified by the [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT) constant, this can in cases be lower (for example with [`None`] variants which are always encoded as a single, null byte).
//!
//! When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
//! It is recommended for implementors to adhere to this convention as well.
//!
//! After serialisation, the s-stream records the new write-to position of the buffer. This allows for *chaining* of serialisations, which can prove useful when implementing the trait for custom types.
//!
//! ## Deserialisation
//!
//! As with serialisation, deserialisation uses streams (just with the [`Dstream`] type; short for *deserialisation stream*):
//!
//! ```
//! let data = [0x45, 0x54];
//!
//! let mut stream = bzipper::Dstream::new(&data);
//! ```
//!
//! Using these streams is also just as simple as with s-streams:
//!
//! ```
//! use bzipper::Deserialise;
//!
//! let data = [0x45, 0x54];
//! let mut stream = bzipper::Dstream::new(&data);
//!
//! assert_eq!(u16::deserialise(&mut stream).unwrap(), 0x4554);
//! ```
//!
//! When chaining serialisations, keep in mind that appropriate deserialisations should come in **reverse order** (streams function similarly to stacks in this sense).
#![no_std]
extern crate alloc;
macro_rules! use_mod {
($vis:vis $name:ident) => {
mod $name;
$vis use $name::*;
};
}
pub(in crate) use use_mod;
use_mod!(pub buffer);
use_mod!(pub deserialise);
use_mod!(pub dstream);
use_mod!(pub error);
use_mod!(pub fixed_string);
use_mod!(pub fixed_string_iter);
use_mod!(pub serialise);
use_mod!(pub sstream);

View file

@ -1,676 +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/>.
#[cfg(test)]
mod test;
use crate::{Error, Sstream};
use alloc::boxed::Box;
use core::convert::Infallible;
use core::error::Error as StdError;
use core::mem::size_of;
use core::num::NonZero;
/// Types capable of being serialised.
pub trait Serialise: Sized {
/// The error of serialisation.
///
/// Use [`Infallible`] if **all** deserialisations are infallible, as is the case of zero-length types (such as [the unit type](unit)).
type Error;
/// The maximum amount of bytes that can result from serialisation.
///
/// Until derive macros are implemented, this value should be set to the sum of the members' own size limits (if chaining is used, that is):
///
/// ```
/// use bzipper::{Serialise, Sstream};
///
/// struct Foo {
/// bar: u16,
/// baz: f32,
/// }
///
/// impl Serialise for Foo {
/// type Error = bzipper::Error;
///
/// const SERIALISE_LIMIT: usize = u16::SERIALISE_LIMIT + f32::SERIALISE_LIMIT;
///
/// fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
/// let mut count = 0x0;
///
/// // Serialise fields using chaining.
/// count += self.bar.serialise(stream)?;
/// count += self.baz.serialise(stream)?;
///
/// Ok(count)
/// }
/// }
/// ```
///
/// In the future, dervice macros will make manual chaining redundant.
const SERIALISE_LIMIT: usize;
/// Serialises `self` into a byte stream.
///
/// The number of bytes written is returned.
/// This should **not** exceed [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT), and doing so is considered a logic error.
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error>;
}
macro_rules! impl_float {
($type:ty) => {
impl Serialise for $type {
type Error = Error;
const SERIALISE_LIMIT: usize = size_of::<$type>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let data = self.to_be_bytes();
stream.add(&data)?;
Ok(data.len())
}
}
};
}
macro_rules! impl_int {
($type:ty) => {
impl Serialise for $type {
type Error = Error;
const SERIALISE_LIMIT: usize = size_of::<$type>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let data = self.to_be_bytes();
stream.add(&data)?;
Ok(data.len())
}
}
};
}
macro_rules! impl_non_zero {
($type:ty) => {
impl Serialise for NonZero<$type> {
type Error = <$type as Serialise>::Error;
const SERIALISE_LIMIT: usize = size_of::<$type>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
self.get().serialise(stream)
}
}
};
}
impl<T0, T1> Serialise for (T0, T1)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2> Serialise for (T0, T1, T2)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3> Serialise for (T0, T1, T2, T3)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4> Serialise for (T0, T1, T2, T3, T4)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5> Serialise for (T0, T1, T2, T3, T4, T5)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6> Serialise for (T0, T1, T2, T3, T4, T5, T6)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>,
T7: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
count += self.7.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>,
T7: Serialise<Error: StdError + 'static>,
T8: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT
+ T8::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
count += self.7.serialise(stream)?;
count += self.8.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>,
T7: Serialise<Error: StdError + 'static>,
T8: Serialise<Error: StdError + 'static>,
T9: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT
+ T8::SERIALISE_LIMIT
+ T9::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
count += self.7.serialise(stream)?;
count += self.8.serialise(stream)?;
count += self.9.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>,
T7: Serialise<Error: StdError + 'static>,
T8: Serialise<Error: StdError + 'static>,
T9: Serialise<Error: StdError + 'static>,
T10: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT
+ T8::SERIALISE_LIMIT
+ T9::SERIALISE_LIMIT
+ T10::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
count += self.7.serialise(stream)?;
count += self.8.serialise(stream)?;
count += self.9.serialise(stream)?;
count += self.10.serialise(stream)?;
Ok(count)
}
}
impl<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Serialise for (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)
where
T0: Serialise<Error: StdError + 'static>,
T1: Serialise<Error: StdError + 'static>,
T2: Serialise<Error: StdError + 'static>,
T3: Serialise<Error: StdError + 'static>,
T4: Serialise<Error: StdError + 'static>,
T5: Serialise<Error: StdError + 'static>,
T6: Serialise<Error: StdError + 'static>,
T7: Serialise<Error: StdError + 'static>,
T8: Serialise<Error: StdError + 'static>,
T9: Serialise<Error: StdError + 'static>,
T10: Serialise<Error: StdError + 'static>,
T11: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize =
T0::SERIALISE_LIMIT
+ T1::SERIALISE_LIMIT
+ T2::SERIALISE_LIMIT
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT
+ T8::SERIALISE_LIMIT
+ T9::SERIALISE_LIMIT
+ T10::SERIALISE_LIMIT
+ T11::SERIALISE_LIMIT;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
count += self.0.serialise(stream)?;
count += self.1.serialise(stream)?;
count += self.2.serialise(stream)?;
count += self.3.serialise(stream)?;
count += self.4.serialise(stream)?;
count += self.5.serialise(stream)?;
count += self.6.serialise(stream)?;
count += self.7.serialise(stream)?;
count += self.8.serialise(stream)?;
count += self.9.serialise(stream)?;
count += self.10.serialise(stream)?;
count += self.11.serialise(stream)?;
Ok(count)
}
}
impl<T: Serialise<Error: StdError + 'static>, const N: usize> Serialise for [T; N] {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize = T::SERIALISE_LIMIT * N;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
self.len().serialise(stream)?;
for v in self { count += v.serialise(stream)? }
Ok(count)
}
}
impl Serialise for () {
type Error = Infallible;
const SERIALISE_LIMIT: usize = size_of::<Self>();
#[inline(always)]
fn serialise(&self, mut _stream: &mut Sstream) -> Result<usize, Self::Error> {
Ok(Self::SERIALISE_LIMIT)
}
}
impl Serialise for bool {
type Error = Error;
const SERIALISE_LIMIT: usize = size_of::<Self>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
u8::from(*self).serialise(stream)
}
}
impl Serialise for char {
type Error = Error;
const SERIALISE_LIMIT: usize = size_of::<Self>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
u32::from(*self).serialise(stream)
}
}
// Especially useful for `Result<T, Infallible>`.
impl Serialise for Infallible {
type Error = Self;
const SERIALISE_LIMIT: usize = size_of::<Self>();
fn serialise(&self, mut _stream: &mut Sstream) -> Result<usize, Self::Error> { unreachable!() }
}
impl Serialise for isize {
type Error = Error;
const SERIALISE_LIMIT: usize = size_of::<i16>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let value = i16::try_from(*self)
.map_err(|_| Error::IsizeOutOfRange { value: *self })?;
value.serialise(stream)
}
}
impl<T: Serialise<Error: StdError + 'static>> Serialise for Option<T> {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize = T::SERIALISE_LIMIT + 0x1;
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let mut count = 0x0;
match *self {
None => {
count += false.serialise(stream)?;
// No need to zero-fill.
},
Some(ref v) => {
count += true.serialise(stream)?;
count += v.serialise(stream)?;
},
};
Ok(count)
}
}
impl<T, E> Serialise for core::result::Result<T, E>
where
T: Serialise<Error: StdError + 'static>,
E: Serialise<Error: StdError + 'static>, {
type Error = Box<dyn StdError>;
const SERIALISE_LIMIT: usize = const {
if size_of::<T>() > size_of::<T>() {
size_of::<T>()
} else {
size_of::<E>()
}
};
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
// Remember the descriminant.
let mut count = 0x0;
match *self {
Ok(ref v) => {
count += false.serialise(stream)?;
count += v.serialise(stream)?;
},
Err(ref e) => {
count += true.serialise(stream)?;
count += e.serialise(stream)?;
},
};
Ok(count)
}
}
impl Serialise for usize {
type Error = Error;
const SERIALISE_LIMIT: Self = size_of::<u16>();
fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
let value = u16::try_from(*self)
.map_err(|_| Error::UsizeOutOfRange { value: *self })?;
value.serialise(stream)
}
}
impl_float!(f32);
impl_float!(f64);
impl_int!(i128);
impl_int!(i16);
impl_int!(i32);
impl_int!(i64);
impl_int!(i8);
impl_int!(u128);
impl_int!(u16);
impl_int!(u32);
impl_int!(u64);
impl_int!(u8);
impl_non_zero!(i128);
impl_non_zero!(i16);
impl_non_zero!(i32);
impl_non_zero!(i64);
impl_non_zero!(i8);
impl_non_zero!(isize);
impl_non_zero!(u128);
impl_non_zero!(u16);
impl_non_zero!(u32);
impl_non_zero!(u64);
impl_non_zero!(u8);
impl_non_zero!(usize);

View file

@ -1,72 +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 crate::{FixedString, Serialise, Sstream};
use alloc::boxed::Box;
use alloc::vec;
#[test]
fn test_serialise() {
let mut buf = vec![0x00; 0x50];
let mut stream = Sstream::new(&mut buf);
0x00_u8.serialise(&mut stream).unwrap();
0xFF_u8.serialise(&mut stream).unwrap();
0x7F_u8.serialise(&mut stream).unwrap();
0x0F_7E_u16.serialise(&mut stream).unwrap();
0x00_2F_87_E7_u32.serialise(&mut stream).unwrap();
0xF3_37_CF_8B_DB_03_2B_39_u64.serialise(&mut stream).unwrap();
0x45_A0_15_6A_36_77_17_8A_83_2E_3C_2C_84_10_58_1A_u128.serialise(&mut stream).unwrap();
FixedString::<0x1>::new("A").unwrap().serialise(&mut stream).unwrap();
FixedString::<0x8>::new("l\u{00F8}gma\u{00F0}ur").unwrap().serialise(&mut stream).unwrap();
['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'].serialise(&mut stream).unwrap();
Ok::<u16, char>(0x45_45).serialise(&mut stream).unwrap();
Err::<u16, char>(char::REPLACEMENT_CHARACTER).serialise(&mut stream).unwrap();
None::<()>.serialise(&mut stream).unwrap();
Some::<()>(()).serialise(&mut stream).unwrap();
let data: Box<[u8]> = buf.into();
assert_eq!(
data.as_ref(),
[
0x00, 0xFF, 0x7F, 0x0F, 0x7E, 0x00, 0x2F, 0x87,
0xE7, 0xF3, 0x37, 0xCF, 0x8B, 0xDB, 0x03, 0x2B,
0x39, 0x45, 0xA0, 0x15, 0x6A, 0x36, 0x77, 0x17,
0x8A, 0x83, 0x2E, 0x3C, 0x2C, 0x84, 0x10, 0x58,
0x1A, 0x00, 0x01, 0x41, 0x00, 0x0A, 0x6C, 0xC3,
0xB8, 0x67, 0x6D, 0x61, 0xC3, 0xB0, 0x75, 0x72,
0x00, 0x05, 0x00, 0x00, 0x03, 0xB4, 0x00, 0x00,
0x01, 0x90, 0x00, 0x00, 0x03, 0xBB, 0x00, 0x00,
0x03, 0xA4, 0x00, 0x00, 0x03, 0xB1, 0x00, 0x45,
0x45, 0x01, 0x00, 0x00, 0xFF, 0xFD, 0x00, 0x01,
],
);
}

View file

@ -1,103 +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 crate::{Error, Result};
use core::fmt::{Debug, Formatter};
/// Byte stream for serialisation.
///
/// This type borrows a byte slice (hence [`new`](Sstream::new)), keeping track internally of the used bytes.
#[derive(Eq, PartialEq)]
pub struct Sstream<'a> {
data: &'a mut [u8],
len: usize
}
impl<'a> Sstream<'a> {
/// Constructs a new byte stream.
///
/// If the borrowed slice already contains data, this may overwritten by subsequent serialisations.
#[inline(always)]
#[must_use]
pub fn new(data: &'a mut [u8]) -> Self { Self { data, len: 0x0 } }
/// Extends the byte stream.
///
/// # Errors
///
/// If the stream cannot hold the requested bytes, an [`EndOfStream`](Error::EndOfStream) instance is returned.
pub fn add(&mut self, extra: &[u8]) -> Result<usize> {
let rem = self.data.len() - self.len;
let req = extra.len();
if rem.checked_sub(req).is_none() {
return Err(Error::EndOfStream { req, rem });
}
let start = self.len;
let stop = self.len + req;
self.len += req;
self.data[start..stop].copy_from_slice(extra);
Ok(req)
}
/// Extends the byte stream by a single byte.
///
/// # Errors
///
/// If the stream cannot hold the byte, an [`EndOfStream`](Error::EndOfStream) instance is returned.
pub fn add_byte(&mut self, extra: u8) -> Result<usize> {
self.add(&[extra])
}
/// Yields the length of the stream.
///
/// That is, the amount of bytes written so far.
#[inline(always)]
#[must_use]
pub const fn len(&self) -> usize { self.len }
/// Tests if the stream is empty.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len == 0x0 }
/// Returns a slice to the stream contents.
///
/// This includes all previously written bytes.
#[inline(always)]
#[must_use]
pub fn as_slice(&self) -> &[u8] { &self.data[0x0..self.len] }
}
impl AsRef<[u8]> for Sstream<'_> {
#[inline(always)]
fn as_ref(&self) -> &[u8] { self.as_slice() }
}
impl Debug for Sstream<'_> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
self.data.fmt(f)
}
}