1
+ /// Tools for storing abomonated objects with correct alignment
2
+ ///
3
+ /// Use of `decode::<T>()` requires that the input bytes are aligned on a
4
+ /// `T::alignment()` boundary, or else undefined behavior will ensue.
5
+ ///
6
+ /// This module provides tools for ensuring this alignment constraint on input
7
+ /// bytes of unknown or known-incorrect alignment before calling `decode()`.
8
+
9
+ use crate :: {
10
+ Entomb ,
11
+ Exhume ,
12
+ } ;
13
+
14
+ use std:: {
15
+ alloc:: { self , Layout } ,
16
+ marker:: PhantomData ,
17
+ ops:: { Deref , DerefMut } ,
18
+ ptr:: NonNull ,
19
+ } ;
20
+
21
+
22
+ /// Overaligned `Box<[u8]>` for abomonated objects of type T
23
+ ///
24
+ /// Compared with a regular `Box<[u8]>`, this heap-allocated bag of bytes also
25
+ /// ensures that the heap allocation is aligned on `T::alignment()`, and thus
26
+ /// suitable for use as input to `decode::<T>()`.
27
+ pub struct Coffin < T : Entomb > ( NonNull < [ u8 ] > , PhantomData < T > ) ;
28
+
29
+ impl < T : Entomb > Coffin < T > {
30
+ /// Copy abomonated bytes into a suitably aligned heap allocation
31
+ ///
32
+ /// May abort the computation if memory is exhausted or the system allocator
33
+ /// is not able to satisfy the size or alignment requirements.
34
+ pub fn new ( bytes : & [ u8 ] ) -> Self {
35
+ // Perform the memory allocation using the system allocator. This is
36
+ // safe because all safety preconditions are checked by Self::layout().
37
+ let size = bytes. len ( ) ;
38
+ let layout = Self :: layout ( size) ;
39
+ let ptr = unsafe { alloc:: alloc ( layout) } ;
40
+
41
+ // Abort on memory allocation errors the recommended way. Since the
42
+ // system allocator may abort, no point in not aborting ourselves...
43
+ if ptr. is_null ( ) { alloc:: handle_alloc_error ( layout) ; }
44
+
45
+ // Transfer the input bytes on our new allocation. This is safe as...
46
+ // - `bytes.as_ptr()` has to be valid for `size` by slice construction
47
+ // - `ptr` is non-null and must point to a memory region of `size` bytes
48
+ // - Pointers are always byte-aligned, so alignment is irrelevant.
49
+ // - Heap allocations may not overlap with existing objects.
50
+ unsafe { ptr. copy_from_nonoverlapping ( bytes. as_ptr ( ) , size) ; }
51
+
52
+ // Produce the output slice. The transmute is safe as...
53
+ // - We don't care about lifetimes as we want a NonNull in the end
54
+ // - As discussed above, `ptr` is non-null and well-aligned.
55
+ // - The bytes of the slice have been initialized above
56
+ Self ( unsafe { std:: slice:: from_raw_parts_mut ( ptr, size) } . into ( ) ,
57
+ PhantomData )
58
+ }
59
+
60
+ /// Compute the proper layout for a coffin allocation, checking the safety
61
+ /// preconditions of the system memory allocator along the way.
62
+ ///
63
+ /// We handle errors via panics because they all emerge from edge cases that
64
+ /// should only be encountered by users actively trying to break this code.
65
+ fn layout ( size : usize ) -> Layout {
66
+ // Basic sanity check for debug builds
67
+ debug_assert ! ( size > std:: mem:: size_of:: <T >( ) ,
68
+ "Requested size is quite obviously not big enough" ) ;
69
+
70
+ // We're going to use the system allocator, so we cannot accept
71
+ // zero-sized slices of bytes.
72
+ assert ! ( size > 0 , "Allocation size must be positive" ) ;
73
+
74
+ // At this point, the only layout errors that remain are those caused by
75
+ // a bad Abomonation::alignment implementation (alignment is zero or not
76
+ // a power of 2) or by a huge input size (close to usize::MAX).
77
+ Layout :: from_size_align ( size, T :: alignment ( ) )
78
+ . expect ( "Bad Abomonation::alignment() impl or excessive size" )
79
+ }
80
+ }
81
+
82
+ impl < T : Entomb > Deref for Coffin < T > {
83
+ type Target = [ u8 ] ;
84
+
85
+ fn deref ( & self ) -> & Self :: Target {
86
+ // This is safe as...
87
+ // - The target allocation is live until the Coffin will be dropped.
88
+ // - Normal borrow-checking rules apply and prevent the user from
89
+ // aliasing or retaining the output reference in an invalid way.
90
+ //
91
+ // ...but see the Drop documentation for a possible edge case :(
92
+ unsafe { self . 0 . as_ref ( ) }
93
+ }
94
+ }
95
+
96
+ impl < T : Entomb > DerefMut for Coffin < T > {
97
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
98
+ // This is safe for the same reason that Deref is.
99
+ unsafe { self . 0 . as_mut ( ) }
100
+ }
101
+ }
102
+
103
+ impl < T : Entomb > Drop for Coffin < T > {
104
+ fn drop ( & mut self ) {
105
+ // In principle, this should be safe for the same reason that DerefMut
106
+ // is, however there is a wrinkle for all of those...
107
+ //
108
+ // If we want any form of Deref to be safe, the Rust compiler must
109
+ // prevent LLVM from inserting memory reads from the slice after
110
+ // deallocation, and currently it doesn't.
111
+ //
112
+ // There is no clear reason why LLVM would do this, though, and `std`
113
+ // encounters the same problem everywhere, so we'll take the risk...
114
+ //
115
+ // FIXME: Once the Rust team has figured out the right way to handle
116
+ // this, use it here if it requires manual action.
117
+ //
118
+ // Here's one ongoing discussion of this topic for reference:
119
+ // https://github.com/rust-lang/rust/issues/55005
120
+ let slice = unsafe { self . 0 . as_mut ( ) } ;
121
+
122
+ // This is safe because...
123
+ // - Every Coffin is always created with its own allocation, only Drop
124
+ // can liberate it, and Drop will only be called once.
125
+ // - Layout is computed in the same way as in `Coffin::new()`, and the
126
+ // size of the target slice is the same as that of new's input bytes.
127
+ unsafe { alloc:: dealloc ( slice. as_mut_ptr ( ) ,
128
+ Self :: layout ( slice. len ( ) ) ) ; }
129
+ }
130
+ }
131
+
132
+
133
+ /// `Cow`-style abstraction for aligning abomonated bytes before `decode()`
134
+ ///
135
+ /// Often, one needs to decode input bytes which are _probably_ well-aligned,
136
+ /// but may not always to be. For example, POSIX memory allocations are aligned
137
+ /// on 16-byte boundaries, which is sufficient for most types... as long as
138
+ /// multiple abomonated objects are not stored in a sequence without padding
139
+ /// bytes in between.
140
+ ///
141
+ /// In those circumstances, pessimistically using `Coffin<T>` all the time
142
+ /// would cause unnecessarily intensive use of the system memory allocator.
143
+ /// Instead, it is better to check if the input bytes are well-aligned and only
144
+ /// reallocate them if necessary, which is what this abstraction does.
145
+ pub enum AlignedBytes < ' bytes , T : Exhume < ' bytes > > {
146
+ /// The orignal bytes were sufficiently well-aligned
147
+ Borrowed ( & ' bytes mut [ u8 ] ) ,
148
+
149
+ /// The abomonated bytes were relocated into a well-aligned heap location
150
+ Owned ( Coffin < T > ) ,
151
+ }
152
+
153
+ impl < ' bytes , T : Exhume < ' bytes > > AlignedBytes < ' bytes , T > {
154
+ /// Prepare possibly misaligned bytes for decoding
155
+ pub fn new ( bytes : & ' bytes mut [ u8 ] ) -> Self {
156
+ let misalignment = ( bytes. as_ptr ( ) as usize ) % T :: alignment ( ) ;
157
+ if misalignment == 0 {
158
+ Self :: Borrowed ( bytes)
159
+ } else {
160
+ Self :: Owned ( Coffin :: new ( bytes) )
161
+ }
162
+ }
163
+ }
164
+
165
+ impl < ' bytes , T : Exhume < ' bytes > > From < & ' bytes mut [ u8 ] > for AlignedBytes < ' bytes , T > {
166
+ fn from ( bytes : & ' bytes mut [ u8 ] ) -> Self {
167
+ Self :: new ( bytes)
168
+ }
169
+ }
170
+
171
+ impl < ' bytes , T : Exhume < ' bytes > > From < Coffin < T > > for AlignedBytes < ' bytes , T > {
172
+ fn from ( coffin : Coffin < T > ) -> Self {
173
+ Self :: Owned ( coffin)
174
+ }
175
+ }
176
+
177
+ impl < ' bytes , T : Exhume < ' bytes > > Deref for AlignedBytes < ' bytes , T > {
178
+ type Target = [ u8 ] ;
179
+
180
+ fn deref ( & self ) -> & [ u8 ] {
181
+ match self {
182
+ Self :: Borrowed ( b) => b,
183
+ Self :: Owned ( o) => o,
184
+ }
185
+ }
186
+ }
187
+
188
+ impl < ' bytes , T : Exhume < ' bytes > > DerefMut for AlignedBytes < ' bytes , T > {
189
+ fn deref_mut ( & mut self ) -> & mut [ u8 ] {
190
+ match self {
191
+ Self :: Borrowed ( b) => b,
192
+ Self :: Owned ( o) => o,
193
+ }
194
+ }
195
+ }
196
+
197
+
198
+ // TODO: Add tests
0 commit comments