summaryrefslogtreecommitdiffstats
path: root/gfx/wr/peek-poke/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/peek-poke/src/lib.rs')
-rw-r--r--gfx/wr/peek-poke/src/lib.rs440
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);