Skip to content

Commit c98070d

Browse files
committed
relax size and alignment requirements for in-place iteration
1 parent 439f630 commit c98070d

File tree

1 file changed

+11
-8
lines changed

1 file changed

+11
-8
lines changed

library/alloc/src/vec/in_place_collect.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
//! Otherwise the input capacity (and thus layout) in bytes may not be representable by the output
5353
//! `Vec<U>`. In that case `alloc.shrink()` is used to update the allocation's layout.
5454
//!
55-
//! Currently alignments of `T` and `U` must be the same. In principle smaller output alignments
56-
//! could be supported but that would require always calling `alloc.shrink` for those transformations.
55+
//! Alignments of `T` must be the same or larger than `U`. Since alignments are always a power
56+
//! of two _larger_ implies _is a multiple of_.
5757
//!
5858
//! See `in_place_collectible()` for the current conditions.
5959
//!
@@ -168,7 +168,7 @@ const fn in_place_collectible<DEST, SRC>(
168168
step_merge: Option<NonZeroUsize>,
169169
step_expand: Option<NonZeroUsize>,
170170
) -> bool {
171-
if DEST::IS_ZST || mem::align_of::<SRC>() != mem::align_of::<DEST>() {
171+
if DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() {
172172
return false;
173173
}
174174

@@ -178,7 +178,7 @@ const fn in_place_collectible<DEST, SRC>(
178178
// e.g.
179179
// - 1 x [u8; 4] -> 4x u8, via flatten
180180
// - 4 x u8 -> 1x [u8; 4], via array_chunks
181-
mem::size_of::<SRC>() * step_merge.get() == mem::size_of::<DEST>() * step_expand.get()
181+
mem::size_of::<SRC>() * step_merge.get() >= mem::size_of::<DEST>() * step_expand.get()
182182
}
183183
// Fall back to other from_iter impls if an overflow occurred in the step merge/expansion
184184
// tracking.
@@ -255,12 +255,15 @@ where
255255
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap };
256256
src.forget_allocation_drop_remaining();
257257

258-
// Adjust the allocation size if the source had a capacity in bytes that wasn't a multiple
259-
// of the destination type size.
258+
// Adjust the allocation if the alignment didn't match or the source had a capacity in bytes
259+
// that wasn't a multiple of the destination type size.
260260
// Since the discrepancy should generally be small this should only result in some
261261
// bookkeeping updates and no memmove.
262-
if const { mem::size_of::<T>() > mem::size_of::<I::Src>() }
263-
&& src_cap * mem::size_of::<I::Src>() != dst_cap * mem::size_of::<T>()
262+
if (const {
263+
let src_sz = mem::size_of::<I::Src>();
264+
src_sz > 0 && mem::size_of::<T>() % src_sz != 0
265+
} && src_cap * mem::size_of::<I::Src>() != dst_cap * mem::size_of::<T>())
266+
|| const { mem::align_of::<T>() != mem::align_of::<I::Src>() }
264267
{
265268
let alloc = Global;
266269
unsafe {

0 commit comments

Comments
 (0)