From c5af97546a805d0d389171fd4ed7c741b6ad14e9 Mon Sep 17 00:00:00 2001 From: The8472 Date: Sat, 3 Oct 2020 03:44:40 +0200 Subject: [PATCH 01/12] reuse RHS allocation in vec.extend() when the LHS is empty --- library/alloc/src/vec.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index b20ccd388d1f1..9eb436a2bcd46 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2199,19 +2199,11 @@ impl SpecFromIter> for Vec { // But it is a conservative choice. let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; if !has_advanced || iterator.len() >= iterator.cap / 2 { - unsafe { - let it = ManuallyDrop::new(iterator); - if has_advanced { - ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); - } - return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); - } + return iterator.into_vec(); } let mut vec = Vec::new(); - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vec.spec_extend(iterator); + iterator.move_to(&mut vec); vec } } @@ -2391,11 +2383,12 @@ where } impl SpecExtend> for Vec { - fn spec_extend(&mut self, mut iterator: IntoIter) { - unsafe { - self.append_elements(iterator.as_slice() as _); + fn spec_extend(&mut self, iterator: IntoIter) { + if mem::size_of::() > 0 && self.len == 0 && self.capacity() < iterator.len() { + *self = iterator.into_vec(); + return; } - iterator.ptr = iterator.end; + iterator.move_to(self); } } @@ -2928,6 +2921,23 @@ impl IntoIter { self.ptr = self.buf.as_ptr(); self.end = self.buf.as_ptr(); } + + /// Shifts the remaining elements to the front and then converts the whole allocation to a Vec + fn into_vec(self) -> Vec { + if self.ptr != self.buf.as_ptr() as *const _ { + unsafe { ptr::copy(self.ptr, self.buf.as_ptr(), self.len()) } + } + + let iter = ManuallyDrop::new(self); + unsafe { Vec::from_raw_parts(iter.buf.as_ptr(), iter.len(), iter.cap) } + } + + fn move_to(mut self, dest: &mut Vec) { + unsafe { + dest.append_elements(self.as_slice() as _); + } + self.ptr = self.end; + } } #[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] From 24096537edf34f8bb6dc3cdfc5b3ed66d4cf8d50 Mon Sep 17 00:00:00 2001 From: The8472 Date: Sun, 4 Oct 2020 17:26:44 +0200 Subject: [PATCH 02/12] vec.extend() reuse RHS allocation if combined data does not fit into LHS --- library/alloc/src/vec.rs | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 9eb436a2bcd46..d4a9c83208831 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2199,7 +2199,8 @@ impl SpecFromIter> for Vec { // But it is a conservative choice. let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; if !has_advanced || iterator.len() >= iterator.cap / 2 { - return iterator.into_vec(); + // Safety: passing 0 is always valid + return unsafe { iterator.into_vec(0) }; } let mut vec = Vec::new(); @@ -2384,8 +2385,20 @@ where impl SpecExtend> for Vec { fn spec_extend(&mut self, iterator: IntoIter) { - if mem::size_of::() > 0 && self.len == 0 && self.capacity() < iterator.len() { - *self = iterator.into_vec(); + // Avoid reallocation if we can use iterator's storage instead. This requires 1 memcpy and 0-1 memmove + // while reallocation would require 1 alloc, 1-2 memcpy, 1-2 free + if mem::size_of::() > 0 + && self.capacity() - self.len() < iterator.len() + && iterator.cap - iterator.len() >= self.len() + { + // Safety: we just checked that IntoIter has sufficient capacity to prepend our elements. + // Prepending will then fill the uninitialized prefix. + *self = unsafe { + let mut v = iterator.into_vec(self.len() as isize); + ptr::copy_nonoverlapping(self.as_ptr(), v.as_mut_ptr(), self.len); + self.set_len(0); + v + }; return; } iterator.move_to(self); @@ -2922,14 +2935,25 @@ impl IntoIter { self.end = self.buf.as_ptr(); } - /// Shifts the remaining elements to the front and then converts the whole allocation to a Vec - fn into_vec(self) -> Vec { - if self.ptr != self.buf.as_ptr() as *const _ { - unsafe { ptr::copy(self.ptr, self.buf.as_ptr(), self.len()) } + /// Shifts the remaining elements to `offset` and then converts the whole allocation into a Vec + /// with `vec.len() == offset + self.len()` + /// + /// # Safety + /// + /// When a non-zero offset is passed the resulting Vec will have an uninitialized prefix + /// that needs to be filled before the Vec is valid again. + /// + /// * `offset + self.len()` must not exceed `self.cap` + /// * `offset == 0` is always valid + /// * `offset` must be positive + unsafe fn into_vec(self, offset: isize) -> Vec { + let dst = unsafe { self.buf.as_ptr().offset(offset) }; + if self.ptr != dst as *const _ { + unsafe { ptr::copy(self.ptr, dst, self.len()) } } let iter = ManuallyDrop::new(self); - unsafe { Vec::from_raw_parts(iter.buf.as_ptr(), iter.len(), iter.cap) } + unsafe { Vec::from_raw_parts(iter.buf.as_ptr(), offset as usize + iter.len(), iter.cap) } } fn move_to(mut self, dest: &mut Vec) { From 8a0a13a738adad3d853ead5f3a0d5fe4106cc19b Mon Sep 17 00:00:00 2001 From: the8472 Date: Sun, 4 Oct 2020 16:03:31 +0200 Subject: [PATCH 03/12] add comment for IntoIter::move_to Co-authored-by: Ivan Tham --- library/alloc/src/vec.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index d4a9c83208831..f4f71a685e9ab 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2956,6 +2956,7 @@ impl IntoIter { unsafe { Vec::from_raw_parts(iter.buf.as_ptr(), offset as usize + iter.len(), iter.cap) } } + /// Move remaining elements to the end of `dest`. fn move_to(mut self, dest: &mut Vec) { unsafe { dest.append_elements(self.as_slice() as _); From 0c443cf529059939186dafee61be0ac9a99df2ba Mon Sep 17 00:00:00 2001 From: The8472 Date: Mon, 5 Oct 2020 23:40:57 +0200 Subject: [PATCH 04/12] rename function --- library/alloc/src/vec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index f4f71a685e9ab..aa2d1b17f1c8c 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2200,7 +2200,7 @@ impl SpecFromIter> for Vec { let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; if !has_advanced || iterator.len() >= iterator.cap / 2 { // Safety: passing 0 is always valid - return unsafe { iterator.into_vec(0) }; + return unsafe { iterator.into_vec_with_uninit_prefix(0) }; } let mut vec = Vec::new(); @@ -2394,7 +2394,7 @@ impl SpecExtend> for Vec { // Safety: we just checked that IntoIter has sufficient capacity to prepend our elements. // Prepending will then fill the uninitialized prefix. *self = unsafe { - let mut v = iterator.into_vec(self.len() as isize); + let mut v = iterator.into_vec_with_uninit_prefix(self.len() as isize); ptr::copy_nonoverlapping(self.as_ptr(), v.as_mut_ptr(), self.len); self.set_len(0); v @@ -2946,7 +2946,7 @@ impl IntoIter { /// * `offset + self.len()` must not exceed `self.cap` /// * `offset == 0` is always valid /// * `offset` must be positive - unsafe fn into_vec(self, offset: isize) -> Vec { + unsafe fn into_vec_with_uninit_prefix(self, offset: isize) -> Vec { let dst = unsafe { self.buf.as_ptr().offset(offset) }; if self.ptr != dst as *const _ { unsafe { ptr::copy(self.ptr, dst, self.len()) } From 153a9162db452429200a1e7f263bd812f37a61e9 Mon Sep 17 00:00:00 2001 From: The8472 Date: Mon, 5 Oct 2020 23:41:08 +0200 Subject: [PATCH 05/12] add debug asserts --- library/alloc/src/vec.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index aa2d1b17f1c8c..d3c2d597f7d98 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2947,6 +2947,8 @@ impl IntoIter { /// * `offset == 0` is always valid /// * `offset` must be positive unsafe fn into_vec_with_uninit_prefix(self, offset: isize) -> Vec { + debug_assert!(offset > 0); + debug_assert!(offset as usize + self.len() <= self.cap); let dst = unsafe { self.buf.as_ptr().offset(offset) }; if self.ptr != dst as *const _ { unsafe { ptr::copy(self.ptr, dst, self.len()) } From cb477d45d82b0664fe04fbe5a4176aeb3076b158 Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 6 Oct 2020 22:34:01 +0200 Subject: [PATCH 06/12] rename function --- library/alloc/src/vec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index d3c2d597f7d98..452b73e50e4dc 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2204,7 +2204,7 @@ impl SpecFromIter> for Vec { } let mut vec = Vec::new(); - iterator.move_to(&mut vec); + iterator.move_into(&mut vec); vec } } @@ -2401,7 +2401,7 @@ impl SpecExtend> for Vec { }; return; } - iterator.move_to(self); + iterator.move_into(self); } } @@ -2959,7 +2959,7 @@ impl IntoIter { } /// Move remaining elements to the end of `dest`. - fn move_to(mut self, dest: &mut Vec) { + fn move_into(mut self, dest: &mut Vec) { unsafe { dest.append_elements(self.as_slice() as _); } From 5e6abf12f0449e5f7ce05af0e6f3840ba6f37f8e Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 6 Oct 2020 22:34:48 +0200 Subject: [PATCH 07/12] add ascii illustration --- library/alloc/src/vec.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 452b73e50e4dc..5b3e9d1624c31 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2386,7 +2386,26 @@ where impl SpecExtend> for Vec { fn spec_extend(&mut self, iterator: IntoIter) { // Avoid reallocation if we can use iterator's storage instead. This requires 1 memcpy and 0-1 memmove - // while reallocation would require 1 alloc, 1-2 memcpy, 1-2 free + // while reallocation would require 1 alloc, 1-2 memcpy, 1-2 free. + // + // A) non-empty self, partially consumed iterator + // + // self iterator + // |AAAA | | BBB | + // |AAAA | | BBB | into_vec_with_uninit_prefix + // | | |AAAABBB | prepend + // |AAAABBB | -- *self = v + // + // B) empty self, partially consumed iterator + // + // | | | BBBB | + // | | |BBBB | into_vec_with_uninit_prefix + // |BBBB | -- *self = v + // + // C) empty self, pristine iterator + // + // | | |BBBB | + // |BBBB | -- *self = v if mem::size_of::() > 0 && self.capacity() - self.len() < iterator.len() && iterator.cap - iterator.len() >= self.len() From 567cd5202fe53946914cd15ec3fe819592d9c91b Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 6 Oct 2020 22:35:41 +0200 Subject: [PATCH 08/12] move self-replacement to separate line --- library/alloc/src/vec.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 5b3e9d1624c31..ff8b84442465c 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2412,12 +2412,13 @@ impl SpecExtend> for Vec { { // Safety: we just checked that IntoIter has sufficient capacity to prepend our elements. // Prepending will then fill the uninitialized prefix. - *self = unsafe { + let v = unsafe { let mut v = iterator.into_vec_with_uninit_prefix(self.len() as isize); ptr::copy_nonoverlapping(self.as_ptr(), v.as_mut_ptr(), self.len); self.set_len(0); v }; + *self = v; return; } iterator.move_into(self); From 1dbab4844bec22ca49e9ba4ef20b70c9cfd7bb5c Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 6 Oct 2020 23:02:34 +0200 Subject: [PATCH 09/12] memory-based ascii illustration --- library/alloc/src/vec.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index ff8b84442465c..6c8deee389ebc 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2388,24 +2388,28 @@ impl SpecExtend> for Vec { // Avoid reallocation if we can use iterator's storage instead. This requires 1 memcpy and 0-1 memmove // while reallocation would require 1 alloc, 1-2 memcpy, 1-2 free. // - // A) non-empty self, partially consumed iterator + // ## non-empty self, partially consumed iterator // - // self iterator - // |AAAA | | BBB | - // |AAAA | | BBB | into_vec_with_uninit_prefix - // | | |AAAABBB | prepend - // |AAAABBB | -- *self = v + // == step == == memory == == self == == iter / v == + // 0123456789abcdef0123456789abcdef + // 0---------------1--------------- // - // B) empty self, partially consumed iterator + // [initial] AAAA_-----__BBB___-------------- Vec(0x00, 4, 5) IntoIter(0x0a, 0x0c, 0x0f, 8) + // into_vec AAAA_-----____BBB_-------------- Vec(0x00, 4, 5) Vec(0x0a, 7, 8) + // prepend _____-----AAAABBB_-------------- Vec(0x00, 0, 5) Vec(0x0a, 7, 8) + // *self = v ----------AAAABBB_-------------- Vec(0x0a, 7, 8) // - // | | | BBBB | - // | | |BBBB | into_vec_with_uninit_prefix - // |BBBB | -- *self = v + // ## empty self, partially consumed iterator // - // C) empty self, pristine iterator + // [initial] ____------__BBBB__-------------- Vec(0x00, 0, 4) IntoIter(0x0a, 0x0c, 0x10, 8) + // into_vec ____------BBBB____-------------- Vec(0x00, 0, 4) Vec(0x0a, 4, 8) + // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) + // + // ## empty self, pristine iterator + // + // [initial] ----------BBBB____-------------- Vec(0x00, 0, 0) IntoIter(0x0a, 0x0a, 0x0e, 8) + // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) // - // | | |BBBB | - // |BBBB | -- *self = v if mem::size_of::() > 0 && self.capacity() - self.len() < iterator.len() && iterator.cap - iterator.len() >= self.len() From aa4a4acb88dff8eec7c1bc8f430dd8648aabe3c0 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 8 Oct 2020 01:18:59 +0200 Subject: [PATCH 10/12] annotate used primitives in ascii illustration --- library/alloc/src/vec.rs | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 6c8deee389ebc..587cea565c5de 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2388,27 +2388,41 @@ impl SpecExtend> for Vec { // Avoid reallocation if we can use iterator's storage instead. This requires 1 memcpy and 0-1 memmove // while reallocation would require 1 alloc, 1-2 memcpy, 1-2 free. // + // # illustration of some extend scenarios (not exhaustive) + // + // == step == == memory == == self == == iter / v == + // 0123456789abcdef0123456789abcdef + // 0---------------1--------------- + // // ## non-empty self, partially consumed iterator // - // == step == == memory == == self == == iter / v == - // 0123456789abcdef0123456789abcdef - // 0---------------1--------------- + // [initial] AAAA_-----__BBB___-------------- Vec(0x00, 4, 5) IntoIter(0x0a, 0x0c, 0x0f, 8) + // ³ into_vec AAAA_-----____BBB_-------------- Vec(0x00, 4, 5) Vec(0x0a, 7, 8) + // ² prepend _____-----AAAABBB_-------------- Vec(0x00, 0, 5) Vec(0x0a, 7, 8) + // ⁴ *self = v ----------AAAABBB_-------------- Vec(0x0a, 7, 8) + // + // ## empty self, partially consumed iterator + // + // [initial] ____------__BBBB__-------------- Vec(0x00, 0, 4) IntoIter(0x0a, 0x0c, 0x10, 8) + // ³ into_vec ____------BBBB____-------------- Vec(0x00, 0, 4) Vec(0x0a, 4, 8) + // ⁴ *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) // - // [initial] AAAA_-----__BBB___-------------- Vec(0x00, 4, 5) IntoIter(0x0a, 0x0c, 0x0f, 8) - // into_vec AAAA_-----____BBB_-------------- Vec(0x00, 4, 5) Vec(0x0a, 7, 8) - // prepend _____-----AAAABBB_-------------- Vec(0x00, 0, 5) Vec(0x0a, 7, 8) - // *self = v ----------AAAABBB_-------------- Vec(0x0a, 7, 8) + // ## empty self, pristine iterator // - // ## empty self, partially consumed iterator + // [initial] ----------BBBB____-------------- Vec(0x00, 0, 0) IntoIter(0x0a, 0x0a, 0x0e, 8) + // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) // - // [initial] ____------__BBBB__-------------- Vec(0x00, 0, 4) IntoIter(0x0a, 0x0c, 0x10, 8) - // into_vec ____------BBBB____-------------- Vec(0x00, 0, 4) Vec(0x0a, 4, 8) - // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) + // ## insufficient capacity // - // ## empty self, pristine iterator + // [initial] AAAAA-----BBBBBB__-------------- Vec(0x00, 5, 5) IntoIter(0x0a, 0x0a, 0x0f, 8) + // ¹² reserve(6) ----------BBBBBB__--AAAAA______- Vec(0x14, 5, 11) IntoIter(0x0a, 0x0a, 0x0f, 8) + // ² ptr:copy_n ----------________--AAAAABBBBBB- Vec(0x14, 11, 11) IntoIter(0x0a, 0x0f, 0x0f, 8) + // ⁴ drop --------------------AAAAABBBBBB- Vec(0x14, 11, 11) // - // [initial] ----------BBBB____-------------- Vec(0x00, 0, 0) IntoIter(0x0a, 0x0a, 0x0e, 8) - // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) + // ¹ malloc + // ² memcpy + // ³ memmove + // ⁴ free // if mem::size_of::() > 0 && self.capacity() - self.len() < iterator.len() From 1885f38ee53993d626d46859b064a49df2e2f5c2 Mon Sep 17 00:00:00 2001 From: The8472 Date: Sat, 10 Oct 2020 01:29:05 +0200 Subject: [PATCH 11/12] reserve() was not annotated with free --- library/alloc/src/vec.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 587cea565c5de..5ed98a35e2c51 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2390,34 +2390,34 @@ impl SpecExtend> for Vec { // // # illustration of some extend scenarios (not exhaustive) // - // == step == == memory == == self == == iter / v == - // 0123456789abcdef0123456789abcdef - // 0---------------1--------------- + // = step == == memory == == self == == iter / v == + // 0123456789abcdef0123456789abcdef + // 0---------------1--------------- // // ## non-empty self, partially consumed iterator // - // [initial] AAAA_-----__BBB___-------------- Vec(0x00, 4, 5) IntoIter(0x0a, 0x0c, 0x0f, 8) - // ³ into_vec AAAA_-----____BBB_-------------- Vec(0x00, 4, 5) Vec(0x0a, 7, 8) - // ² prepend _____-----AAAABBB_-------------- Vec(0x00, 0, 5) Vec(0x0a, 7, 8) - // ⁴ *self = v ----------AAAABBB_-------------- Vec(0x0a, 7, 8) + // [initial] AAAA_-----__BBB___-------------- Vec(0x00, 4, 5) IntoIter(0x0a, 0x0c, 0x0f, 8) + // ³ into_vec AAAA_-----____BBB_-------------- Vec(0x00, 4, 5) Vec(0x0a, 7, 8) + // ² prepend _____-----AAAABBB_-------------- Vec(0x00, 0, 5) Vec(0x0a, 7, 8) + // ⁴ *self = v ----------AAAABBB_-------------- Vec(0x0a, 7, 8) // // ## empty self, partially consumed iterator // - // [initial] ____------__BBBB__-------------- Vec(0x00, 0, 4) IntoIter(0x0a, 0x0c, 0x10, 8) - // ³ into_vec ____------BBBB____-------------- Vec(0x00, 0, 4) Vec(0x0a, 4, 8) - // ⁴ *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) + // [initial] ____------__BBBB__-------------- Vec(0x00, 0, 4) IntoIter(0x0a, 0x0c, 0x10, 8) + // ³ into_vec ____------BBBB____-------------- Vec(0x00, 0, 4) Vec(0x0a, 4, 8) + // ⁴ *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) // // ## empty self, pristine iterator // - // [initial] ----------BBBB____-------------- Vec(0x00, 0, 0) IntoIter(0x0a, 0x0a, 0x0e, 8) - // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) + // [initial] ----------BBBB____-------------- Vec(0x00, 0, 0) IntoIter(0x0a, 0x0a, 0x0e, 8) + // *self = v ----------BBBB____-------------- Vec(0x0a, 4, 8) // // ## insufficient capacity // - // [initial] AAAAA-----BBBBBB__-------------- Vec(0x00, 5, 5) IntoIter(0x0a, 0x0a, 0x0f, 8) - // ¹² reserve(6) ----------BBBBBB__--AAAAA______- Vec(0x14, 5, 11) IntoIter(0x0a, 0x0a, 0x0f, 8) - // ² ptr:copy_n ----------________--AAAAABBBBBB- Vec(0x14, 11, 11) IntoIter(0x0a, 0x0f, 0x0f, 8) - // ⁴ drop --------------------AAAAABBBBBB- Vec(0x14, 11, 11) + // [initial] AAAAA-----BBBBBB__-------------- Vec(0x00, 5, 5) IntoIter(0x0a, 0x0a, 0x0f, 8) + // ¹²⁴ reserve(6) ----------BBBBBB__--AAAAA______- Vec(0x14, 5, 11) IntoIter(0x0a, 0x0a, 0x0f, 8) + // ² ptr:copy_n ----------________--AAAAABBBBBB- Vec(0x14, 11, 11) IntoIter(0x0a, 0x0f, 0x0f, 8) + // ⁴ drop --------------------AAAAABBBBBB- Vec(0x14, 11, 11) // // ¹ malloc // ² memcpy From d460c85844f8364abd3d4ff8e2a02c490ffafc62 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 15 Oct 2020 23:33:21 +0200 Subject: [PATCH 12/12] fix debug_assert and improve associated comment --- library/alloc/src/vec.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 5ed98a35e2c51..c97e2bbea321c 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2979,16 +2979,18 @@ impl IntoIter { /// # Safety /// /// When a non-zero offset is passed the resulting Vec will have an uninitialized prefix - /// that needs to be filled before the Vec is valid again. + /// that needs to be filled before the Vec is valid again. Conversely this means + /// that passing `offset = 0` results in a Vec that's immediately valid. /// /// * `offset + self.len()` must not exceed `self.cap` - /// * `offset == 0` is always valid - /// * `offset` must be positive + /// * `offset >= 0` unsafe fn into_vec_with_uninit_prefix(self, offset: isize) -> Vec { - debug_assert!(offset > 0); + debug_assert!(offset >= 0); debug_assert!(offset as usize + self.len() <= self.cap); let dst = unsafe { self.buf.as_ptr().offset(offset) }; if self.ptr != dst as *const _ { + // Move the remaining items to the offset. Even when offset == 0 it can still be necessary + // to move the data if the iterator was partially consumed. unsafe { ptr::copy(self.ptr, dst, self.len()) } }