FIX: Fix .extend() for ArrayVec with zero-sized type elements

The raw pointer walk did not do the right thing for ZST, because when
offsets are zero, the start and end pointer would be the same and the
loop ends before incrementing the length correctly.
This commit is contained in:
bluss
2019-09-01 13:50:25 +02:00
parent f7381fa699
commit 2a3397995d
+22 -4
View File
@@ -926,8 +926,8 @@ impl<A: Array> Extend<A::Item> for ArrayVec<A> {
let take = self.capacity() - self.len(); let take = self.capacity() - self.len();
unsafe { unsafe {
let len = self.len(); let len = self.len();
let mut ptr = self.as_mut_ptr().offset(len as isize); let mut ptr = raw_ptr_add(self.as_mut_ptr(), len);
let end_ptr = ptr.offset(take as isize); let end_ptr = raw_ptr_add(ptr, take);
// Keep the length in a separate variable, write it back on scope // Keep the length in a separate variable, write it back on scope
// exit. To help the compiler with alias analysis and stuff. // exit. To help the compiler with alias analysis and stuff.
// We update the length to handle panic in the iteration of the // We update the length to handle panic in the iteration of the
@@ -943,8 +943,8 @@ impl<A: Array> Extend<A::Item> for ArrayVec<A> {
loop { loop {
if ptr == end_ptr { break; } if ptr == end_ptr { break; }
if let Some(elt) = iter.next() { if let Some(elt) = iter.next() {
ptr::write(ptr, elt); raw_ptr_write(ptr, elt);
ptr = ptr.offset(1); ptr = raw_ptr_add(ptr, 1);
guard.data += 1; guard.data += 1;
} else { } else {
break; break;
@@ -954,6 +954,24 @@ impl<A: Array> Extend<A::Item> for ArrayVec<A> {
} }
} }
/// Rawptr add but uses arithmetic distance for ZST
unsafe fn raw_ptr_add<T>(ptr: *mut T, offset: usize) -> *mut T {
if mem::size_of::<T>() == 0 {
// Special case for ZST
(ptr as usize).wrapping_add(offset) as _
} else {
ptr.offset(offset as isize)
}
}
unsafe fn raw_ptr_write<T>(ptr: *mut T, value: T) {
if mem::size_of::<T>() == 0 {
/* nothing */
} else {
ptr::write(ptr, value)
}
}
/// Create an `ArrayVec` from an iterator. /// Create an `ArrayVec` from an iterator.
/// ///
/// Does not extract more items than there is space for. No error /// Does not extract more items than there is space for. No error