summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Bjørnager Jensen2024-08-31 12:55:15 +0200
committerGabriel Bjørnager Jensen2024-08-31 12:55:15 +0200
commita872a5c2454ac7d7d66216ccebd69bf97d485b10 (patch)
tree057062f986f2bdb95672bb8e94b01e5b35133e3f
parent3b29e7262487bd8435dcfb7b7d5518ba22ba258e (diff)
Make 'alloc' and 'std' default features; Make serialisations variably sized again; Refactor derive implementations; Completely rework streams; Fix tuple deserialisation; Encode 'FixedString' in UTF-8; Remove methods 'from_chars' and 'set_len' from 'FixedString'; Rename 'as_slice' and 'as_mut_slice' methods in 'FixedString' to 'as_st' and 'as_mut_str'; Add methods 'as_bytes', 'push_str', 'chars', 'capacity', and 'char_indices' to 'FixedString'; Rework 'FixedString' traits; Remove 'FixedIter'; Update lints; Add methods 'set_len' and 'set_len_unchecked' to 'Buffer'; Elaborate docs; Update readme; Do not require 'Serialise' for 'Deserialise'; Rename 'SERIALISED_SIZE' in 'Serialise' to 'MAX_SERIALISED_SIZE'; Use streams in 'Serialise' and 'Deserialise'; Drop 'Serialise' requirement for 'Buffer'; Add methods 'with_capacity' and 'capacity' to 'Buffer';HEAD7.0.0master
-rw-r--r--CHANGELOG.md23
-rw-r--r--Cargo.toml2
-rw-r--r--README.md96
-rw-r--r--bzipper/Cargo.toml6
-rw-r--r--bzipper/src/buffer/mod.rs152
-rw-r--r--bzipper/src/buffer/test.rs4
-rw-r--r--bzipper/src/deserialise/mod.rs142
-rw-r--r--bzipper/src/deserialise/test.rs11
-rw-r--r--bzipper/src/deserialise/tuple.rs254
-rw-r--r--bzipper/src/dstream/mod.rs91
-rw-r--r--bzipper/src/error/mod.rs42
-rw-r--r--bzipper/src/fixed_iter/mod.rs46
-rw-r--r--bzipper/src/fixed_string/mod.rs413
-rw-r--r--bzipper/src/fixed_string/test.rs6
-rw-r--r--bzipper/src/lib.rs99
-rw-r--r--bzipper/src/serialise/mod.rs168
-rw-r--r--bzipper/src/serialise/test.rs37
-rw-r--r--bzipper/src/serialise/tuple.rs426
-rw-r--r--bzipper/src/sstream/mod.rs93
-rw-r--r--bzipper_macros/Cargo.toml2
-rw-r--r--bzipper_macros/src/impls/deserialise_enum.rs20
-rw-r--r--bzipper_macros/src/impls/deserialise_struct.rs57
-rw-r--r--bzipper_macros/src/impls/serialise_enum.rs19
-rw-r--r--bzipper_macros/src/impls/serialise_struct.rs20
24 files changed, 1202 insertions, 1027 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ffd76f..a32a290 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,29 @@
This is the changelog of bzipper.
See `"README.md"` for more information.
+## 0.7.0
+
+* Make `alloc` and `std` default features
+* Make serialisations variably sized again
+* Refactor derive implementations
+* Completely rework streams
+* Fix tuple deserialisation
+* Encode `FixedString` in UTF-8
+* Remove methods `from_chars` and `set_len` from `FixedString`
+* Rename `as_slice` and `as_mut_slice` methods in `FixedString` to `as_st` and `as_mut_str`
+* Add methods `as_bytes`, `push_str`, `chars`, `capacity`, and `char_indices` to `FixedString`
+* Rework `FixedString` traits
+* Remove `FixedIter`
+* Update lints
+* Add methods `set_len` and `set_len_unchecked` to `Buffer`
+* Elaborate docs
+* Update readme
+* Do not require `Serialise` for `Deserialise`
+* Rename `SERIALISED_SIZE` in `Serialise` to `MAX_SERIALISED_SIZE`
+* Use streams in `Serialise` and `Deserialise`
+* Drop `Serialise` requirement for `Buffer`
+* Add methods `with_capacity` and `capacity` to `Buffer`
+
## 0.6.2
* Fix `Deserialise` derive for unit variants
diff --git a/Cargo.toml b/Cargo.toml
index 90953b6..9194694 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -101,7 +101,6 @@ option_as_ref_cloned = "warn"
option_if_let_else = "warn"
option_option = "deny"
or_fun_call = "deny"
-panic_in_result_fn = "deny"
path_buf_push_overwrite = "deny"
pattern_type_mismatch = "deny"
ptr_as_ptr = "forbid"
@@ -122,7 +121,6 @@ return_self_not_must_use = "deny"
same_functions_in_if_condition = "deny"
same_name_method = "deny"
self_named_module_files = "deny"
-semicolon_outside_block = "warn"
single_char_pattern = "warn"
str_split_at_newline = "warn"
string_lit_as_bytes = "deny"
diff --git a/README.md b/README.md
index 94ae801..bffd266 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[bzipper](https://crates.io/crates/bzipper/) is a binary (de)serialiser for the Rust language.
-Contrary to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the goal of bzipper is to serialise with a known size constraint.
+In contrast to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the primary goal of bzipper is to serialise with a known size constraint.
Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.
Keep in mind that this project is still work-in-progress.
@@ -20,15 +20,15 @@ For strings, the `FixedString` type is also provided.
## Usage
-This crate revolves around the `Serialise` and `Deserialise` traits, both of which are commonly used in conjunction with streams (more specifically, s-streams and d-streams).
+This crate revolves around the `Serialise` and `Deserialise` traits, both of which use *streams* – or more specifically – s-streams and d-streams.
Many core types come implemented with bzipper, including primitives as well as some standard library types such as `Option` and `Result`.
-It is recommended in most cases to just derive these traits for custom types (enumerations and structures only).
-Here, each field is chained in declaration order:
+It is recommended in most cases to just derive these two traits for custom types (although this is only supported with enumerations and structures).
+Here, each field is *chained* according to declaration order:
-```rs
-use bzipper::{Deserialise, Serialise};
+```rust
+use bzipper::{Buffer, Deserialise, Serialise};
#[derive(Debug, Deserialise, PartialEq, Serialise)]
struct IoRegister {
@@ -36,45 +36,55 @@ struct IoRegister {
value: u16,
}
-let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
-IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
+let mut buf = Buffer::new();
+buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
+
+assert_eq!(buf.len(), 0x6);
assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
-assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
+assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
```
### Serialisation
-To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation.
-The required size of any given serialisation is specified by the `SERIALISED_SIZE` constant:
+To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation and wrap it in an s-stream (*serialisation stream*) with the `Sstream` type.
+
+```rust
+use bzipper::{Serialise, Sstream};
-```rs
-use bzipper::Serialise;
+let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE];
+let mut stream = Sstream::new(&mut buf);
-let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
-'Ж'.serialise(&mut buf).unwrap();
+'Ж'.serialise(&mut stream).unwrap();
-assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
+assert_eq!(stream, [0x00, 0x00, 0x04, 0x16]);
```
-The only special requirement of the `serialise` method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
+The maximum size of any given serialisation is specified by the `MAX_SERIALISED_SIZE` constant.
+
+We can also use streams to chain multiple elements together:
-We can also use streams to *chain* multiple elements together:
+```rust
+use bzipper::{Serialise, Sstream};
-```rs
-use bzipper::Serialise;
+let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE * 0x5];
+let mut stream = Sstream::new(&mut buf);
-let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
-let mut stream = bzipper::Sstream::new(&mut buf);
+// Note: For serialising multiple characters, the
+// `FixedString` type is usually preferred.
-stream.append(&'ل');
-stream.append(&'ا');
-stream.append(&'م');
-stream.append(&'د');
-stream.append(&'ا');
+'ل'.serialise(&mut stream).unwrap();
+'ا'.serialise(&mut stream).unwrap();
+'م'.serialise(&mut stream).unwrap();
+'د'.serialise(&mut stream).unwrap();
+'ا'.serialise(&mut stream).unwrap();
-assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);
+assert_eq!(buf, [
+ 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27,
+ 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F,
+ 0x00, 0x00, 0x06, 0x27
+]);
```
When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
@@ -82,25 +92,33 @@ It is recommended for implementors to adhere to this convention as well.
### Deserialisation
-Deserialisation works with an almost identical syntax to serialisation.
+Deserialisation works with a similar syntax to serialisation.
-To deserialise a buffer, simply call the `deserialise` method:
+D-streams (*deserialisation streams*) use the `Dstream` type and are constructed in a manner similar to s-streams.
+To deserialise a buffer, simply call the `deserialise` method with the strema:
-```rs
-use bzipper::Deserialise;
+```rust
+use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
-assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
+let stream = Dstream::new(&data);
+assert_eq!(u16::deserialise(&stream).unwrap(), 0x4554);
```
-Just like with serialisations, the `Dstream` can be used to deserialise chained elements:
+And just like s-streams, d-streams can also be used to handle chaining:
-```rs
-use bzipper::Deserialise;
+```rust
+use bzipper::{Deserialise, Dstream};
let data = [0x45, 0x54];
-let stream = bzipper::Dstream::new(&data);
+let stream = Dstream::new(&data);
+
+assert_eq!(u8::deserialise(&stream).unwrap(), 0x45);
+assert_eq!(u8::deserialise(&stream).unwrap(), 0x54);
+
+// The data can also be deserialised as a tuple (up
+// to twelve elements).
-assert_eq!(stream.take::<u8>().unwrap(), 0x45);
-assert_eq!(stream.take::<u8>().unwrap(), 0x54);
+let stream = Dstream::new(&data);
+assert_eq!(<(u8, u8)>::deserialise(&stream).unwrap(), (0x45, 0x54));
```
diff --git a/bzipper/Cargo.toml b/bzipper/Cargo.toml
index 8f6f98d..28fae76 100644
--- a/bzipper/Cargo.toml
+++ b/bzipper/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bzipper"
-version = "0.6.2"
+version = "0.7.0"
edition = "2021"
rust-version = "1.81"
documentation = "https://docs.rs/bzipper/"
@@ -16,11 +16,13 @@ license.workspace = true
all-features = true
[features]
+default = ["alloc", "std"]
+
alloc = []
std = []
[dependencies]
-bzipper_macros = { path = "../bzipper_macros", version = "0.6.2"}
+bzipper_macros = { path = "../bzipper_macros", version = "0.7.0"}
[lints]
workspace = true
diff --git a/bzipper/src/buffer/mod.rs b/bzipper/src/buffer/mod.rs
index f39b108..c0da902 100644
--- a/bzipper/src/buffer/mod.rs
+++ b/bzipper/src/buffer/mod.rs
@@ -22,112 +22,201 @@
#[cfg(test)]
mod test;
-use crate::{Deserialise, Result, Serialise};
+use crate::{Deserialise, Dstream, Result, Serialise, Sstream};
use alloc::vec;
use alloc::boxed::Box;
+use core::borrow::Borrow;
use core::fmt::{Debug, Formatter};
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
-// We cannot use arrays for the `Buffer` type as
-// that would require `generic_const_exprs`.
-
/// Typed (de)serialisation buffer.
///
/// This structure is intended as a lightweight wrapper around byte buffers for specific (de)serialisations of specific types.
///
-/// The methods [`write`](Self::write) and [`read`](Self::read) can be used to <interpreting> the internal buffer.
+/// The methods [`write`](Self::write) and [`read`](Self::read) can be used to handle the internal buffer.
/// Other methods exist for accessing the internal buffer directly.
///
/// # Examples
///
/// Create a buffer for holding a `Request` enumeration:
///
-/// ```
+/// ```rust
/// use bzipper::{Buffer, FixedString, Serialise};
///
/// #[derive(Serialise)]
/// enum Request {
-/// Join { username: FixedString<0x10> },
+/// Join { username: FixedString<0x40> },
///
-/// Quit { username: FixedString<0x10> },
+/// Quit { username: FixedString<0x40> },
///
-/// SendMessage { message: FixedString<0x20> },
+/// SendMessage { message: FixedString<0x80> },
/// }
///
/// use Request::*;
///
/// let join_request = Join { username: FixedString::try_from("epsiloneridani").unwrap() };
///
-/// let mut buf = Buffer::<Request>::new();
-/// buf.write(&join_request);
+/// let mut buf = Buffer::new();
+/// buf.write(join_request);
///
/// // Do something with the buffer...
/// ```
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
#[derive(Clone, Eq, PartialEq)]
-pub struct Buffer<T: Serialise> {
+pub struct Buffer<T> {
buf: Box<[u8]>,
+ len: usize,
_phanton: PhantomData<T>
}
-impl<T: Serialise> Buffer<T> {
- /// Allocates a new buffer suitable for (de)serialisation.
+impl<T> Buffer<T> {
+ /// Allocates a new buffer suitable for serialisation.
+ ///
+ /// The given capacity should be large enough to hold any expected serialisation of `T`.
+ /// Therefore, if `T` implements [`Serialise`], it is recommended to use [`new`](Self::new) instead, which is equivalent to passing [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) to this function:
+ #[inline]
#[must_use]
- pub fn new() -> Self { Self { buf: vec![0x00; T::SERIALISED_SIZE].into(), _phanton: PhantomData } }
+ pub fn with_capacity(len: usize) -> Self {
+ Self {
+ buf: vec![0x00; len].into(),
+ len: 0x0,
- /// Serialises into the contained buffer.
+ _phanton: PhantomData,
+ }
+ }
+
+ /// Sets the length of the used buffer.
+ ///
+ /// The provided size is checked before being written.
+ /// For the same operation *without* checks, see [`set_len_unchecked`](Self::set_len_unchecked).
+ ///
+ /// # Panics
+ ///
+ /// The provided size must not be greater than the buffer's capacity.
+ /// If this is the case, however, this method will panic.
+ #[inline(always)]
+ pub fn set_len(&mut self, len: usize) {
+ assert!(len <= self.capacity(), "cannot extend buffer beyond capacity");
+
+ self.len = len;
+ }
+
+ /// Sets the length of the used buffer without checks.
+ ///
+ /// The validity of the provided size is **not** checked before being written.
+ /// For the same operation *with* checks, see [`set_len`](Self::set_len).
+ ///
+ /// # Safety
+ ///
+ /// If the value of `len` is greater than the buffer's capacity, behaviour is undefined.
#[inline(always)]
- pub fn write(&mut self, value: &T) -> Result<()> { value.serialise(&mut self.buf) }
+ pub unsafe fn set_len_unchecked(&mut self, len: usize) { self.len = len }
- /// Retrieves a pointer to the first byte.
+ /// Retrieves a pointer to the first byte of the internal buffer.
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() }
- /// Retrieves a mutable pointer to the first byte.
+ /// Retrieves a mutable pointer to the first byte of the internal buffer.
#[inline(always)]
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut u8 { self.buf.as_mut_ptr() }
/// Gets a slice of the internal buffer.
+ ///
+ /// The returned slice will only include the used part of the buffer (as specified by [`len`](Self::len)).
#[inline(always)]
#[must_use]
pub const fn as_slice(&self) -> &[u8] { unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) } }
/// Gets a mutable slice of the internal buffer.
+ ///
+ /// In contrast to [`as_slice`](Self::as_slice), this method returns a slice of the **entire** internal buffer.
+ ///
+ /// If the returned reference is written through, the new buffer length -- if different -- should be set using [`set_len`](Self::set_len).
#[inline(always)]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u8] { &mut self.buf }
/// Gets the length of the buffer.
- ///
- /// This is defined as (and therefore always equal to) the value of [SERIALISED_SIZE](Serialise::SERIALISED_SIZE) as specified by `T`.
#[allow(clippy::len_without_is_empty)]
#[inline(always)]
#[must_use]
- pub const fn len(&self) -> usize { T::SERIALISED_SIZE }
+ pub const fn len(&self) -> usize { self.len }
+
+ /// Gets the capacity of the buffer.
+ ///
+ /// If the buffer was constructed using [`new`](Self::new), this value is exactly the same as [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE).
+ #[inline(always)]
+ #[must_use]
+ pub const fn capacity(&self) -> usize { self.buf.len() }
+}
+
+impl<T: Serialise> Buffer<T> {
+ /// Allocates a new buffer suitable for serialisation.
+ ///
+ /// The capacity of the internal buffer is set so that any serialisation of `T` may be stored.
+ ///
+ /// This is equivalent to calling [`with_capacity`](Self::with_capacity) with [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE).
+ #[inline(always)]
+ #[must_use]
+ pub fn new() -> Self { Self::with_capacity(T::MAX_SERIALISED_SIZE) }
+
+ /// Serialises into the contained buffer.
+ ///
+ /// # Errors
+ ///
+ /// Any error that occurs during serialisation is passed on and returned from this method.
+ ///
+ /// # Panics
+ ///
+ /// If the amount of bytes read by [`serialise`](Serialise::serialise) is greater than that specified by [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE), this method panics.
+ ///
+ /// In reality, however, this error can only be detected if the buffer's capacity is set to a value greater than `MAX_SERIALISED_SIZE` to begin with (e.g. using [`with_capacity`](Self::with_capacity)).
+ #[inline(always)]
+ pub fn write<U: Borrow<T>>(&mut self, value: U) -> Result<()> {
+ let mut stream = Sstream::new(&mut self.buf);
+ value.borrow().serialise(&mut stream)?;
+
+ assert!(stream.len() <= T::MAX_SERIALISED_SIZE);
+ self.len = stream.len();
+
+ Ok(())
+ }
}
impl<T: Deserialise> Buffer<T> {
/// Deserialises from the contained buffer.
+ ///
+ /// # Errors
+ ///
+ /// Any error that occurs during deserialisation is passed on and returned from this method.
#[inline(always)]
- pub fn read(&self) -> Result<T> { T::deserialise(&self.buf) }
+ pub fn read(&self) -> Result<T> {
+ // We should only pass the used part of the buffer
+ // to `deserialise`.
+
+ let stream = Dstream::new(&self.buf[0x0..self.len()]);
+ let value = Deserialise::deserialise(&stream)?;
+
+ Ok(value)
+ }
}
-impl<T: Serialise> AsMut<[u8]> for Buffer<T> {
+impl<T> AsMut<[u8]> for Buffer<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_slice() }
}
-impl<T: Serialise> AsRef<[u8]> for Buffer<T> {
+impl<T> AsRef<[u8]> for Buffer<T> {
#[inline(always)]
fn as_ref(&self) -> &[u8] { self.as_slice() }
}
-impl<T: Serialise> Debug for Buffer<T> {
+impl<T> Debug for Buffer<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "{:?}", self.as_slice()) }
}
@@ -137,19 +226,24 @@ impl<T: Serialise> Default for Buffer<T> {
fn default() -> Self { Self::new() }
}
-impl<T: Serialise> Deref for Buffer<T> {
+impl<T> Deref for Buffer<T> {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &Self::Target { self.as_slice() }
}
-impl<T: Serialise> DerefMut for Buffer<T> {
+impl<T> DerefMut for Buffer<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_slice() }
}
-impl<T: Serialise> PartialEq<&[u8]> for Buffer<T> {
+impl<T> PartialEq<&[u8]> for Buffer<T> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
}
+
+impl<T, const N: usize> PartialEq<[u8; N]> for Buffer<T> {
+ #[inline(always)]
+ fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
+}
diff --git a/bzipper/src/buffer/test.rs b/bzipper/src/buffer/test.rs
index 4f24e45..e92ae4b 100644
--- a/bzipper/src/buffer/test.rs
+++ b/bzipper/src/buffer/test.rs
@@ -25,11 +25,11 @@ use crate::{Buffer, Error};
fn test_buffer() {
let mut buf = Buffer::<char>::new();
- buf.write(&'\u{1F44D}').unwrap();
+ buf.write('\u{1F44D}').unwrap();
assert_eq!(buf, [0x00, 0x01, 0xF4, 0x4D].as_slice());
buf.as_mut_slice().copy_from_slice(&[0x00, 0x00, 0xD8, 0x00]);
- assert!(matches!(buf.read(), Err(Error::InvalidCodePoint { value: 0xD800 })));
+ assert!(matches!(buf.read(), Err(Error::InvalidCodePoint(0xD800))));
buf.as_mut_slice().copy_from_slice(&[0x00, 0x00, 0xFF, 0x3A]);
assert_eq!(buf.read().unwrap(), '\u{FF3A}');
diff --git a/bzipper/src/deserialise/mod.rs b/bzipper/src/deserialise/mod.rs
index 49ca74e..e51a552 100644
--- a/bzipper/src/deserialise/mod.rs
+++ b/bzipper/src/deserialise/mod.rs
@@ -31,15 +31,12 @@ use core::num::NonZero;
mod tuple;
-/// Types capable of being deserialised.
-///
-/// This trait requires [`Serialise`] also being implemented as it relies on the [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE) constant.
-pub trait Deserialise: Serialise + Sized {
- /// Deserialises a slice into an object.
+/// Denotes a type capable of deserialisation.
+pub trait Deserialise: Sized {
+ /// Deserialises an object from the given d-stream.
///
- /// This function must **never** take more bytes than specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE).
+ /// This method must **never** read more bytes than specified by [`MAX_SERIALISED_SIZE`](crate::Serialise::MAX_SERIALISED_SIZE) (if [`Serialise`] is defined, that is).
/// Doing so is considered a logic error.
- /// Likewise, providing more than this amount is also disfavoured.
///
/// # Errors
///
@@ -47,22 +44,20 @@ pub trait Deserialise: Serialise + Sized {
///
/// # Panics
///
- /// This method will usually panic if the provided slice has a length *less* than the value of `SERIALISED_SIZE`.
+ /// This method will usually panic if the provided slice has a length *less* than the value of `MAX_SERIALISED_SIZE`.
/// Official implementations of this trait (including those that are derived) always panic in debug mode if the provided slice has a length that is different at all.
- fn deserialise(data: &[u8]) -> Result<Self>;
+ fn deserialise(stream: &Dstream) -> Result<Self>;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Deserialise for $ty {
- fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
- ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
-
- const SIZE: usize = ::core::mem::size_of::<$ty>();
-
- let data = data
- .get(0x0..SIZE)
- .ok_or(::bzipper::Error::EndOfStream { req: SIZE, rem: data.len() })?
+ #[inline]
+ fn deserialise(stream: &Dstream) -> ::bzipper::Result<Self> {
+ let data = stream
+ .read(Self::MAX_SERIALISED_SIZE)
+ .unwrap()
+ //.ok_or(::bzipper::Error::EndOfStream { req: Self::MAX_SERIALISED_SIZE, rem: data.len() })?
.try_into()
.unwrap();
@@ -75,34 +70,29 @@ macro_rules! impl_numeric {
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Deserialise for NonZero<$ty> {
- fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
- ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
+ #[inline]
+ fn deserialise(stream: &Dstream) -> ::bzipper::Result<Self> {
+ let value = <$ty as ::bzipper::Deserialise>::deserialise(stream)?;
- let value = <$ty as ::bzipper::Deserialise>::deserialise(data)?;
+ let value = NonZero::new(value)
+ .ok_or(Error::NullInteger)?;
- NonZero::new(value)
- .ok_or(Error::NullInteger)
+ Ok(value)
}
}
};
}
-impl<T, const N: usize> Deserialise for [T; N]
-where
- T: Deserialise {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
-
+impl<T: Deserialise, const N: usize> Deserialise for [T; N] {
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
// Initialise the array incrementally.
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
- let mut pos = 0x0;
for item in &mut buf {
- let range = pos..pos + T::SERIALISED_SIZE;
-
- pos = range.end;
- item.write(Deserialise::deserialise(&data[range])?);
+ let value = T::deserialise(stream)?;
+ item.write(value);
}
// This should be safe as `MaybeUninit<T>` is
@@ -118,83 +108,80 @@ where
}
impl Deserialise for bool {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
-
- let value = u8::deserialise(data)?;
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = u8::deserialise(stream)?;
match value {
0x00 => Ok(false),
0x01 => Ok(true),
- _ => Err(Error::InvalidBoolean { value })
+ _ => Err(Error::InvalidBoolean(value))
}
}
}
impl Deserialise for char {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = u32::deserialise(stream)?;
- let value = u32::deserialise(data)?;
+ let value = value
+ .try_into()
+ .map_err(|_| Error::InvalidCodePoint(value))?;
- Self::from_u32(value)
- .ok_or(Error::InvalidCodePoint { value })
+ Ok(value)
}
}
impl Deserialise for Infallible {
#[allow(clippy::panic_in_result_fn)]
#[inline(always)]
- fn deserialise(_data: &[u8]) -> Result<Self> { panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with") }
+ fn deserialise(_stream: &Dstream) -> Result<Self> { panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with") }
}
impl Deserialise for isize {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = i32::deserialise(stream)?;
- let value = i32::deserialise(data)?
- .try_into().expect("unable to convert from `i32` to `isize`");
+ let value = value
+ .try_into()
+ .expect("unable to convert from `i32` to `isize`");
Ok(value)
}
}
impl<T: Deserialise> Deserialise for Option<T> {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
-
- let stream = Dstream::new(data);
-
- let sign = stream.take::<bool>()?;
+ #[allow(clippy::if_then_some_else_none)]
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let sign = bool::deserialise(stream)?;
- if sign {
- Ok(Some(stream.take::<T>()?))
+ let value = if sign {
+ Some(T::deserialise(stream)?)
} else {
- Ok(None)
- }
+ None
+ };
+
+ Ok(value)
}
}
impl<T> Deserialise for PhantomData<T> {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
-
- Ok(Self)
- }
+ #[inline(always)]
+ fn deserialise(_stream: &Dstream) -> Result<Self> { Ok(Self) }
}
impl<T: Deserialise, E: Deserialise> Deserialise for core::result::Result<T, E> {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
-
- let stream = Dstream::new(data);
-
- let sign = stream.take::<bool>()?;
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let sign = bool::deserialise(stream)?;
let value = if sign {
- Err(stream.take::<E>()?)
+ Err(E::deserialise(stream)?)
} else {
- Ok(stream.take::<T>()?)
+ Ok(T::deserialise(stream)?)
};
Ok(value)
@@ -202,15 +189,18 @@ impl<T: Deserialise, E: Deserialise> Deserialise for core::result::Result<T, E>
}
impl Deserialise for () {
- fn deserialise(_data: &[u8]) -> Result<Self> { Ok(()) }
+ #[inline(always)]
+ fn deserialise(_stream: &Dstream) -> Result<Self> { Ok(()) }
}
impl Deserialise for usize {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ #[inline]
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = u32::deserialise(stream)?;
- let value = u32::deserialise(data)?
- .try_into().expect("unable to convert from `u32` to `usize`");
+ let value = value
+ .try_into()
+ .expect("must be able to convert from `u32` to `usize`");
Ok(value)
}
diff --git a/bzipper/src/deserialise/test.rs b/bzipper/src/deserialise/test.rs
index 3caebae..8624448 100644
--- a/bzipper/src/deserialise/test.rs
+++ b/bzipper/src/deserialise/test.rs
@@ -19,7 +19,9 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Deserialise, Serialise};
+use core::char;
+
+use crate::{Deserialise, Dstream, Serialise};
#[test]
fn test() {
@@ -46,9 +48,10 @@ fn test() {
($ty:ty: $data:expr => $value:expr) => {{
use ::bzipper::{Deserialise, Serialise};
- let buf: [u8; <$ty as Serialise>::SERIALISED_SIZE] = $data;
+ let mut buf: [u8; <$ty as Serialise>::MAX_SERIALISED_SIZE] = $data;
+ let stream = Dstream::new(&mut buf);
- let left = <$ty as Deserialise>::deserialise(&buf).unwrap();
+ let left = <$ty as Deserialise>::deserialise(&stream).unwrap();
let right = $value;
assert_eq!(left, right);
@@ -80,6 +83,8 @@ fn test() {
0xBF, 0x4F, 0xAF, 0x5F, 0x9F, 0x6F, 0x8F, 0x7F,
] => 0xFF_0F_EF_1F_DF_2F_CF_3F_BF_4F_AF_5F_9F_6F_8F_7F);
+ test!(char: [0x00, 0x00, 0xFF, 0xFD] => char::REPLACEMENT_CHARACTER);
+
test!([char; 0x5]: [
0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, 0x91,
0x00, 0x00, 0x03, 0xBC, 0x00, 0x00, 0x03, 0x94,
diff --git a/bzipper/src/deserialise/tuple.rs b/bzipper/src/deserialise/tuple.rs
index b1f7ac1..fedbad6 100644
--- a/bzipper/src/deserialise/tuple.rs
+++ b/bzipper/src/deserialise/tuple.rs
@@ -19,17 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Deserialise, Result, Serialise};
+use crate::{Deserialise, Dstream, Result};
impl<T0> Deserialise for (T0, )
where
T0: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -37,13 +37,13 @@ impl<T0, T1> Deserialise for (T0, T1)
where
T0: Deserialise,
T1: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -52,14 +52,14 @@ where
T0: Deserialise,
T1: Deserialise,
T2: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -69,15 +69,15 @@ where
T1: Deserialise,
T2: Deserialise,
T3: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -88,16 +88,16 @@ where
T2: Deserialise,
T3: Deserialise,
T4: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -109,17 +109,17 @@ where
T3: Deserialise,
T4: Deserialise,
T5: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -132,18 +132,18 @@ where
T4: Deserialise,
T5: Deserialise,
T6: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -157,19 +157,19 @@ where
T5: Deserialise,
T6: Deserialise,
T7: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -184,20 +184,20 @@ where
T6: Deserialise,
T7: Deserialise,
T8: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -213,21 +213,21 @@ where
T7: Deserialise,
T8: Deserialise,
T9: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -244,22 +244,22 @@ where
T8: Deserialise,
T9: Deserialise,
T10: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
@@ -277,22 +277,22 @@ where
T9: Deserialise,
T10: Deserialise,
T11: Deserialise, {
- fn deserialise(data: &[u8]) -> Result<Self> {
- debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
+ fn deserialise(stream: &Dstream) -> Result<Self> {
+ let value = (
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ Deserialise::deserialise(stream)?,
+ );
- Ok((
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- Deserialise::deserialise(data)?,
- ))
+ Ok(value)
}
}
diff --git a/bzipper/src/dstream/mod.rs b/bzipper/src/dstream/mod.rs
index e87edf8..3cdae50 100644
--- a/bzipper/src/dstream/mod.rs
+++ b/bzipper/src/dstream/mod.rs
@@ -19,16 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Deserialise, Error, Result};
+use crate::{Error, Result};
use core::cell::Cell;
+use core::fmt::{Debug, Formatter};
-/// Byte stream for deserialisation.
+/// Byte stream suitable for deserialisation.
///
-/// This type borrows a slice, keeping track internally of the used bytes.
+/// This type borrows a buffer, keeping track internally of the used bytes.
pub struct Dstream<'a> {
- data: &'a [u8],
- pos: Cell<usize>,
+ pub(in crate) data: &'a [u8],
+ pub(in crate) pos: Cell<usize>,
}
impl<'a> Dstream<'a> {
@@ -37,22 +38,84 @@ impl<'a> Dstream<'a> {
#[must_use]
pub const fn new(data: &'a [u8]) -> Self { Self { data, pos: Cell::new(0x0) } }
- /// Deserialises an object from the stream.
- ///
- /// # Errors
- ///
- /// If the stream doesn't hold at least the amount of bytes specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE), an [`EndOfStream`](Error::EndOfStream) error is returned.
+ /// Takes (borrows) raw bytes from the stream.
#[inline]
- pub fn take<T: Deserialise>(&self) -> Result<T> {
+ pub fn read(&self, count: usize) -> Result<&[u8]> {
let rem = self.data.len() - self.pos.get();
- let req = T::SERIALISED_SIZE;
+ let req = count;
- if rem < req { return Err(Error::EndOfStream { req, rem }) };
+ if rem < req { return Err(Error::EndOfStream { req, rem }) }
let start = self.pos.get();
let stop = start + req;
self.pos.set(stop);
- T::deserialise(&self.data[start..stop])
+
+ let data = &self.data[start..stop];
+ Ok(data)
+ }
+
+ /// Gets a pointer to the first byte in the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn as_ptr(&self) -> *const u8 { self.data.as_ptr() }
+
+ /// Gets a slice of the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn as_slice(&self) -> &[u8] {
+ let ptr = self.as_ptr();
+ let len = self.len();
+
+ unsafe { core::slice::from_raw_parts(ptr, len) }
}
+
+ /// Gets the length of the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn len(&self) -> usize { unsafe { self.pos.as_ptr().read() } }
+
+ /// Tests if the stream is empty.
+ ///
+ /// If no deserialisations have been made at the time of calling, this method returns `false`.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
+
+ /// Tests if the stream is full.
+ ///
+ /// Note that zero-sized types such as [`()`](unit) can still be deserialised from this stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_full(&self) -> bool { self.len() == self.data.len() }
+}
+
+impl Debug for Dstream<'_> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_slice(), f) }
+}
+
+impl<'a> From<&'a [u8]> for Dstream<'a> {
+ #[inline(always)]
+ fn from(value: &'a [u8]) -> Self { Self::new(value) }
+}
+
+impl<'a> From<&'a mut [u8]> for Dstream<'a> {
+ #[inline(always)]
+ fn from(value: &'a mut [u8]) -> Self { Self::new(value) }
+}
+
+impl PartialEq for Dstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
+}
+
+impl PartialEq<&[u8]> for Dstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
+}
+
+impl<const N: usize> PartialEq<[u8; N]> for Dstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
}
diff --git a/bzipper/src/error/mod.rs b/bzipper/src/error/mod.rs
index 090215a..4e0fe11 100644
--- a/bzipper/src/error/mod.rs
+++ b/bzipper/src/error/mod.rs
@@ -28,47 +28,53 @@ use alloc::boxed::Box;
/// Mapping of [`core::result::Result`].
pub type Result<T> = core::result::Result<T, Error>;
-/// (De)serialisation failures.
+/// bzipper errors.
///
/// These variants are used when deserialisation fails.
/// Serialisations are assumed infallible.
#[derive(Debug)]
pub enum Error {
/// An array could not hold the requested amount of elements.
- ArrayTooShort { req: usize, len: usize },
+ ArrayTooShort {
+ /// The required amount of bytes.
+ req: usize,
+
+ /// The remaining amount of bytes.
+ len: usize,
+ },
/// A string encountered an invalid UTF-8 sequence.
BadString { source: Utf8Error },
- /// An implementor-defined error.
+ /// An unspecified (de)serialisation error.
///
/// This is mainly useful if none of the predefined errors are appropriate.
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
- CustomError { source: Box<dyn core::error::Error> },
+ CustomError(Box<dyn core::error::Error>),
/// Bytes were requested on an empty stream.
EndOfStream { req: usize, rem: usize },
/// A boolean encountered a value outside `0` and `1`.
- InvalidBoolean { value: u8 },
+ InvalidBoolean(u8),
/// An invalid code point was encountered.
///
/// This includes surrogate points in the inclusive range `U+D800` to `U+DFFF`, as well as values larger than `U+10FFFF`.
- InvalidCodePoint { value: u32 },
+ InvalidCodePoint(u32),
/// An invalid enumeration descriminant was provided.
- InvalidDiscriminant { value: u32 },
+ InvalidDiscriminant(u32),
- /// An `isize` value couldn't fit into `16` bits.
- IsizeOutOfRange { value: isize },
+ /// An `isize` value couldn't fit into `32` bits.
+ IsizeOutOfRange(isize),
/// A non-zero integer encountered the value `0`.
NullInteger,
- /// A `usize` value couldn't fit into `16` bits.
- UsizeOutOfRange { value: usize },
+ /// A `usize` value couldn't fit into `32` bits.
+ UsizeOutOfRange(usize),
}
impl Display for Error {
@@ -83,28 +89,28 @@ impl Display for Error {
=> write!(f, "unable to parse utf8: \"{source}\""),
#[cfg(feature = "alloc")]
- CustomError { ref source }
+ CustomError(ref source)
=> write!(f, "{source}"),
EndOfStream { req, rem }
=> write!(f, "({req}) byte(s) were requested but only ({rem}) byte(s) were left"),
- InvalidBoolean { value }
+ InvalidBoolean(value)
=> write!(f, "expected boolean but got {value:#02X}"),
- InvalidCodePoint { value }
+ InvalidCodePoint(value)
=> write!(f, "code point U+{value:04X} is not valid"),
- InvalidDiscriminant { value }
+ InvalidDiscriminant(value)
=> write!(f, "discriminant ({value}) is not valid for the given enumeration"),
- IsizeOutOfRange { value }
+ IsizeOutOfRange(value)
=> write!(f, "signed size value ({value}) cannot be serialised: must be in the range ({}) to ({})", i16::MIN, i16::MAX),
NullInteger
=> write!(f, "expected non-zero integer but got (0)"),
- UsizeOutOfRange { value }
+ UsizeOutOfRange(value)
=> write!(f, "unsigned size value ({value}) cannot be serialised: must be at most ({})", u16::MAX),
}
}
@@ -118,7 +124,7 @@ impl core::error::Error for Error {
BadString { ref source } => Some(source),
#[cfg(feature = "alloc")]
- CustomError { ref source } => Some(source.as_ref()),
+ CustomError(ref source) => Some(source.as_ref()),
_ => None,
}
diff --git a/bzipper/src/fixed_iter/mod.rs b/bzipper/src/fixed_iter/mod.rs
deleted file mode 100644
index cc2110d..0000000
--- a/bzipper/src/fixed_iter/mod.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2024 Gabriel Bjørnager Jensen.
-//
-// This file is part of bzipper.
-//
-// bzipper is free software: you can redistribute
-// it and/or modify it under the terms of the GNU
-// Lesser General Public License as published by
-// the Free Software Foundation, either version 3
-// of the License, or (at your option) any later
-// version.
-//
-// bzipper is distributed in the hope that it will
-// be useful, but WITHOUT ANY WARRANTY; without
-// even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Less-
-// er General Public License along with bzipper. If
-// not, see <https://www.gnu.org/licenses/>.
-
-use core::mem::MaybeUninit;
-
-/// Iterator to a fixed vector.
-///
-/// This type is used by the [`FixedString`](crate::FixedString) type for iterating over an owned string.
-#[must_use]
-pub struct FixedIter<T, const N: usize> {
- pub(in crate) buf: [MaybeUninit<T>; N],
-
- pub(in crate) pos: usize,
- pub(in crate) len: usize,
-}
-
-impl<T, const N: usize> Iterator for FixedIter<T, N> {
- type Item = T;
-
- fn next(&mut self) -> Option<Self::Item> {
- if self.pos >= self.len { return None };
-
- let item = unsafe { self.buf[self.pos].assume_init_read() };
- self.pos += 0x1;
-
- Some(item)
- }
-}
diff --git a/bzipper/src/fixed_string/mod.rs b/bzipper/src/fixed_string/mod.rs
index 15c31aa..377937a 100644
--- a/bzipper/src/fixed_string/mod.rs
+++ b/bzipper/src/fixed_string/mod.rs
@@ -22,39 +22,55 @@
#[cfg(test)]
mod test;
-use crate::{Deserialise, Error, FixedIter, Serialise};
-
+use crate::{
+ Deserialise,
+ Dstream,
+ Error,
+ Serialise,
+ Sstream,
+};
+
+use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::fmt::{Debug, Display, Formatter};
-use core::mem::MaybeUninit;
-use core::ops::{Deref, DerefMut, Index, IndexMut};
+use core::hash::{Hash, Hasher};
+use core::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut};
use core::slice::SliceIndex;
-use core::str::FromStr;
+use core::str::{Chars, CharIndices, FromStr};
#[cfg(feature = "alloc")]
-use alloc::string::{String, ToString};
+use alloc::string::String;
+
+#[cfg(feature = "std")]
+use std::ffi::OsStr;
+
+#[cfg(feature = "std")]
+use std::net::ToSocketAddrs;
+
+#[cfg(feature = "std")]
+use std::path::Path;
-/// Owned string with maximum size.
+/// Heap-allocated string with maximum size.
///
/// This is in contrast to [String] -- which has no size limit in practice -- and [str], which is unsized.
///
-/// # Examples
+/// The string itself is encoded in UTF-8 for interoperability wtih Rust's standard string facilities, as well as for memory concerns.
///
-/// All instances of this type have the same size if the value of `N` is also the same.
-/// This size can be found through
+/// Keep in mind that the size limit specified by `N` denotes *bytes* and not *characters* -- i.e. a value of `8` may translate to between two and eight characters, depending on their codepoints.
///
-/// `size_of::<char>() * N + size_of::<usize>()`.
+/// # Examples
///
+/// All instances of this type have the same size if the value of `N` is also the same.
/// Therefore, the following four strings have -- despite their different contents -- the same total size.
///
-/// ```
+/// ```rust
/// use bzipper::FixedString;
/// use std::str::FromStr;
///
-/// let str0 = FixedString::<0xF>::new(); // Empty string.
-/// let str1 = FixedString::<0xF>::from_str("Hello there!");
-/// let str2 = FixedString::<0xF>::from_str("أنا من أوروپا");
-/// let str3 = FixedString::<0xF>::from_str("COGITO ERGO SUM");
+/// let str0 = FixedString::<0x40>::new(); // Empty string.
+/// let str1 = FixedString::<0x40>::from_str("Hello there!").unwrap();
+/// let str2 = FixedString::<0x40>::from_str("أنا من أوروپا").unwrap();
+/// let str3 = FixedString::<0x40>::from_str("COGITO ERGO SUM").unwrap();
///
/// assert_eq!(size_of_val(&str0), size_of_val(&str1));
/// assert_eq!(size_of_val(&str0), size_of_val(&str2));
@@ -64,10 +80,10 @@ use alloc::string::{String, ToString};
/// assert_eq!(size_of_val(&str2), size_of_val(&str3));
/// ```
///
-/// These three strings can---by extend in theory---also interchange their contents between each other.
-#[derive(Clone, Deserialise, Serialise)]
+/// These three strings can -- by extend in theory -- also interchange their contents between each other.
+#[derive(Clone)]
pub struct FixedString<const N: usize> {
- buf: [char; N],
+ buf: [u8; N],
len: usize,
}
@@ -81,62 +97,78 @@ impl<const N: usize> FixedString<N> {
/// The constructed string will have a null length.
/// All characters inside the internal buffer are instanced as `U+0000 NULL`.
///
- /// For constructing a string with an already defined buffer, see [`from_chars`](Self::from_chars) and [`from_raw_parts`](Self::from_raw_parts).
+ /// For constructing a string with an already defined buffer, see [`from_raw_parts`](Self::from_raw_parts) and [`from_str`](Self::from_str).
#[inline(always)]
#[must_use]
- pub const fn new() -> Self { Self { buf: ['\0'; N], len: 0x0 } }
+ pub const fn new() -> Self { Self { buf: [0x00; N], len: 0x0 } }
- /// Consumes the buffer into a fixed string.
+ /// Constructs a new, fixed-size string from raw parts.
///
- /// The internal length is to `N`.
- /// For a similar function but with an explicit size, see [`from_raw_parts`](Self::from_raw_parts).
- #[inline(always)]
- #[must_use]
- pub const fn from_chars(buf: [char; N]) -> Self { Self { buf, len: N } }
-
- /// Constructs a fixed string from raw parts.
+ /// The provided parts are not tested in any way.
+ ///
+ /// # Safety
+ ///
+ /// The value of `len` may not exceed that of `N`.
+ /// Additionally, the octets in `buf` (from index zero up to the value of `len`) must be valid UTF-8 codepoints.
+ ///
+ /// If any of these requirements are violated, behaviour is undefined.
#[inline(always)]
#[must_use]
- pub const fn from_raw_parts(buf: [char; N], len: usize) -> Self { Self { buf, len } }
+ pub const unsafe fn from_raw_parts(buf: [u8; N], len: usize) -> Self { Self { buf, len } }
- /// Deconstructs a fixed string into its raw parts.
+ /// Destructs the provided string into its raw parts.
+ ///
+ /// The returned values are valid to pass on to [`from_raw_parts`](Self::from_raw_parts).
+ ///
+ /// The returned byte array is guaranteed to be fully initialised.
+ /// However, only octets up to an index of [`len`](Self::len) are also guaranteed to be valid UTF-8 codepoints.
#[inline(always)]
#[must_use]
- pub const fn into_raw_parts(self) -> ([char; N], usize) { (self.buf, self.len) }
+ pub const fn into_raw_parts(self) -> ([u8; N], usize) { (self.buf, self.len) }
- /// Gets a pointer to the first character.
+ /// Gets a pointer to the first octet.
#[inline(always)]
#[must_use]
- pub const fn as_ptr(&self) -> *const char { self.buf.as_ptr() }
+ pub const fn as_ptr(&self) -> *const u8 { 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 for more information.
+ /// Gets a mutable pointer to the first octet.
///
- /// 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() }
+ pub fn as_mut_ptr(&mut self) -> *mut u8 { self.buf.as_mut_ptr() }
- /// Borrows the string as a character slice.
+ /// Borrows the string as a byte slice.
///
/// The range of the returned slice only includes characters that are "used."
- /// For borrowing the entire internal buffer, see [`as_mut_slice`](Self::as_mut_slice).
#[inline(always)]
#[must_use]
- pub const fn as_slice(&self) -> &[char] {
+ pub const fn as_bytes(&self) -> &[u8] {
// We need to use `from_raw_parts` to mark this
// function `const`.
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
- /// Mutably borrows the string as a character slice.
+ /// Borrows the string as a string slice.
+ ///
+ /// The range of the returned slice only includes characters that are "used."
+ #[inline(always)]
+ #[must_use]
+ pub const fn as_str(&self) -> &str { unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } }
+
+ /// Mutably borrows the string as a string slice.
///
- /// The range of the returned slice includes the entire internal buffer.
- /// For borrowing only the "used" characters, see [`as_slice`](Self::as_slice).
+ /// The range of the returned slice only includes characters that are "used."
#[inline(always)]
#[must_use]
- pub fn as_mut_slice(&mut self) -> &mut [char] { &mut self.buf[0x0..self.len] }
+ pub fn as_mut_str(&mut self) -> &mut str {
+ let range = 0x0..self.len();
+
+ unsafe { core::str::from_utf8_unchecked_mut(&mut self.buf[range]) }
+ }
/// Returns the length of the string.
///
@@ -145,42 +177,67 @@ impl<const N: usize> FixedString<N> {
#[must_use]
pub const fn len(&self) -> usize { self.len }
- /// Checks if the string is empty, i.e. `self.len() == 0x0`.
+ /// Checks if the string is empty, i.e. no characters are contained.
#[inline(always)]
#[must_use]
pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
- /// Checks if the string is full, i.e. `self.len() == N`.
+ /// Checks if the string is full, i.e. it cannot hold any more characters.
#[inline(always)]
#[must_use]
pub const fn is_full(&self) -> bool { self.len() == N }
- /// Sets the internal length.
+ /// Returns the total capacity of the string.
///
- /// The length is compared with `N` to guarantee that bounds are honoured.
+ /// This is defined as being exactly the value of `N`.
+ #[inline(always)]
+ #[must_use]
+ pub const fn capacity(&self) -> usize { N }
+
+ /// Gets a substring of the string.
+ #[inline(always)]
+ #[must_use]
+ pub fn get<I: SliceIndex<str>>(&self, index: I) -> Option<&I::Output> { self.as_str().get(index) }
+
+ /// Gets a mutable substring of the string.
+ #[inline(always)]
+ #[must_use]
+ pub fn get_mut<I: SliceIndex<str>>(&mut self, index: I) -> Option<&mut I::Output> { self.as_mut_str().get_mut(index) }
+
+ /// Pushes a character into the string.
+ ///
+ /// The internal length is updated accordingly.
///
/// # Panics
///
- /// This method panics if the value of `len` is greater than that of `N`.
+ /// If the string cannot hold the provided character *after* encoding, this method will panic.
#[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;
+ pub fn push(&mut self, c: char) {
+ let mut buf = [0x00; 0x4];
+ let s = c.encode_utf8(&mut buf);
+
+ self.push_str(s);
}
- /// Pushes a character into the string.
+ /// Pushes a string slice 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.
+ /// If the string cannot hold the provided slice, this method will panic.
#[inline(always)]
- pub fn push(&mut self, c: char) {
- assert!(!self.is_full(), "cannot push character to full string");
+ pub fn push_str(&mut self, s: &str) {
+ let rem = self.buf.len() - self.len;
+ let req = s.len();
+
+ assert!(rem >= req, "cannot push string beyond fixed length");
- self.buf[self.len] = c;
- self.len += 0x1;
+ let start = self.len;
+ let stop = start + req;
+
+ let buf = &mut self.buf[start..stop];
+ buf.copy_from_slice(s.as_bytes());
}
/// Pops a character from the string.
@@ -188,38 +245,76 @@ impl<const N: usize> FixedString<N> {
/// The internal length is updated accordingly.
///
/// If no characters are left (i.e. the string is empty), an instance of [`None`] is returned.
+ ///
+ /// **Note that this method is currently unimplemented.**
+ #[deprecated = "temporarily unimplemented"]
#[inline(always)]
- pub fn pop(&mut self) -> Option<char> {
- self.len
- .checked_sub(0x1)
- .map(|len| {
- let c = self.buf[self.len];
- self.len = len;
+ pub fn pop(&mut self) -> Option<char> { todo!() }
- c
- })
+ /// Returns an iterator of the string's characters.
+ #[inline(always)]
+ pub fn chars(&self) -> Chars { self.as_str().chars() }
+
+ /// Returns an iterator of the string's characters along with their positions.
+ #[inline(always)]
+ pub fn char_indices(&self) -> CharIndices { self.as_str().char_indices() }
+}
+
+impl<const N: usize> Add<&str> for FixedString<N> {
+ type Output = Self;
+
+ fn add(mut self, rhs: &str) -> Self::Output {
+ self.push_str(rhs);
+ self
}
}
-impl<const N: usize> AsMut<[char]> for FixedString<N> {
+impl<const N: usize> AddAssign<&str> for FixedString<N> {
+ fn add_assign(&mut self, rhs: &str) { self.push_str(rhs) }
+}
+
+impl<const N: usize> AsMut<str> for FixedString<N> {
+ #[inline(always)]
+ fn as_mut(&mut self) -> &mut str { self.as_mut_str() }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(doc, doc(cfg(feature = "std")))]
+impl<const N: usize> AsRef<OsStr> for FixedString<N> {
+ #[inline(always)]
+ fn as_ref(&self) -> &OsStr { self.as_str().as_ref() }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(doc, doc(cfg(feature = "std")))]
+impl<const N: usize> AsRef<Path> for FixedString<N> {
+ #[inline(always)]
+ fn as_ref(&self) -> &Path { self.as_str().as_ref() }
+}
+
+impl<const N: usize> AsRef<str> for FixedString<N> {
+ #[inline(always)]
+ fn as_ref(&self) -> &str { self.as_str() }
+}
+
+impl<const N: usize> AsRef<[u8]> for FixedString<N> {
+ #[inline(always)]
+ fn as_ref(&self) -> &[u8] { self.as_bytes() }
+}
+
+impl<const N: usize> Borrow<str> for FixedString<N> {
#[inline(always)]
- fn as_mut(&mut self) -> &mut [char] { self.as_mut_slice() }
+ fn borrow(&self) -> &str { self.as_str() }
}
-impl<const N: usize> AsRef<[char]> for FixedString<N> {
+impl<const N: usize> BorrowMut<str> for FixedString<N> {
#[inline(always)]
- fn as_ref(&self) -> &[char] { self.as_slice() }
+ fn borrow_mut(&mut self) -> &mut str { self.as_mut_str() }
}
impl<const N: usize> Debug for FixedString<N> {
#[inline]
- fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
- write!(f, "\"")?;
- for c in self { write!(f, "{}", c.escape_debug())? }
- write!(f, "\"")?;
-
- Ok(())
- }
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_str(), f) }
}
impl<const N: usize> Default for FixedString<N> {
@@ -227,159 +322,129 @@ impl<const N: usize> Default for FixedString<N> {
fn default() -> Self { Self { buf: [Default::default(); N], len: 0x0 } }
}
-/// See [`as_slice`](Self::as_slice).
impl<const N: usize> Deref for FixedString<N> {
- type Target = [char];
+ type Target = str;
#[inline(always)]
- fn deref(&self) -> &Self::Target { self.as_slice() }
+ fn deref(&self) -> &Self::Target { self.as_str() }
}
-/// See [`as_mut_slice`](Self::as_mut_slice).
impl<const N: usize> DerefMut for FixedString<N> {
#[inline(always)]
- fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_slice() }
+ fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_str() }
}
-impl<const N: usize> Display for FixedString<N> {
+impl<const N: usize> Deserialise for FixedString<N> {
#[inline]
- fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
- for c in self { write!(f, "{c}")? }
+ fn deserialise(stream: &Dstream) -> Result<Self, Error> {
+ let len = Deserialise::deserialise(stream)?;
+ if len > N { return Err(Error::ArrayTooShort { req: len, len: N }) };
- Ok(())
+ let bytes = stream.read(len)?;
+
+ let s = core::str::from_utf8(bytes)
+ .map_err(|e| Error::BadString { source: e })?;
+
+ Self::from_str(s)
}
}
-impl<const N: usize> 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> Display for FixedString<N> {
+ #[inline]
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Display::fmt(self.as_str(), f) }
}
+impl<const N: usize> Eq for FixedString<N> { }
+
impl<const N: usize> FromStr for FixedString<N> {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let mut buf = [Default::default(); N];
- let len = s.chars().count();
+ let len = s.len();
+ if len > N { return Err(Error::ArrayTooShort { req: len, len: N }) };
- for (i, c) in s.chars().enumerate() {
- if i >= N { return Err(Error::ArrayTooShort { req: len, len: N }) }
+ let mut buf = [0x00; N];
+ unsafe { core::ptr::copy_nonoverlapping(s.as_ptr(), buf.as_mut_ptr(), len) };
- buf[i] = c;
- }
+ // The remaining bytes are already initialised to
+ // null.
Ok(Self { buf, len })
}
}
-impl<I: SliceIndex<[char]>, const N: usize> Index<I> for FixedString<N> {
- type Output = I::Output;
+impl<const N: usize> Hash for FixedString<N> {
+ #[inline(always)]
+ fn hash<H: Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
+}
+
+impl<I: SliceIndex<str>, const N: usize> Index<I> for FixedString<N> {
+ type Output = I::Output;
#[inline(always)]
fn index(&self, index: I) -> &Self::Output { self.get(index).unwrap() }
}
-impl<I: SliceIndex<[char]>, const N: usize> IndexMut<I> for FixedString<N> {
+impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for FixedString<N> {
#[inline(always)]
fn index_mut(&mut self, index: I) -> &mut Self::Output { self.get_mut(index).unwrap() }
}
-impl<const N: usize> IntoIterator for FixedString<N> {
- type Item = char;
-
- type IntoIter = FixedIter<char, N>;
-
+impl<const N: usize> Ord for FixedString<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,
- }
- }
+ fn cmp(&self, other: &Self) -> Ordering { self.as_str().cmp(other.as_str()) }
}
-impl<'a, const N: usize> IntoIterator for &'a FixedString<N> {
- type Item = &'a char;
-
- type IntoIter = core::slice::Iter<'a, char>;
-
+impl<const N: usize, const M: usize> PartialEq<FixedString<M>> for FixedString<N> {
#[inline(always)]
- fn into_iter(self) -> Self::IntoIter { self.iter() }
+ fn eq(&self, other: &FixedString<M>) -> bool { self.as_str() == other.as_str() }
}
-impl<'a, const N: usize> IntoIterator for &'a mut FixedString<N> {
- type Item = &'a mut char;
-
- type IntoIter = core::slice::IterMut<'a, char>;
-
+impl<const N: usize> PartialEq<&str> for FixedString<N> {
#[inline(always)]
- fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
+ fn eq(&self, other: &&str) -> bool { self.as_str() == *other }
}
-impl<const N: usize> Ord for FixedString<N> {
+impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<N> {
#[inline(always)]
- fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() }
+ fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> { self.as_str().partial_cmp(other.as_str()) }
}
-impl<const N: usize, const M: usize> PartialEq<FixedString<M>> for FixedString<N> {
+impl<const N: usize> PartialOrd<&str> for FixedString<N> {
#[inline(always)]
- fn eq(&self, other: &FixedString<M>) -> bool { self.as_slice() == other.as_slice() }
+ fn partial_cmp(&self, other: &&str) -> Option<Ordering> { self.as_str().partial_cmp(*other) }
}
-impl<const N: usize> PartialEq<&[char]> for FixedString<N> {
- #[inline(always)]
- fn eq(&self, other: &&[char]) -> bool { self.as_slice() == *other }
-}
+impl<const N: usize> Serialise for FixedString<N> {
+ const MAX_SERIALISED_SIZE: usize = N + usize::MAX_SERIALISED_SIZE;
-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 };
- }
+ fn serialise(&self, stream: &mut Sstream) -> Result<(), Error> {
+ self.len().serialise(stream)?;
+ stream.write(self.as_bytes())?;
- true
+ Ok(())
}
}
-impl<const N: usize, const M: usize> PartialOrd<FixedString<M>> for FixedString<N> {
- #[inline(always)]
- fn partial_cmp(&self, other: &FixedString<M>) -> Option<Ordering> { self.partial_cmp(&other.as_slice()) }
-}
+#[cfg(feature = "std")]
+#[cfg_attr(doc, doc(cfg(feature = "std")))]
+impl<const N: usize> ToSocketAddrs for FixedString<N> {
+ type Iter = <str as ToSocketAddrs>::Iter;
-impl<const N: usize> PartialOrd<&[char]> for FixedString<N> {
#[inline(always)]
- fn partial_cmp(&self, other: &&[char]) -> Option<Ordering> { self.as_slice().partial_cmp(other) }
+ fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> { self.as_str().to_socket_addrs() }
}
-impl<const N: usize> PartialOrd<&str> for FixedString<N> {
- #[inline]
- fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
- let llen = self.len();
- let rlen = other.chars().count();
-
- match llen.cmp(&rlen) {
- Ordering::Equal => {},
-
- ordering => return Some(ordering),
- };
-
- for (i, rc) in other.chars().enumerate() {
- let lc = self[i];
-
- match lc.cmp(&rc) {
- Ordering::Equal => {},
+impl<const N: usize> TryFrom<char> for FixedString<N> {
+ type Error = <Self as FromStr>::Err;
- ordering => return Some(ordering),
- }
- }
+ #[inline(always)]
+ fn try_from(value: char) -> Result<Self, Self::Error> {
+ let mut buf = [0x00; 0x4];
+ let s = value.encode_utf8(&mut buf);
- Some(Ordering::Equal)
+ s.parse()
}
}
@@ -391,6 +456,7 @@ impl<const N: usize> TryFrom<&str> for FixedString<N> {
}
#[cfg(feature = "alloc")]
+#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<const N: usize> TryFrom<String> for FixedString<N> {
type Error = <Self as FromStr>::Err;
@@ -398,8 +464,17 @@ impl<const N: usize> TryFrom<String> for FixedString<N> {
fn try_from(value: String) -> Result<Self, Self::Error> { Self::from_str(&value) }
}
+/// Converts the fixed-size string into a dynamic string.
+///
+/// The capacity of the resulting [`String`] object is equal to the value of `N`.
#[cfg(feature = "alloc")]
+#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<const N: usize> From<FixedString<N>> for String {
#[inline(always)]
- fn from(value: FixedString<N>) -> Self { value.to_string() }
+ fn from(value: FixedString<N>) -> Self {
+ let mut s = Self::with_capacity(N);
+ s.push_str(value.as_str());
+
+ s
+ }
}
diff --git a/bzipper/src/fixed_string/test.rs b/bzipper/src/fixed_string/test.rs
index c86a944..09f4b39 100644
--- a/bzipper/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>::try_from("Hello there!").unwrap();
- let str1 = FixedString::<0xE>::try_from("MEIN_GRO\u{1E9E}_GOTT").unwrap();
- let str2 = FixedString::<0x5>::try_from("Hello").unwrap();
+ let str0 = FixedString::<0x0C>::try_from("Hello there!").unwrap();
+ let str1 = FixedString::<0x12>::try_from("MEIN_GRO\u{1E9E}_GOTT").unwrap();
+ let str2 = FixedString::<0x05>::try_from("Hello").unwrap();
assert_eq!(str0.partial_cmp(&str0), Some(Ordering::Equal));
assert_eq!(str0.partial_cmp(&str1), Some(Ordering::Less));
diff --git a/bzipper/src/lib.rs b/bzipper/src/lib.rs
index dd64693..c505f50 100644
--- a/bzipper/src/lib.rs
+++ b/bzipper/src/lib.rs
@@ -23,7 +23,7 @@
//! Binary (de)serialisation.
//!
-//! Contrary to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the goal of bzipper is to serialise with a known size constraint.
+//! In contrast to [Serde](https://crates.io/crates/serde/)/[Bincode](https://crates.io/crates/bincode/), the primary goal of bzipper is to serialise with a known size constraint.
//! Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.
//!
//! Keep in mind that this project is still work-in-progress.
@@ -41,15 +41,15 @@
//!
//! # Usage
//!
-//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which are commonly used in conjunction with streams (more specifically, [s-streams](Sstream) and [d-streams](Dstream)).
+//! This crate revolves around the [`Serialise`] and [`Deserialise`] traits, both of which use *streams* -- or more specifically -- [s-streams](Sstream) and [d-streams](Dstream).
//!
//! Many core types come implemented with bzipper, including primitives as well as some standard library types such as [`Option`] and [`Result`](core::result::Result).
//!
-//! It is recommended in most cases to just derive these traits for custom types (enumerations and structures only).
-//! Here, each field is chained in declaration order:
+//! It is recommended in most cases to just derive these two traits for custom types (although this is only supported with enumerations and structures).
+//! Here, each field is *chained* according to declaration order:
//!
//! ```
-//! use bzipper::{Deserialise, Serialise};
+//! use bzipper::{Buffer, Deserialise, Serialise};
//!
//! #[derive(Debug, Deserialise, PartialEq, Serialise)]
//! struct IoRegister {
@@ -57,45 +57,55 @@
//! value: u16,
//! }
//!
-//! let mut buf: [u8; IoRegister::SERIALISED_SIZE] = Default::default();
-//! IoRegister { addr: 0x04000000, value: 0x0402 }.serialise(&mut buf).unwrap();
+//! let mut buf = Buffer::new();
//!
+//! buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
+//!
+//! assert_eq!(buf.len(), 0x6);
//! assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02]);
//!
-//! assert_eq!(IoRegister::deserialise(&buf).unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
+//! assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
//! ```
//!
//! ## Serialisation
//!
-//! To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation.
-//! The required size of any given serialisation is specified by the [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) constant:
+//! To serialise an object implementing `Serialise`, simply allocate a buffer for the serialisation and wrap it in an s-stream (*serialisation stream*) with the [`Sstream`] type.
//!
//! ```
-//! use bzipper::Serialise;
+//! use bzipper::{Serialise, Sstream};
+//!
+//! let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE];
+//! let mut stream = Sstream::new(&mut buf);
//!
-//! let mut buf: [u8; char::SERIALISED_SIZE] = Default::default();
-//! 'Ж'.serialise(&mut buf).unwrap();
+//! 'Ж'.serialise(&mut stream).unwrap();
//!
-//! assert_eq!(buf, [0x00, 0x00, 0x04, 0x16]);
+//! assert_eq!(stream, [0x00, 0x00, 0x04, 0x16]);
//! ```
//!
-//! The only special requirement of the [`serialise`](Serialise::serialise) method is that the provided byte slice has an element count of exactly `SERIALISED_SIZE`.
+//! The maximum size of any given serialisation is specified by the [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) constant.
//!
-//! We can also use streams to *chain* multiple elements together.
+//! We can also use streams to chain multiple elements together:
//!
//! ```
-//! use bzipper::Serialise;
+//! use bzipper::{Serialise, Sstream};
//!
-//! let mut buf: [u8; char::SERIALISED_SIZE * 5] = Default::default();
-//! let mut stream = bzipper::Sstream::new(&mut buf);
+//! let mut buf = [Default::default(); char::MAX_SERIALISED_SIZE * 0x5];
+//! let mut stream = Sstream::new(&mut buf);
//!
-//! stream.append(&'ل');
-//! stream.append(&'ا');
-//! stream.append(&'م');
-//! stream.append(&'د');
-//! stream.append(&'ا');
+//! // Note: For serialising multiple characters, the
+//! // `FixedString` type is usually preferred.
//!
-//! assert_eq!(buf, [0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27, 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F, 0x00, 0x00, 0x06, 0x27]);
+//! 'ل'.serialise(&mut stream).unwrap();
+//! 'ا'.serialise(&mut stream).unwrap();
+//! 'م'.serialise(&mut stream).unwrap();
+//! 'د'.serialise(&mut stream).unwrap();
+//! 'ا'.serialise(&mut stream).unwrap();
+//!
+//! assert_eq!(buf, [
+//! 0x00, 0x00, 0x06, 0x44, 0x00, 0x00, 0x06, 0x27,
+//! 0x00, 0x00, 0x06, 0x45, 0x00, 0x00, 0x06, 0x2F,
+//! 0x00, 0x00, 0x06, 0x27
+//! ]);
//! ```
//!
//! When serialising primitives, the resulting byte stream is in big endian (a.k.a. network endian).
@@ -103,27 +113,35 @@
//!
//! ## Deserialisation
//!
-//! Deserialisation works with an almost identical syntax to serialisation.
+//! Deserialisation works with a similar syntax to serialisation.
//!
-//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method:
+//! D-streams (*deserialisation streams*) use the [`Dstream`] type and are constructed in a manner similar to s-streams.
+//! To deserialise a buffer, simply call the [`deserialise`](Deserialise::deserialise) method with the strema:
//!
//! ```
-//! use bzipper::Deserialise;
+//! use bzipper::{Deserialise, Dstream};
//!
//! let data = [0x45, 0x54];
-//! assert_eq!(<u16>::deserialise(&data).unwrap(), 0x4554);
+//! let stream = Dstream::new(&data);
+//! assert_eq!(u16::deserialise(&stream).unwrap(), 0x4554);
//! ```
//!
-//! Just like with serialisations, the [`Dstream`] can be used to deserialise chained elements:
+//! And just like s-streams, d-streams can also be used to handle chaining:
//!
//! ```
-//! use bzipper::Deserialise;
+//! use bzipper::{Deserialise, Dstream};
//!
//! let data = [0x45, 0x54];
-//! let stream = bzipper::Dstream::new(&data);
+//! let stream = Dstream::new(&data);
+//!
+//! assert_eq!(u8::deserialise(&stream).unwrap(), 0x45);
+//! assert_eq!(u8::deserialise(&stream).unwrap(), 0x54);
//!
-//! assert_eq!(stream.take::<u8>().unwrap(), 0x45);
-//! assert_eq!(stream.take::<u8>().unwrap(), 0x54);
+//! // The data can also be deserialised as a tuple (up
+//! // to twelve elements).
+//!
+//! let stream = Dstream::new(&data);
+//! assert_eq!(<(u8, u8)>::deserialise(&stream).unwrap(), (0x45, 0x54));
//! ```
#![no_std]
@@ -139,8 +157,6 @@ extern crate alloc;
extern crate std;
/// Implements [`Deserialise`] for the provided type.
-///
-/// This macro assumes that `Serialise` was also derived, although this is not strictly required as it is unenforceable.
#[doc(inline)]
pub use bzipper_macros::Deserialise;
@@ -151,7 +167,7 @@ pub use bzipper_macros::Deserialise;
/// For structures, each element is chained in **order of declaration.**
/// For example, the following struct will serialise its field `foo` before `bar`:
///
-/// ```
+/// ```rust
/// use bzipper::Serialise;
///
/// #[derive(Serialise)]
@@ -161,9 +177,9 @@ pub use bzipper_macros::Deserialise;
/// }
/// ```
///
-/// Should the order of declaration change, then most of---if not all---previous dervied serialisations become void.
+/// Should the structure's declaration change, then all previous derived serialisations be considered void.
///
-/// The value of [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) is set to the combined value of all fields.
+/// The value of [`MAX_SERIALISED_SIZE`](Serialise::MAX_SERIALISED_SIZE) is set to the combined value of all fields.
///
/// If the structure is a unit structure (i.e. it has *no* fields), it is serialised equivalently to the [unit] type.
///
@@ -176,12 +192,12 @@ pub use bzipper_macros::Deserialise;
/// Variants with fields are serialised exactly like structures.
/// That is, each field is chained in order of declaration.
///
-/// Each variant has its own serialised size, and the largest of these values is chosen as the serialised size of the enumeration type.
+/// Each variant has its own value of `MAX_SERIALISED_SIZE`, and the largest of these values is chosen as the value of the enumeration's own `MAX_SERIALISED_SIZE`.
///
/// # Unions
///
/// Unions cannot derive `Serialise` due to the uncertainty of their contents.
-/// The trait should therefore be implemented manually.
+/// The trait should therefore be implemented manually for such types.
#[doc(inline)]
pub use bzipper_macros::Serialise;
@@ -196,7 +212,6 @@ pub(in crate) use use_mod;
use_mod!(pub deserialise);
use_mod!(pub dstream);
use_mod!(pub error);
-use_mod!(pub fixed_iter);
use_mod!(pub fixed_string);
use_mod!(pub serialise);
use_mod!(pub sstream);
diff --git a/bzipper/src/serialise/mod.rs b/bzipper/src/serialise/mod.rs
index 9c89e09..b22d68e 100644
--- a/bzipper/src/serialise/mod.rs
+++ b/bzipper/src/serialise/mod.rs
@@ -24,16 +24,20 @@ mod test;
use crate::{Error, Result, Sstream};
-use core::{convert::Infallible, marker::PhantomData};
+use core::{convert::Infallible, hint::unreachable_unchecked, marker::PhantomData};
mod tuple;
-/// Denotes a type capable of being serialised.
+/// Denotes a type capable of serialisation.
///
/// It is recommended to simply derive this trait for custom types.
-/// It can, however, be manually implemented:
+/// It can, however, also be manually implemented:
+///
+/// ```rust
+/// // Manual implementation of custom type. This im-
+/// // plementation is equivalent to what would have
+/// // been derived.
///
-/// ```
/// use bzipper::{Result, Serialise, Sstream};
///
/// struct Foo {
@@ -42,60 +46,46 @@ mod tuple;
/// }
///
/// impl Serialise for Foo {
-/// const SERIALISED_SIZE: usize = u16::SERIALISED_SIZE + f32::SERIALISED_SIZE;
-///
-/// fn serialise(&self, buf: &mut [u8]) -> Result<()> {
-/// debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+/// const MAX_SERIALISED_SIZE: usize = u16::MAX_SERIALISED_SIZE + f32::MAX_SERIALISED_SIZE;
///
+/// fn serialise(&self, stream: &mut Sstream) -> Result<()> {
/// // Serialise fields using chaining.
///
-/// let mut stream = Sstream::new(buf);
-///
-/// stream.append(&self.bar)?;
-/// stream.append(&self.baz)?;
+/// self.bar.serialise(stream)?;
+/// self.baz.serialise(stream)?;
///
/// Ok(())
/// }
/// }
/// ```
///
-/// Implementors of this trait should make sure that [`SERIALISED_SIZE`](Serialise::SERIALISED_SIZE) is properly defined.
-/// This value indicates the definitive size of any serialisation of the `Self` type.
+/// Implementors of this trait should make sure that [`MAX_SERIALISED_SIZE`](Self::MAX_SERIALISED_SIZE) is properly defined.
+/// This value indicates the definitively largest size of any serialisation of `Self`.
pub trait Serialise: Sized {
- /// The amount of bytes that result from a serialisation.
+ /// The maximum amount of bytes that can result from a serialisation.
///
/// Implementors of this trait should make sure that no serialisation (or deserialisation) uses more than the amount specified by this constant.
- /// When using these traits, always assume that exactly this amount has or will be used.
- const SERIALISED_SIZE: usize;
+ const MAX_SERIALISED_SIZE: usize;
- /// Serialises `self` into a slice.
+ /// Serialises `self` into the given s-stream.
///
- /// In most cases it is wiser to chain serialisations using [`Sstream`] instead of using this method directly.
+ /// This method must **never** write more bytes than specified by [`MAX_SERIALISED_SIZE`](Self::MAX_SERIALISED_SIZE).
+ /// Doing so is considered a logic error.
///
/// # Errors
///
- /// If serialisation failed, e.g. by an unencodable value being provided, an error is returned.
- ///
- /// # Panics
- ///
- /// This method will usually panic if the provided slice has a length *less* than the value of `SERIALISED_SIZE`.
- /// Official implementations of this trait (including those that are derived) always panic in debug mode if the provided slice has a length that is different at all.
- fn serialise(&self, buf: &mut [u8]) -> Result<()>;
+ /// If serialisation fails, e.g. by an unencodable value being provided, an error is returned.
+ fn serialise(&self, stream: &mut Sstream) -> Result<()>;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Serialise for $ty {
- const SERIALISED_SIZE: usize = size_of::<$ty>();
+ const MAX_SERIALISED_SIZE: usize = size_of::<$ty>();
#[inline]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let data = self.to_be_bytes();
- buf.copy_from_slice(&data);
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ stream.write(&self.to_be_bytes())?;
Ok(())
}
@@ -106,101 +96,82 @@ macro_rules! impl_numeric {
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Serialise for ::core::num::NonZero<$ty> {
- const SERIALISED_SIZE: usize = ::core::mem::size_of::<$ty>();
+ const MAX_SERIALISED_SIZE: usize = ::core::mem::size_of::<$ty>();
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- self.get().serialise(buf)
- }
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> { self.get().serialise(stream) }
}
};
}
impl<T: Serialise, const N: usize> Serialise for [T; N] {
- const SERIALISED_SIZE: usize = T::SERIALISED_SIZE * N;
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+ const MAX_SERIALISED_SIZE: usize = T::MAX_SERIALISED_SIZE * N;
- let mut stream = Sstream::new(buf);
-
- for v in self { stream.append(v)? }
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ for v in self { v.serialise(stream)? }
Ok(())
}
}
impl Serialise for bool {
- const SERIALISED_SIZE: usize = u8::SERIALISED_SIZE;
+ const MAX_SERIALISED_SIZE: usize = u8::MAX_SERIALISED_SIZE;
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- u8::from(*self).serialise(buf)
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ u8::from(*self).serialise(stream)
}
}
impl Serialise for char {
- const SERIALISED_SIZE: usize = u32::SERIALISED_SIZE;
+ const MAX_SERIALISED_SIZE: usize = u32::MAX_SERIALISED_SIZE;
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- u32::from(*self).serialise(buf)
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ u32::from(*self).serialise(stream)
}
}
// Especially useful for `Result<T, Infallible>`.
-// *If* that is needed, of course.
+// *If* that is even needed, of course.
impl Serialise for Infallible {
- const SERIALISED_SIZE: usize = 0x0;
+ const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
- fn serialise(&self, _buf: &mut [u8]) -> Result<()> { unreachable!() }
+ fn serialise(&self, _stream: &mut Sstream) -> Result<()> { unsafe { unreachable_unchecked() } }
}
impl Serialise for isize {
- const SERIALISED_SIZE: usize = i32::SERIALISED_SIZE;
+ const MAX_SERIALISED_SIZE: usize = i32::MAX_SERIALISED_SIZE;
#[inline]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
let value = i32::try_from(*self)
- .map_err(|_| Error::IsizeOutOfRange { value: *self })?;
+ .map_err(|_| Error::IsizeOutOfRange(*self))?;
- value.serialise(buf)
+ value.serialise(stream)
}
}
impl<T: Serialise> Serialise for Option<T> {
- const SERIALISED_SIZE: usize = bool::SERIALISED_SIZE + T::SERIALISED_SIZE;
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+ const MAX_SERIALISED_SIZE: usize = bool::MAX_SERIALISED_SIZE + T::MAX_SERIALISED_SIZE;
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
// The first element is of type `bool` and is
// called the "sign." It signifies whether there is
- // a following element or not. The remaining bytes
- // are preserved if `self` is `None`.
-
- let mut stream = Sstream::new(buf);
+ // a following element or not.
match *self {
None => {
- stream.append(&false)?;
+ false.serialise(stream)?;
// No need to zero-fill.
},
Some(ref v) => {
- stream.append(&true)?;
- stream.append(v)?;
+ true.serialise(stream)?;
+ v.serialise(stream)?;
},
};
@@ -209,37 +180,30 @@ impl<T: Serialise> Serialise for Option<T> {
}
impl<T> Serialise for PhantomData<T> {
- const SERIALISED_SIZE: usize = size_of::<Self>();
+ const MAX_SERIALISED_SIZE: usize = size_of::<Self>();
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- Ok(())
- }
+ fn serialise(&self, _stream: &mut Sstream) -> Result<()> { Ok(()) }
}
impl<T, E> Serialise for core::result::Result<T, E>
where
T: Serialise,
E: Serialise, {
- const SERIALISED_SIZE: usize = bool::SERIALISED_SIZE + if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let mut stream = Sstream::new(buf);
+ const MAX_SERIALISED_SIZE: usize = bool::MAX_SERIALISED_SIZE + if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
// Remember the descriminant.
+
match *self {
Ok(ref v) => {
- stream.append(&false)?;
- stream.append(v)?;
+ false.serialise(stream)?;
+ v.serialise(stream)?;
},
Err(ref e) => {
- stream.append(&true)?;
- stream.append(e)?;
+ true.serialise(stream)?;
+ e.serialise(stream)?;
},
};
@@ -248,26 +212,20 @@ where
}
impl Serialise for () {
- const SERIALISED_SIZE: usize = size_of::<Self>();
+ const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- Ok(())
- }
+ fn serialise(&self, _stream: &mut Sstream) -> Result<()> { Ok(()) }
}
impl Serialise for usize {
- const SERIALISED_SIZE: Self = u32::SERIALISED_SIZE;
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+ const MAX_SERIALISED_SIZE: Self = u32::MAX_SERIALISED_SIZE;
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
let value = u32::try_from(*self)
- .map_err(|_| Error::UsizeOutOfRange { value: *self })?;
+ .map_err(|_| Error::UsizeOutOfRange(*self))?;
- value.serialise(buf)
+ value.serialise(stream)
}
}
diff --git a/bzipper/src/serialise/test.rs b/bzipper/src/serialise/test.rs
index f2332a5..2dee489 100644
--- a/bzipper/src/serialise/test.rs
+++ b/bzipper/src/serialise/test.rs
@@ -18,7 +18,7 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{FixedString, Serialise};
+use crate::{FixedString, Serialise, Sstream};
#[test]
fn test_serialise() {
@@ -32,19 +32,19 @@ fn test_serialise() {
Teacher { initials: [char; 0x3] },
}
- assert_eq!(Foo::SERIALISED_SIZE, 0x4);
- assert_eq!(Bar::SERIALISED_SIZE, 0x10);
+ assert_eq!(Foo::MAX_SERIALISED_SIZE, 0x4);
+ assert_eq!(Bar::MAX_SERIALISED_SIZE, 0x10);
macro_rules! test {
($ty:ty: $value:expr => $data:expr) => {{
use ::bzipper::Serialise;
- let data: [u8; <$ty as Serialise>::SERIALISED_SIZE] = $data;
+ let mut buf = [0x00; <$ty as Serialise>::MAX_SERIALISED_SIZE];
- let mut buf = [0x00; <$ty as Serialise>::SERIALISED_SIZE];
- <$ty as Serialise>::serialise(&mut $value, &mut buf).unwrap();
+ let mut stream = Sstream::new(&mut buf);
+ <$ty as Serialise>::serialise(&mut $value, &mut stream).unwrap();
- assert_eq!(buf, data);
+ assert_eq!(stream, $data);
}};
}
@@ -63,14 +63,11 @@ fn test_serialise() {
0x83, 0x2E, 0x3C, 0x2C, 0x84, 0x10, 0x58, 0x1A,
]);
- test!(FixedString::<0x1>: FixedString::try_from("A").unwrap() => [0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x01]);
+ test!(FixedString::<0x1>: FixedString::try_from("A").unwrap() => [0x00, 0x00, 0x00, 0x01, 0x41]);
- test!(FixedString::<0x9>: FixedString::try_from("l\u{00F8}gma\u{00F0}ur").unwrap() => [
- 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0xF8,
- 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x6D,
- 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0xF0,
- 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x72,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ test!(FixedString::<0x24>: FixedString::try_from("l\u{00F8}gma\u{00F0}ur").unwrap() => [
+ 0x00, 0x00, 0x00, 0x0A, 0x6C, 0xC3, 0xB8, 0x67,
+ 0x6D, 0x61, 0xC3, 0xB0, 0x75, 0x72,
]);
test!([char; 0x5]: ['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'] => [
@@ -79,7 +76,7 @@ fn test_serialise() {
0x00, 0x00, 0x03, 0xB1,
]);
- test!(Result::<u16, char>: Ok(0x45_45) => [0x00, 0x45, 0x45, 0x00, 0x00]);
+ test!(Result::<u16, char>: Ok(0x45_45) => [0x00, 0x45, 0x45]);
test!(Result::<u16, char>: Err(char::REPLACEMENT_CHARACTER) => [0x01, 0x00, 0x00, 0xFF, 0xFD]);
test!(Option<()>: None => [0x00]);
@@ -87,15 +84,9 @@ fn test_serialise() {
test!(Foo: Foo('\u{FDF2}') => [0x00, 0x00, 0xFD, 0xF2]);
- test!(Bar: Bar::Unit => [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ]);
+ test!(Bar: Bar::Unit => [0x00, 0x00, 0x00, 0x00]);
- test!(Bar: Bar::Pretty(true) => [
- 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ]);
+ test!(Bar: Bar::Pretty(true) => [0x00, 0x00, 0x00, 0x01, 0x01]);
test!(Bar: Bar::Teacher { initials: ['T', 'L', '\0'] } => [
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54,
diff --git a/bzipper/src/serialise/tuple.rs b/bzipper/src/serialise/tuple.rs
index feee2e2..f2332b8 100644
--- a/bzipper/src/serialise/tuple.rs
+++ b/bzipper/src/serialise/tuple.rs
@@ -24,15 +24,11 @@ use crate::{Result, Serialise, Sstream};
impl<T0> Serialise for (T0, )
where
T0: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE;
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let mut stream = Sstream::new(buf);
-
- stream.append(&self.0)?;
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
Ok(())
}
@@ -42,17 +38,13 @@ impl<T0, T1> Serialise for (T0, T1)
where
T0: Serialise,
T1: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE;
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE;
- let mut stream = Sstream::new(buf);
-
- stream.append(&self.0)?;
- stream.append(&self.1)?;
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
Ok(())
}
@@ -63,19 +55,15 @@ where
T0: Serialise,
T1: Serialise,
T2: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE;
-
- fn serialise(&self, buf: &mut [u8]) -> Result<()> {
- debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let mut stream = Sstream::new(buf);
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE;
- stream.append(&self.0)?;
- stream.append(&self.1)?;
- stream.append(&self.2)?;
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
Ok(())
}
@@ -87,21 +75,17 @@ where
T1: Serialise,
T2: Serialise,
T3: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
Ok(())
}
@@ -114,23 +98,19 @@ where
T2: Serialise,
T3: Serialise,
T4: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
Ok(())
}
@@ -144,25 +124,21 @@ where
T3: Serialise,
T4: Serialise,
T5: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
Ok(())
}
@@ -177,27 +153,23 @@ where
T4: Serialise,
T5: Serialise,
T6: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
Ok(())
}
@@ -213,29 +185,25 @@ where
T5: Serialise,
T6: Serialise,
T7: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE
- + T7::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE
+ + T7::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
+ self.7.serialise(stream)?;
Ok(())
}
@@ -252,31 +220,27 @@ where
T6: Serialise,
T7: Serialise,
T8: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE
- + T7::SERIALISED_SIZE
- + T8::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE
+ + T7::MAX_SERIALISED_SIZE
+ + T8::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
+ self.7.serialise(stream)?;
+ self.8.serialise(stream)?;
Ok(())
}
@@ -294,33 +258,29 @@ where
T7: Serialise,
T8: Serialise,
T9: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE
- + T7::SERIALISED_SIZE
- + T8::SERIALISED_SIZE
- + T9::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE
+ + T7::MAX_SERIALISED_SIZE
+ + T8::MAX_SERIALISED_SIZE
+ + T9::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
+ self.7.serialise(stream)?;
+ self.8.serialise(stream)?;
+ self.9.serialise(stream)?;
Ok(())
}
@@ -339,35 +299,31 @@ where
T8: Serialise,
T9: Serialise,
T10: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE
- + T7::SERIALISED_SIZE
- + T8::SERIALISED_SIZE
- + T9::SERIALISED_SIZE
- + T10::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE
+ + T7::MAX_SERIALISED_SIZE
+ + T8::MAX_SERIALISED_SIZE
+ + T9::MAX_SERIALISED_SIZE
+ + T10::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
+ self.7.serialise(stream)?;
+ self.8.serialise(stream)?;
+ self.9.serialise(stream)?;
+ self.10.serialise(stream)?;
Ok(())
}
@@ -387,37 +343,33 @@ where
T9: Serialise,
T10: Serialise,
T11: Serialise, {
- const SERIALISED_SIZE: usize =
- T0::SERIALISED_SIZE
- + T1::SERIALISED_SIZE
- + T2::SERIALISED_SIZE
- + T3::SERIALISED_SIZE
- + T4::SERIALISED_SIZE
- + T5::SERIALISED_SIZE
- + T6::SERIALISED_SIZE
- + T7::SERIALISED_SIZE
- + T8::SERIALISED_SIZE
- + T9::SERIALISED_SIZE
- + T10::SERIALISED_SIZE
- + T11::SERIALISED_SIZE;
-
- 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)?;
+ const MAX_SERIALISED_SIZE: usize =
+ T0::MAX_SERIALISED_SIZE
+ + T1::MAX_SERIALISED_SIZE
+ + T2::MAX_SERIALISED_SIZE
+ + T3::MAX_SERIALISED_SIZE
+ + T4::MAX_SERIALISED_SIZE
+ + T5::MAX_SERIALISED_SIZE
+ + T6::MAX_SERIALISED_SIZE
+ + T7::MAX_SERIALISED_SIZE
+ + T8::MAX_SERIALISED_SIZE
+ + T9::MAX_SERIALISED_SIZE
+ + T10::MAX_SERIALISED_SIZE
+ + T11::MAX_SERIALISED_SIZE;
+
+ fn serialise(&self, stream: &mut Sstream) -> Result<()> {
+ self.0.serialise(stream)?;
+ self.1.serialise(stream)?;
+ self.2.serialise(stream)?;
+ self.3.serialise(stream)?;
+ self.4.serialise(stream)?;
+ self.5.serialise(stream)?;
+ self.6.serialise(stream)?;
+ self.7.serialise(stream)?;
+ self.8.serialise(stream)?;
+ self.9.serialise(stream)?;
+ self.10.serialise(stream)?;
+ self.11.serialise(stream)?;
Ok(())
}
diff --git a/bzipper/src/sstream/mod.rs b/bzipper/src/sstream/mod.rs
index 257be95..470a27f 100644
--- a/bzipper/src/sstream/mod.rs
+++ b/bzipper/src/sstream/mod.rs
@@ -19,16 +19,17 @@
// er General Public License along with bzipper. If
// not, see <https://www.gnu.org/licenses/>.
-use crate::{Error, Result, Serialise};
+use crate::{Dstream, Error, Result};
use core::cell::Cell;
+use core::fmt::{Debug, Formatter};
-/// Byte stream for deserialisation.
+/// Byte stream suitable for serialisation.
///
-/// This type borrows a slice, keeping track internally of the used bytes.
+/// This type mutably borrows a buffer, keeping track internally of the used bytes.
pub struct Sstream<'a> {
- buf: &'a mut [u8],
- pos: Cell<usize>,
+ pub(in crate) buf: &'a mut [u8],
+ pub(in crate) pos: Cell<usize>,
}
impl<'a> Sstream<'a> {
@@ -37,22 +38,86 @@ impl<'a> Sstream<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self { Self { buf, pos: Cell::new(0x0) } }
- /// Extends the stream by appending a new serialisation.
- ///
- /// # Errors
- ///
- /// If the stream cannot hold any arbitrary serialisation of `T`, an [`EndOfStream`](Error::EndOfStream) instance is returned.
+ /// Appends raw bytes to the stream.
#[inline]
- pub fn append<T: Serialise>(&mut self, value: &T) -> Result<()> {
+ pub fn write(&mut self, bytes: &[u8]) -> Result<()> {
let rem = self.buf.len() - self.pos.get();
- let req = T::SERIALISED_SIZE;
+ let req = bytes.len();
- if rem < req { return Err(Error::EndOfStream { req, rem }) };
+ if rem < req { return Err(Error::EndOfStream { req, rem }) }
let start = self.pos.get();
let stop = start + req;
self.pos.set(stop);
- value.serialise(&mut self.buf[start..stop])
+
+ let buf = &mut self.buf[start..stop];
+ buf.copy_from_slice(bytes);
+
+ Ok(())
}
+
+ /// Gets a pointer to the first byte in the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() }
+
+ /// Gets an immutable slice of the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn as_slice(&self) -> &[u8] {
+ let ptr = self.as_ptr();
+ let len = self.len();
+
+ unsafe { core::slice::from_raw_parts(ptr, len) }
+ }
+
+ /// Gets the length of the stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn len(&self) -> usize { unsafe { self.pos.as_ptr().read() } }
+
+ /// Tests if the stream is empty.
+ ///
+ /// If no serialisations have been made so far, this method returns `false`.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_empty(&self) -> bool { self.len() == 0x0 }
+
+ /// Tests if the stream is full.
+ ///
+ /// Note that zero-sized types such as [`()`](unit) can still be serialised into this stream.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_full(&self) -> bool { self.len() == self.buf.len() }
+}
+
+impl Debug for Sstream<'_> {
+ #[inline(always)]
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { Debug::fmt(self.as_slice(), f) }
+}
+
+impl<'a> From<&'a mut [u8]> for Sstream<'a> {
+ #[inline(always)]
+ fn from(value: &'a mut [u8]) -> Self { Self::new(value) }
+}
+
+impl PartialEq for Sstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &Self) -> bool { self.as_slice() == other.as_slice() }
+}
+
+impl PartialEq<&[u8]> for Sstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &&[u8]) -> bool { self.as_slice() == *other }
+}
+
+impl<const N: usize> PartialEq<[u8; N]> for Sstream<'_> {
+ #[inline(always)]
+ fn eq(&self, other: &[u8; N]) -> bool { self.as_slice() == other.as_slice() }
+}
+
+impl<'a> From<Sstream<'a>> for Dstream<'a> {
+ #[inline(always)]
+ fn from(value: Sstream<'a>) -> Self { Self { data: value.buf, pos: value.pos } }
}
diff --git a/bzipper_macros/Cargo.toml b/bzipper_macros/Cargo.toml
index 5b72f9d..8167029 100644
--- a/bzipper_macros/Cargo.toml
+++ b/bzipper_macros/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bzipper_macros"
-version = "0.6.2"
+version = "0.7.0"
edition = "2021"
documentation = "https://docs.rs/bzipper_macros/"
diff --git a/bzipper_macros/src/impls/deserialise_enum.rs b/bzipper_macros/src/impls/deserialise_enum.rs
index 7065c86..4c88a41 100644
--- a/bzipper_macros/src/impls/deserialise_enum.rs
+++ b/bzipper_macros/src/impls/deserialise_enum.rs
@@ -38,36 +38,30 @@ pub fn deserialise_enum(data: &DataEnum) -> TokenStream {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
for field in &variant.fields {
- let field_ty = &field.ty;
-
let command = field.ident
.as_ref()
.map_or_else(
- || quote! { stream.take::<#field_ty>()? },
- |field_name| quote! { #field_name: stream.take::<#field_ty>()? }
+ || quote! { Deserialise::deserialise(stream)? },
+ |field_name| quote! { #field_name: Deserialise::deserialise(stream)? }
);
chain_commands.push(command);
}
- let block = match variant.fields {
+ let value = match variant.fields {
Fields::Named( ..) => quote! { Self::#variant_name { #chain_commands } },
Fields::Unnamed(..) => quote! { Self::#variant_name(#chain_commands) },
Fields::Unit => quote! { Self::#variant_name },
};
- match_arms.push(quote! { #discriminant => #block });
+ match_arms.push(quote! { #discriminant => #value });
}
- match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant { value }) });
+ match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant(value)) });
quote! {
- fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
- ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
-
- let mut stream = ::bzipper::Dstream::new(data);
-
- let value = match (stream.take::<u32>()?) { #match_arms };
+ fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> {
+ let value = match (<u32 as ::bzipper::Deserialise>::deserialise(stream)?) { #match_arms };
Ok(value)
}
}
diff --git a/bzipper_macros/src/impls/deserialise_struct.rs b/bzipper_macros/src/impls/deserialise_struct.rs
index 414a313..f8c167b 100644
--- a/bzipper_macros/src/impls/deserialise_struct.rs
+++ b/bzipper_macros/src/impls/deserialise_struct.rs
@@ -26,52 +26,35 @@ use syn::punctuated::Punctuated;
#[must_use]
pub fn deserialise_struct(data: &DataStruct) -> TokenStream {
- if let Fields::Named(..) = data.fields {
- let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
-
- for field in &data.fields {
- let name = field.ident.as_ref().unwrap();
- let ty = &field.ty;
-
- chain_commands.push(quote! { #name: stream.take::<#ty>()? });
- }
-
+ if matches!(data.fields, Fields::Unit) {
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 })
- }
+ #[inline(always)]
+ fn deserialise(_stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> { Ok(Self) }
}
- } else if let Fields::Unnamed(..) = data.fields {
+ } else {
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
for field in &data.fields {
- let ty = &field.ty;
-
- chain_commands.push(quote! { stream.take::<#ty>()? });
+ let command = field.ident
+ .as_ref()
+ .map_or_else(
+ || quote! { Deserialise::deserialise(stream)? },
+ |field_name| quote! { #field_name: Deserialise::deserialise(stream)? }
+ );
+
+ chain_commands.push(command);
}
- 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
+ let value = if let Fields::Named(..) = data.fields {
+ quote! { Self { #chain_commands } }
+ } else {
+ quote! { Self(#chain_commands) }
+ };
quote! {
- #[inline(always)]
- fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
- ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
-
- Ok(Self)
+ fn deserialise(stream: &::bzipper::Dstream) -> ::bzipper::Result<Self> {
+ let value = #value;
+ Ok(value)
}
}
}
diff --git a/bzipper_macros/src/impls/serialise_enum.rs b/bzipper_macros/src/impls/serialise_enum.rs
index a554351..825886c 100644
--- a/bzipper_macros/src/impls/serialise_enum.rs
+++ b/bzipper_macros/src/impls/serialise_enum.rs
@@ -38,15 +38,15 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
let variant_name = &variant.ident;
let discriminant = u32::try_from(index)
- .expect("enumeration discriminants must be representable in `u32`");
+ .expect("enumeration discriminants must be representable as `u32`");
// Discriminant size:
- serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::SERIALISED_SIZE });
+ serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
let mut captures = Punctuated::<Capture, Token![,]>::new();
let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
- chain_commands.push(quote! { stream.append(&#discriminant)? });
+ chain_commands.push(quote! { #discriminant.serialise(stream)? });
for (index, field) in variant.fields.iter().enumerate() {
let field_ty = &field.ty;
@@ -55,14 +55,14 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
.as_ref()
.map_or_else(|| Ident::new(&format!("v{index}"), Span::call_site()), Clone::clone);
- serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::SERIALISED_SIZE });
+ serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
captures.push(Capture {
ref_token: Token![ref](Span::call_site()),
ident: field_name.clone(),
});
- chain_commands.push(quote! { stream.append(#field_name)? });
+ chain_commands.push(quote! { #field_name.serialise(stream)? });
}
chain_commands.push_punct(Token![;](Span::call_site()));
@@ -90,14 +90,11 @@ pub fn serialise_enum(data: &DataEnum) -> TokenStream {
size_tests.push(quote! { { core::unreachable!(); } });
quote! {
- const SERIALISED_SIZE: usize = const { #size_tests };
-
- fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
- ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let mut stream = ::bzipper::Sstream::new(buf);
+ const MAX_SERIALISED_SIZE: usize = const { #size_tests };
+ fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> {
match (*self) { #match_arms }
+
Ok(())
}
}
diff --git a/bzipper_macros/src/impls/serialise_struct.rs b/bzipper_macros/src/impls/serialise_struct.rs
index 308a6bb..bd81a39 100644
--- a/bzipper_macros/src/impls/serialise_struct.rs
+++ b/bzipper_macros/src/impls/serialise_struct.rs
@@ -33,14 +33,10 @@ use syn::{
pub fn serialise_struct(data: &DataStruct) -> TokenStream {
if matches!(data.fields, Fields::Unit) {
quote! {
- const SERIALISED_SIZE: usize = 0x0;
+ const MAX_SERIALISED_SIZE: usize = 0x0;
#[inline(always)]
- fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
- ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- Ok(())
- }
+ fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> { Ok(()) }
}
} else {
let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
@@ -53,21 +49,17 @@ pub fn serialise_struct(data: &DataStruct) -> TokenStream {
.as_ref()
.map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream);
- serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::SERIALISED_SIZE });
+ serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::MAX_SERIALISED_SIZE });
- chain_commands.push(quote! { stream.append(&self.#name)? });
+ chain_commands.push(quote! { self.#name.serialise(stream)? });
}
chain_commands.push_punct(Token![;](Span::call_site()));
quote! {
- const SERIALISED_SIZE: usize = #serialised_size;
-
- fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
- ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
-
- let mut stream = ::bzipper::Sstream::new(buf);
+ const MAX_SERIALISED_SIZE: usize = #serialised_size;
+ fn serialise(&self, stream: &mut ::bzipper::Sstream) -> ::bzipper::Result<()> {
#chain_commands
Ok(())