generic_array/internal.rs
1#![allow(dead_code)] // ArrayBuilder is soft-deprecated internally
2
3use crate::*;
4
5pub trait Sealed {}
6
7impl<T> Sealed for [T; 0] {}
8
9/// **UNSAFE**: Creates an array one element at a time using a mutable iterator of pointers.
10///
11/// You MUST increment the position while iterating to mark off created elements,
12/// which will be dropped if `into_inner` is not called.
13///
14/// This is soft-deprecated in favor of [`IntrusiveArrayBuilder`] due to Rust's
15/// lack of return-value optimization causing issues moving the array out of the struct.
16/// Still works fine for smaller arrays, though.
17pub struct ArrayBuilder<T, N: ArrayLength> {
18 array: GenericArray<MaybeUninit<T>, N>,
19 position: usize,
20}
21
22impl<T, N: ArrayLength> ArrayBuilder<T, N> {
23 /// Begin building an array
24 #[inline(always)]
25 pub const fn new() -> ArrayBuilder<T, N> {
26 ArrayBuilder {
27 array: GenericArray::uninit(),
28 position: 0,
29 }
30 }
31
32 /// Consume an iterator, `.zip`-ing it to fill some or all of the array. This does not check if the
33 /// iterator had extra elements or too few elements.
34 ///
35 /// This makes no attempt to continue where a previous `extend` leaves off. Therefore, it should
36 /// only be used once per `ArrayBuilder`.
37 #[inline(always)]
38 pub unsafe fn extend(&mut self, source: impl Iterator<Item = T>) {
39 let (destination, position) = (self.array.iter_mut(), &mut self.position);
40
41 destination.zip(source).for_each(|(dst, src)| {
42 dst.write(src);
43 *position += 1;
44 });
45 }
46
47 /// Returns true if the write position equals the array size
48 #[inline(always)]
49 pub const fn is_full(&self) -> bool {
50 self.position == N::USIZE
51 }
52
53 /// Creates a mutable iterator for writing to the array elements.
54 ///
55 /// You MUST increment the position value (given as a mutable reference) as you iterate
56 /// to mark how many elements have been created.
57 ///
58 /// ```
59 /// #[cfg(feature = "internals")]
60 /// # {
61 /// # use generic_array::{GenericArray, internals::ArrayBuilder, typenum::U5};
62 /// # struct SomeType;
63 /// fn make_some_struct() -> SomeType { SomeType }
64 /// unsafe {
65 /// let mut builder = ArrayBuilder::<SomeType, U5>::new();
66 /// let (dst_iter, position) = builder.iter_position();
67 /// for dst in dst_iter {
68 /// dst.write(make_some_struct());
69 /// // MUST be done AFTER ownership of the value has been given to `dst.write`
70 /// *position += 1;
71 /// }
72 /// let your_array = builder.assume_init();
73 /// }
74 /// # }
75 /// ```
76 #[inline(always)]
77 pub unsafe fn iter_position(&mut self) -> (slice::IterMut<MaybeUninit<T>>, &mut usize) {
78 (self.array.iter_mut(), &mut self.position)
79 }
80
81 /// When done writing (assuming all elements have been written to),
82 /// get the inner array.
83 #[inline(always)]
84 pub const unsafe fn assume_init(self) -> GenericArray<T, N> {
85 debug_assert!(self.is_full());
86
87 let array = ptr::read(&self.array);
88 mem::forget(self);
89 GenericArray::assume_init(array)
90 }
91}
92
93impl<T, N: ArrayLength> Drop for ArrayBuilder<T, N> {
94 fn drop(&mut self) {
95 unsafe {
96 ptr::drop_in_place(
97 // Same cast as MaybeUninit::slice_assume_init_mut
98 self.array.get_unchecked_mut(..self.position) as *mut [MaybeUninit<T>]
99 as *mut [T],
100 );
101 }
102 }
103}
104
105/// Similar to [`ArrayBuilder`] but uses a reference to a pre-allocated array, be
106/// it on the stack or heap.
107pub struct IntrusiveArrayBuilder<'a, T, N: ArrayLength> {
108 array: &'a mut GenericArray<MaybeUninit<T>, N>,
109 position: usize,
110}
111
112impl<'a, T, N: ArrayLength> IntrusiveArrayBuilder<'a, T, N> {
113 /// Begin building an array
114 #[inline(always)]
115 pub const fn new(
116 array: &'a mut GenericArray<MaybeUninit<T>, N>,
117 ) -> IntrusiveArrayBuilder<'a, T, N> {
118 IntrusiveArrayBuilder { array, position: 0 }
119 }
120
121 /// Consume an iterator, `.zip`-ing it to fill some or all of the array. This does not check if the
122 /// iterator had extra elements or too few elements.
123 ///
124 /// This makes no attempt to continue where a previous `extend` leaves off. Therefore, it should
125 /// only be used once per `ArrayBuilder`.
126 #[inline(always)]
127 pub unsafe fn extend(&mut self, source: impl Iterator<Item = T>) {
128 let (destination, position) = (self.array.iter_mut(), &mut self.position);
129
130 destination.zip(source).for_each(|(dst, src)| {
131 dst.write(src);
132 *position += 1;
133 });
134 }
135
136 /// Returns true if the write position equals the array size
137 #[inline(always)]
138 pub const fn is_full(&self) -> bool {
139 self.position == N::USIZE
140 }
141
142 /// Creates a mutable iterator for writing to the array elements.
143 ///
144 /// You MUST increment the position value (given as a mutable reference) as you iterate
145 /// to mark how many elements have been created.
146 ///
147 /// ```
148 /// #[cfg(feature = "internals")]
149 /// # {
150 /// # use generic_array::{GenericArray, internals::IntrusiveArrayBuilder, typenum::U5};
151 /// # struct SomeType;
152 /// fn make_some_struct() -> SomeType { SomeType }
153 /// unsafe {
154 /// let mut array = GenericArray::uninit();
155 /// let mut builder = IntrusiveArrayBuilder::<SomeType, U5>::new(&mut array);
156 /// let (dst_iter, position) = builder.iter_position();
157 /// for dst in dst_iter {
158 /// dst.write(make_some_struct());
159 /// // MUST be done AFTER ownership of the value has been given to `dst.write`
160 /// *position += 1;
161 /// }
162 /// let your_array = { builder.finish(); IntrusiveArrayBuilder::array_assume_init(array) };
163 /// }
164 /// # }
165 /// ```
166 #[inline(always)]
167 pub unsafe fn iter_position(&mut self) -> (slice::IterMut<MaybeUninit<T>>, &mut usize) {
168 (self.array.iter_mut(), &mut self.position)
169 }
170
171 /// When done writing (assuming all elements have been written to),
172 /// get the inner array.
173 #[inline(always)]
174 pub const unsafe fn finish(self) {
175 debug_assert!(self.is_full());
176 mem::forget(self)
177 }
178
179 /// Similar to [`GenericArray::assume_init`] but not `const` and optimizes better.
180 #[inline(always)]
181 pub unsafe fn array_assume_init(array: GenericArray<MaybeUninit<T>, N>) -> GenericArray<T, N> {
182 ptr::read(&array as *const _ as *const MaybeUninit<GenericArray<T, N>>).assume_init()
183 }
184}
185
186impl<T, N: ArrayLength> Drop for IntrusiveArrayBuilder<'_, T, N> {
187 fn drop(&mut self) {
188 unsafe {
189 ptr::drop_in_place(
190 // Same cast as MaybeUninit::slice_assume_init_mut
191 self.array.get_unchecked_mut(..self.position) as *mut [MaybeUninit<T>]
192 as *mut [T],
193 );
194 }
195 }
196}
197
198/// **UNSAFE**: Consumes an array one element at a time.
199///
200/// You MUST increment the position while iterating and any leftover elements
201/// will be dropped if position does not go to N
202pub struct ArrayConsumer<T, N: ArrayLength> {
203 array: ManuallyDrop<GenericArray<T, N>>,
204 position: usize,
205}
206
207impl<T, N: ArrayLength> ArrayConsumer<T, N> {
208 /// Give ownership of the array to the consumer
209 #[inline(always)]
210 pub const fn new(array: GenericArray<T, N>) -> ArrayConsumer<T, N> {
211 ArrayConsumer {
212 array: ManuallyDrop::new(array),
213 position: 0,
214 }
215 }
216
217 /// Creates an iterator and mutable reference to the internal position
218 /// to keep track of consumed elements.
219 ///
220 /// You MUST increment the position as you iterate to mark off consumed elements.
221 #[inline(always)]
222 pub unsafe fn iter_position(&mut self) -> (slice::Iter<T>, &mut usize) {
223 (self.array.iter(), &mut self.position)
224 }
225}
226
227impl<T, N: ArrayLength> Drop for ArrayConsumer<T, N> {
228 fn drop(&mut self) {
229 unsafe {
230 ptr::drop_in_place(self.array.get_unchecked_mut(self.position..));
231 }
232 }
233}