From c98070d522d8d12e61219f268ac377aceda30e92 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 29 Apr 2023 18:31:06 +0200 Subject: relax size and alignment requirements for in-place iteration --- library/alloc/src/vec/in_place_collect.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'library/alloc') diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 90ba1b3a96f..1f03f0d19ec 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -52,8 +52,8 @@ //! Otherwise the input capacity (and thus layout) in bytes may not be representable by the output //! `Vec`. In that case `alloc.shrink()` is used to update the allocation's layout. //! -//! Currently alignments of `T` and `U` must be the same. In principle smaller output alignments -//! could be supported but that would require always calling `alloc.shrink` for those transformations. +//! Alignments of `T` must be the same or larger than `U`. Since alignments are always a power +//! of two _larger_ implies _is a multiple of_. //! //! See `in_place_collectible()` for the current conditions. //! @@ -168,7 +168,7 @@ const fn in_place_collectible( step_merge: Option, step_expand: Option, ) -> bool { - if DEST::IS_ZST || mem::align_of::() != mem::align_of::() { + if DEST::IS_ZST || mem::align_of::() < mem::align_of::() { return false; } @@ -178,7 +178,7 @@ const fn in_place_collectible( // e.g. // - 1 x [u8; 4] -> 4x u8, via flatten // - 4 x u8 -> 1x [u8; 4], via array_chunks - mem::size_of::() * step_merge.get() == mem::size_of::() * step_expand.get() + mem::size_of::() * step_merge.get() >= mem::size_of::() * step_expand.get() } // Fall back to other from_iter impls if an overflow occurred in the step merge/expansion // tracking. @@ -255,12 +255,15 @@ where let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap }; src.forget_allocation_drop_remaining(); - // Adjust the allocation size if the source had a capacity in bytes that wasn't a multiple - // of the destination type size. + // Adjust the allocation if the alignment didn't match or the source had a capacity in bytes + // that wasn't a multiple of the destination type size. // Since the discrepancy should generally be small this should only result in some // bookkeeping updates and no memmove. - if const { mem::size_of::() > mem::size_of::() } - && src_cap * mem::size_of::() != dst_cap * mem::size_of::() + if (const { + let src_sz = mem::size_of::(); + src_sz > 0 && mem::size_of::() % src_sz != 0 + } && src_cap * mem::size_of::() != dst_cap * mem::size_of::()) + || const { mem::align_of::() != mem::align_of::() } { let alloc = Global; unsafe { -- cgit 1.4.1-3-g733a5