|
| 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 |
0 commit comments