peek_poke/lib.rs
1// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! Fast binary serialization and deserialization for types with a known maximum size.
12//!
13//! ## Binary Encoding Scheme
14//!
15//! ## Usage
16//!
17//! ## Comparison to bincode
18
19#[cfg(feature = "derive")]
20pub use peek_poke_derive::*;
21
22use core::{marker::PhantomData, mem::size_of, slice};
23use crate::{slice_ext::*, vec_ext::*};
24
25mod slice_ext;
26mod vec_ext;
27
28union MaybeUninitShim<T: Copy> {
29 uninit: (),
30 init: T,
31}
32
33/// Peek helper for constructing a `T` by `Copy`ing into an uninitialized stack
34/// allocation.
35///
36/// # Safety
37///
38/// * `bytes` must denote a valid pointer to a block of memory.
39///
40/// * `bytes` must point to at least the number of bytes returned by
41/// `Poke::max_size()`.
42pub unsafe fn peek_from_uninit<T: Copy + Peek>(bytes: *const u8) -> (T, *const u8) {
43 let mut val = MaybeUninitShim { uninit: () };
44 let bytes = <T>::peek_from(bytes, &mut val.init);
45 (val.init, bytes)
46}
47
48/// Peek helper for constructing a `T` by `Default` initialized stack
49/// allocation.
50///
51/// # Safety
52///
53/// * `bytes` must denote a valid pointer to a block of memory.
54///
55/// * `bytes` must point to at least the number of bytes returned by
56/// `Poke::max_size()`.
57pub unsafe fn peek_from_default<T: Default + Peek>(bytes: *const u8) -> (T, *const u8) {
58 let mut val = T::default();
59 let bytes = <T>::peek_from(bytes, &mut val);
60 (val, bytes)
61}
62
63/// Peek inplace a `T` from a slice of bytes, returning a slice of the remaining
64/// bytes. `src` must contain at least `T::max_size()` bytes.
65///
66/// [`ensure_red_zone`] can be used to add required padding.
67pub fn peek_from_slice<'a, T: Peek>(src: &'a [u8], dst: &mut T) -> &'a [u8] {
68 unsafe {
69 // If src.len() == T::max_size() then src is at the start of the red-zone.
70 assert!(T::max_size() < src.len(), "WRDL: unexpected end of display list");
71 let end_ptr = T::peek_from(src.as_ptr(), dst);
72 let len = end_ptr as usize - src.as_ptr() as usize;
73 // Did someone break the T::peek_from() can't read more than T::max_size()
74 // bytes contract?
75 assert!(len <= src.len(), "WRDL: Peek::max_size was wrong");
76 slice::from_raw_parts(end_ptr, src.len() - len)
77 }
78}
79
80/// Poke helper to insert a serialized version of `src` at the beginning for `dst`.
81pub fn poke_inplace_slice<T: Poke>(src: &T, dst: &mut [u8]) {
82 assert!(T::max_size() <= dst.len(), "WRDL: buffer too small to write into");
83 unsafe {
84 src.poke_into(dst.as_mut_ptr());
85 }
86}
87
88/// Poke helper to append a serialized version of `src` to the end of `dst`.
89pub fn poke_into_vec<T: Poke>(src: &T, dst: &mut Vec<u8>) {
90 dst.reserve(T::max_size());
91 unsafe {
92 let ptr = dst.as_end_mut_ptr();
93 let end_ptr = src.poke_into(ptr);
94 dst.set_end_ptr(end_ptr);
95 }
96}
97
98// TODO: Is returning the len of the iterator of any practical use?
99pub fn poke_extend_vec<I>(src: I, dst: &mut Vec<u8>) -> usize
100where
101 I: ExactSizeIterator,
102 I::Item: Poke,
103{
104 let len = src.len();
105 let max_size = len * I::Item::max_size();
106 dst.reserve(max_size);
107 unsafe {
108 let ptr = dst.as_end_mut_ptr();
109 // Guard against the possibility of a misbehaved implementation of
110 // ExactSizeIterator by writing at most `len` items.
111 let end_ptr = src.take(len).fold(ptr, |ptr, item| item.poke_into(ptr));
112 dst.set_end_ptr(end_ptr);
113 }
114
115 len
116}
117
118/// Add `T::max_size()` "red zone" (padding of zeroes) to the end of the vec of
119/// `bytes`. This allows deserialization to assert that at least `T::max_size()`
120/// bytes exist at all times.
121pub fn ensure_red_zone<T: Poke>(bytes: &mut Vec<u8>) {
122 bytes.reserve(T::max_size());
123 unsafe {
124 let end_ptr = bytes.as_end_mut_ptr();
125 end_ptr.write_bytes(0, T::max_size());
126 bytes.set_end_ptr(end_ptr.add(T::max_size()));
127 }
128}
129
130/// Remove the "red zone" (padding of zeroes) from the end of the vec of `bytes`.
131/// This is effectively the inverse of `ensure_red_zone`, with the caveat that
132/// space reserved for the red zone is not un-reserved. Callers are repsonsible
133/// for making sure the vec actually has a red zone, otherwise data bytes can
134/// get stripped instead.
135pub fn strip_red_zone<T: Poke>(bytes: &mut Vec<u8>) {
136 assert!(bytes.len() >= T::max_size());
137 unsafe {
138 let end_ptr = bytes.as_end_mut_ptr();
139 bytes.set_end_ptr(end_ptr.sub(T::max_size()));
140 }
141}
142
143#[inline]
144unsafe fn read_verbatim<T>(src: *const u8, dst: *mut T) -> *const u8 {
145 *dst = (src as *const T).read_unaligned();
146 src.add(size_of::<T>())
147}
148
149#[inline]
150unsafe fn write_verbatim<T>(src: T, dst: *mut u8) -> *mut u8 {
151 (dst as *mut T).write_unaligned(src);
152 dst.add(size_of::<T>())
153}
154
155#[cfg(feature = "extras")]
156mod euclid;
157
158/// A trait for values that provide serialization into buffers of bytes.
159///
160/// # Example
161///
162/// ```no_run
163/// use peek_poke::Poke;
164///
165/// struct Bar {
166/// a: u32,
167/// b: u8,
168/// c: i16,
169/// }
170///
171/// unsafe impl Poke for Bar {
172/// fn max_size() -> usize {
173/// <u32>::max_size() + <u8>::max_size() + <i16>::max_size()
174/// }
175/// unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
176/// let bytes = self.a.poke_into(bytes);
177/// let bytes = self.b.poke_into(bytes);
178/// self.c.poke_into(bytes)
179/// }
180/// }
181/// ```
182///
183/// # Safety
184///
185/// The `Poke` trait is an `unsafe` trait for the reasons, and implementors must
186/// ensure that they adhere to these contracts:
187///
188/// * `max_size()` query and calculations in general must be correct. Callers
189/// of this trait are expected to rely on the contract defined on each
190/// method, and implementors must ensure such contracts remain true.
191pub unsafe trait Poke {
192 /// Return the maximum number of bytes that the serialized version of `Self`
193 /// will occupy.
194 ///
195 /// # Safety
196 ///
197 /// Implementors of `Poke` guarantee to not write more than the result of
198 /// calling `max_size()` into the buffer pointed to by `bytes` when
199 /// `poke_into()` is called.
200 fn max_size() -> usize;
201 /// Serialize into the buffer pointed to by `bytes`.
202 ///
203 /// Returns a pointer to the next byte after the serialized representation of `Self`.
204 ///
205 /// # Safety
206 ///
207 /// This function is unsafe because undefined behavior can result if the
208 /// caller does not ensure all of the following:
209 ///
210 /// * `bytes` must denote a valid pointer to a block of memory.
211 ///
212 /// * `bytes` must pointer to at least the number of bytes returned by
213 /// `max_size()`.
214 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8;
215}
216
217/// A trait for values that provide deserialization from buffers of bytes.
218///
219/// # Example
220///
221/// ```ignore
222/// use peek_poke::Peek;
223///
224/// struct Bar {
225/// a: u32,
226/// b: u8,
227/// c: i16,
228/// }
229///
230/// ...
231///
232/// impl Peek for Bar {
233/// unsafe fn peek_from(&mut self, bytes: *const u8) -> *const u8 {
234/// let bytes = self.a.peek_from(bytes);
235/// let bytes = self.b.peek_from(bytes);
236/// self.c.peek_from(bytes)
237/// }
238/// }
239/// ```
240///
241/// # Safety
242///
243/// The `Peek` trait contains unsafe methods for the following reasons, and
244/// implementors must ensure that they adhere to these contracts:
245///
246/// * Callers of this trait are expected to rely on the contract defined on each
247/// method, and implementors must ensure that `peek_from()` doesn't read more
248/// bytes from `bytes` than is returned by `Peek::max_size()`.
249pub trait Peek: Poke {
250 /// Deserialize from the buffer pointed to by `bytes`.
251 ///
252 /// Returns a pointer to the next byte after the unconsumed bytes not used
253 /// to deserialize the representation of `Self`.
254 ///
255 /// # Safety
256 ///
257 /// This function is unsafe because undefined behavior can result if the
258 /// caller does not ensure all of the following:
259 ///
260 /// * `bytes` must denote a valid pointer to a block of memory.
261 ///
262 /// * `bytes` must pointer to at least the number of bytes returned by
263 /// `Poke::max_size()`.
264 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8;
265}
266
267macro_rules! impl_poke_for_deref {
268 (<$($desc:tt)+) => {
269 unsafe impl <$($desc)+ {
270 #[inline(always)]
271 fn max_size() -> usize {
272 <T>::max_size()
273 }
274 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
275 (**self).poke_into(bytes)
276 }
277 }
278 }
279}
280
281impl_poke_for_deref!(<'a, T: Poke> Poke for &'a T);
282impl_poke_for_deref!(<'a, T: Poke> Poke for &'a mut T);
283
284macro_rules! impl_for_primitive {
285 ($($ty:ty)+) => {
286 $(unsafe impl Poke for $ty {
287 #[inline(always)]
288 fn max_size() -> usize {
289 size_of::<Self>()
290 }
291 #[inline(always)]
292 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
293 write_verbatim(*self, bytes)
294 }
295 }
296 impl Peek for $ty {
297 #[inline(always)]
298 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
299 read_verbatim(bytes, output)
300 }
301 })+
302 };
303}
304
305impl_for_primitive! {
306 i8 i16 i32 i64 isize
307 u8 u16 u32 u64 usize
308 f32 f64
309}
310
311unsafe impl Poke for bool {
312 #[inline(always)]
313 fn max_size() -> usize {
314 u8::max_size()
315 }
316 #[inline]
317 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
318 (*self as u8).poke_into(bytes)
319 }
320}
321
322impl Peek for bool {
323 #[inline]
324 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
325 let mut int_bool = 0u8;
326 let ptr = <u8>::peek_from(bytes, &mut int_bool);
327 *output = int_bool != 0;
328 ptr
329 }
330}
331
332unsafe impl<T> Poke for PhantomData<T> {
333 #[inline(always)]
334 fn max_size() -> usize {
335 0
336 }
337 #[inline(always)]
338 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
339 bytes
340 }
341}
342
343impl<T> Peek for PhantomData<T> {
344 #[inline(always)]
345 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
346 *output = PhantomData;
347 bytes
348 }
349}
350
351unsafe impl<T: Poke> Poke for Option<T> {
352 #[inline(always)]
353 fn max_size() -> usize {
354 u8::max_size() + T::max_size()
355 }
356
357 #[inline]
358 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
359 match self {
360 None => 0u8.poke_into(bytes),
361 Some(ref v) => {
362 let bytes = 1u8.poke_into(bytes);
363 v.poke_into(bytes)
364 }
365 }
366 }
367}
368
369impl<T: Default + Peek> Peek for Option<T> {
370 #[inline]
371 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
372 let (variant, bytes) = peek_from_default::<u8>(bytes);
373 match variant {
374 0 => {
375 *output = None;
376 bytes
377 }
378 1 => {
379 let (val, bytes) = peek_from_default(bytes);
380 *output = Some(val);
381 bytes
382 }
383 _ => unreachable!(),
384 }
385 }
386}
387
388macro_rules! impl_for_arrays {
389 ($($len:tt)+) => {
390 $(unsafe impl<T: Poke> Poke for [T; $len] {
391 fn max_size() -> usize {
392 $len * T::max_size()
393 }
394 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
395 self.iter().fold(bytes, |bytes, e| e.poke_into(bytes))
396 }
397 }
398 impl<T: Peek> Peek for [T; $len] {
399 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
400 (&mut *output).iter_mut().fold(bytes, |bytes, e| <T>::peek_from(bytes, e))
401 }
402 })+
403 }
404}
405
406impl_for_arrays! {
407 1 2 3 4 5 6 7 8 9 10
408 11 12 13 14 15 16 17 18 19 20
409 21 22 23 24 25 26 27 28 29 30
410 31 32
411}
412
413unsafe impl Poke for () {
414 fn max_size() -> usize {
415 0
416 }
417 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
418 bytes
419 }
420}
421impl Peek for () {
422 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
423 *output = ();
424 bytes
425 }
426}
427
428macro_rules! impl_for_tuple {
429 ($($n:tt: $ty:ident),+) => {
430 unsafe impl<$($ty: Poke),+> Poke for ($($ty,)+) {
431 #[inline(always)]
432 fn max_size() -> usize {
433 0 $(+ <$ty>::max_size())+
434 }
435 unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
436 $(let bytes = self.$n.poke_into(bytes);)+
437 bytes
438 }
439 }
440 impl<$($ty: Peek),+> Peek for ($($ty,)+) {
441 unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
442 $(let bytes = $ty::peek_from(bytes, &mut (*output).$n);)+
443 bytes
444 }
445 }
446 }
447}
448
449impl_for_tuple!(0: A);
450impl_for_tuple!(0: A, 1: B);
451impl_for_tuple!(0: A, 1: B, 2: C);
452impl_for_tuple!(0: A, 1: B, 2: C, 3: D);
453impl_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);