summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md23
-rw-r--r--Cargo.toml25
-rw-r--r--README.md95
-rw-r--r--bzipper.svg14
-rw-r--r--bzipper/Cargo.toml26
-rw-r--r--bzipper/src/deserialise/mod.rs245
-rw-r--r--bzipper/src/deserialise/test.rs118
-rw-r--r--bzipper/src/deserialise/tuple.rs298
-rw-r--r--bzipper/src/dstream/mod.rs58
-rw-r--r--bzipper/src/error/mod.rs (renamed from src/error/mod.rs)78
-rw-r--r--bzipper/src/fixed_iter/mod.rs46
-rw-r--r--bzipper/src/fixed_string/mod.rs448
-rw-r--r--bzipper/src/fixed_string/test.rs (renamed from src/fixed_string/test.rs)6
-rw-r--r--bzipper/src/lib.rs202
-rw-r--r--bzipper/src/serialise/mod.rs300
-rw-r--r--bzipper/src/serialise/test.rs104
-rw-r--r--bzipper/src/serialise/tuple.rs424
-rw-r--r--bzipper/src/sstream/mod.rs58
-rw-r--r--bzipper_macros/Cargo.toml23
-rw-r--r--bzipper_macros/src/closure/mod.rs41
-rw-r--r--bzipper_macros/src/discriminant/mod.rs98
-rw-r--r--bzipper_macros/src/generic_name/mod.rs74
-rw-r--r--bzipper_macros/src/impls/deserialise_enum.rs78
-rw-r--r--bzipper_macros/src/impls/deserialise_struct.rs78
-rw-r--r--bzipper_macros/src/impls/mod.rs (renamed from src/fixed_string_iter/mod.rs)27
-rw-r--r--bzipper_macros/src/impls/serialise_enum.rs108
-rw-r--r--bzipper_macros/src/impls/serialise_struct.rs77
-rw-r--r--bzipper_macros/src/lib.rs102
-rw-r--r--doc-icon.svg9
-rw-r--r--src/buffer/mod.rs172
-rw-r--r--src/deserialise/mod.rs512
-rw-r--r--src/deserialise/test.rs81
-rw-r--r--src/dstream/mod.rs86
-rw-r--r--src/fixed_string/mod.rs298
-rw-r--r--src/lib.rs123
-rw-r--r--src/serialise/mod.rs676
-rw-r--r--src/serialise/test.rs72
-rw-r--r--src/sstream/mod.rs103
38 files changed, 3167 insertions, 2239 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6b156e..74c20ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/Cargo.toml b/Cargo.toml
index 4584048..b02fd1f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/README.md b/README.md
index a59958b..a198dff 100644
--- a/README.md
+++ b/README.md
@@ -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`.
+
+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();
-Many core types come implemented with `bzipper`, including primitives as well as some standard library types such as `Option` and `Result`.
+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:
+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
-let mut buf: [u8; 16] = Default::default();
+use bzipper::Serialise;
-let mut stream = bzipper::Sstream::new(&mut buf);
+let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
+'Ж'.serialise(&mut buf).unwrap();
+
+assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
```
-The resulting stream is immutable in the sense that it cannot grow its buffer, altough it does keep track of the buffer's state.
+The only special requirement of the `serialise` method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
-A byte sequence can be added to our new stream by passing the stream to a call to the `serialise` method:
+We can also use streams to *chain* multiple elements together.
```rs
use bzipper::Serialise;
-let mut buf: [u8; 2] = Default::default();
+let mut buf: [u8; char::SERIALISED_SIZE * 5] = 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>`).
+stream.append(&'ل');
+stream.append(&'ا');
+stream.append(&'م');
+stream.append(&'د');
+stream.append(&'ا');
-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).
+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.
+
+To deserialise a buffer, simply call the `deserialise` method:
```rs
-let data = [0x45, 0x54];
+use bzipper::Deserialise;
-let mut stream = bzipper::Dstream::new(&data);
+let data = [0x45, 0x54];
+assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
```
-Using these streams is also just as simple as with s-streams:
+Just like with serialisations, the `Dstream` can be used to deserialise chained elements:
```rs
use bzipper::Deserialise;
let data = [0x45, 0x54];
-let mut stream = bzipper::Dstream::new(&data);
+let stream = bzipper::Dstream::new(&data);
-assert_eq!(u16::deserialise(&mut stream).unwrap(), 0x4554);
+assert_eq!(stream.take::<u8>().unwrap(), 0x45);
+assert_eq!(stream.take::<u8>().unwrap(), 0x54);
```
-
-When chaining serialisations, keep in mind that appropriate deserialisations should come in **reverse order** (streams function similarly to stacks in this sense).
-
-## Copyright & Licensing
-
-Copyright 2024 Gabriel Bjørnager Jensen.
-
-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/>.
diff --git a/bzipper.svg b/bzipper.svg
index 974c50b..52050cc 100644
--- a/bzipper.svg
+++ b/bzipper.svg
@@ -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>
diff --git a/bzipper/Cargo.toml b/bzipper/Cargo.toml
new file mode 100644
index 0000000..d8575f0
--- /dev/null
+++ b/bzipper/Cargo.toml
@@ -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
diff --git a/bzipper/src/deserialise/mod.rs b/bzipper/src/deserialise/mod.rs
new file mode 100644
index 0000000..49ca74e
--- /dev/null
+++ b/bzipper/src/deserialise/mod.rs
@@ -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);
diff --git a/bzipper/src/deserialise/test.rs b/bzipper/src/deserialise/test.rs
new file mode 100644
index 0000000..f593978
--- /dev/null
+++ b/bzipper/src/deserialise/test.rs
@@ -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 });
+}
diff --git a/bzipper/src/deserialise/tuple.rs b/bzipper/src/deserialise/tuple.rs
new file mode 100644
index 0000000..b1f7ac1
--- /dev/null
+++ b/bzipper/src/deserialise/tuple.rs
@@ -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)?,
+ ))
+ }
+}
diff --git a/bzipper/src/dstream/mod.rs b/bzipper/src/dstream/mod.rs
new file mode 100644
index 0000000..e87edf8
--- /dev/null
+++ b/bzipper/src/dstream/mod.rs
@@ -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])
+ }
+}
diff --git a/src/error/mod.rs b/bzipper/src/error/mod.rs
index 30e4c1e..090215a 100644
--- a/src/error/mod.rs
+++ b/bzipper/src/error/mod.rs
@@ -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,
}
}
diff --git a/bzipper/src/fixed_iter/mod.rs b/bzipper/src/fixed_iter/mod.rs
new file mode 100644
index 0000000..cc2110d
--- /dev/null
+++ b/bzipper/src/fixed_iter/mod.rs
@@ -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)
+ }
+}
diff --git a/bzipper/src/fixed_string/mod.rs b/bzipper/src/fixed_string/mod.rs
new file mode 100644
index 0000000..5b40a87
--- /dev/null
+++ b/bzipper/src/fixed_string/mod.rs
@@ -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() }
+}
diff --git a/src/fixed_string/test.rs b/bzipper/src/fixed_string/test.rs
index 1599efc..e2af6ce 100644
--- a/src/fixed_string/test.rs
+++ b/bzipper/src/fixed_string/test.rs
@@ -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));
diff --git a/bzipper/src/lib.rs b/bzipper/src/lib.rs
new file mode 100644
index 0000000..3689176
--- /dev/null
+++ b/bzipper/src/lib.rs
@@ -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);
diff --git a/bzipper/src/serialise/mod.rs b/bzipper/src/serialise/mod.rs
new file mode 100644
index 0000000..9c89e09
--- /dev/null
+++ b/bzipper/src/serialise/mod.rs
@@ -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);
diff --git a/bzipper/src/serialise/test.rs b/bzipper/src/serialise/test.rs
new file mode 100644
index 0000000..f2332a5
--- /dev/null
+++ b/bzipper/src/serialise/test.rs
@@ -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,
+ ]);
+} \ No newline at end of file
diff --git a/bzipper/src/serialise/tuple.rs b/bzipper/src/serialise/tuple.rs
new file mode 100644
index 0000000..feee2e2
--- /dev/null
+++ b/bzipper/src/serialise/tuple.rs
@@ -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(())
+ }
+}
diff --git a/bzipper/src/sstream/mod.rs b/bzipper/src/sstream/mod.rs
new file mode 100644
index 0000000..257be95
--- /dev/null
+++ b/bzipper/src/sstream/mod.rs
@@ -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])
+ }
+}
diff --git a/bzipper_macros/Cargo.toml b/bzipper_macros/Cargo.toml
new file mode 100644
index 0000000..562251e
--- /dev/null
+++ b/bzipper_macros/Cargo.toml
@@ -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
diff --git a/bzipper_macros/src/closure/mod.rs b/bzipper_macros/src/closure/mod.rs
new file mode 100644
index 0000000..86d19d4
--- /dev/null
+++ b/bzipper_macros/src/closure/mod.rs
@@ -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);
+ }
+}
diff --git a/bzipper_macros/src/discriminant/mod.rs b/bzipper_macros/src/discriminant/mod.rs
new file mode 100644
index 0000000..21a835a
--- /dev/null
+++ b/bzipper_macros/src/discriminant/mod.rs
@@ -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() }
+}
diff --git a/bzipper_macros/src/generic_name/mod.rs b/bzipper_macros/src/generic_name/mod.rs
new file mode 100644
index 0000000..a0c51f5
--- /dev/null
+++ b/bzipper_macros/src/generic_name/mod.rs
@@ -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),
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/deserialise_enum.rs b/bzipper_macros/src/impls/deserialise_enum.rs
new file mode 100644
index 0000000..7bf0220
--- /dev/null
+++ b/bzipper_macros/src/impls/deserialise_enum.rs
@@ -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)
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/deserialise_struct.rs b/bzipper_macros/src/impls/deserialise_struct.rs
new file mode 100644
index 0000000..414a313
--- /dev/null
+++ b/bzipper_macros/src/impls/deserialise_struct.rs
@@ -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)
+ }
+ }
+ }
+}
diff --git a/src/fixed_string_iter/mod.rs b/bzipper_macros/src/impls/mod.rs
index 088f61f..d61cf90 100644
--- a/src/fixed_string_iter/mod.rs
+++ b/bzipper_macros/src/impls/mod.rs
@@ -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);
diff --git a/bzipper_macros/src/impls/serialise_enum.rs b/bzipper_macros/src/impls/serialise_enum.rs
new file mode 100644
index 0000000..8f0693a
--- /dev/null
+++ b/bzipper_macros/src/impls/serialise_enum.rs
@@ -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(())
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/serialise_struct.rs b/bzipper_macros/src/impls/serialise_struct.rs
new file mode 100644
index 0000000..308a6bb
--- /dev/null
+++ b/bzipper_macros/src/impls/serialise_struct.rs
@@ -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(())
+ }
+ }
+ }
+}
diff --git a/bzipper_macros/src/lib.rs b/bzipper_macros/src/lib.rs
new file mode 100644
index 0000000..f7979c8
--- /dev/null
+++ b/bzipper_macros/src/lib.rs
@@ -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()
+}
diff --git a/doc-icon.svg b/doc-icon.svg
index 5a6e1f8..bfea201 100644
--- a/doc-icon.svg
+++ b/doc-icon.svg
@@ -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" />
diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs
deleted file mode 100644
index 3ce47bd..0000000
--- a/src/buffer/mod.rs
+++ /dev/null
@@ -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() }
-}
diff --git a/src/deserialise/mod.rs b/src/deserialise/mod.rs
deleted file mode 100644
index eaebe2b..0000000
--- a/src/deserialise/mod.rs
+++ /dev/null
@@ -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);
diff --git a/src/deserialise/test.rs b/src/deserialise/test.rs
deleted file mode 100644
index e398b4d..0000000
--- a/src/deserialise/test.rs
+++ /dev/null
@@ -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),
- );
-}
diff --git a/src/dstream/mod.rs b/src/dstream/mod.rs
deleted file mode 100644
index ca7f619..0000000
--- a/src/dstream/mod.rs
+++ /dev/null
@@ -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) }
-}
diff --git a/src/fixed_string/mod.rs b/src/fixed_string/mod.rs
deleted file mode 100644
index b0342d3..0000000
--- a/src/fixed_string/mod.rs
+++ /dev/null
@@ -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) }
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 4355f75..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -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);
diff --git a/src/serialise/mod.rs b/src/serialise/mod.rs
deleted file mode 100644
index 16c8313..0000000
--- a/src/serialise/mod.rs
+++ /dev/null
@@ -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);
diff --git a/src/serialise/test.rs b/src/serialise/test.rs
deleted file mode 100644
index 43f2b9f..0000000
--- a/src/serialise/test.rs
+++ /dev/null
@@ -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,
- ],
- );
-} \ No newline at end of file
diff --git a/src/sstream/mod.rs b/src/sstream/mod.rs
deleted file mode 100644
index 83536d0..0000000
--- a/src/sstream/mod.rs
+++ /dev/null
@@ -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)
- }
-}