about summary refs log tree commit diff
path: root/library/alloc/src/vec
diff options
context:
space:
mode:
authorThe 8472 <git@infinite-source.de>2023-04-29 18:31:06 +0200
committerThe 8472 <git@infinite-source.de>2023-09-03 19:59:47 +0200
commitc98070d522d8d12e61219f268ac377aceda30e92 (patch)
treefc312217408ab736e800a8a102913386375e0272 /library/alloc/src/vec
parent439f63019fccbbdcc00074856f67ce6af2abcac2 (diff)
downloadrust-c98070d522d8d12e61219f268ac377aceda30e92.tar.gz
rust-c98070d522d8d12e61219f268ac377aceda30e92.zip
relax size and alignment requirements for in-place iteration
Diffstat (limited to 'library/alloc/src/vec')
-rw-r--r--library/alloc/src/vec/in_place_collect.rs19
1 files changed, 11 insertions, 8 deletions
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<U>`. 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<DEST, SRC>(
     step_merge: Option<NonZeroUsize>,
     step_expand: Option<NonZeroUsize>,
 ) -> bool {
-    if DEST::IS_ZST || mem::align_of::<SRC>() != mem::align_of::<DEST>() {
+    if DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() {
         return false;
     }
 
@@ -178,7 +178,7 @@ const fn in_place_collectible<DEST, SRC>(
             // e.g.
             // - 1 x [u8; 4] -> 4x u8, via flatten
             // - 4 x u8 -> 1x [u8; 4], via array_chunks
-            mem::size_of::<SRC>() * step_merge.get() == mem::size_of::<DEST>() * step_expand.get()
+            mem::size_of::<SRC>() * step_merge.get() >= mem::size_of::<DEST>() * 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::<T>() > mem::size_of::<I::Src>() }
-            && src_cap * mem::size_of::<I::Src>() != dst_cap * mem::size_of::<T>()
+        if (const {
+            let src_sz = mem::size_of::<I::Src>();
+            src_sz > 0 && mem::size_of::<T>() % src_sz != 0
+        } && src_cap * mem::size_of::<I::Src>() != dst_cap * mem::size_of::<T>())
+            || const { mem::align_of::<T>() != mem::align_of::<I::Src>() }
         {
             let alloc = Global;
             unsafe {