Skip to content

Commit d9bb68b

Browse files
bugadaniest31
andcommitted
TypedArena: Eliminate intermediate copy if Iterator size is known
Co-authored-by: est31 <[email protected]>
1 parent 5565241 commit d9bb68b

File tree

1 file changed

+61
-3
lines changed
  • compiler/rustc_arena/src

1 file changed

+61
-3
lines changed

compiler/rustc_arena/src/lib.rs

+61-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#![feature(dropck_eyepatch)]
1515
#![feature(new_uninit)]
1616
#![feature(maybe_uninit_slice)]
17+
#![feature(min_specialization)]
18+
#![feature(trusted_len)]
1719
#![cfg_attr(test, feature(test))]
1820

1921
use rustc_data_structures::cold_path;
@@ -22,6 +24,7 @@ use smallvec::SmallVec;
2224
use std::alloc::Layout;
2325
use std::cell::{Cell, RefCell};
2426
use std::cmp;
27+
use std::iter::TrustedLen;
2528
use std::marker::{PhantomData, Send};
2629
use std::mem::{self, MaybeUninit};
2730
use std::ptr;
@@ -109,6 +112,53 @@ impl<T> Default for TypedArena<T> {
109112
}
110113
}
111114

115+
trait IterExt<I, T> {
116+
fn write_to_arena(iter: I, arena: &TypedArena<T>) -> &mut [T];
117+
}
118+
119+
impl<T, I> IterExt<I, T> for I
120+
where
121+
I: Iterator<Item = T>,
122+
{
123+
#[inline]
124+
default fn write_to_arena(iter: I, arena: &TypedArena<T>) -> &mut [T] {
125+
arena.alloc_from_iter_gen(iter)
126+
}
127+
}
128+
129+
impl<T, I> IterExt<I, T> for I
130+
where
131+
I: Iterator<Item = T> + TrustedLen,
132+
{
133+
#[inline]
134+
fn write_to_arena(mut iter: I, arena: &TypedArena<T>) -> &mut [T] {
135+
let size_hint = iter.size_hint();
136+
137+
match size_hint {
138+
(0, Some(_)) => &mut [],
139+
(len, Some(_)) => {
140+
// no need to check min == max because of TrustedLen
141+
142+
// SAFETY: TrustedLen implementors must ensure they return exactly as many
143+
// elements as they tell via `size_hint()`.
144+
unsafe {
145+
// We know the exact number of elements the iterator will produce here
146+
let start_addr = arena.alloc_raw_slice(len);
147+
let mut mem = start_addr;
148+
149+
while let Some(value) = iter.next() {
150+
ptr::write(mem, value);
151+
mem = mem.add(1);
152+
}
153+
154+
return slice::from_raw_parts_mut(start_addr, len);
155+
}
156+
}
157+
_ => panic!("Trying to allocate from an iterator with more than usize::MAX elements"),
158+
}
159+
}
160+
}
161+
112162
impl<T> TypedArena<T> {
113163
/// Allocates an object in the `TypedArena`, returning a reference to it.
114164
#[inline]
@@ -183,10 +233,10 @@ impl<T> TypedArena<T> {
183233
}
184234
}
185235

236+
/// General case implementation for `alloc_from_iter()`.
186237
#[inline]
187-
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
188-
assert!(mem::size_of::<T>() != 0);
189-
let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
238+
fn alloc_from_iter_gen<I: Iterator<Item = T>>(&self, iter: I) -> &mut [T] {
239+
let mut vec: SmallVec<[_; 8]> = iter.collect();
190240
if vec.is_empty() {
191241
return &mut [];
192242
}
@@ -201,6 +251,14 @@ impl<T> TypedArena<T> {
201251
}
202252
}
203253

254+
#[inline]
255+
pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
256+
let iter = iter.into_iter();
257+
assert!(mem::size_of::<T>() != 0);
258+
259+
I::IntoIter::write_to_arena(iter, self)
260+
}
261+
204262
/// Grows the arena.
205263
#[inline(never)]
206264
#[cold]

0 commit comments

Comments
 (0)