Skip to content

Commit a3cfa34

Browse files
committed
Add Vec::drain()
1 parent f4b35e2 commit a3cfa34

File tree

7 files changed

+502
-7
lines changed

7 files changed

+502
-7
lines changed

Diff for: subspace/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ target_sources(subspace PUBLIC
4545
"containers/__private/vec_marker.h"
4646
"containers/iterators/array_iter.h"
4747
"containers/iterators/chunks.h"
48+
"containers/iterators/drain.h"
4849
"containers/iterators/slice_iter.h"
4950
"containers/iterators/vec_iter.h"
5051
"containers/iterators/windows.h"

Diff for: subspace/containers/iterators/drain.h

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "subspace/containers/iterators/slice_iter.h"
18+
#include "subspace/iter/iterator_defn.h"
19+
#include "subspace/mem/move.h"
20+
#include "subspace/mem/nonnull.h"
21+
#include "subspace/num/unsigned_integer.h"
22+
#include "subspace/ops/range.h"
23+
#include "subspace/ptr/copy.h"
24+
25+
namespace sus::containers {
26+
template <class T>
27+
class Vec;
28+
} // namespace sus::containers
29+
30+
namespace sus::containers {
31+
32+
/// A draining iterator for Vec<T>.
33+
///
34+
/// This struct is created by Vec::drain. See its documentation for more.
35+
template <class ItemT>
36+
struct [[nodiscard]] [[sus_trivial_abi]] Drain final
37+
: public ::sus::iter::IteratorBase<Drain<ItemT>, ItemT> {
38+
public:
39+
using Item = ItemT;
40+
41+
public:
42+
Drain(Drain&&) = default;
43+
Drain& operator=(Drain&&) = default;
44+
45+
~Drain() noexcept {
46+
// The `iter_` is None if keep_rest() was run, in which cast the Vec is
47+
// already restored. Or if Drain was moved from, in which case it has
48+
// nothing to do.
49+
if (iter_.is_some()) {
50+
restore_vec(0u);
51+
}
52+
}
53+
54+
/// Keep unyielded elements in the source `Vec`.
55+
void keep_rest() && noexcept {
56+
const usize unyielded_len = iter_->exact_size_hint();
57+
Item* const unyielded_ptr =
58+
iter_.take().unwrap().as_mut_slice().as_mut_ptr();
59+
60+
const usize start = vec_.len();
61+
Item* const start_ptr = vec_.as_mut_ptr() + start;
62+
63+
// Move back unyielded elements.
64+
if (unyielded_ptr != start_ptr) {
65+
Item* const src = unyielded_ptr;
66+
Item* const dst = start_ptr;
67+
68+
if constexpr (::sus::mem::relocate_by_memcpy<Item>) {
69+
// Since the Drain'd elements have been moved, and they are trivially
70+
// relocatable, move+destroy is a no-op, so we can skip the destructors
71+
// here and just memmove `src` into them moved-from `dst` objects.
72+
if (unyielded_len > 0) {
73+
::sus::ptr::copy(::sus::marker::unsafe_fn, src, dst, unyielded_len);
74+
}
75+
} else {
76+
for (usize i; i < unyielded_len; i += 1u) {
77+
*(dst + i) = ::sus::move(*(src + i));
78+
}
79+
}
80+
}
81+
82+
restore_vec(unyielded_len);
83+
}
84+
85+
// sus::iter::Iterator trait.
86+
Option<Item> next() noexcept {
87+
// Moves from each element as it is drained. The moved-from element will
88+
// be destroyed when Drain is destroyed.
89+
return iter_->next().map([](Item& i) { return sus::move(i); });
90+
}
91+
92+
// sus::iter::DoubleEndedIterator trait.
93+
Option<Item> next_back() noexcept {
94+
// Moves from each element as it is drained. The moved-from element will
95+
// be destroyed when Drain is destroyed.
96+
return iter_->next_back().map([](Item& i) { return sus::move(i); });
97+
}
98+
99+
// Replace the default impl in sus::iter::IteratorBase.
100+
::sus::iter::SizeHint size_hint() const noexcept final {
101+
return iter_->size_hint();
102+
}
103+
104+
/// sus::iter::ExactSizeIterator trait.
105+
::sus::num::usize exact_size_hint() const noexcept {
106+
return iter_->exact_size_hint();
107+
}
108+
109+
private:
110+
// Constructed by Vec.
111+
friend class Vec<Item>;
112+
113+
void restore_vec(usize kept) {
114+
const usize start = vec_.len() + kept;
115+
const usize tail = tail_start_;
116+
if (start != tail) {
117+
// Drain range was not empty.
118+
119+
const usize drop_len = tail - start;
120+
Item* const src = vec_.as_mut_ptr() + tail;
121+
Item* const dst = vec_.as_mut_ptr() + start;
122+
123+
if constexpr (::sus::mem::relocate_by_memcpy<Item>) {
124+
// Since the Drain'd elements have been moved, and they are trivially
125+
// relocatable, move+destroy is a no-op, so we can skip the destructors
126+
// here and just memmove `src` into them moved-from `dst` objects.
127+
if (tail_len_ > 0) {
128+
::sus::ptr::copy(::sus::marker::unsafe_fn, src, dst, tail_len_);
129+
}
130+
} else {
131+
usize i;
132+
for (; i < tail_len_; i += 1u) {
133+
*(dst + i) = ::sus::move(*(src + i));
134+
}
135+
for (; i < tail_len_ + drop_len; i += 1u) {
136+
(dst + i)->~Item();
137+
}
138+
}
139+
}
140+
vec_.set_len(::sus::marker::unsafe_fn, start + tail_len_);
141+
original_vec_.as_mut() = ::sus::move(vec_);
142+
}
143+
144+
static constexpr auto with(Vec<Item>&& vec sus_lifetimebound,
145+
::sus::ops::Range<usize> range) noexcept {
146+
return Drain(::sus::move(vec), range);
147+
}
148+
149+
constexpr Drain(Vec<Item>&& vec sus_lifetimebound,
150+
::sus::ops::Range<usize> range) noexcept
151+
: tail_start_(range.finish),
152+
tail_len_(vec.len() - range.finish),
153+
iter_(::sus::some(SliceIterMut<Item&>::with(
154+
vec.as_mut_ptr() + range.start, range.finish - range.start))),
155+
vec_(::sus::move(vec)),
156+
original_vec_(sus::mem::nonnull(vec)) {
157+
vec_.set_len(::sus::marker::unsafe_fn, range.start);
158+
}
159+
160+
/// Index of tail to preserve.
161+
usize tail_start_;
162+
/// Length of tail.
163+
usize tail_len_;
164+
/// Current remaining range to remove.
165+
Option<SliceIterMut<Item&>> iter_;
166+
/// The elements from the original_vec_, held locally for safe keeping so that
167+
/// mutation of the original Vec during drain will be flagged as
168+
/// use-after-move.
169+
Vec<Item> vec_;
170+
/// The original moved-from Vec which is restored when the iterator is
171+
/// destroyed.
172+
sus::mem::NonNull<Vec<Item>> original_vec_;
173+
174+
sus_class_trivially_relocatable(::sus::marker::unsafe_fn,
175+
decltype(tail_start_), decltype(tail_len_),
176+
decltype(vec_), decltype(tail_start_),
177+
decltype(original_vec_));
178+
};
179+
} // namespace sus::containers

Diff for: subspace/containers/iterators/slice_iter.h

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ namespace sus::containers {
3131

3232
template <class T>
3333
class Slice;
34+
template <class T>
35+
class SliceMut;
3436

3537
template <class ItemT>
3638
struct [[nodiscard]] [[sus_trivial_abi]] SliceIter final
@@ -50,6 +52,12 @@ struct [[nodiscard]] [[sus_trivial_abi]] SliceIter final
5052
return SliceIter(start, len);
5153
}
5254

55+
/// Returns a slice of the items left to be iterated.
56+
Slice<Item> as_slice() const& {
57+
return Slice<Item>::from_raw_parts(::sus::marker::unsafe_fn, ptr_,
58+
end_ - ptr_);
59+
}
60+
5361
Option<Item> next() noexcept {
5462
if (ptr_ == end_) [[unlikely]]
5563
return Option<Item>::none();
@@ -120,6 +128,13 @@ struct [[sus_trivial_abi]] SliceIterMut final
120128
return SliceIterMut(start, len);
121129
}
122130

131+
/// Returns a mutable slice of the items left to be iterated, consuming the
132+
/// iterator.
133+
SliceMut<RawItem> as_mut_slice() && {
134+
return SliceMut<RawItem>::from_raw_parts_mut(::sus::marker::unsafe_fn, ptr_,
135+
usize::from(end_ - ptr_));
136+
}
137+
123138
// sus::iter::Iterator trait.
124139
Option<Item> next() noexcept {
125140
if (ptr_ == end_) [[unlikely]]

Diff for: subspace/containers/vec.h

+22
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "subspace/containers/__private/vec_marker.h"
2525
#include "subspace/containers/concat.h"
2626
#include "subspace/containers/iterators/chunks.h"
27+
#include "subspace/containers/iterators/drain.h"
2728
#include "subspace/containers/iterators/slice_iter.h"
2829
#include "subspace/containers/iterators/vec_iter.h"
2930
#include "subspace/containers/slice.h"
@@ -244,6 +245,27 @@ class Vec final {
244245
}
245246
}
246247

248+
/// Removes the specified range from the vector in bulk, returning all
249+
/// removed elements as an iterator. If the iterator is dropped before
250+
/// being fully consumed, it drops the remaining removed elements.
251+
///
252+
/// The Vec becomes moved-from and will panic on use while the Drain iterator
253+
/// is in use, and will be usable again once Drain is destroyed or
254+
/// `Drain::keep_remain()` is called.
255+
///
256+
/// # Panics
257+
///
258+
/// Panics if the starting point is greater than the end point or if
259+
/// the end point is greater than the length of the vector.
260+
Drain<T> drain(::sus::ops::RangeBounds<usize> auto range) noexcept {
261+
::sus::ops::Range<usize> bounded_range =
262+
range.start_at(range.start_bound().unwrap_or(0u))
263+
.end_at(range.end_bound().unwrap_or(len()));
264+
::sus::check(bounded_range.start <= bounded_range.finish);
265+
::sus::check(bounded_range.finish <= len());
266+
return Drain<T>::with(::sus::move(*this), bounded_range);
267+
}
268+
247269
/// Decomposes a `Vec<T>` into its raw components.
248270
///
249271
/// Returns the raw pointer to the underlying data, the length of the vector

0 commit comments

Comments
 (0)