summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md35
-rw-r--r--Cargo.toml2
-rw-r--r--README.md13
-rw-r--r--bzipper.svg17
-rw-r--r--src/buffer/mod.rs172
-rw-r--r--src/deserialise/mod.rs92
-rw-r--r--src/deserialise/test.rs16
-rw-r--r--src/dstream/mod.rs59
-rw-r--r--src/error/mod.rs32
-rw-r--r--src/fixed_string/mod.rs46
-rw-r--r--src/fixed_string/test.rs2
-rw-r--r--src/lib.rs14
-rw-r--r--src/serialise/mod.rs559
-rw-r--r--src/serialise/test.rs53
-rw-r--r--src/sstream/mod.rs99
15 files changed, 834 insertions, 377 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67c5a1d..bf2ecba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,27 @@
-# 0.3.0
+# Changelog
+
+This is the changelog of `bzipper`.
+
+## 0.4.0
+
+* Add logo
+* Clean up code
+* Fix array deserialisation (require `Default`)
+* Bump minor
+* Update commenting
+* Make serialisations fallible
+* Impl `Serialise` and `Deserialise` for `usize` and `isize` (restrict to 16 bits)
+* Add new errors: `UsizeOutOfRange`, `IsizeOutOfRange`
+* Rework sstreams
+* Add buffer type
+* Fix serialisation of `Option<T>`
+* Disable `std`
+* Rename error: `EndOfDStream` -> `EndOfStream`
+* Update documentation
+* Update readme
+* Reformat changelog
+
+## 0.3.0
* Bump minor
* Document errors
@@ -17,7 +40,7 @@
* Make `Deserialise` require `Serialise`
* Fix copyright/license notice in `"src/serialise/test.rs"`
-# 0.2.0
+## 0.2.0
* Clean up code
* Implement `Ord` and `PartialOrd` for `FixedString`
@@ -27,7 +50,7 @@
* Bump minor
* Implement `Serialise` and `Deserialise` for tuples
-# 0.1.0
+## 0.1.0
* Bump minor
* Export all in crate root
@@ -37,17 +60,17 @@
* Add `as_d_stream` method to `SStream`
* Add `to_s_stream` and `as_slice` methods to `DStream`
-# 0.0.2
+## 0.0.2
* Add license files
-# 0.0.1
+## 0.0.1
* Fix copyright notices
* Add license notices
* Update readme
-# 0.0.0
+## 0.0.0
* Add changelog
* Fork from `backspace`
diff --git a/Cargo.toml b/Cargo.toml
index 22857a6..54c4ccd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bzipper"
-version = "0.3.0"
+version = "0.4.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Binary (de)serialiser."
diff --git a/README.md b/README.md
index d7effbe..0348372 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+# 'bzipper`
+
[`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.
@@ -7,7 +9,16 @@ Keep in mind that this project is still work-in-progress.
This crate does not require any dependencies at the moment.
-# Copyright & Licensing
+## 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, are not supported.
+Instead, array should be used.
+For strings, the `FixedString` type is also provided.
+
+## Copyright & Licensing
Copyright 2024 Gabriel Bjørnager Jensen.
diff --git a/bzipper.svg b/bzipper.svg
new file mode 100644
index 0000000..762e587
--- /dev/null
+++ b/bzipper.svg
@@ -0,0 +1,17 @@
+<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg">
+ <!-- gradients: -->
+
+ <!-- clips: -->
+
+ <!-- masks: -->
+
+ <mask id="z">
+ <polyline fill="none" points="20,28 20,20 78,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" />
+ </mask>
+
+ <!-- fills: -->
+
+ <rect fill="#FFFFFF" mask="" height="96" width="96" x="0" y="0" />
+ <rect fill="#B4202D" mask="url(#z)" height="96" width="96" x="0" y="0" />
+</svg>
diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs
new file mode 100644
index 0000000..3ce47bd
--- /dev/null
+++ b/src/buffer/mod.rs
@@ -0,0 +1,172 @@
+// 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
index 59e2b69..eaebe2b 100644
--- a/src/deserialise/mod.rs
+++ b/src/deserialise/mod.rs
@@ -22,20 +22,25 @@
#[cfg(test)]
mod test;
-use crate::{Dstream, Error, Serialise};
-
-use std::convert::Infallible;
-use std::error::Error as StdError;
-use std::mem::size_of;
-use std::num::NonZero;
-
-/// Denotes a type capable of being deserialised.
-pub trait Deserialise: Serialise + Sized {
+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 [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
+ /// 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
@@ -75,7 +80,11 @@ macro_rules! impl_int {
Ok(Self::from_be_bytes(data))
}
}
+ };
+}
+macro_rules! impl_non_zero {
+ ($type:ty) => {
impl Deserialise for NonZero<$type> {
type Error = Error;
@@ -353,19 +362,33 @@ where
}
}
-impl<T: Deserialise<Error: StdError + 'static>, const N: usize> Deserialise for [T; N] {
+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::try_from(u64::deserialise(stream)?).unwrap();
+ let len = usize::deserialise(stream)?;
+
if len != N { return Err(Box::new(Error::ArrayTooShort { req: len, len: N })) };
- let mut buf = Vec::with_capacity(len);
- for _ in 0x0..len { buf.push(Deserialise::deserialise(stream)?); }
+ let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
- // If we had used the checked unwrap, we would also
- // have to require `T: Debug`.
- Ok(unsafe { buf.try_into().unwrap_unchecked() })
+ // 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)
}
}
@@ -406,6 +429,17 @@ impl Deserialise for Infallible {
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>;
@@ -439,6 +473,17 @@ where
}
}
+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);
@@ -452,3 +497,16 @@ 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
index 27acfda..70293cb 100644
--- a/src/deserialise/test.rs
+++ b/src/deserialise/test.rs
@@ -19,23 +19,21 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Deserialise, Dstream, FixedString};
+use crate::{Deserialise, 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, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x09, 0x6D, 0xC3, 0xA1, 0x6E, 0x61, 0xC3,
- 0xB0, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 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,
+ 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::from(&data);
+ let mut stream = From::from(&data);
assert_eq!(
u8::deserialise(&mut stream).unwrap(),
diff --git a/src/dstream/mod.rs b/src/dstream/mod.rs
index a9cfa89..ca7f619 100644
--- a/src/dstream/mod.rs
+++ b/src/dstream/mod.rs
@@ -19,15 +19,13 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Error, Result, Sstream};
+use crate::{Error, Result};
-use std::fmt::{Debug, Formatter};
+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.
-///
-/// The stream may be converted to an [`Sstream`] using [`to_sstream`](Dstream::to_sstream).
#[derive(Clone)]
pub struct Dstream<'a> {
data: &'a [u8],
@@ -47,15 +45,16 @@ impl<'a> Dstream<'a> {
///
/// # Errors
///
- /// If the internal buffer doesn't hold at least the requested ammount of bytes, an [`EndOfDStream`](Error::EndOfDStream) error is returned.
- pub fn take(&mut self, len: usize) -> Result<&[u8]> {
- if self.len < len { return Err(Error::EndOfDStream { req: len, rem: self.len } ) }
+ /// 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;
- let start = self.data.len() - self.len;
- let stop = start + len;
+ if rem < req { return Err(Error::EndOfStream { req, rem } ) }
- self.len -= len;
+ let start = self.data.len() - rem;
+ let stop = start + req;
+ self.len -= req;
Ok(&self.data[start..stop])
}
@@ -63,53 +62,25 @@ impl<'a> Dstream<'a> {
///
/// # Errors
///
- /// If the internal buffer doesn't hold at least the requested ammount of bytes, an [`EndOfDStream`](Error::EndOfDStream) error is returned.
+ /// 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::EndOfDStream { req: LEN, rem: self.len } ) }
+ 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])
}
-
- /// Takes a slice of the remaining data.
- #[must_use]
- pub fn as_slice(&self) -> &[u8] {
- let stop = self.data.len();
- let start = stop - self.len;
-
- &self.data[start..stop]
- }
-
- /// Converts the stream to a `Sstream` object.
- ///
- /// The returned object owns a copy of the remaining data.
- #[inline(always)]
- #[must_use]
- pub fn to_sstream(&self) -> Sstream {
- Sstream(self.as_slice().to_vec())
- }
}
impl Debug for Dstream<'_> {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- write!(f, "[")?;
-
- for v in self.as_slice() { write!(f, "{v:#02X},")? };
-
- write!(f, "]")?;
-
- Ok(())
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
+ self.data.fmt(f)
}
}
-impl<'a> From<&'a [u8]> for Dstream<'a> {
- fn from(value: &'a [u8]) -> Self { Self::new(value) }
-}
-
-impl<'a, const N: usize> From<&'a [u8; N]> for Dstream<'a> {
- fn from(value: &'a [u8; N]) -> Self { Self::new(value) }
+impl<'a, T: AsRef<[u8]>> From<&'a T> for Dstream<'a> {
+ fn from(value: &'a T) -> Self { Self::new(value) }
}
diff --git a/src/error/mod.rs b/src/error/mod.rs
index d4df6ed..30e4c1e 100644
--- a/src/error/mod.rs
+++ b/src/error/mod.rs
@@ -19,12 +19,12 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use std::error::Error as StdError;
-use std::fmt::{Display, Formatter};
-use std::str::Utf8Error;
+use core::error::Error as StdError;
+use core::fmt::{Display, Formatter};
+use core::str::Utf8Error;
-/// Mapping of [`std::result::Result`].
-pub type Result<T> = std::result::Result<T, Error>;
+/// Mapping of [`core::result::Result`].
+pub type Result<T> = core::result::Result<T, Error>;
/// Denotes an error.
///
@@ -32,14 +32,14 @@ pub type Result<T> = std::result::Result<T, Error>;
/// Serialisations are assumed infallible.
#[derive(Debug)]
pub enum Error {
- /// An array could not hold the requested ammount of elements.
+ /// An array could not hold the requested amount of elements.
ArrayTooShort { req: usize, len: usize },
/// A string encountered an invalid UTF-8 sequence.
BadString { source: Utf8Error },
/// Bytes were requested on an empty stream.
- EndOfDStream { req: usize, rem: usize },
+ EndOfStream { req: usize, rem: usize },
/// A boolean encountered a value outside (0) and (1).
InvalidBoolean { value: u8 },
@@ -49,12 +49,18 @@ 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.
+ IsizeOutOfRange { value: isize },
+
/// A non-zero integer encountered the value (0).
NullInteger,
+
+ /// A `usize` value couldn't fit into (16) bits.
+ UsizeOutOfRange { value: usize },
}
impl Display for Error {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
use Error::*;
match *self {
@@ -66,7 +72,7 @@ impl Display for Error {
write!(f, "unable to parse utf8: \"{source}\"")
},
- EndOfDStream { req, rem } => {
+ EndOfStream { req, rem } => {
write!(f, "({req}) byte(s) were requested but only ({rem}) byte(s) were left")
},
@@ -78,9 +84,17 @@ impl Display for Error {
write!(f, "code point U+{value:04X} is not valid")
},
+ 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)
+ },
}
}
}
diff --git a/src/fixed_string/mod.rs b/src/fixed_string/mod.rs
index 14226a1..b0342d3 100644
--- a/src/fixed_string/mod.rs
+++ b/src/fixed_string/mod.rs
@@ -31,10 +31,11 @@ use crate::{
Sstream,
};
-use std::cmp::Ordering;
-use std::fmt::{Debug, Display, Formatter, Write};
-use std::ops::{Index, IndexMut};
-use std::str::FromStr;
+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.
///
@@ -107,18 +108,18 @@ impl<const N: usize> FixedString<N> {
/// Returns an iterator to the contained characters.
#[inline(always)]
- pub fn iter(&self) -> std::slice::Iter<'_, char> { self.buf[0x0..self.len].iter() }
+ 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) -> std::slice::IterMut<'_, char> { self.buf[0x0..self.len].iter_mut() }
+ 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) -> std::fmt::Result {
- f.write_char('"')?;
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
+ write!(f, "\"")?;
for c in self { write!(f, "{}", c.escape_debug())? }
- f.write_char('"')?;
+ write!(f, "\"")?;
Ok(())
}
@@ -128,13 +129,14 @@ impl<const N: usize> Deserialise for FixedString<N> {
type Error = Error;
fn deserialise(stream: &mut Dstream) -> Result<Self, Self::Error> {
- let len = usize::try_from(u64::deserialise(stream)?).unwrap();
-
+ let len = usize::deserialise(stream)?;
let data = stream.take(len)?;
- let s = std::str::from_utf8(data)
+
+ 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 });
}
@@ -157,7 +159,7 @@ impl<const N: usize> Default for FixedString<N> {
}
impl<const N: usize> Display for FixedString<N> {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
for c in self { write!(f, "{c}")? }
Ok(())
@@ -207,7 +209,7 @@ impl<const N: usize> IntoIterator for FixedString<N> {
impl<'a, const N: usize> IntoIterator for &'a FixedString<N> {
type Item = &'a char;
- type IntoIter = std::slice::Iter<'a, char>;
+ type IntoIter = core::slice::Iter<'a, char>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
@@ -215,7 +217,7 @@ impl<'a, const N: usize> IntoIterator for &'a FixedString<N> {
impl<'a, const N: usize> IntoIterator for &'a mut FixedString<N> {
type Item = &'a mut char;
- type IntoIter = std::slice::IterMut<'a, char>;
+ type IntoIter = core::slice::IterMut<'a, char>;
fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
}
@@ -272,15 +274,19 @@ impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<
}
impl<const N: usize> Serialise for FixedString<N> {
- const SERIALISE_LIMIT: usize = 0x4 * N;
+ type Error = Error;
+
+ const SERIALISE_LIMIT: usize = N * 0x4;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
+ let mut count = 0x0;
- fn serialise(&self, stream: &mut Sstream) {
let s: String = self.iter().collect();
- let len = u64::try_from(s.len()).unwrap();
+ count += s.len().serialise(stream)?;
+ count += stream.add(&s.into_bytes())?;
- stream.append(&len.to_be_bytes());
- stream.append(&s.into_bytes());
+ Ok(count)
}
}
diff --git a/src/fixed_string/test.rs b/src/fixed_string/test.rs
index 1e3be42..1599efc 100644
--- a/src/fixed_string/test.rs
+++ b/src/fixed_string/test.rs
@@ -21,7 +21,7 @@
use crate::FixedString;
-use std::cmp::Ordering;
+use core::cmp::Ordering;
#[test]
fn test_fixed_string() {
diff --git a/src/lib.rs b/src/lib.rs
index d7742ec..168ad75 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,6 +27,19 @@
//! Keep in mind that this project is still work-in-progress.
//!
//! This crate does not require any dependencies at the moment.
+//!
+//! # 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.
+
+#![no_std]
+
+extern crate alloc;
macro_rules! use_mod {
($vis:vis $name:ident) => {
@@ -36,6 +49,7 @@ macro_rules! use_mod {
}
pub(in crate) use use_mod;
+use_mod!(pub buffer);
use_mod!(pub deserialise);
use_mod!(pub dstream);
use_mod!(pub error);
diff --git a/src/serialise/mod.rs b/src/serialise/mod.rs
index 98c1417..0685323 100644
--- a/src/serialise/mod.rs
+++ b/src/serialise/mod.rs
@@ -22,31 +22,43 @@
#[cfg(test)]
mod test;
-use crate::Sstream;
+use crate::{Error, Sstream};
-use std::convert::Infallible;
-use std::mem::size_of;
-use std::num::NonZero;
+use alloc::boxed::Box;
+use core::convert::Infallible;
+use core::error::Error as StdError;
+use core::mem::size_of;
+use core::num::NonZero;
-/// Denotes a type capable of being serialised.
+/// Types capable of being serialised.
pub trait Serialise: Sized {
- /// The maximum ammount of bytes that can result from serialisation.
+ /// The error of serialisation.
+ ///
+ /// Use [`Infallible`] if **all** deserialisations are infallible, as is the case of zero-length types.
+ type Error;
+
+ /// The maximum amount of bytes that can result from serialisation.
const SERIALISE_LIMIT: usize;
/// Serialises `self` into a byte stream.
///
- /// This function should not append *more* bytes than specified in [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT).
- /// Doing so is considered a logic error.
- fn serialise(&self, stream: &mut Sstream);
+ /// 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) {
- stream.append(&self.to_be_bytes())
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
+ let data = self.to_be_bytes();
+ stream.add(&data)?;
+
+ Ok(data.len())
}
}
};
@@ -55,17 +67,28 @@ macro_rules! impl_float {
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) {
- stream.append(&self.to_be_bytes())
+ 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) {
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
self.get().serialise(stream)
}
}
@@ -74,62 +97,82 @@ macro_rules! impl_int {
impl<T0, T1> Serialise for (T0, T1)
where
- T0: Serialise,
- T1: Serialise, {
+ 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) {
- self.0.serialise(stream);
- self.1.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise, {
+ 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) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise, {
+ 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) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise, {
+ 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
@@ -137,23 +180,29 @@ where
+ T3::SERIALISE_LIMIT
+ T4::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise, {
+ 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
@@ -162,25 +211,31 @@ where
+ T4::SERIALISE_LIMIT
+ T5::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise, {
+ 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
@@ -190,27 +245,33 @@ where
+ T5::SERIALISE_LIMIT
+ T6::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise,
- T7: Serialise, {
+ 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
@@ -221,29 +282,35 @@ where
+ T6::SERIALISE_LIMIT
+ T7::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
- self.7.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise,
- T7: Serialise,
- T8: Serialise, {
+ 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
@@ -255,31 +322,37 @@ where
+ T7::SERIALISE_LIMIT
+ T8::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
- self.7.serialise(stream);
- self.8.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise,
- T7: Serialise,
- T8: Serialise,
- T9: Serialise, {
+ 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
@@ -292,33 +365,39 @@ where
+ T8::SERIALISE_LIMIT
+ T9::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
- self.7.serialise(stream);
- self.8.serialise(stream);
- self.9.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise,
- T7: Serialise,
- T8: Serialise,
- T9: Serialise,
- T10: Serialise, {
+ 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
@@ -332,35 +411,41 @@ where
+ T9::SERIALISE_LIMIT
+ T10::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
- self.7.serialise(stream);
- self.8.serialise(stream);
- self.9.serialise(stream);
- self.10.serialise(stream);
+ 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,
- T1: Serialise,
- T2: Serialise,
- T3: Serialise,
- T4: Serialise,
- T5: Serialise,
- T6: Serialise,
- T7: Serialise,
- T8: Serialise,
- T9: Serialise,
- T10: Serialise,
- T11: Serialise, {
+ 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
@@ -375,79 +460,124 @@ where
+ T10::SERIALISE_LIMIT
+ T11::SERIALISE_LIMIT;
- fn serialise(&self, stream: &mut Sstream) {
- self.0.serialise(stream);
- self.1.serialise(stream);
- self.2.serialise(stream);
- self.3.serialise(stream);
- self.4.serialise(stream);
- self.5.serialise(stream);
- self.6.serialise(stream);
- self.7.serialise(stream);
- self.8.serialise(stream);
- self.9.serialise(stream);
- self.10.serialise(stream);
- self.11.serialise(stream);
+ 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, const N: usize> Serialise for [T; N] {
+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) {
- u64::try_from(self.len()).unwrap().serialise(stream);
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
+ let mut count = 0x0;
- for v in self { v.serialise(stream) }
+ 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>();
- fn serialise(&self, _stream: &mut Sstream) { }
+ #[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) {
+ 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) {
+ 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, _stream: &mut Sstream) { unreachable!() }
+ 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> Serialise for Option<T> {
+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) {
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
+ let mut count = 0x0;
+
match *self {
None => {
- stream.append_byte(0x00);
- stream.append(&vec![0x00; size_of::<T>()]);
+ count += false.serialise(stream)?;
+ // No need to zero-fill.
},
Some(ref v) => {
- stream.append_byte(0x01);
- v.serialise(stream);
+ count += true.serialise(stream)?;
+ count += v.serialise(stream)?;
},
};
+
+ Ok(count)
}
}
-impl<T: Serialise, E: Serialise> Serialise for Result<T, E> {
+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>()
@@ -456,18 +586,36 @@ impl<T: Serialise, E: Serialise> Serialise for Result<T, E> {
}
};
- fn serialise(&self, stream: &mut Sstream) {
+ fn serialise(&self, stream: &mut Sstream) -> Result<usize, Self::Error> {
+ // Remember the descriminant.
+ let mut count = 0x0;
+
match *self {
Ok(ref v) => {
- stream.append_byte(0x00);
- v.serialise(stream);
+ count += false.serialise(stream)?;
+ count += v.serialise(stream)?;
},
Err(ref e) => {
- stream.append_byte(0x01);
- e.serialise(stream);
+ 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)
}
}
@@ -484,3 +632,16 @@ 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
index 245c2c3..43f2b9f 100644
--- a/src/serialise/test.rs
+++ b/src/serialise/test.rs
@@ -21,34 +21,38 @@
use crate::{FixedString, Serialise, Sstream};
+use alloc::boxed::Box;
+use alloc::vec;
+
#[test]
fn test_serialise() {
- let mut stream = Sstream::new();
+ let mut buf = vec![0x00; 0x50];
+ let mut stream = Sstream::new(&mut buf);
- 0x00_u8.serialise(&mut stream);
- 0xFF_u8.serialise(&mut stream);
- 0x7F_u8.serialise(&mut stream);
+ 0x00_u8.serialise(&mut stream).unwrap();
+ 0xFF_u8.serialise(&mut stream).unwrap();
+ 0x7F_u8.serialise(&mut stream).unwrap();
- 0x0F_7E_u16.serialise(&mut stream);
+ 0x0F_7E_u16.serialise(&mut stream).unwrap();
- 0x00_2F_87_E7_u32.serialise(&mut stream);
+ 0x00_2F_87_E7_u32.serialise(&mut stream).unwrap();
- 0xF3_37_CF_8B_DB_03_2B_39_u64.serialise(&mut stream);
+ 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);
+ 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);
- FixedString::<0x8>::new("l\u{00F8}gma\u{00F0}ur").unwrap().serialise(&mut stream);
+ 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);
+ ['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'].serialise(&mut stream).unwrap();
- Result::<u16, char>::Ok(0x45_45).serialise(&mut stream);
- Result::<u16, char>::Err(char::REPLACEMENT_CHARACTER).serialise(&mut stream);
+ Ok::<u16, char>(0x45_45).serialise(&mut stream).unwrap();
+ Err::<u16, char>(char::REPLACEMENT_CHARACTER).serialise(&mut stream).unwrap();
- Option::<()>::None.serialise(&mut stream);
- Option::<()>::Some(()).serialise(&mut stream);
+ None::<()>.serialise(&mut stream).unwrap();
+ Some::<()>(()).serialise(&mut stream).unwrap();
- let data: Box<[u8]> = stream.into();
+ let data: Box<[u8]> = buf.into();
assert_eq!(
data.as_ref(),
@@ -57,15 +61,12 @@ fn test_serialise() {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0A, 0x6C, 0xC3, 0xB8, 0x67, 0x6D, 0x61,
- 0xC3, 0xB0, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 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,
- ]
+ 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
index f28875a..83536d0 100644
--- a/src/sstream/mod.rs
+++ b/src/sstream/mod.rs
@@ -19,74 +19,85 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Dstream, Serialise};
+use crate::{Error, Result};
-use std::fmt::{Debug, Formatter};
-use std::mem::size_of;
+use core::fmt::{Debug, Formatter};
/// Byte stream for serialisation.
///
-/// The bytes themselves are contained by the type.
-/// The stream may be converted to [`Dstream`] using [`as_dstream`](Sstream::as_dstream)
-#[derive(Clone, Eq, PartialEq)]
-pub struct Sstream(pub(in crate) Vec<u8>);
+/// 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 Sstream {
- /// Constructs a new, empty byte stream.
+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 const fn new() -> Self { Self(Vec::new()) }
+ pub fn new(data: &'a mut [u8]) -> Self { Self { data, len: 0x0 } }
/// Extends the byte stream.
- pub fn append(&mut self, extra: &[u8]) {
- self.0.extend(extra);
+ ///
+ /// # 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.
- pub fn append_byte(&mut self, extra: u8) {
- self.0.push(extra);
+ ///
+ /// # 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])
}
- /// Converts the stream to a `Dstream` object.
+ /// Yields the length of the stream.
///
- /// The returned object references the original stream.
+ /// That is, the amount of bytes written so far.
#[inline(always)]
#[must_use]
- pub fn as_dstream(&self) -> Dstream { Dstream::new(&self.0) }
-}
+ pub const fn len(&self) -> usize { self.len }
-impl AsRef<[u8]> for Sstream {
+ /// Tests if the stream is empty.
#[inline(always)]
- fn as_ref(&self) -> &[u8] { self.0.as_ref() }
-}
-
-impl Debug for Sstream {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- write!(f, "[")?;
-
- for v in &self.0 { write!(f, "{v:#02X},")? };
-
- write!(f, "]")?;
+ #[must_use]
+ pub const fn is_empty(&self) -> bool { self.len == 0x0 }
- Ok(())
- }
+ /// 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 Default for Sstream {
+impl AsRef<[u8]> for Sstream<'_> {
#[inline(always)]
- fn default() -> Self { Self::new() }
+ fn as_ref(&self) -> &[u8] { self.as_slice() }
}
-impl<T: Serialise> From<&T> for Sstream {
- fn from(value: &T) -> Self {
- let mut stream = Self(Vec::with_capacity(size_of::<T>()));
- value.serialise(&mut stream);
-
- stream
+impl Debug for Sstream<'_> {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
+ self.data.fmt(f)
}
}
-
-impl From<Sstream> for Box<[u8]> {
- #[inline(always)]
- fn from(value: Sstream) -> Self { value.0.into_boxed_slice() }
-}