packed/
lib.rs

1#![deny(missing_docs)]
2#![cfg_attr(feature = "oibit", feature(optin_builtin_traits))]
3
4//! A safe approach to using `#[repr(packed)]` data.
5//!
6//! See `nue_macros` for the automagic `#[packed]` attribute.
7
8use std::mem::{transmute, uninitialized, forget, align_of, size_of};
9use std::ptr::write;
10use std::marker::PhantomData;
11
12/// A marker trait indicating that a type has an alignment of `1`.
13///
14/// In general, only applies to `()`, `bool`, `i8`, `u8`, and any types that
15/// contain only members of these types.
16pub unsafe trait Unaligned { }
17
18/// A type alias that represents the unaligned type of `T`.
19pub type Un<T> = <T as Aligned>::Unaligned;
20
21/// Determines whether a pointer or reference is correctly aligned for type `T`.
22#[inline]
23pub fn is_aligned_for<T, U>(ptr: *const U) -> bool {
24    align_of::<U>() >= align_of::<T>() ||
25        ptr as usize % align_of::<T>() == 0
26}
27
28/// Determines whether a slice is correctly aligned for type `T`.
29#[inline]
30pub fn is_aligned_for_slice<T, U>(slice: &[U]) -> bool {
31    is_aligned_for::<T, _>(slice.as_ptr())
32}
33
34/// Calculates the total byte size of a slice.
35#[inline]
36pub fn size_of_slice<T>(slice: &[T]) -> usize {
37    slice.len() * size_of::<T>()
38}
39
40/// A trait for converting types with alignments greater than `1`
41/// into their unaligned equivalent.
42pub unsafe trait Aligned: Sized {
43    /// An unaligned representation of this type. Usually a u8 array of the
44    /// same size.
45    type Unaligned: Unaligned + Sized + Copy;
46
47    /// Determines whether an unaligned representation of this type is aligned.
48    #[inline]
49    fn is_aligned(unaligned: &Self::Unaligned) -> bool {
50        is_aligned_for::<Self, _>(unaligned)
51    }
52
53    /// Borrows the value as unaligned.
54    #[inline]
55    fn as_unaligned(&self) -> &Self::Unaligned {
56        unsafe { transmute(self) }
57    }
58
59    /// Mutably borrows the value as unaligned.
60    #[inline]
61    unsafe fn as_unaligned_mut(&mut self) -> &mut Self::Unaligned {
62        transmute(self)
63    }
64
65    /// Borrows an unaligned type as an aligned value.
66    ///
67    /// Returns `None` if `unaligned` is not aligned.
68    #[inline]
69    fn from_unaligned_ref(unaligned: &Self::Unaligned) -> Option<&Self> {
70        if Self::is_aligned(unaligned) {
71            Some(unsafe { Self::from_unaligned_unchecked(unaligned) })
72        } else {
73            None
74        }
75    }
76
77    /// Mutably borrows an unaligned type as an aligned value.
78    ///
79    /// Returns `None` if `unaligned` is not aligned.
80    #[inline]
81    unsafe fn from_unaligned_mut(unaligned: &mut Self::Unaligned) -> Option<&mut Self> {
82        if Self::is_aligned(unaligned) {
83            Some(Self::from_unaligned_mut_unchecked(unaligned))
84        } else {
85            None
86        }
87    }
88
89    /// Borrows an unaligned type as an aligned value, without first checking the alignment.
90    ///
91    /// Causes undefined behaviour if used improprly!
92    #[inline]
93    unsafe fn from_unaligned_unchecked(unaligned: &Self::Unaligned) -> &Self {
94        transmute(unaligned)
95    }
96
97    /// Mutably borrows an unaligned type as an aligned value, without first checking the alignment.
98    ///
99    /// Causes undefined behaviour if used improprly!
100    #[inline]
101    unsafe fn from_unaligned_mut_unchecked(unaligned: &mut Self::Unaligned) -> &mut Self {
102        transmute(unaligned)
103    }
104
105    /// Converts a value to its unaligned representation without dropping `self`.
106    #[inline]
107    fn into_unaligned(self) -> Self::Unaligned {
108        unsafe {
109            let mut un: Self::Unaligned = uninitialized();
110            write(&mut un, *self.as_unaligned());
111            forget(self);
112            un
113        }
114    }
115
116    /// Creates a value from its unaligned representation.
117    #[inline]
118    unsafe fn from_unaligned(u: Self::Unaligned) -> Self {
119        let mut s: Self = uninitialized();
120        write(s.as_unaligned_mut(), u);
121        s
122    }
123
124    #[doc(hidden)]
125    unsafe fn __assert_unaligned() { }
126}
127
128/// A marker trait indicating that a type is `#[repr(packed)]`.
129///
130/// This means that all its members are packed or have an alignment of `1`,
131/// and its memory layout is guaranteed to be in member declaration order.
132pub unsafe trait Packed: Unaligned {
133    #[doc(hidden)]
134    fn __assert_unaligned() { }
135}
136
137#[cfg(feature = "oibit")]
138mod impls {
139    use super::Unaligned;
140
141    unsafe impl Unaligned for .. { }
142
143    // All primitives except the packed ones listed below
144    impl !Unaligned for char { }
145    impl !Unaligned for f32 { }
146    impl !Unaligned for f64 { }
147    impl !Unaligned for i16 { }
148    impl !Unaligned for u16 { }
149    impl !Unaligned for i32 { }
150    impl !Unaligned for u32 { }
151    impl !Unaligned for i64 { }
152    impl !Unaligned for u64 { }
153    impl !Unaligned for isize { }
154    impl !Unaligned for usize { }
155    impl<T> !Unaligned for *const T { }
156    impl<T> !Unaligned for *mut T { }
157    impl<'a, T> !Unaligned for &'a T { }
158    impl<'a, T> !Unaligned for &'a mut T { }
159}
160
161#[cfg(not(feature = "oibit"))]
162mod impls {
163    use super::Unaligned;
164
165    unsafe impl Unaligned for () { }
166    unsafe impl Unaligned for i8 { }
167    unsafe impl Unaligned for u8 { }
168    unsafe impl Unaligned for bool { }
169}
170
171unsafe impl<T> Unaligned for PhantomData<T> { }
172
173macro_rules! aligned_assert {
174    ($t:ident) => {
175        unsafe fn __assert_unaligned() {
176            ::std::mem::forget(::std::mem::transmute::<$t, $t::Unaligned>(::std::mem::uninitialized()));
177        }
178    };
179}
180
181macro_rules! aligned_self {
182    ($t:ty) => {
183        unsafe impl Aligned for $t {
184            type Unaligned = $t;
185        }
186    };
187    ($($t:ty),*) => {
188        $(
189            aligned_self!($t);
190        )*
191    };
192}
193
194macro_rules! aligned_impl {
195    ($t:ident: $s:expr) => {
196        unsafe impl Aligned for $t {
197            type Unaligned = [u8; $s];
198
199            aligned_assert!(Self);
200        }
201    };
202    ($($t:ident: $e:expr),*) => {
203        $(
204            aligned_impl!($t: $e);
205        )*
206    };
207}
208
209aligned_impl! {
210    char: 4,
211    f32: 4,
212    f64: 8,
213    i16: 2,
214    u16: 2,
215    i32: 4,
216    u32: 4,
217    i64: 8,
218    u64: 8
219}
220
221aligned_self! {
222    u8,
223    i8,
224    (),
225    bool
226}
227
228#[cfg(target_pointer_width = "32")]
229mod impl32 {
230    use super::Aligned;
231    aligned_impl! { isize: 4, usize: 4 }
232    unsafe impl<T: Sized> Aligned for *const T { type Unaligned = [u8; 4]; aligned_assert!(Self); }
233    unsafe impl<T: Sized> Aligned for *mut T { type Unaligned = [u8; 4]; aligned_assert!(Self); }
234    unsafe impl<'a, T: Sized> Aligned for &'a T { type Unaligned = [u8; 4]; }
235    unsafe impl<'a, T: Sized> Aligned for &'a mut T { type Unaligned = [u8; 4]; }
236}
237
238#[cfg(target_pointer_width = "64")]
239mod impl64 {
240    use super::Aligned;
241    aligned_impl! { isize: 8, usize: 8 }
242    unsafe impl<T: Sized> Aligned for *const T { type Unaligned = [u8; 8]; aligned_assert!(Self); }
243    unsafe impl<T: Sized> Aligned for *mut T { type Unaligned = [u8; 8]; aligned_assert!(Self); }
244    unsafe impl<'a, T: Sized> Aligned for &'a T { type Unaligned = [u8; 8]; }
245    unsafe impl<'a, T: Sized> Aligned for &'a mut T { type Unaligned = [u8; 8]; }
246}
247
248// TODO: why does this conflict?
249//unsafe impl<T: Unaligned + Sized + Copy> Aligned for T { type Unaligned = T; }
250
251unsafe impl Packed for () { }
252unsafe impl Packed for i8 { }
253unsafe impl Packed for u8 { }
254unsafe impl Packed for bool { }
255unsafe impl<T> Packed for PhantomData<T> { }
256
257macro_rules! packed_def {
258    (=> $id_0:ident) => {};
259    (=> $id_0:ident $(, $ids:ident)*) => {
260        packed_def! {
261            => $($ids),*
262        }
263
264        unsafe impl<$($ids: Unaligned),*> Unaligned for ($($ids),*,) { }
265
266        // TODO: The language probably doesn't guarantee ordering for tuples, even when all are 1-aligned, so this is incorrect?
267        unsafe impl<$($ids: Unaligned),*> Packed for ($($ids),*,) { }
268    };
269    ($($x:expr),*) => {
270        $(
271            unsafe impl<T: Unaligned> Unaligned for [T; $x] { }
272
273            unsafe impl<T: Unaligned> Packed for [T; $x] { }
274        )*
275    };
276}
277
278unsafe impl<T: Aligned> Aligned for (T,) {
279    type Unaligned = T::Unaligned;
280}
281
282unsafe impl<T: Aligned> Aligned for [T; 1] {
283    type Unaligned = T::Unaligned;
284}
285
286unsafe impl<T: Aligned> Aligned for [T; 0] {
287    type Unaligned = ();
288}
289
290packed_def! {
291    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
292    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
293    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
294    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
295    0x40,
296    0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00,
297    0x1000
298}
299packed_def! { => __, A, B, C, D, E, F, G, H, I, J, K }
300
301#[cfg(test)]
302mod test {
303    use super::*;
304
305    #[test]
306    fn assert_packed() {
307        fn is<T: Packed>() { }
308        fn is_unaligned<T: Unaligned>() { }
309
310        is::<()>();
311        is::<u8>();
312        is::<i8>();
313        is::<bool>();
314        is_unaligned::<(bool, u8)>();
315    }
316
317    #[test]
318    fn unaligned_conversion() {
319        let f = 0.5f32;
320        let x = Aligned::into_unaligned(f);
321        assert!(<f32 as Aligned>::is_aligned(&x));
322        assert_eq!(&f, <f32 as Aligned>::from_unaligned_ref(&x).unwrap());
323    }
324
325    #[test]
326    fn pointer_alignment() {
327        use std::mem::align_of;
328
329        let f = 0.5f32;
330        assert!(is_aligned_for::<f32, _>(&f));
331        assert!(is_aligned_for::<u8, _>(&f));
332        if align_of::<f32>() > 1 {
333            assert!(!is_aligned_for::<f32, _>((&f as *const _ as usize + 1) as *const u8));
334        }
335    }
336}