diff options
-rw-r--r-- | CHANGELOG.md | 10 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/d_stream/mod.rs (renamed from src/deserialise/d_stream/mod.rs) | 29 | ||||
-rw-r--r-- | src/deserialise/deserialise/mod.rs | 181 | ||||
-rw-r--r-- | src/deserialise/mod.rs | 170 | ||||
-rw-r--r-- | src/deserialise/test.rs | 19 | ||||
-rw-r--r-- | src/error/mod.rs | 27 | ||||
-rw-r--r-- | src/fixed_string/mod.rs | 223 | ||||
-rw-r--r-- | src/fixed_string_iter/mod.rs | 43 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/s_stream/mod.rs (renamed from src/serialise/s_stream/mod.rs) | 19 | ||||
-rw-r--r-- | src/serialise/mod.rs | 121 | ||||
-rw-r--r-- | src/serialise/serialise/mod.rs | 135 | ||||
-rw-r--r-- | src/serialise/test.rs | 40 |
14 files changed, 656 insertions, 375 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c463ec..1520343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 0.1.0 + +* Bump minor +* Export all in crate root +* Add fixed string type +* Add new errors +* Update documentation +* Add `as_d_stream` method to `SStream` +* Add `to_s_stream` and `as_slice` methods to `DStream` + # 0.0.2 * Add license files @@ -1,6 +1,6 @@ [package] name = "bzipper" -version = "0.0.2" +version = "0.1.0" authors = ["Gabriel Bjørnager Jensen"] edition = "2021" description = "Binary (de)serialiser." diff --git a/src/deserialise/d_stream/mod.rs b/src/d_stream/mod.rs index 900aee3..088d751 100644 --- a/src/deserialise/d_stream/mod.rs +++ b/src/d_stream/mod.rs @@ -19,11 +19,11 @@ // er General Public License along with bzipper. If // not, see <https://www.gnu.org/licenses/>. -use crate::error::{Error, Result}; +use crate::{Error, Result, SStream}; use std::fmt::{Debug, Formatter}; -/// A byte stream for deserialisation. +/// Byte stream for deserialisation. /// /// This type borrows a byte slice (hence [`new`](DStream::new)), keeping track internally of the used bytes. #[derive(Clone)] @@ -34,6 +34,8 @@ pub struct DStream<'a> { impl<'a> DStream<'a> { /// Constructs a new byte stream. + #[inline(always)] + #[must_use] pub fn new<T: AsRef<[u8]> + ?Sized>(buf: &'a T) -> Self { Self { data: buf.as_ref(), len: buf.as_ref().len(), @@ -54,16 +56,31 @@ impl<'a> DStream<'a> { Ok(&self.data[start..stop]) } + + /// Takes a slice of the remaining data. + #[must_use] + pub fn as_slice(&self) -> &[u8] { + let stop = self.data.len(); + let start = stop - self.len; + + &self.data[start..stop] + } + + /// Converts the stream to a `SStream` object. + /// + /// The returned object owns a copy of the remaining data. + #[inline(always)] + #[must_use] + pub fn to_s_stream(&self) -> SStream { + SStream(self.as_slice().to_vec()) + } } impl Debug for DStream<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - let stop = self.data.len(); - let start = self.data.len() - self.len; - write!(f, "[")?; - for v in &self.data[start..stop] { write!(f, "{v:#02X},")? }; + for v in self.as_slice() { write!(f, "{v:#02X},")? }; write!(f, "]")?; diff --git a/src/deserialise/deserialise/mod.rs b/src/deserialise/deserialise/mod.rs deleted file mode 100644 index e345b12..0000000 --- a/src/deserialise/deserialise/mod.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use crate::deserialise::DStream; -use crate::error::Error; - -use std::convert::Infallible; -use std::error::Error as StdError; -use std::mem::size_of; -use std::num::NonZero; - -/// Denotes a type capable of being deserialised. -pub trait Deserialise: Sized { - type Error; - - /// Deserialises the byte stream to an object. - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error>; -} - -macro_rules! impl_float { - ($type:ty) => { - impl Deserialise for $type { - type Error = Error; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let data = stream - .take(size_of::<Self>())? - .try_into() - .unwrap(); - - Ok(Self::from_be_bytes(data)) - } - } - }; -} - -macro_rules! impl_int { - ($type:ty) => { - impl Deserialise for $type { - type Error = Error; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let data = stream - .take(size_of::<Self>())? - .try_into() - .unwrap(); - - Ok(Self::from_be_bytes(data)) - } - } - - impl Deserialise for NonZero<$type> { - type Error = Error; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let value = <$type>::deserialise(stream)?; - - NonZero::new(value) - .ok_or(Error::NullInteger) - } - } - }; -} - -impl<T: Deserialise<Error: StdError + 'static>, const N: usize> Deserialise for [T; N] { - type Error = Box<dyn StdError>; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let len = usize::try_from(u64::deserialise(stream)?).unwrap(); - if len != N { return Err(Box::new(Error::ArrayLengthMismatch { len, ok_len: N })) }; - - let mut buf = Vec::with_capacity(len); - for _ in 0x0..len { buf.push(Deserialise::deserialise(stream)?); } - - // If we had used the checked unwrap, we would also - // have to require `T: Debug`. - Ok(unsafe { buf.try_into().unwrap_unchecked() }) - } -} - -impl Deserialise for () { - type Error = Error; - - fn deserialise(_stream: &mut DStream) -> Result<Self, Self::Error> { Ok(()) } -} - -impl Deserialise for bool { - type Error = Error; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let value = u8::deserialise(stream)?; - - match value { - 0x00 => Ok(false), - 0x01 => Ok(true), - _ => Err(Error::InvalidBoolean { value }) - } - } -} - -impl Deserialise for char { - type Error = Error; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let value = u32::deserialise(stream)?; - - Self::from_u32(value) - .ok_or(Error::InvalidCodePoint { value }) - } -} - -impl Deserialise for Infallible { - type Error = Error; - - fn deserialise(_stream: &mut DStream) -> Result<Self, Self::Error> { unreachable!() } -} - -impl<T: Deserialise<Error: StdError + 'static>> Deserialise for Option<T> { - type Error = Box<dyn StdError>; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let sign = bool::deserialise(stream)?; - - if sign { - Ok(Some(T::deserialise(stream)?)) - } else { - Ok(None) - } - } -} - -impl<T: Deserialise, E: Deserialise> Deserialise for Result<T, E> -where - <T as Deserialise>::Error: StdError + 'static, - <E as Deserialise>::Error: StdError + 'static, { - type Error = Box<dyn StdError>; - - fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { - let sign = bool::deserialise(stream)?; - - let value = if sign { - Err(E::deserialise(stream)?) - } else { - Ok(T::deserialise(stream)?) - }; - - Ok(value) - } -} - -impl_float!(f32); -impl_float!(f64); - -impl_int!(i128); -impl_int!(i16); -impl_int!(i32); -impl_int!(i64); -impl_int!(i8); -impl_int!(u128); -impl_int!(u16); -impl_int!(u32); -impl_int!(u64); -impl_int!(u8); diff --git a/src/deserialise/mod.rs b/src/deserialise/mod.rs index cd0889a..2678670 100644 --- a/src/deserialise/mod.rs +++ b/src/deserialise/mod.rs @@ -19,11 +19,169 @@ // er General Public License along with bzipper. If // not, see <https://www.gnu.org/licenses/>. -//! Deserialisation utilities. - -use crate::use_mod; -use_mod!(pub d_stream); -use_mod!(pub deserialise); - #[cfg(test)] mod test; + +use crate::{DStream, Error}; + +use std::convert::Infallible; +use std::error::Error as StdError; +use std::mem::size_of; +use std::num::NonZero; + +/// Denotes a type capable of being deserialised. +pub trait Deserialise: Sized { + type Error; + + /// Deserialises the byte stream to an object. + /// + /// # Errors + /// + /// If deserialisation failed, e.g. by an invalid value being found, an error is returned. + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error>; +} + +macro_rules! impl_float { + ($type:ty) => { + impl Deserialise for $type { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let data = stream + .take(size_of::<Self>())? + .try_into() + .unwrap(); + + Ok(Self::from_be_bytes(data)) + } + } + }; +} + +macro_rules! impl_int { + ($type:ty) => { + impl Deserialise for $type { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let data = stream + .take(size_of::<Self>())? + .try_into() + .unwrap(); + + Ok(Self::from_be_bytes(data)) + } + } + + impl Deserialise for NonZero<$type> { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let value = <$type>::deserialise(stream)?; + + NonZero::new(value) + .ok_or(Error::NullInteger) + } + } + }; +} + +impl<T: Deserialise<Error: StdError + 'static>, const N: usize> Deserialise for [T; N] { + type Error = Box<dyn StdError>; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let len = usize::try_from(u64::deserialise(stream)?).unwrap(); + if len != N { return Err(Box::new(Error::ArrayLengthMismatch { len, ok_len: N })) }; + + let mut buf = Vec::with_capacity(len); + for _ in 0x0..len { buf.push(Deserialise::deserialise(stream)?); } + + // If we had used the checked unwrap, we would also + // have to require `T: Debug`. + Ok(unsafe { buf.try_into().unwrap_unchecked() }) + } +} + +impl Deserialise for () { + type Error = Error; + + fn deserialise(_stream: &mut DStream) -> Result<Self, Self::Error> { Ok(()) } +} + +impl Deserialise for bool { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let value = u8::deserialise(stream)?; + + match value { + 0x00 => Ok(false), + 0x01 => Ok(true), + _ => Err(Error::InvalidBoolean { value }) + } + } +} + +impl Deserialise for char { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let value = u32::deserialise(stream)?; + + Self::from_u32(value) + .ok_or(Error::InvalidCodePoint { value }) + } +} + +impl Deserialise for Infallible { + type Error = Error; + + fn deserialise(_stream: &mut DStream) -> Result<Self, Self::Error> { unreachable!() } +} + +impl<T: Deserialise<Error: StdError + 'static>> Deserialise for Option<T> { + type Error = Box<dyn StdError>; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let sign = bool::deserialise(stream)?; + + if sign { + Ok(Some(T::deserialise(stream)?)) + } else { + Ok(None) + } + } +} + +impl<T: Deserialise, E: Deserialise> Deserialise for Result<T, E> +where + <T as Deserialise>::Error: StdError + 'static, + <E as Deserialise>::Error: StdError + 'static, { + type Error = Box<dyn StdError>; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let sign = bool::deserialise(stream)?; + + let value = if sign { + Err(E::deserialise(stream)?) + } else { + Ok(T::deserialise(stream)?) + }; + + Ok(value) + } +} + +impl_float!(f32); +impl_float!(f64); + +impl_int!(i128); +impl_int!(i16); +impl_int!(i32); +impl_int!(i64); +impl_int!(i8); +impl_int!(u128); +impl_int!(u16); +impl_int!(u32); +impl_int!(u64); +impl_int!(u8); diff --git a/src/deserialise/test.rs b/src/deserialise/test.rs index fb95cc6..394ed64 100644 --- a/src/deserialise/test.rs +++ b/src/deserialise/test.rs @@ -19,18 +19,20 @@ // er General Public License along with bzipper. If // not, see <https://www.gnu.org/licenses/>. -use crate::deserialise::{Deserialise, DStream}; +use crate::{Deserialise, DStream, FixedString}; #[test] -fn test_serialise() { +fn test_deserialise() { let data = [ 0x00, 0xFF, 0xFF, 0x0F, 0xEF, 0x1F, 0xDF, 0x2F, 0xCF, 0x3F, 0xBF, 0x4F, 0xAF, 0x5F, 0x9F, 0x6F, 0x8F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, - 0x03, 0x91, 0x00, 0x00, 0x03, 0xBC, 0x00, 0x00, - 0x03, 0x94, 0x00, 0x00, 0x03, 0xB1, 0x01, 0x00, - 0x00, 0x01, 0x80, + 0x00, 0x09, 0x6D, 0xC3, 0xA1, 0x6E, 0x61, 0xC3, + 0xB0, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0xBB, 0x00, + 0x00, 0x03, 0x91, 0x00, 0x00, 0x03, 0xBC, 0x00, + 0x00, 0x03, 0x94, 0x00, 0x00, 0x03, 0xB1, 0x01, + 0x00, 0x00, 0x01, 0x80, ]; let mut stream = DStream::from(&data); @@ -50,6 +52,11 @@ fn test_serialise() { ); assert_eq!( + FixedString::<0x10>::deserialise(&mut stream).unwrap(), + "m\u{00E1}na\u{00F0}ur", + ); + + assert_eq!( <[char; 0x5]>::deserialise(&mut stream).unwrap(), ['\u{03BB}', '\u{0391}', '\u{03BC}', '\u{0394}', '\u{03B1}'], ); diff --git a/src/error/mod.rs b/src/error/mod.rs index 964d83e..6eb6c15 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -23,6 +23,7 @@ use std::error::Error as StdError; use std::fmt::{Display, Formatter}; +use std::str::Utf8Error; /// Mapping of [`std::result::Result`]. pub type Result<T> = std::result::Result<T, Error>; @@ -37,10 +38,14 @@ pub enum Error { EndOfDStream { len: usize, ok_len: usize }, + FixedStringTooShort { len: usize, s: String }, + InvalidBoolean { value: u8 }, InvalidCodePoint { value: u32 }, + InvalidUtf8 { source: Utf8Error }, + NullInteger, } @@ -57,13 +62,21 @@ impl Display for Error { write!(f, "({ok_len}) byte(s) were requested but only ({len}) byte(s) were left") }, + FixedStringTooShort { len, ref s } => { + write!(f, "fixed string with `N = {len}` cannot hold {s:?}") + }, + InvalidBoolean { value } => { write!(f, "expected boolean but got {value:#02X}") }, InvalidCodePoint { value } => { write!(f, "code point U+{value:04X} is not valid") - } + }, + + InvalidUtf8 { ref source } =>{ + write!(f, "unable to parse utf8: \"{source}\"") + }, NullInteger => { write!(f, "expected non-zero integer but got (0)") @@ -72,4 +85,14 @@ impl Display for Error { } } -impl StdError for Error { } +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + use Error::*; + + match *self { + InvalidUtf8 { ref source } => Some(source), + + _ => None, + } + } +} diff --git a/src/fixed_string/mod.rs b/src/fixed_string/mod.rs new file mode 100644 index 0000000..ca369ce --- /dev/null +++ b/src/fixed_string/mod.rs @@ -0,0 +1,223 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bzipper. +// +// bzipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bzipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bzipper. If +// not, see <https://www.gnu.org/licenses/>. + +use crate::{ + Deserialise, + DStream, + Error, + FixedStringIter, + Serialise, + SStream, +}; + +use std::fmt::{Display, Debug, Formatter}; +use std::str::FromStr; + +/// Owned string with maximum size. +/// +/// This is in contrast to [String], which has no size limit is practice, and [str], which is unsized. +#[derive(Clone)] +pub struct FixedString<const N: usize> { + buf: [char; N], + len: usize, +} + +impl<const N: usize> FixedString<N> { + /// Constructs a new fixed string. + /// + /// The contents of the provided string are copied into the internal buffer. + /// All residual characters are instanced as U+0000 `NULL`. + /// + /// # Errors + /// + /// If the given string `s` cannot fit into `N` characters, a [`FixedStringTooShort`](Error::FixedStringTooShort) error is returned. + pub fn new(s: &str) -> Result<Self, Error> { + let mut buf = ['\0'; N]; + let len = s.chars().count(); + + for (i, c) in s.chars().enumerate() { + if i >= N { return Err(Error::FixedStringTooShort { len: N, s: s.to_owned() }) } + + buf[i] = c; + } + + Ok(Self { buf, len }) + } + + /// Returns the length of the string. + /// + /// This does not necessarily equal the value of `N`, as the internal buffer is not required to be used fully. + #[inline(always)] + #[must_use] + pub const fn len(&self) -> usize { self.len } + + /// Checks if the string is empty, i.e. `self.len() == 0x0`. + #[inline(always)] + #[must_use] + pub const fn is_empty(&self) -> bool { self.len == 0x0 } + + /// Returns an iterator to the contained characters. + #[inline(always)] + pub fn iter(&self) -> std::slice::Iter<'_, char> { self.buf[0x0..self.len].iter() } + + /// Returns a mutable iterator to the contained characters. + #[inline(always)] + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, char> { self.buf[0x0..self.len].iter_mut() } +} + +impl<const N: usize> Debug for FixedString<N> { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "\"")?; + + for c in self { + if c.is_ascii_graphic() { + write!(f, "{c}")?; + } else if *c == '\0' { + write!(f, "\\0")?; + } else { + write!(f, "{c}")?; + } + } + + write!(f, "\"")?; + + Ok(()) + } +} + +impl<const N: usize> Deserialise for FixedString<N> { + type Error = Error; + + fn deserialise(stream: &mut DStream) -> Result<Self, Self::Error> { + let len = usize::try_from(u64::deserialise(stream)?).unwrap(); + + let data = stream.take(len)?; + let s = std::str::from_utf8(data) + .map_err(|e| Error::InvalidUtf8 { source: e })?; + + let len = s.chars().count(); + if len > N { + return Err(Error::FixedStringTooShort { len, s: s.to_owned() }); + } + + let mut buf = ['\0'; N]; + for (i, c) in s.chars().enumerate() { + buf[i] = c; + } + + Ok(Self { buf, len }) + } +} + +impl<const N: usize> Default for FixedString<N> { + #[inline(always)] + fn default() -> Self { Self { + buf: ['\0'; N], + len: 0x0, + } } +} + +impl<const N: usize> Display for FixedString<N> { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + for c in self { write!(f, "{c}")? } + + Ok(()) + } +} + +impl<const N: usize> Eq for FixedString<N> { } + +impl<const N: usize> FromStr for FixedString<N> { + type Err = Error; + + fn from_str(s: &str) -> Result<Self, Error> { Self::new(s) } +} + +impl<const N: usize> IntoIterator for FixedString<N> { + type Item = char; + + type IntoIter = FixedStringIter<N>; + + fn into_iter(self) -> Self::IntoIter { + FixedStringIter { + buf: self.buf, + len: self.len, + + pos: Some(0x0), + } + } +} + +impl<'a, const N: usize> IntoIterator for &'a FixedString<N> { + type Item = &'a char; + + type IntoIter = std::slice::Iter<'a, char>; + + fn into_iter(self) -> Self::IntoIter { self.iter() } +} + +impl<'a, const N: usize> IntoIterator for &'a mut FixedString<N> { + type Item = &'a mut char; + + type IntoIter = std::slice::IterMut<'a, char>; + + fn into_iter(self) -> Self::IntoIter { self.iter_mut() } +} + +impl<const N: usize> PartialEq for FixedString<N> { + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { return false }; + + for i in 0x0..self.len() { + if self.buf[i] != other.buf[i] { return false }; + } + + true + } +} + +impl<const N: usize> PartialEq<&str> for FixedString<N> { + fn eq(&self, other: &&str) -> bool { + for (i, c) in other.chars().enumerate() { + if self.buf.get(i) != Some(&c) { return false }; + } + + true + } +} + +impl<const N: usize> Serialise for FixedString<N> { + fn serialise(&self, stream: &mut SStream) { + let s: String = self.iter().collect(); + + let len = u64::try_from(s.len()).unwrap(); + + stream.append(&len.to_be_bytes()); + stream.append(&s.into_bytes()); + } +} + +impl<const N: usize> TryFrom<&str> for FixedString<N> { + type Error = Error; + + #[inline(always)] + fn try_from(value: &str) -> Result<Self, Self::Error> { Self::new(value) } +} diff --git a/src/fixed_string_iter/mod.rs b/src/fixed_string_iter/mod.rs new file mode 100644 index 0000000..088f61f --- /dev/null +++ b/src/fixed_string_iter/mod.rs @@ -0,0 +1,43 @@ +// 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/>. + +/// Iterator to a fixed string. +pub struct FixedStringIter<const N: usize> { + pub(in crate) buf: [char; N], + pub(in crate) len: usize, + + pub(in crate) pos: Option<usize>, +} + +impl<const N: usize> Iterator for FixedStringIter<N> { + type Item = char; + + fn next(&mut self) -> Option<Self::Item> { + let pos = self.pos.as_mut()?; + + if *pos >= self.len { return None }; + + let item = self.buf[*pos]; + *pos += 0x1; + + Some(item) + } +} @@ -28,10 +28,6 @@ //! //! This crate does not require any dependencies at the moment. -pub mod deserialise; -pub mod error; -pub mod serialise; - macro_rules! use_mod { ($vis:vis $name:ident) => { mod $name; @@ -39,3 +35,11 @@ macro_rules! use_mod { }; } pub(in crate) use use_mod; + +use_mod!(pub d_stream); +use_mod!(pub deserialise); +use_mod!(pub error); +use_mod!(pub fixed_string); +use_mod!(pub fixed_string_iter); +use_mod!(pub s_stream); +use_mod!(pub serialise); diff --git a/src/serialise/s_stream/mod.rs b/src/s_stream/mod.rs index 20f3fb9..454adb8 100644 --- a/src/serialise/s_stream/mod.rs +++ b/src/s_stream/mod.rs @@ -19,21 +19,36 @@ // er General Public License along with bzipper. If // not, see <https://www.gnu.org/licenses/>. -use crate::serialise::Serialise; +use crate::{DStream, Serialise}; use std::fmt::{Debug, Formatter}; use std::mem::size_of; +/// Byte stream for serialisation. +/// +/// The bytes themselves are contained by the type. +/// The stream can #[derive(Clone, Eq, PartialEq)] -pub struct SStream(Vec<u8>); +pub struct SStream(pub(in crate) Vec<u8>); impl SStream { + /// Constructs a new, empty byte stream. + #[inline(always)] #[must_use] pub const fn new() -> Self { Self(Vec::new()) } + /// Extends the byte stream. + #[inline(always)] pub fn append(&mut self, extra: &[u8]) { self.0.extend(extra); } + + /// Converts the stream to a `DStream` object. + /// + /// The returned object references the original stream. + #[inline(always)] + #[must_use] + pub fn as_d_stream(&self) -> DStream { DStream::new(&self.0) } } impl AsRef<[u8]> for SStream { diff --git a/src/serialise/mod.rs b/src/serialise/mod.rs index 9fb56e3..1a63080 100644 --- a/src/serialise/mod.rs +++ b/src/serialise/mod.rs @@ -19,11 +19,120 @@ // er General Public License along with bzipper. If // not, see <https://www.gnu.org/licenses/>. -//! Serialisation utilities. - -use crate::use_mod; -use_mod!(pub s_stream); -use_mod!(pub serialise); - #[cfg(test)] mod test; + +use crate::SStream; + +use std::convert::Infallible; +use std::mem::size_of; +use std::num::NonZero; + +/// Denotes a type capable of being serialised. +pub trait Serialise: Sized { + /// Serialises `self` into a byte stream. + /// + /// One may assume that the resulting stream has at most the same ammount of bytes as before serialisation. + /// Therefore, not observing this rule is a logic error. + fn serialise(&self, stream: &mut SStream); +} + +macro_rules! impl_float { + ($type:ty) => { + impl Serialise for $type { + fn serialise(&self, stream: &mut SStream) { + stream.append(&self.to_be_bytes()) + } + } + }; +} + +macro_rules! impl_int { + ($type:ty) => { + impl Serialise for $type { + fn serialise(&self, stream: &mut SStream) { + stream.append(&self.to_be_bytes()) + } + } + + impl Serialise for NonZero<$type> { + fn serialise(&self, stream: &mut SStream) { + self.get().serialise(stream) + } + } + }; +} + +impl<T: Serialise, const N: usize> Serialise for [T; N] { + fn serialise(&self, stream: &mut SStream) { + u64::try_from(self.len()).unwrap().serialise(stream); + + for v in self { v.serialise(stream) } + } +} + +impl Serialise for () { + fn serialise(&self, _stream: &mut SStream) { } +} + +impl Serialise for bool { + fn serialise(&self, stream: &mut SStream) { + u8::from(*self).serialise(stream) + } +} + +impl Serialise for char { + fn serialise(&self, stream: &mut SStream) { + u32::from(*self).serialise(stream) + } +} + +impl Serialise for Infallible { + fn serialise(&self, _stream: &mut SStream) { unreachable!() } +} + +impl<T: Serialise> Serialise for Option<T> { + fn serialise(&self, stream: &mut SStream) { + match *self { + None => { + stream.append(&[0x00]); + stream.append(&vec![0x00; size_of::<T>()]); + }, + + Some(ref v) => { + stream.append(&[0x01]); + v.serialise(stream); + }, + }; + } +} + +impl<T: Serialise, E: Serialise> Serialise for Result<T, E> { + fn serialise(&self, stream: &mut SStream) { + match *self { + Ok(ref v) => { + stream.append(&[0x00]); + v.serialise(stream); + }, + + Err(ref e) => { + stream.append(&[0x01]); + e.serialise(stream); + }, + }; + } +} + +impl_float!(f32); +impl_float!(f64); + +impl_int!(i128); +impl_int!(i16); +impl_int!(i32); +impl_int!(i64); +impl_int!(i8); +impl_int!(u128); +impl_int!(u16); +impl_int!(u32); +impl_int!(u64); +impl_int!(u8); diff --git a/src/serialise/serialise/mod.rs b/src/serialise/serialise/mod.rs deleted file mode 100644 index 8ee610c..0000000 --- a/src/serialise/serialise/mod.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2024 Gabriel Bjørnager Jensen. -// -// This file is part of bzipper. -// -// bzipper is free software: you can redistribute -// it and/or modify it under the terms of the GNU -// Lesser General Public License as published by -// the Free Software Foundation, either version 3 -// of the License, or (at your option) any later -// version. -// -// bzipper is distributed in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without -// even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Less- -// er General Public License along with bzipper. If -// not, see <https://www.gnu.org/licenses/>. - -use crate::serialise::SStream; - -use std::convert::Infallible; -use std::mem::size_of; -use std::num::NonZero; - -/// Denotes a type capable of being serialised. -pub trait Serialise: Sized { - /// Serialises `self` into a byte stream. - /// - /// One may assume that the resulting stream has at most the same ammount of bytes as before serialisation. - /// Therefore, not observing this rule is a logic error. - fn serialise(&self, stream: &mut SStream); -} - -macro_rules! impl_float { - ($type:ty) => { - impl Serialise for $type { - fn serialise(&self, stream: &mut SStream) { - stream.append(&self.to_be_bytes()) - } - } - }; -} - -macro_rules! impl_int { - ($type:ty) => { - impl Serialise for $type { - fn serialise(&self, stream: &mut SStream) { - stream.append(&self.to_be_bytes()) - } - } - - impl Serialise for NonZero<$type> { - fn serialise(&self, stream: &mut SStream) { - self.get().serialise(stream) - } - } - }; -} - -impl<T: Serialise, const N: usize> Serialise for [T; N] { - fn serialise(&self, stream: &mut SStream) { - u64::try_from(self.len()).unwrap().serialise(stream); - - for v in self { v.serialise(stream) } - } -} - -impl Serialise for () { - fn serialise(&self, _stream: &mut SStream) { } -} - -impl Serialise for bool { - fn serialise(&self, stream: &mut SStream) { - u8::from(*self).serialise(stream) - } -} - -impl Serialise for char { - fn serialise(&self, stream: &mut SStream) { - u32::from(*self).serialise(stream) - } -} - -impl Serialise for Infallible { - fn serialise(&self, _stream: &mut SStream) { unreachable!() } -} - -impl<T: Serialise> Serialise for Option<T> { - fn serialise(&self, stream: &mut SStream) { - match *self { - None => { - stream.append(&[0x00]); - stream.append(&vec![0x00; size_of::<T>()]); - }, - - Some(ref v) => { - stream.append(&[0x01]); - v.serialise(stream); - }, - }; - } -} - -impl<T: Serialise, E: Serialise> Serialise for Result<T, E> { - fn serialise(&self, stream: &mut SStream) { - match *self { - Ok(ref v) => { - stream.append(&[0x00]); - v.serialise(stream); - }, - - Err(ref e) => { - stream.append(&[0x01]); - e.serialise(stream); - }, - }; - } -} - -impl_float!(f32); -impl_float!(f64); - -impl_int!(i128); -impl_int!(i16); -impl_int!(i32); -impl_int!(i64); -impl_int!(i8); -impl_int!(u128); -impl_int!(u16); -impl_int!(u32); -impl_int!(u64); -impl_int!(u8); diff --git a/src/serialise/test.rs b/src/serialise/test.rs index 8b06ed2..e0a0004 100644 --- a/src/serialise/test.rs +++ b/src/serialise/test.rs @@ -1,25 +1,6 @@ -// 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/>. +// Copyright 2022-2024 Gabriel Bjørnager Jensen. -use crate::serialise::{SStream, Serialise}; +use crate::{FixedString, SStream, Serialise}; #[test] fn test_serialise() { @@ -37,6 +18,9 @@ fn test_serialise() { 0x45_A0_15_6A_36_77_17_8A_83_2E_3C_2C_84_10_58_1A_u128.serialise(&mut stream); + FixedString::<0x1>::new("A").unwrap().serialise(&mut stream); + FixedString::<0x8>::new("l\u{00F8}gma\u{00F0}ur").unwrap().serialise(&mut stream); + ['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'].serialise(&mut stream); Result::<u16, char>::Ok(0x45_45).serialise(&mut stream); @@ -55,10 +39,14 @@ fn test_serialise() { 0x39, 0x45, 0xA0, 0x15, 0x6A, 0x36, 0x77, 0x17, 0x8A, 0x83, 0x2E, 0x3C, 0x2C, 0x84, 0x10, 0x58, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x03, 0xB4, 0x00, 0x00, 0x01, - 0x90, 0x00, 0x00, 0x03, 0xBB, 0x00, 0x00, 0x03, - 0xA4, 0x00, 0x00, 0x03, 0xB1, 0x00, 0x45, 0x45, - 0x01, 0x00, 0x00, 0xFF, 0xFD, 0x00, 0x01, + 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x6C, 0xC3, 0xB8, 0x67, 0x6D, 0x61, + 0xC3, 0xB0, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0xB4, + 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x03, 0xBB, + 0x00, 0x00, 0x03, 0xA4, 0x00, 0x00, 0x03, 0xB1, + 0x00, 0x45, 0x45, 0x01, 0x00, 0x00, 0xFF, 0xFD, + 0x00, 0x01, ] ); -} +}
\ No newline at end of file |