// (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. use std::error::Error; use std::fmt::{Debug, Display}; use std::io::Write; use crate::buffer::Buffer; /// Must be larger than any object we want to use with to_bytes() or from_bytes(). /// This hack can go away once Rust allows us to reference trait consts as generics. const TEMP_BUF_SIZE: usize = 8192; /// A super-lightweight zero-allocation serialization interface. pub trait Marshalable: Sized { const MAX_MARSHAL_SIZE: usize; /// Write this object into a buffer. fn marshal(&self, buf: &mut Buffer) -> Result<(), UnmarshalError>; /// Read this object from a buffer. /// /// The supplied cursor is advanced by the number of bytes read. If an Err is returned /// the value of the cursor is undefined but likely points to about where the error /// occurred. It may also point beyond the buffer, which would indicate an overrun error. fn unmarshal(buf: &Buffer, cursor: &mut usize) -> Result; /// Write this marshalable entity into a buffer of the given size. /// /// This will return an Err if the buffer is too small or some other error occurs. It's just /// a shortcut to creating a buffer and marshaling into it. #[inline] fn to_buffer(&self) -> Result, UnmarshalError> { let mut tmp = Buffer::new(); self.marshal(&mut tmp)?; Ok(tmp) } /// Unmarshal this object from a buffer. /// /// This is just a shortcut to calling unmarshal() with a zero cursor and then discarding the cursor. #[inline] fn from_buffer(buf: &Buffer) -> Result { let mut tmp = 0; Self::unmarshal(buf, &mut tmp) } /// Marshal and convert to a Rust vector. #[inline] fn to_bytes(&self) -> Vec { assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE); let mut tmp = Buffer::::new(); assert!(self.marshal(&mut tmp).is_ok()); // panics if TEMP_BUF_SIZE is too small tmp.as_bytes().to_vec() } /// Unmarshal from a raw slice. #[inline] fn from_bytes(b: &[u8]) -> Result { if b.len() <= TEMP_BUF_SIZE { let mut tmp = Buffer::::new_boxed(); assert!(tmp.append_bytes(b).is_ok()); let mut cursor = 0; Self::unmarshal(&tmp, &mut cursor) } else { Err(UnmarshalError::OutOfBounds) } } /// Marshal a slice of marshalable objects to a concatenated byte vector. #[inline] fn marshal_multiple_to_bytes(objects: &[Self]) -> Result, UnmarshalError> { assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE); let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new(); let mut v: Vec = Vec::with_capacity(objects.len() * Self::MAX_MARSHAL_SIZE); for i in objects.iter() { i.marshal(&mut tmp)?; let _ = v.write_all(tmp.as_bytes()); tmp.clear(); } Ok(v) } /// Unmarshal a concatenated byte slice of marshalable objects. #[inline] fn unmarshal_multiple_from_bytes(mut bytes: &[u8]) -> Result, UnmarshalError> { assert!(Self::MAX_MARSHAL_SIZE <= TEMP_BUF_SIZE); let mut tmp: Buffer<{ TEMP_BUF_SIZE }> = Buffer::new(); let mut v: Vec = Vec::new(); while bytes.len() > 0 { let chunk_size = bytes.len().min(Self::MAX_MARSHAL_SIZE); if tmp.append_bytes(&bytes[..chunk_size]).is_err() { return Err(UnmarshalError::OutOfBounds); } let mut cursor = 0; v.push(Self::unmarshal(&mut tmp, &mut cursor)?); if cursor == 0 { return Err(UnmarshalError::InvalidData); } let _ = tmp.erase_first_n(cursor); bytes = &bytes[chunk_size..]; } Ok(v) } /// Unmarshal a buffer with a byte slice of marshalable objects. #[inline] fn unmarshal_multiple(buf: &Buffer, cursor: &mut usize, eof: usize) -> Result, UnmarshalError> { let mut v: Vec = Vec::new(); while *cursor < eof { v.push(Self::unmarshal(buf, cursor)?); } Ok(v) } } pub enum UnmarshalError { OutOfBounds, InvalidData, UnsupportedVersion, IoError(std::io::Error), } impl Display for UnmarshalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::OutOfBounds => f.write_str("out of bounds"), Self::InvalidData => f.write_str("invalid data"), Self::UnsupportedVersion => f.write_str("unsupported version"), Self::IoError(e) => f.write_str(e.to_string().as_str()), } } } impl Debug for UnmarshalError { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } } impl Error for UnmarshalError {} impl From for UnmarshalError { #[inline(always)] fn from(_: crate::buffer::OutOfBoundsError) -> Self { Self::OutOfBounds } } impl From for UnmarshalError { #[inline(always)] fn from(e: std::io::Error) -> Self { Self::IoError(e) } }