diff options
Diffstat (limited to 'gfx/wr/peek-poke/src/lib.rs')
-rw-r--r-- | gfx/wr/peek-poke/src/lib.rs | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/gfx/wr/peek-poke/src/lib.rs b/gfx/wr/peek-poke/src/lib.rs new file mode 100644 index 0000000000..18f6a9acd8 --- /dev/null +++ b/gfx/wr/peek-poke/src/lib.rs @@ -0,0 +1,440 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: Copy> { + uninit: (), + init: T, +} + +/// Peek helper for constructing a `T` by `Copy`ing into an uninitialized stack +/// allocation. +pub unsafe fn peek_from_uninit<T: Copy + Peek>(bytes: *const u8) -> (T, *const u8) { + let mut val = MaybeUninitShim { uninit: () }; + let bytes = <T>::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<T: Default + Peek>(bytes: *const u8) -> (T, *const u8) { + let mut val = T::default(); + let bytes = <T>::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<T: Poke>(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<T: Poke>(src: &T, dst: &mut Vec<u8>) { + 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<I>(src: I, dst: &mut Vec<u8>) -> 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<T: Poke>(bytes: &mut Vec<u8>) { + 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<T: Poke>(bytes: &mut Vec<u8>) { + 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<T>(src: *const u8, dst: *mut T) -> *const u8 { + *dst = (src as *const T).read_unaligned(); + src.add(size_of::<T>()) +} + +#[inline] +unsafe fn write_verbatim<T>(src: T, dst: *mut u8) -> *mut u8 { + (dst as *mut T).write_unaligned(src); + dst.add(size_of::<T>()) +} + +#[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 { +/// <u32>::max_size() + <u8>::max_size() + <i16>::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 { + <T>::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::<Self>() + } + #[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 = <u8>::peek_from(bytes, &mut int_bool); + *output = int_bool != 0; + ptr + } +} + +unsafe impl<T> Poke for PhantomData<T> { + #[inline(always)] + fn max_size() -> usize { + 0 + } + #[inline(always)] + unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { + bytes + } +} + +impl<T> Peek for PhantomData<T> { + #[inline(always)] + unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { + *output = PhantomData; + bytes + } +} + +unsafe impl<T: Poke> Poke for Option<T> { + #[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<T: Default + Peek> Peek for Option<T> { + #[inline] + unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { + let (variant, bytes) = peek_from_default::<u8>(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<T: Poke> 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<T: Peek> Peek for [T; $len] { + unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { + (&mut *output).iter_mut().fold(bytes, |bytes, e| <T>::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); |