bytes_cast/lib.rs
1//! Utilities for safely re-interpreting `&[u8]` bytes as custom structs
2//! and back without copying, for efficiently reading structured binary data.
3//!
4//! # Example
5//!
6//! Reading bytes:
7//!
8//! ```
9//! use bytes_cast::{BytesCast, unaligned};
10//!
11//! #[derive(BytesCast)]
12//! #[repr(C)]
13//! struct Foo {
14//! bar: [u8; 2],
15//! baz: unaligned::U32Be,
16//! }
17//!
18//! let input = &[1_u8, 2, 3, 4, 5, 6, 7, 8];
19//!
20//! let (foo, rest) = Foo::from_bytes(input).unwrap();
21//! assert_eq!(foo.bar, [1_u8, 2]);
22//! assert_eq!(foo.baz.get(), 0x0304_0506_u32);
23//! assert_eq!(rest, &[7_u8, 8]);
24//!
25//! assert!(<[Foo; 2]>::from_bytes(input).is_err()); // input is too short
26//!
27//! let (values, rest) = unaligned::U16Le::slice_from_bytes(input, 2).unwrap();
28//! assert_eq!(values.len(), 2);
29//! assert_eq!(values[0].get(), 0x02_01_u16);
30//! assert_eq!(values[1].get(), 0x04_03_u16);
31//! assert_eq!(rest, &[5_u8, 6, 7, 8]);
32//!
33//! assert!(unaligned::U16Le::slice_from_bytes(input, 5).is_err()); // input is too short
34//! ```
35//!
36//! Writing bytes:
37//!
38//! ```
39//! # use bytes_cast::{BytesCast, unaligned};
40//! # #[derive(BytesCast)]
41//! # #[repr(C)]
42//! # struct Foo {
43//! # bar: [u8; 2],
44//! # baz: unaligned::U32Be,
45//! # }
46//!
47//! let foo = Foo { bar: [1, 2], baz: 0x0304_0506.into() };
48//! assert_eq!(foo.as_bytes(), &[1_u8, 2, 3, 4, 5, 6]);
49//!
50//! let slice: &[unaligned::U16Le] = &[0x02_01.into(), 0x04_03.into()];
51//! assert_eq!(slice.as_bytes(), &[1_u8, 2, 3, 4]);
52//! ```
53
54#![no_std]
55
56pub use bytes_cast_derive::BytesCast;
57use core::fmt;
58use core::mem;
59use core::slice;
60
61pub mod unaligned;
62
63#[cfg(doctest)]
64mod compile_fail_tests;
65
66/// Marks a type as safe to interpret from and to bytes without copying.
67///
68/// # Safety
69///
70/// For a type to implement this trait:
71///
72/// * All initialized bit patterns must be valid. (This excludes `bool`, enums, etc.)
73/// * There must not be an alignment requirement. (`align_of() == 1`)
74/// * There must be no padding or otherwise uninitialized bytes
75///
76/// # Deriving
77///
78/// Instead of writing `unsafe impl` blocks this trait should be derived.
79/// `#[derive(BytesCast)]` on a type definition invokes a procedural macro
80/// that implements the trait after checking that the type:
81///
82/// * Is a `struct`
83/// * Is not generic
84/// * Has a `#[repr(C)]` or `#[repr(transparent)]` attribute
85/// * Has `align_of() == 1`
86/// * Only has fields whose respective type implement `BytesCast`.
87///
88/// Failing any of these checks causes a compile-time error.
89/// This excludes some types that could implement `BytesCast` without memory safety
90/// issue:
91///
92/// * By choice: disabling field reordering with `repr` is not about memory
93/// safety but making memory layout / field offsets predictable.
94/// * By necessity: generics would make `align_of` potentially depend on type
95/// parameters and not possible to statically check at the struct definition
96/// site.
97pub unsafe trait BytesCast {
98 /// Interpret the start of the given slice of bytes as reference to this
99 /// type.
100 ///
101 /// If the given input is large enough, returns a tuple of the new
102 /// reference and the remaining of the bytes.
103 #[inline]
104 fn from_bytes(bytes: &[u8]) -> Result<(&Self, &[u8]), FromBytesError>
105 where
106 Self: Sized,
107 {
108 let expected_len = mem::size_of::<Self>();
109 remaining_bytes(bytes, expected_len).map(|rest| {
110 // Safety: this cast and dereference are made sound by the length
111 // check done in `remaining_bytes` together with the
112 // invariants of `BytesCast`.
113 let this = unsafe { &*bytes.as_ptr().cast::<Self>() };
114 (this, rest)
115 })
116 }
117
118 /// Interpret the start of the given slice of bytes as slice of this type.
119 ///
120 /// If the given input is large enough, returns a tuple of the new
121 /// slice and the remaining of the bytes.
122 #[inline]
123 fn slice_from_bytes(bytes: &[u8], slice_len: usize) -> Result<(&[Self], &[u8]), FromBytesError>
124 where
125 Self: Sized,
126 {
127 let expected_byte_len =
128 mem::size_of::<Self>()
129 .checked_mul(slice_len)
130 .ok_or(FromBytesError {
131 input_len: bytes.len(),
132 expected_len: None,
133 })?;
134 remaining_bytes(bytes, expected_byte_len).map(|rest| {
135 // Safety: this cast and call are made sound by the length check
136 // done in `remaining_bytes` together with the invariants of
137 // `BytesCast`.
138 let this = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<Self>(), slice_len) };
139 (this, rest)
140 })
141 }
142
143 /// Interpret this value as the bytes of its memory representation.
144 #[inline]
145 fn as_bytes(&self) -> &[u8] {
146 let ptr: *const Self = self;
147 let bytes_ptr = ptr.cast::<u8>();
148 let bytes_len = mem::size_of_val(self);
149 // Safety: the invariants of `BytesCast` make this sound by definition:
150 unsafe { slice::from_raw_parts(bytes_ptr, bytes_len) }
151 }
152}
153
154/// If the given slice is long enough, return the the remaining bytes after the
155/// given length.
156#[inline]
157fn remaining_bytes(bytes: &[u8], expected_byte_len: usize) -> Result<&[u8], FromBytesError> {
158 bytes.get(expected_byte_len..).ok_or(FromBytesError {
159 input_len: bytes.len(),
160 expected_len: Some(expected_byte_len),
161 })
162}
163
164/// The error type for [`BytesCast::from_bytes`] and
165/// [`BytesCast::slice_from_bytes`].
166pub struct FromBytesError {
167 expected_len: Option<usize>,
168 input_len: usize,
169}
170
171impl fmt::Display for FromBytesError {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 if let Some(expected_len) = self.expected_len {
174 write!(
175 f,
176 "Expected at least {} bytes, got {}",
177 expected_len, self.input_len
178 )
179 } else {
180 write!(f, "Expected byte size overflowed in slice_from_bytes")
181 }
182 }
183}
184
185impl fmt::Debug for FromBytesError {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 fmt::Display::fmt(self, f)
188 }
189}
190
191unsafe impl<T: ?Sized> BytesCast for core::marker::PhantomData<T> {}
192unsafe impl<T: BytesCast> BytesCast for [T] {}
193unsafe impl BytesCast for () {}
194unsafe impl BytesCast for u8 {}
195
196// NOTE: We don’t implement BytesCast for tuples with 2 or more fields
197// because they are subject to field reordering.
198// Like with default-`repr` structs this is not a memory safety issue but still
199// a footgun. Single-field tuples don’t have that problem but are much less
200// useful in the first place.
201
202// FIXME: Use const generics when we require Rust 1.51+
203macro_rules! array_impls {
204 ($($N: expr)+) => {
205 $(
206 unsafe impl<T: BytesCast> BytesCast for [T; $N] {}
207 )+
208 };
209}
210
211array_impls!(
212 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
213 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
214);