1
1
use core:: mem:: { self , MaybeUninit } ;
2
- use core:: ptr;
3
2
4
3
/// An array of at most `N` elements.
5
4
struct ArrayBuilder < T , const N : usize > {
@@ -17,7 +16,7 @@ struct ArrayBuilder<T, const N: usize> {
17
16
impl < T , const N : usize > ArrayBuilder < T , N > {
18
17
/// Initializes a new, empty `ArrayBuilder`.
19
18
pub fn new ( ) -> Self {
20
- // SAFETY: the validity invariant trivially hold for a zero-length array .
19
+ // SAFETY: The safety invariant of `arr` trivially holds for `len = 0` .
21
20
Self {
22
21
arr : [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ,
23
22
len : 0 ,
@@ -28,54 +27,228 @@ impl<T, const N: usize> ArrayBuilder<T, N> {
28
27
///
29
28
/// # Panics
30
29
///
31
- /// This panics if `self.len() >= N`.
30
+ /// This panics if `self.len >= N`.
31
+ #[ inline( always) ]
32
32
pub fn push ( & mut self , value : T ) {
33
- // SAFETY: we maintain the invariant here that arr[..len] is valid.
34
- // Indexing with self.len also ensures self.len < N, and thus <= N after
35
- // the increment.
36
- self . arr [ self . len ] = MaybeUninit :: new ( value) ;
33
+ // PANICS: This will panic if `self.len >= N`.
34
+ let place = & mut self . arr [ self . len ] ;
35
+ // SAFETY: The safety invariant of `self.arr` applies to elements at
36
+ // indices `0..self.len` — not to the element at `self.len`. Writing to
37
+ // the element at index `self.len` therefore does not violate the safety
38
+ // invariant of `self.arr`. Even if this line panics, we have not
39
+ // created any intermediate invalid state.
40
+ * place = MaybeUninit :: new ( value) ;
41
+ // PANICS: This cannot panic, since `self.len < N <= usize::MAX`.
42
+ // `0..self.len` are valid. Due to the above write, the element at
43
+ // `self.len` is now also valid. Consequently, all elements at indicies
44
+ // `0..(self.len + 1)` are valid, and `self.len` can be safely
45
+ // incremented without violating `self.arr`'s invariant. It is fine if
46
+ // this increment panics, as we have not created any intermediate
47
+ // invalid state.
37
48
self . len += 1 ;
38
49
}
39
50
40
- /// Consumes the elements in the `ArrayBuilder` and returns them as an array `[T; N]`.
51
+ /// Consumes the elements in the `ArrayBuilder` and returns them as an array
52
+ /// `[T; N]`.
41
53
///
42
54
/// If `self.len() < N`, this returns `None`.
43
55
pub fn take ( & mut self ) -> Option < [ T ; N ] > {
44
56
if self . len == N {
45
- // Take the array, resetting our length back to zero.
57
+ // SAFETY: Decreasing the value of `self.len` cannot violate the
58
+ // safety invariant on `self.arr`.
46
59
self . len = 0 ;
60
+
61
+ // SAFETY: Since `self.len` is 0, `self.arr` may safely contain
62
+ // uninitialized elements.
47
63
let arr = mem:: replace ( & mut self . arr , [ ( ) ; N ] . map ( |_| MaybeUninit :: uninit ( ) ) ) ;
48
64
49
- // SAFETY: we had len == N, so all elements in arr are valid.
50
- Some ( unsafe { arr. map ( |v| v. assume_init ( ) ) } )
65
+ Some ( arr. map ( |v| {
66
+ // SAFETY: We know that all elements of `arr` are valid because
67
+ // we checked that `len == N`.
68
+ unsafe { v. assume_init ( ) }
69
+ } ) )
51
70
} else {
52
71
None
53
72
}
54
73
}
55
74
}
56
75
76
+ impl < T , const N : usize > AsMut < [ T ] > for ArrayBuilder < T , N > {
77
+ fn as_mut ( & mut self ) -> & mut [ T ] {
78
+ let valid = & mut self . arr [ ..self . len ] ;
79
+ // SAFETY: By invariant on `self.arr`, the elements of `self.arr` at
80
+ // indices `0..self.len` are in a valid state. Since `valid` references
81
+ // only these elements, the safety precondition of
82
+ // `slice_assume_init_mut` is satisfied.
83
+ unsafe { slice_assume_init_mut ( valid) }
84
+ }
85
+ }
86
+
57
87
impl < T , const N : usize > Drop for ArrayBuilder < T , N > {
88
+ // We provide a non-trivial `Drop` impl, because the trivial impl would be a
89
+ // no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and
90
+ // so it can only forget its contents. By leveraging the safety invariant of
91
+ // `self.arr`, we do know which elements of `self.arr` are valid, and can
92
+ // selectively run their destructors.
58
93
fn drop ( & mut self ) {
59
- unsafe {
60
- // SAFETY: arr[..len] is valid, so must be dropped. First we create
61
- // a pointer to this valid slice, then drop that slice in-place.
62
- // The cast from *mut MaybeUninit<T> to *mut T is always sound by
63
- // the layout guarantees of MaybeUninit.
64
- let ptr_to_first: * mut MaybeUninit < T > = self . arr . as_mut_ptr ( ) ;
65
- let ptr_to_slice = ptr:: slice_from_raw_parts_mut ( ptr_to_first. cast :: < T > ( ) , self . len ) ;
66
- ptr:: drop_in_place ( ptr_to_slice) ;
67
- }
94
+ // SAFETY:
95
+ // - by invariant on `&[T]`, `self.as_mut()` is:
96
+ // - valid for reads and writes
97
+ // - properly aligned
98
+ // - non-null
99
+ // - the dropped `T` are valid for dropping; they do not have any
100
+ // additional library invariants that we've violated
101
+ // - no other pointers to `valid` exist (since we're in the context of
102
+ // `drop`)
103
+ unsafe { core:: ptr:: drop_in_place ( self . as_mut ( ) ) }
68
104
}
69
105
}
70
106
107
+ /// Assuming all the elements are initialized, get a mutable slice to them.
108
+ ///
109
+ /// # Safety
110
+ ///
111
+ /// The caller guarantees that the elements `T` referenced by `slice` are in a
112
+ /// valid state.
113
+ unsafe fn slice_assume_init_mut < T > ( slice : & mut [ MaybeUninit < T > ] ) -> & mut [ T ] {
114
+ // SAFETY: Casting `&mut [MaybeUninit<T>]` to `&mut [T]` is sound, because
115
+ // `MaybeUninit<T>` is guaranteed to have the same size, alignment and ABI
116
+ // as `T`, and because the caller has guaranteed that `slice` is in the
117
+ // valid state.
118
+ unsafe { & mut * ( slice as * mut [ MaybeUninit < T > ] as * mut [ T ] ) }
119
+ }
120
+
71
121
/// Equivalent to `it.next_array()`.
72
- pub fn next_array < I , T , const N : usize > ( it : & mut I ) -> Option < [ T ; N ] >
122
+ pub ( crate ) fn next_array < I , const N : usize > ( it : & mut I ) -> Option < [ I :: Item ; N ] >
73
123
where
74
- I : Iterator < Item = T > ,
124
+ I : Iterator ,
75
125
{
76
126
let mut builder = ArrayBuilder :: new ( ) ;
77
127
for _ in 0 ..N {
78
128
builder. push ( it. next ( ) ?) ;
79
129
}
80
130
builder. take ( )
81
131
}
132
+
133
+ #[ cfg( test) ]
134
+ mod test {
135
+ use super :: ArrayBuilder ;
136
+
137
+ #[ test]
138
+ fn zero_len_take ( ) {
139
+ let mut builder = ArrayBuilder :: < ( ) , 0 > :: new ( ) ;
140
+ let taken = builder. take ( ) ;
141
+ assert_eq ! ( taken, Some ( [ ( ) ; 0 ] ) ) ;
142
+ }
143
+
144
+ #[ test]
145
+ #[ should_panic]
146
+ fn zero_len_push ( ) {
147
+ let mut builder = ArrayBuilder :: < ( ) , 0 > :: new ( ) ;
148
+ builder. push ( ( ) ) ;
149
+ }
150
+
151
+ #[ test]
152
+ fn push_4 ( ) {
153
+ let mut builder = ArrayBuilder :: < ( ) , 4 > :: new ( ) ;
154
+ assert_eq ! ( builder. take( ) , None ) ;
155
+
156
+ builder. push ( ( ) ) ;
157
+ assert_eq ! ( builder. take( ) , None ) ;
158
+
159
+ builder. push ( ( ) ) ;
160
+ assert_eq ! ( builder. take( ) , None ) ;
161
+
162
+ builder. push ( ( ) ) ;
163
+ assert_eq ! ( builder. take( ) , None ) ;
164
+
165
+ builder. push ( ( ) ) ;
166
+ assert_eq ! ( builder. take( ) , Some ( [ ( ) ; 4 ] ) ) ;
167
+ }
168
+
169
+ #[ test]
170
+ fn tracked_drop ( ) {
171
+ use std:: panic:: { catch_unwind, AssertUnwindSafe } ;
172
+ use std:: sync:: atomic:: { AtomicU16 , Ordering } ;
173
+
174
+ static DROPPED : AtomicU16 = AtomicU16 :: new ( 0 ) ;
175
+
176
+ #[ derive( Debug , PartialEq ) ]
177
+ struct TrackedDrop ;
178
+
179
+ impl Drop for TrackedDrop {
180
+ fn drop ( & mut self ) {
181
+ DROPPED . fetch_add ( 1 , Ordering :: Relaxed ) ;
182
+ }
183
+ }
184
+
185
+ {
186
+ let builder = ArrayBuilder :: < TrackedDrop , 0 > :: new ( ) ;
187
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 0 ) ;
188
+ drop ( builder) ;
189
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 0 ) ;
190
+ }
191
+
192
+ {
193
+ let mut builder = ArrayBuilder :: < TrackedDrop , 2 > :: new ( ) ;
194
+ builder. push ( TrackedDrop ) ;
195
+ assert_eq ! ( builder. take( ) , None ) ;
196
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 0 ) ;
197
+ drop ( builder) ;
198
+ assert_eq ! ( DROPPED . swap( 0 , Ordering :: Relaxed ) , 1 ) ;
199
+ }
200
+
201
+ {
202
+ let mut builder = ArrayBuilder :: < TrackedDrop , 2 > :: new ( ) ;
203
+ builder. push ( TrackedDrop ) ;
204
+ builder. push ( TrackedDrop ) ;
205
+ assert ! ( matches!( builder. take( ) , Some ( _) ) ) ;
206
+ assert_eq ! ( DROPPED . swap( 0 , Ordering :: Relaxed ) , 2 ) ;
207
+ drop ( builder) ;
208
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 0 ) ;
209
+ }
210
+
211
+ {
212
+ let mut builder = ArrayBuilder :: < TrackedDrop , 2 > :: new ( ) ;
213
+
214
+ builder. push ( TrackedDrop ) ;
215
+ builder. push ( TrackedDrop ) ;
216
+
217
+ assert ! ( catch_unwind( AssertUnwindSafe ( || {
218
+ builder. push( TrackedDrop ) ;
219
+ } ) )
220
+ . is_err( ) ) ;
221
+
222
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 1 ) ;
223
+
224
+ drop ( builder) ;
225
+
226
+ assert_eq ! ( DROPPED . swap( 0 , Ordering :: Relaxed ) , 3 ) ;
227
+ }
228
+
229
+ {
230
+ let mut builder = ArrayBuilder :: < TrackedDrop , 2 > :: new ( ) ;
231
+
232
+ builder. push ( TrackedDrop ) ;
233
+ builder. push ( TrackedDrop ) ;
234
+
235
+ assert ! ( catch_unwind( AssertUnwindSafe ( || {
236
+ builder. push( TrackedDrop ) ;
237
+ } ) )
238
+ . is_err( ) ) ;
239
+
240
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 1 ) ;
241
+
242
+ assert ! ( matches!( builder. take( ) , Some ( _) ) ) ;
243
+
244
+ assert_eq ! ( DROPPED . load( Ordering :: Relaxed ) , 3 ) ;
245
+
246
+ builder. push ( TrackedDrop ) ;
247
+ builder. push ( TrackedDrop ) ;
248
+
249
+ assert ! ( matches!( builder. take( ) , Some ( _) ) ) ;
250
+
251
+ assert_eq ! ( DROPPED . swap( 0 , Ordering :: Relaxed ) , 5 ) ;
252
+ }
253
+ }
254
+ }
0 commit comments