Skip to main content

alloc/vec/
spec_extend_with.rs

1use core::clone::TrivialClone;
2use core::{mem, ptr};
3
4use crate::alloc::Allocator;
5use crate::vec::{SetLenOnDrop, Vec};
6
7pub(super) trait SpecExtendWith<T> {
8    /// Extend the vector by `n` clones of value.
9    fn spec_extend_with(&mut self, n: usize, value: T);
10}
11
12impl<T: Clone, A: Allocator> SpecExtendWith<T> for Vec<T, A> {
13    default fn spec_extend_with(&mut self, n: usize, value: T) {
14        self.reserve(n);
15
16        // SAFETY: Since `reserve` guarantees `self.len() + n` <= `capacity`, this
17        // pointer is always within bounds, and valid for writes for the next `n` items.
18        let mut ptr = unsafe { self.as_mut_ptr().add(self.len()) };
19        // Use SetLenOnDrop to work around bug where compiler
20        // might not realize the store through `ptr` through self.set_len()
21        // don't alias.
22        let mut local_len = SetLenOnDrop::new(&mut self.len);
23
24        // Write all elements except the last one
25        for _ in 1..n {
26            // three things have been taken into consideration:
27            // - `T::clone` can be provided by the user, and can panic.
28            // - `T` may implement Drop.
29            // - Since we already have a `value: T`, we can clone `n - 1` times.
30
31            // This loop runs at most `n - 1` times, for all slots except the last one.
32            // As per the guarantee of `reserve`, increments to `ptr` within this range won't wrap around.
33
34            // if clone() panics and starts unwinding;
35            // - the write has not been done yet
36            // - the length will be set by SetLenOnDrop, to the amount of initialized elements
37            // - later, when the Vec is dropped, only initialized elements are dropped.
38            // SAFETY: According to the diagram above, this write is within bounds.
39            unsafe { ptr::write(ptr, value.clone()) };
40            // SAFETY: According to the diagram above, this increment is within bounds of the
41            // allocation and won't wrap around
42            ptr = unsafe { ptr.add(1) };
43            // Increment the length of SetLenOnDrop in every step in case clone() panics to drop the
44            // already-initialized elements.
45            local_len.increment_len(1);
46        }
47
48        if n > 0 {
49            // We can write the last element directly without cloning needlessly
50
51            // SAFETY: According to the diagram above, the last element is not yet written,
52            // so this write is within bounds.
53            unsafe { ptr::write(ptr, value) };
54            local_len.increment_len(1);
55        }
56
57        // len set by scope guard
58    }
59}
60
61impl<T: TrivialClone, A: Allocator> SpecExtendWith<T> for Vec<T, A> {
62    fn spec_extend_with(&mut self, n: usize, value: T) {
63        // The `n == 0` check is already being inserted by the loop canonicalization pass,
64        // so this costs nothing.
65        // https://llvm.org/docs/LoopTerminology.html#more-canonical-loops
66        if n == 0 {
67            // three things:
68            // `self.reserve(0)` does not need to be run, since reserving space for 0 elements
69            // is a no-op. since the `value` argument did not have anywhere to be moved to, it is
70            // dropped here. with this branch present here, we can elide the write through self.len,
71            // since `self.len += 0` would be a no-op.
72            return;
73        }
74
75        self.reserve(n);
76
77        // SAFETY: Since `reserve` guarantees `self.len() + n` <= `capacity`, this
78        // pointer is always within bounds, and valid for writes for the next `n` items.
79        let ptr = unsafe { self.as_mut_ptr().add(self.len()) };
80
81        let mut i = 0;
82        // Write all the elements by copying `value`.
83        // We use an incrementing index here instead of incrementing pointer because the former
84        // has been observed to have less register pressure in the loop initialization.
85        // https://llvm.org/docs/Vectorizers.html#inductions
86        while i < n {
87            // SAFETY: `TrivialClone` indicates that copying `value` is equivalent to
88            // calling `Clone::clone` for `T`. As per the safety comment for `ptr`, this pointer
89            // is within bounds, won't wrap around while `i < n`, and valid for writes. The values
90            // do not overlap since `value` is stored as a function argument, and the target is
91            // in the Vec.
92            unsafe { ptr.add(i).copy_from_nonoverlapping(&value, 1) };
93
94            // Because above is infallible, we don't need to worry about setting the len
95            // in this loop, and instead we just increment the loop induction variable.
96            i += 1;
97        }
98
99        // Above, `n` items have been infallibly copied into the vector
100        self.len += n;
101
102        // The reason we did not treat the last position in the vector specially by doing
103        // `n - 1` copies and semantically moving `value` into it is that we are forced to do `n`
104        // copies of the value no matter what, and it's better to do it in the above loop than
105        // by manually writing since it can be vectorized.
106        // Then, `value` was never semantically moved into the vec, only copied from,
107        // so we forget it here to prevent the Drop.
108        mem::forget(value);
109    }
110}