Skip to content

Commit 13ddbf3

Browse files
committed
IntrusiveArrayBuilder
1 parent 2bc2034 commit 13ddbf3

File tree

5 files changed

+174
-36
lines changed

5 files changed

+174
-36
lines changed

src/impl_alloc.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloc::{boxed::Box, vec::Vec};
22

3-
use crate::{ArrayLength, GenericArray, LengthError};
3+
use crate::{ArrayLength, GenericArray, IntrusiveArrayBuilder, LengthError};
44

55
impl<T, N: ArrayLength> TryFrom<Vec<T>> for GenericArray<T, N> {
66
type Error = crate::LengthError;
@@ -11,11 +11,15 @@ impl<T, N: ArrayLength> TryFrom<Vec<T>> for GenericArray<T, N> {
1111
}
1212

1313
unsafe {
14-
let mut destination = crate::ArrayBuilder::new();
14+
let mut destination = GenericArray::uninit();
15+
let mut builder = IntrusiveArrayBuilder::new(&mut destination);
1516

16-
destination.extend(v.into_iter());
17+
builder.extend(v.into_iter());
1718

18-
Ok(destination.assume_init())
19+
Ok({
20+
builder.finish();
21+
IntrusiveArrayBuilder::array_assume_init(destination)
22+
})
1923
}
2024
}
2125
}
@@ -162,11 +166,35 @@ unsafe impl<T, N: ArrayLength> GenericSequence<T> for Box<GenericArray<T, N>> {
162166
where
163167
F: FnMut(usize) -> T,
164168
{
165-
let mut v = Vec::with_capacity(N::USIZE);
166-
for i in 0..N::USIZE {
167-
v.push(f(i));
169+
unsafe {
170+
use core::{
171+
alloc::Layout,
172+
mem::{size_of, MaybeUninit},
173+
ptr,
174+
};
175+
176+
// Box::new_uninit() is nightly-only
177+
let ptr: *mut GenericArray<MaybeUninit<T>, N> = if size_of::<T>() == 0 {
178+
ptr::NonNull::dangling().as_ptr()
179+
} else {
180+
alloc::alloc::alloc(Layout::new::<GenericArray<MaybeUninit<T>, N>>()).cast()
181+
};
182+
183+
let mut builder = IntrusiveArrayBuilder::new(&mut *ptr);
184+
185+
{
186+
let (builder_iter, position) = builder.iter_position();
187+
188+
builder_iter.enumerate().for_each(|(i, dst)| {
189+
dst.write(f(i));
190+
*position += 1;
191+
});
192+
}
193+
194+
builder.finish();
195+
196+
Box::from_raw(ptr.cast()) // IntrusiveArrayBuilder::array_assume_init
168197
}
169-
GenericArray::try_from_vec(v).unwrap()
170198
}
171199
}
172200

src/impl_serde.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Serde serialization/deserialization implementation
22
3-
use crate::{ArrayLength, GenericArray};
3+
use crate::{ArrayLength, GenericArray, IntrusiveArrayBuilder};
44
use core::fmt;
55
use core::marker::PhantomData;
6+
67
use serde::de::{self, SeqAccess, Visitor};
78
use serde::{ser::SerializeTuple, Deserialize, Deserializer, Serialize, Serializer};
89

@@ -62,11 +63,12 @@ where
6263
}
6364

6465
unsafe {
65-
let mut dst = crate::ArrayBuilder::new();
66+
let mut dst = GenericArray::uninit();
67+
let mut builder = IntrusiveArrayBuilder::new(&mut dst);
6668

67-
let (dst_iter, position) = dst.iter_position();
69+
let (build_iter, position) = builder.iter_position();
6870

69-
for dst in dst_iter {
71+
for dst in build_iter {
7072
match seq.next_element()? {
7173
Some(el) => {
7274
dst.write(el);
@@ -81,7 +83,10 @@ where
8183
return Err(de::Error::invalid_length(*position + 1, &self));
8284
}
8385

84-
return Ok(dst.assume_init());
86+
return Ok({
87+
builder.finish();
88+
IntrusiveArrayBuilder::array_assume_init(dst)
89+
});
8590
}
8691

8792
Err(de::Error::invalid_length(*position, &self))

src/internal.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code)] // ArrayBuilder is soft-deprecated internally
2+
13
use crate::*;
24

35
pub trait Sealed {}
@@ -8,6 +10,10 @@ impl<T> Sealed for [T; 0] {}
810
///
911
/// You MUST increment the position while iterating to mark off created elements,
1012
/// 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.
1117
pub struct ArrayBuilder<T, N: ArrayLength> {
1218
array: GenericArray<MaybeUninit<T>, N>,
1319
position: usize,
@@ -96,6 +102,97 @@ impl<T, N: ArrayLength> Drop for ArrayBuilder<T, N> {
96102
}
97103
}
98104

105+
/// Similar to [`ArrayBuilder`] but uses a reference to a pre-allocated array, be
106+
/// it on the stack or heap.
107+
pub struct IntrusiveArrayBuilder<'a, T, N: ArrayLength> {
108+
array: &'a mut GenericArray<MaybeUninit<T>, N>,
109+
position: usize,
110+
}
111+
112+
impl<'a, T, N: ArrayLength> IntrusiveArrayBuilder<'a, T, N> {
113+
/// Begin building an array
114+
#[inline(always)]
115+
pub fn new(array: &'a mut GenericArray<MaybeUninit<T>, N>) -> IntrusiveArrayBuilder<T, N> {
116+
IntrusiveArrayBuilder { array, position: 0 }
117+
}
118+
119+
/// Consume an iterator, `.zip`-ing it to fill some or all of the array. This does not check if the
120+
/// iterator had extra elements or too few elements.
121+
///
122+
/// This makes no attempt to continue where a previous `extend` leaves off. Therefore, it should
123+
/// only be used once per `ArrayBuilder`.
124+
#[inline(always)]
125+
pub unsafe fn extend(&mut self, source: impl Iterator<Item = T>) {
126+
let (destination, position) = (self.array.iter_mut(), &mut self.position);
127+
128+
destination.zip(source).for_each(|(dst, src)| {
129+
dst.write(src);
130+
*position += 1;
131+
});
132+
}
133+
134+
/// Returns true if the write position equals the array size
135+
#[inline(always)]
136+
pub fn is_full(&self) -> bool {
137+
self.position == N::USIZE
138+
}
139+
140+
/// Creates a mutable iterator for writing to the array elements.
141+
///
142+
/// You MUST increment the position value (given as a mutable reference) as you iterate
143+
/// to mark how many elements have been created.
144+
///
145+
/// ```
146+
/// #[cfg(feature = "internals")]
147+
/// # {
148+
/// # use generic_array::{GenericArray, internals::IntrusiveArrayBuilder, typenum::U5};
149+
/// # struct SomeType;
150+
/// fn make_some_struct() -> SomeType { SomeType }
151+
/// unsafe {
152+
/// let mut array = GenericArray::uninit();
153+
/// let mut builder = IntrusiveArrayBuilder::<SomeType, U5>::new(&mut array);
154+
/// let (dst_iter, position) = builder.iter_position();
155+
/// for dst in dst_iter {
156+
/// dst.write(make_some_struct());
157+
/// // MUST be done AFTER ownership of the value has been given to `dst.write`
158+
/// *position += 1;
159+
/// }
160+
/// let your_array = { builder.finish(); IntrusiveArrayBuilder::array_assume_init(array) };
161+
/// }
162+
/// # }
163+
/// ```
164+
#[inline(always)]
165+
pub unsafe fn iter_position(&mut self) -> (slice::IterMut<MaybeUninit<T>>, &mut usize) {
166+
(self.array.iter_mut(), &mut self.position)
167+
}
168+
169+
/// When done writing (assuming all elements have been written to),
170+
/// get the inner array.
171+
#[inline(always)]
172+
pub unsafe fn finish(self) {
173+
debug_assert!(self.is_full());
174+
mem::forget(self)
175+
}
176+
177+
/// Similar to [`GenericArray::assume_init`] but not `const` and optimizes better.
178+
#[inline(always)]
179+
pub unsafe fn array_assume_init(array: GenericArray<MaybeUninit<T>, N>) -> GenericArray<T, N> {
180+
ptr::read(&array as *const _ as *const MaybeUninit<GenericArray<T, N>>).assume_init()
181+
}
182+
}
183+
184+
impl<'a, T, N: ArrayLength> Drop for IntrusiveArrayBuilder<'a, T, N> {
185+
fn drop(&mut self) {
186+
unsafe {
187+
ptr::drop_in_place(
188+
// Same cast as MaybeUninit::slice_assume_init_mut
189+
self.array.get_unchecked_mut(..self.position) as *mut [MaybeUninit<T>]
190+
as *mut [T],
191+
);
192+
}
193+
}
194+
}
195+
99196
/// **UNSAFE**: Consumes an array one element at a time.
100197
///
101198
/// You MUST increment the position while iterating and any leftover elements

src/iter.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,6 @@ pub struct GenericArrayIter<T, N: ArrayLength> {
1515
index_back: usize,
1616
}
1717

18-
#[cfg(test)]
19-
mod test {
20-
use super::*;
21-
22-
fn send<I: Send>(_iter: I) {}
23-
24-
#[test]
25-
fn test_send_iter() {
26-
send(GenericArray::from([1, 2, 3, 4]).into_iter());
27-
}
28-
}
29-
3018
impl<T, N: ArrayLength> GenericArrayIter<T, N> {
3119
/// Returns the remaining items of this iterator as a slice
3220
#[inline(always)]
@@ -242,3 +230,15 @@ impl<T, N: ArrayLength> ExactSizeIterator for GenericArrayIter<T, N> {
242230
impl<T, N: ArrayLength> FusedIterator for GenericArrayIter<T, N> {}
243231

244232
// TODO: Implement `TrustedLen` when stabilized
233+
234+
#[cfg(test)]
235+
mod test {
236+
use super::*;
237+
238+
fn send<I: Send>(_iter: I) {}
239+
240+
#[test]
241+
fn test_send_iter() {
242+
send(GenericArray::from([1, 2, 3, 4]).into_iter());
243+
}
244+
}

src/lib.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! Before Rust 1.51, arrays `[T; N]` were problematic in that they couldn't be
99
//! generic with respect to the length `N`, so this wouldn't work:
1010
//!
11-
//! ```rust{compile_fail}
11+
//! ```compile_fail
1212
//! struct Foo<N> {
1313
//! data: [i32; N],
1414
//! }
@@ -133,7 +133,7 @@ pub mod functional;
133133
pub mod sequence;
134134

135135
mod internal;
136-
use internal::{ArrayBuilder, ArrayConsumer, Sealed};
136+
use internal::{ArrayConsumer, IntrusiveArrayBuilder, Sealed};
137137

138138
// re-export to allow doc_auto_cfg to handle it
139139
#[cfg(feature = "internals")]
@@ -142,8 +142,10 @@ pub mod internals {
142142
//!
143143
//! These are used internally for building and consuming generic arrays. When used correctly,
144144
//! they can ensure elements are correctly dropped if something panics while using them.
145+
//!
146+
//! The API of these is not guarenteed to be stable, as they are not intended for general use.
145147
146-
pub use crate::internal::{ArrayBuilder, ArrayConsumer};
148+
pub use crate::internal::{ArrayBuilder, ArrayConsumer, IntrusiveArrayBuilder};
147149
}
148150

149151
use self::functional::*;
@@ -510,18 +512,20 @@ where
510512
F: FnMut(usize) -> T,
511513
{
512514
unsafe {
513-
let mut destination = ArrayBuilder::new();
515+
let mut array = GenericArray::<T, N>::uninit();
516+
let mut builder = IntrusiveArrayBuilder::new(&mut array);
514517

515518
{
516-
let (destination_iter, position) = destination.iter_position();
519+
let (builder_iter, position) = builder.iter_position();
517520

518-
destination_iter.enumerate().for_each(|(i, dst)| {
521+
builder_iter.enumerate().for_each(|(i, dst)| {
519522
dst.write(f(i));
520523
*position += 1;
521524
});
522525
}
523526

524-
destination.assume_init()
527+
builder.finish();
528+
IntrusiveArrayBuilder::array_assume_init(array)
525529
}
526530
}
527531

@@ -968,15 +972,19 @@ impl<T, N: ArrayLength> GenericArray<T, N> {
968972
}
969973

970974
unsafe {
971-
let mut destination = ArrayBuilder::new();
975+
let mut array = GenericArray::uninit();
976+
let mut builder = IntrusiveArrayBuilder::new(&mut array);
972977

973-
destination.extend(&mut iter);
978+
builder.extend(&mut iter);
974979

975-
if !destination.is_full() || iter.next().is_some() {
980+
if !builder.is_full() || iter.next().is_some() {
976981
return Err(LengthError);
977982
}
978983

979-
Ok(destination.assume_init())
984+
Ok({
985+
builder.finish();
986+
IntrusiveArrayBuilder::array_assume_init(array)
987+
})
980988
}
981989
}
982990
}

0 commit comments

Comments
 (0)