|
80 | 80 | //! # O(1) collect
|
81 | 81 | //!
|
82 | 82 | //! The main iteration itself is further specialized when the iterator implements
|
83 |
| -//! [`TrustedRandomAccessNoCoerce`] to let the optimizer see that it is a counted loop with a single |
| 83 | +//! [`UncheckedIndexedIterator`] to let the optimizer see that it is a counted loop with a single |
84 | 84 | //! [induction variable]. This can turn some iterators into a noop, i.e. it reduces them from O(n) to
|
85 | 85 | //! O(1). This particular optimization is quite fickle and doesn't always work, see [#79308]
|
86 | 86 | //!
|
|
157 | 157 | use crate::alloc::{handle_alloc_error, Global};
|
158 | 158 | use core::alloc::Allocator;
|
159 | 159 | use core::alloc::Layout;
|
160 |
| -use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce}; |
| 160 | +use core::iter::UncheckedIndexedIterator; |
| 161 | +use core::iter::{InPlaceIterable, SourceIter}; |
161 | 162 | use core::marker::PhantomData;
|
| 163 | +use core::mem::needs_drop; |
162 | 164 | use core::mem::{self, ManuallyDrop, SizedTypeProperties};
|
163 | 165 | use core::num::NonZero;
|
164 | 166 | use core::ptr::{self, NonNull};
|
@@ -257,8 +259,9 @@ where
|
257 | 259 | // caveat: if they weren't we might not even make it to this point
|
258 | 260 | debug_assert_eq!(src_buf, src.buf.as_ptr());
|
259 | 261 | // check InPlaceIterable contract. This is only possible if the iterator advanced the
|
260 |
| - // source pointer at all. If it uses unchecked access via TrustedRandomAccess |
261 |
| - // then the source pointer will stay in its initial position and we can't use it as reference |
| 262 | + // source pointer at all. If it uses unchecked access via UncheckedIndexedIterator |
| 263 | + // and doesn't perform cleanup then the source pointer will stay in its initial position |
| 264 | + // and we can't use it as reference. |
262 | 265 | if src.ptr != src_ptr {
|
263 | 266 | debug_assert!(
|
264 | 267 | unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(),
|
@@ -369,28 +372,96 @@ where
|
369 | 372 | }
|
370 | 373 | }
|
371 | 374 |
|
| 375 | +// impl<T, I> SpecInPlaceCollect<T, I> for I |
| 376 | +// where |
| 377 | +// I: Iterator<Item = T> + TrustedRandomAccessNoCoerce, |
| 378 | +// { |
| 379 | +// #[inline] |
| 380 | +// unsafe fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { |
| 381 | +// let len = self.size(); |
| 382 | +// let mut drop_guard = InPlaceDrop { inner: dst_buf, dst: dst_buf }; |
| 383 | +// for i in 0..len { |
| 384 | +// // Safety: InplaceIterable contract guarantees that for every element we read |
| 385 | +// // one slot in the underlying storage will have been freed up and we can immediately |
| 386 | +// // write back the result. |
| 387 | +// unsafe { |
| 388 | +// let dst = dst_buf.add(i); |
| 389 | +// debug_assert!(dst as *const _ <= end, "InPlaceIterable contract violation"); |
| 390 | +// ptr::write(dst, self.__iterator_get_unchecked(i)); |
| 391 | +// // Since this executes user code which can panic we have to bump the pointer |
| 392 | +// // after each step. |
| 393 | +// drop_guard.dst = dst.add(1); |
| 394 | +// } |
| 395 | +// } |
| 396 | +// mem::forget(drop_guard); |
| 397 | +// len |
| 398 | +// } |
| 399 | +// } |
| 400 | + |
372 | 401 | impl<T, I> SpecInPlaceCollect<T, I> for I
|
373 | 402 | where
|
374 |
| - I: Iterator<Item = T> + TrustedRandomAccessNoCoerce, |
| 403 | + I: Iterator<Item = T> + UncheckedIndexedIterator, |
375 | 404 | {
|
376 | 405 | #[inline]
|
377 | 406 | unsafe fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize {
|
378 |
| - let len = self.size(); |
379 |
| - let mut drop_guard = InPlaceDrop { inner: dst_buf, dst: dst_buf }; |
380 |
| - for i in 0..len { |
381 |
| - // Safety: InplaceIterable contract guarantees that for every element we read |
382 |
| - // one slot in the underlying storage will have been freed up and we can immediately |
383 |
| - // write back the result. |
| 407 | + let len = self.size_hint().0; |
| 408 | + |
| 409 | + if len == 0 { |
| 410 | + return 0; |
| 411 | + } |
| 412 | + |
| 413 | + struct LoopGuard<'a, I> |
| 414 | + where |
| 415 | + I: Iterator + UncheckedIndexedIterator, |
| 416 | + { |
| 417 | + it: &'a mut I, |
| 418 | + len: usize, |
| 419 | + idx: usize, |
| 420 | + dst_buf: *mut I::Item, |
| 421 | + } |
| 422 | + |
| 423 | + impl<I> Drop for LoopGuard<'_, I> |
| 424 | + where |
| 425 | + I: Iterator + UncheckedIndexedIterator, |
| 426 | + { |
| 427 | + #[inline] |
| 428 | + fn drop(&mut self) { |
| 429 | + unsafe { |
| 430 | + let new_len = self.len - self.idx; |
| 431 | + if I::CLEANUP_ON_DROP { |
| 432 | + self.it.set_front_index_from_end_unchecked(new_len, self.len); |
| 433 | + } |
| 434 | + if needs_drop::<I::Item>() && self.idx != self.len { |
| 435 | + let raw_slice = |
| 436 | + ptr::slice_from_raw_parts_mut::<I::Item>(self.dst_buf, self.idx); |
| 437 | + ptr::drop_in_place(raw_slice); |
| 438 | + } |
| 439 | + } |
| 440 | + } |
| 441 | + } |
| 442 | + |
| 443 | + let mut state = LoopGuard { it: self, len, idx: 0, dst_buf }; |
| 444 | + |
| 445 | + loop { |
384 | 446 | unsafe {
|
385 |
| - let dst = dst_buf.add(i); |
| 447 | + let idx = state.idx; |
| 448 | + state.idx = idx.unchecked_add(1); |
| 449 | + let dst = state.dst_buf.add(idx); |
386 | 450 | debug_assert!(dst as *const _ <= end, "InPlaceIterable contract violation");
|
387 |
| - ptr::write(dst, self.__iterator_get_unchecked(i)); |
388 |
| - // Since this executes user code which can panic we have to bump the pointer |
389 |
| - // after each step. |
390 |
| - drop_guard.dst = dst.add(1); |
| 451 | + dst.write(state.it.index_from_end_unchecked(len - idx)); |
| 452 | + } |
| 453 | + if state.idx == len { |
| 454 | + break; |
391 | 455 | }
|
392 | 456 | }
|
393 |
| - mem::forget(drop_guard); |
| 457 | + |
| 458 | + // disarm guard, we don't want the front elements to get dropped |
| 459 | + mem::forget(state); |
| 460 | + // since the guard was disarmed, update the iterator state |
| 461 | + if Self::CLEANUP_ON_DROP { |
| 462 | + unsafe { self.set_front_index_from_end_unchecked(0, len) }; |
| 463 | + } |
| 464 | + |
394 | 465 | len
|
395 | 466 | }
|
396 | 467 | }
|
|
0 commit comments