// Copyright 2019 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Fast binary serialization and deserialization for types with a known maximum size. //! //! ## Binary Encoding Scheme //! //! ## Usage //! //! ## Comparison to bincode #[cfg(feature = "derive")] pub use peek_poke_derive::*; use core::{marker::PhantomData, mem::size_of, slice}; use crate::{slice_ext::*, vec_ext::*}; mod slice_ext; mod vec_ext; union MaybeUninitShim { uninit: (), init: T, } /// Peek helper for constructing a `T` by `Copy`ing into an uninitialized stack /// allocation. pub unsafe fn peek_from_uninit(bytes: *const u8) -> (T, *const u8) { let mut val = MaybeUninitShim { uninit: () }; let bytes = ::peek_from(bytes, &mut val.init); (val.init, bytes) } /// Peek helper for constructing a `T` by `Default` initialized stack /// allocation. pub unsafe fn peek_from_default(bytes: *const u8) -> (T, *const u8) { let mut val = T::default(); let bytes = ::peek_from(bytes, &mut val); (val, bytes) } /// Peek inplace a `T` from a slice of bytes, returning a slice of the remaining /// bytes. `src` must contain at least `T::max_size()` bytes. /// /// [`ensure_red_zone`] can be used to add required padding. pub fn peek_from_slice<'a, T: Peek>(src: &'a [u8], dst: &mut T) -> &'a [u8] { unsafe { // If src.len() == T::max_size() then src is at the start of the red-zone. assert!(T::max_size() < src.len(), "WRDL: unexpected end of display list"); let end_ptr = T::peek_from(src.as_ptr(), dst); let len = end_ptr as usize - src.as_ptr() as usize; // Did someone break the T::peek_from() can't read more than T::max_size() // bytes contract? assert!(len <= src.len(), "WRDL: Peek::max_size was wrong"); slice::from_raw_parts(end_ptr, src.len() - len) } } /// Poke helper to insert a serialized version of `src` at the beginning for `dst`. pub fn poke_inplace_slice(src: &T, dst: &mut [u8]) { assert!(T::max_size() <= dst.len(), "WRDL: buffer too small to write into"); unsafe { src.poke_into(dst.as_mut_ptr()); } } /// Poke helper to append a serialized version of `src` to the end of `dst`. pub fn poke_into_vec(src: &T, dst: &mut Vec) { dst.reserve(T::max_size()); unsafe { let ptr = dst.as_end_mut_ptr(); let end_ptr = src.poke_into(ptr); dst.set_end_ptr(end_ptr); } } // TODO: Is returning the len of the iterator of any practical use? pub fn poke_extend_vec(src: I, dst: &mut Vec) -> usize where I: ExactSizeIterator, I::Item: Poke, { let len = src.len(); let max_size = len * I::Item::max_size(); dst.reserve(max_size); unsafe { let ptr = dst.as_end_mut_ptr(); // Guard against the possibility of a misbehaved implementation of // ExactSizeIterator by writing at most `len` items. let end_ptr = src.take(len).fold(ptr, |ptr, item| item.poke_into(ptr)); dst.set_end_ptr(end_ptr); } len } /// Add `T::max_size()` "red zone" (padding of zeroes) to the end of the vec of /// `bytes`. This allows deserialization to assert that at least `T::max_size()` /// bytes exist at all times. pub fn ensure_red_zone(bytes: &mut Vec) { bytes.reserve(T::max_size()); unsafe { let end_ptr = bytes.as_end_mut_ptr(); end_ptr.write_bytes(0, T::max_size()); bytes.set_end_ptr(end_ptr.add(T::max_size())); } } /// Remove the "red zone" (padding of zeroes) from the end of the vec of `bytes`. /// This is effectively the inverse of `ensure_red_zone`, with the caveat that /// space reserved for the red zone is not un-reserved. Callers are repsonsible /// for making sure the vec actually has a red zone, otherwise data bytes can /// get stripped instead. pub fn strip_red_zone(bytes: &mut Vec) { assert!(bytes.len() >= T::max_size()); unsafe { let end_ptr = bytes.as_end_mut_ptr(); bytes.set_end_ptr(end_ptr.sub(T::max_size())); } } #[inline] unsafe fn read_verbatim(src: *const u8, dst: *mut T) -> *const u8 { *dst = (src as *const T).read_unaligned(); src.add(size_of::()) } #[inline] unsafe fn write_verbatim(src: T, dst: *mut u8) -> *mut u8 { (dst as *mut T).write_unaligned(src); dst.add(size_of::()) } #[cfg(feature = "extras")] mod euclid; /// A trait for values that provide serialization into buffers of bytes. /// /// # Example /// /// ```no_run /// use peek_poke::Poke; /// /// struct Bar { /// a: u32, /// b: u8, /// c: i16, /// } /// /// unsafe impl Poke for Bar { /// fn max_size() -> usize { /// ::max_size() + ::max_size() + ::max_size() /// } /// unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { /// let bytes = self.a.poke_into(bytes); /// let bytes = self.b.poke_into(bytes); /// self.c.poke_into(bytes) /// } /// } /// ``` /// /// # Safety /// /// The `Poke` trait is an `unsafe` trait for the reasons, and implementors must /// ensure that they adhere to these contracts: /// /// * `max_size()` query and calculations in general must be correct. Callers /// of this trait are expected to rely on the contract defined on each /// method, and implementors must ensure such contracts remain true. pub unsafe trait Poke { /// Return the maximum number of bytes that the serialized version of `Self` /// will occupy. /// /// # Safety /// /// Implementors of `Poke` guarantee to not write more than the result of /// calling `max_size()` into the buffer pointed to by `bytes` when /// `poke_into()` is called. fn max_size() -> usize; /// Serialize into the buffer pointed to by `bytes`. /// /// Returns a pointer to the next byte after the serialized representation of `Self`. /// /// # Safety /// /// This function is unsafe because undefined behavior can result if the /// caller does not ensure all of the following: /// /// * `bytes` must denote a valid pointer to a block of memory. /// /// * `bytes` must pointer to at least the number of bytes returned by /// `max_size()`. unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8; } /// A trait for values that provide deserialization from buffers of bytes. /// /// # Example /// /// ```ignore /// use peek_poke::Peek; /// /// struct Bar { /// a: u32, /// b: u8, /// c: i16, /// } /// /// ... /// /// impl Peek for Bar { /// unsafe fn peek_from(&mut self, bytes: *const u8) -> *const u8 { /// let bytes = self.a.peek_from(bytes); /// let bytes = self.b.peek_from(bytes); /// self.c.peek_from(bytes) /// } /// } /// ``` /// /// # Safety /// /// The `Peek` trait contains unsafe methods for the following reasons, and /// implementors must ensure that they adhere to these contracts: /// /// * Callers of this trait are expected to rely on the contract defined on each /// method, and implementors must ensure that `peek_from()` doesn't read more /// bytes from `bytes` than is returned by `Peek::max_size()`. pub trait Peek: Poke { /// Deserialize from the buffer pointed to by `bytes`. /// /// Returns a pointer to the next byte after the unconsumed bytes not used /// to deserialize the representation of `Self`. /// /// # Safety /// /// This function is unsafe because undefined behavior can result if the /// caller does not ensure all of the following: /// /// * `bytes` must denote a valid pointer to a block of memory. /// /// * `bytes` must pointer to at least the number of bytes returned by /// `Poke::max_size()`. unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8; } macro_rules! impl_poke_for_deref { (<$($desc:tt)+) => { unsafe impl <$($desc)+ { #[inline(always)] fn max_size() -> usize { ::max_size() } unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { (**self).poke_into(bytes) } } } } impl_poke_for_deref!(<'a, T: Poke> Poke for &'a T); impl_poke_for_deref!(<'a, T: Poke> Poke for &'a mut T); macro_rules! impl_for_primitive { ($($ty:ty)+) => { $(unsafe impl Poke for $ty { #[inline(always)] fn max_size() -> usize { size_of::() } #[inline(always)] unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { write_verbatim(*self, bytes) } } impl Peek for $ty { #[inline(always)] unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { read_verbatim(bytes, output) } })+ }; } impl_for_primitive! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize f32 f64 } unsafe impl Poke for bool { #[inline(always)] fn max_size() -> usize { u8::max_size() } #[inline] unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { (*self as u8).poke_into(bytes) } } impl Peek for bool { #[inline] unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { let mut int_bool = 0u8; let ptr = ::peek_from(bytes, &mut int_bool); *output = int_bool != 0; ptr } } unsafe impl Poke for PhantomData { #[inline(always)] fn max_size() -> usize { 0 } #[inline(always)] unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { bytes } } impl Peek for PhantomData { #[inline(always)] unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { *output = PhantomData; bytes } } unsafe impl Poke for Option { #[inline(always)] fn max_size() -> usize { u8::max_size() + T::max_size() } #[inline] unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { match self { None => 0u8.poke_into(bytes), Some(ref v) => { let bytes = 1u8.poke_into(bytes); let bytes = v.poke_into(bytes); bytes } } } } impl Peek for Option { #[inline] unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { let (variant, bytes) = peek_from_default::(bytes); match variant { 0 => { *output = None; bytes } 1 => { let (val, bytes) = peek_from_default(bytes); *output = Some(val); bytes } _ => unreachable!(), } } } macro_rules! impl_for_arrays { ($($len:tt)+) => { $(unsafe impl Poke for [T; $len] { fn max_size() -> usize { $len * T::max_size() } unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { self.iter().fold(bytes, |bytes, e| e.poke_into(bytes)) } } impl Peek for [T; $len] { unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { (&mut *output).iter_mut().fold(bytes, |bytes, e| ::peek_from(bytes, e)) } })+ } } impl_for_arrays! { 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } unsafe impl Poke for () { fn max_size() -> usize { 0 } unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { bytes } } impl Peek for () { unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { *output = (); bytes } } macro_rules! impl_for_tuple { ($($n:tt: $ty:ident),+) => { unsafe impl<$($ty: Poke),+> Poke for ($($ty,)+) { #[inline(always)] fn max_size() -> usize { 0 $(+ <$ty>::max_size())+ } unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { $(let bytes = self.$n.poke_into(bytes);)+ bytes } } impl<$($ty: Peek),+> Peek for ($($ty,)+) { unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { $(let bytes = $ty::peek_from(bytes, &mut (*output).$n);)+ bytes } } } } impl_for_tuple!(0: A); impl_for_tuple!(0: A, 1: B); impl_for_tuple!(0: A, 1: B, 2: C); impl_for_tuple!(0: A, 1: B, 2: C, 3: D); impl_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);