diff options
Diffstat (limited to 'src/buffer')
-rw-r--r-- | src/buffer/mod.rs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs new file mode 100644 index 0000000..3ce47bd --- /dev/null +++ b/src/buffer/mod.rs @@ -0,0 +1,172 @@ +// Copyright 2024 Gabriel Bjørnager Jensen. +// +// This file is part of bzipper. +// +// bzipper is free software: you can redistribute +// it and/or modify it under the terms of the GNU +// Lesser General Public License as published by +// the Free Software Foundation, either version 3 +// of the License, or (at your option) any later +// version. +// +// bzipper is distributed in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without +// even the impl<T: Serialise>ied warranty of MERCHANTABILITY<T> or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Less- +// er General Public License along with bzipper. If +// not, see <https://www.gnu.org/licenses/>. + +use crate::{Deserialise, Dstream, Serialise, Sstream}; + +use alloc::vec; +use alloc::boxed::Box; +use core::fmt::{Debug, Formatter}; +use core::marker::PhantomData; + +/// Container type for (de)serialisations. +/// +/// The purpose of this type is to easily hold a buffer than can fit any serialisation of a given type (hence the generic). +/// +/// Do note that the internal buffer does not guarantee the state of any padding bytes that occur as a result of different serialisation sizes. +/// Deserialisations, however, are not affected by these. +#[derive(Clone, Eq, PartialEq)] +pub struct Buffer<T> { + data: Box<[u8]>, + len: usize, + + _phantom: PhantomData<T>, +} + +impl<T> Buffer<T> { + /// Sets the internal length of the buffer without checks. + /// + /// For a safe alternative, see [`set_len`](Self::set_len). + /// + /// # Safety + /// + /// The new length must **not** exceed [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT). + #[inline(always)] + pub unsafe fn set_len_unchecked(&mut self, len: usize) { + self.len = len; + } + + /// Returns a slice of the internal buffer. + /// + /// This only includes bytes written by the last serialisation, or as set by [`set_len`](Self::set_len). + #[inline(always)] + #[must_use] + pub fn as_slice(&self) -> &[u8] { &self.data[0x0..self.len] } + + /// Returns a mutable slice of the entire internal buffer. + /// + /// This is in contrast to [`as_slice`](Self::as_slice), which only yields the last serialisation. + /// + /// The length of bytes written to this slice should be set using [`set_len`](Self::set_len). + #[inline(always)] + #[must_use] + pub fn as_mut_slice(&mut self) -> &mut [u8] { self.data.as_mut() } +} + +impl<T: Serialise> Buffer<T> { + /// Constructs a new, empty buffer. + /// + /// The internal buffer is allocated on the heap instantly with the size [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT). + #[inline(always)] + #[must_use] + pub fn new() -> Self { Self { + data: vec![Default::default(); T::SERIALISE_LIMIT].into(), + len: 0x0, + + _phantom: PhantomData + } } + + /// Sets the length of the current serialisation. + /// + /// This is specifically meant for cases where the buffer is set externally, as is the case for networking: + /// + /// ``` + /// use bzipper::{Buffer, FixedString}; + /// use std::net::{SocketAddr, UdpSocket}; + /// use std::str::FromStr; + /// + /// let destination = SocketAddr::from_str("127.0.0.1:37279")?; + /// + /// let sender = UdpSocket::bind("0.0.0.0:0")?; + /// let reciever = UdpSocket::bind(destination)?; + /// + /// // Create a buffer for holding a fixed string. + /// let mut buffer = Buffer::<FixedString<0x10>>::new(); + /// + /// // Serialise and write the string: + /// buffer.write(&FixedString::new("Hello there!")?); + /// sender.send_to(buffer.as_ref(), destination); + /// + /// // Recieve and deserialise the string: + /// let (count, _source) = reciever.recv_from(buffer.as_mut_slice())?; + /// buffer.set_len(count); + /// + /// assert_eq!(buffer.read()?, "Hello there!"); + /// + /// # Ok::<(), Box<dyn std::error::Error>>(()) + /// ``` + /// + /// # Panics + /// + /// Panics if `len` is larger than [`T::SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT). + /// See [`set_len_unchecked`](Self::set_len_unchecked). + #[inline] + pub fn set_len(&mut self, len: usize) { + assert!(len <= T::SERIALISE_LIMIT); + self.len = len; + } +} + +impl<T: Serialise> Buffer<T> { + /// Serialises into the buffer. + /// + /// The result of [`serialise`](Serialise::serialise) is used as the length. + /// + /// # Panics + /// + /// Panics if the amount of written bytes exceeds [`SERIALISE_LIMIT`](Serialise::SERIALISE_LIMIT). + /// This *should*, in theory, not occur, as the internal buffer can only fit up to this limit, making all writes past this limit fail. + #[allow(clippy::panic_in_result_fn)] + pub fn write(&mut self, value: &T) -> Result<(), <T as Serialise>::Error> { + let mut stream = Sstream::new(&mut self.data); + + let count = value.serialise(&mut stream)?; + assert!(count <= T::SERIALISE_LIMIT); + + self.len = count; + Ok(()) + } +} + +impl<T: Deserialise> Buffer<T> { + /// Deserialises the contained buffer. + /// + /// Only bytes from the last serialisation, or as set by [`set_len`](Self::set_len), are used. + pub fn read(&self) -> Result<T, <T as Deserialise>::Error> { + let mut stream = Dstream::new(self.as_ref()); + T::deserialise(&mut stream) + } +} + +impl<T> AsRef<[u8]> for Buffer<T> { + #[inline(always)] + fn as_ref(&self) -> &[u8] { self.as_slice() } +} + +impl<T> Debug for Buffer<T> { + fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { + self.data.fmt(f) + } +} + +impl<T: Serialise> Default for Buffer<T> { + #[inline(always)] + fn default() -> Self { Self::new() } +} |