Skip to content

Commit ef36811

Browse files
committed
Add Iterator::reverse() based on DoubleEndedIterator
1 parent 4aa9615 commit ef36811

File tree

8 files changed

+125
-24
lines changed

8 files changed

+125
-24
lines changed

subspace/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ target_sources(subspace PUBLIC
6262
"iter/map.h"
6363
"iter/once.h"
6464
"iter/sized_iterator.h"
65+
"iter/reverse.h"
6566
"macros/__private/compiler_bugs.h"
6667
"macros/always_inline.h"
6768
"macros/builtin.h"

subspace/iter/filter.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#pragma once
1616

1717
#include "subspace/fn/fn_defn.h"
18-
#include "subspace/iter/iterator_concept.h"
1918
#include "subspace/iter/iterator_defn.h"
2019
#include "subspace/iter/sized_iterator.h"
2120
#include "subspace/mem/move.h"
@@ -60,10 +59,8 @@ class Filter final
6059
Pred pred_;
6160
InnerSizedIter next_iter_;
6261

63-
// The InnerSizedIter is already known to be trivially relocatable, as
64-
// SizedIterator is only constructed for trivially relocatable iterators.
65-
// Likewise, the predicate is known to be trivially relocatable because FnMut
66-
// is.
62+
// The InnerSizedIter is trivially relocatable. Likewise, the predicate is
63+
// known to be trivially relocatable because FnMut is.
6764
sus_class_trivially_relocatable(::sus::marker::unsafe_fn, decltype(pred_),
6865
decltype(next_iter_));
6966
};

subspace/iter/iterator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@
3232
// them all every time.
3333
#include "subspace/iter/filter.h"
3434
#include "subspace/iter/map.h"
35+
#include "subspace/iter/reverse.h"

subspace/iter/iterator_defn.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "subspace/iter/__private/iterator_loop.h"
2121
#include "subspace/iter/boxed_iterator.h"
2222
#include "subspace/iter/from_iterator.h"
23-
#include "subspace/iter/iterator_defn.h"
2423
#include "subspace/iter/sized_iterator.h"
2524
#include "subspace/macros/__private/compiler_bugs.h"
2625
#include "subspace/mem/move.h"
@@ -48,6 +47,8 @@ class Filter;
4847
template <class FromItem, class Item, size_t InnerIterSize,
4948
size_t InnerIterAlign>
5049
class Map;
50+
template <class Item, class InnerIter>
51+
class Reverse;
5152

5253
struct SizeHint {
5354
::sus::num::usize lower;
@@ -203,6 +204,9 @@ class [[nodiscard]] IteratorImpl : public IteratorBase<Item> {
203204
pred) && noexcept
204205
requires(::sus::mem::relocate_by_memcpy<Iter>);
205206

207+
auto reverse() && noexcept
208+
requires(::sus::mem::relocate_by_memcpy<Iter>);
209+
206210
/// Transforms an iterator into a collection.
207211
///
208212
/// collect() can turn anything iterable into a relevant collection. If this
@@ -305,6 +309,14 @@ auto IteratorImpl<Iter, Item>::filter(
305309
make_sized_iterator(static_cast<Iter&&>(*this)));
306310
}
307311

312+
template <class Iter, class Item>
313+
auto IteratorImpl<Iter, Item>::reverse() && noexcept
314+
requires(::sus::mem::relocate_by_memcpy<Iter>)
315+
{
316+
using Reverse = Reverse<Item, Iter>;
317+
return Reverse::with(make_sized_iterator(static_cast<Iter&&>(*this)));
318+
}
319+
308320
template <class Iter, class Item>
309321
template <::sus::iter::FromIterator<Item> C>
310322
::sus::iter::FromIterator<Item> auto

subspace/iter/iterator_unittest.cc

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "subspace/iter/empty.h"
2323
#include "subspace/iter/filter.h"
2424
#include "subspace/macros/__private/compiler_bugs.h"
25+
#include "subspace/mem/replace.h"
2526
#include "subspace/prelude.h"
2627

2728
using sus::containers::Array;
@@ -82,12 +83,14 @@ class ArrayIterator final : public IteratorImpl<ArrayIterator<Item, N>, Item> {
8283
}
8384

8485
Option<Item> next() noexcept final {
85-
if (++(index_) < N) {
86-
return items_[index_].take();
87-
} else {
88-
--(index_);
89-
return Option<Item>::none();
90-
}
86+
if (front_ == back_) return sus::none();
87+
return items_[sus::mem::replace(front_, front_ + 1u)].take();
88+
}
89+
90+
Option<Item> next_back() noexcept {
91+
if (front_ == back_) return sus::none();
92+
back_ -= 1u;
93+
return items_[back_].take();
9194
}
9295

9396
protected:
@@ -98,11 +101,12 @@ class ArrayIterator final : public IteratorImpl<ArrayIterator<Item, N>, Item> {
98101
})) {}
99102

100103
private:
101-
size_t index_ = static_cast<size_t>(-1);
104+
size_t front_ = size_t{0};
105+
size_t back_ = N;
102106
Array<Option<Item>, N> items_;
103107

104-
sus_class_trivially_relocatable_if_types(unsafe_fn, decltype(index_),
105-
decltype(items_));
108+
sus_class_trivially_relocatable_if_types(unsafe_fn, decltype(front_),
109+
decltype(back_), decltype(items_));
106110
};
107111

108112
static_assert(sus::iter::Iterator<ArrayIterator<int, 1>, int>);
@@ -340,4 +344,18 @@ TEST(Iterator, CollectVec) {
340344
EXPECT_EQ(collected[4u], 5);
341345
}
342346

347+
TEST(Iterator, Reverse) {
348+
i32 nums[5] = {1, 2, 3, 4, 5};
349+
350+
auto it = ArrayIterator<i32, 5>::with_array(nums).reverse();
351+
static_assert(sus::iter::Iterator<decltype(it), i32>);
352+
static_assert(sus::iter::DoubleEndedIterator<decltype(it), i32>);
353+
EXPECT_EQ(it.next(), sus::some(5).construct());
354+
EXPECT_EQ(it.next(), sus::some(4).construct());
355+
EXPECT_EQ(it.next(), sus::some(3).construct());
356+
EXPECT_EQ(it.next(), sus::some(2).construct());
357+
EXPECT_EQ(it.next(), sus::some(1).construct());
358+
EXPECT_EQ(it.next(), sus::None);
359+
}
360+
343361
} // namespace

subspace/iter/map.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,12 @@
1616

1717
#include "subspace/fn/fn_defn.h"
1818
#include "subspace/iter/iterator_defn.h"
19-
#include "subspace/iter/iterator_concept.h"
2019
#include "subspace/iter/sized_iterator.h"
2120
#include "subspace/mem/move.h"
2221
#include "subspace/mem/relocate.h"
2322

2423
namespace sus::iter {
2524

26-
using ::sus::mem::relocate_by_memcpy;
27-
2825
template <class FromItem, class ToItem, size_t InnerIterSize,
2926
size_t InnerIterAlign>
3027
class Map final
@@ -57,10 +54,9 @@ class Map final
5754
MapFn fn_;
5855
InnerSizedIter next_iter_;
5956

60-
// The InnerSizedIter is already known to be trivially relocatable, by
61-
// pushing the inner Iterator onto the heap if needed. Likewise, the
62-
// predicate is known to be trivially relocatable because the FnMut will
63-
// either be a function pointer or a heap allocation itself.
57+
// The InnerSizedIter is trivially relocatable. Likewise, the predicate is
58+
// known to be trivially relocatable because the FnMut will either be a
59+
// function pointer or a heap allocation itself.
6460
sus_class_trivially_relocatable(::sus::marker::unsafe_fn, decltype(fn_),
6561
decltype(next_iter_));
6662
};

subspace/iter/once.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414

1515
#pragma once
1616

17-
#include "subspace/iter/iterator_concept.h"
1817
#include "subspace/iter/iterator_defn.h"
19-
#include "subspace/macros/__private/compiler_bugs.h"
2018
#include "subspace/mem/move.h"
2119
#include "subspace/mem/relocate.h"
2220

subspace/iter/reverse.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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/fn/fn_defn.h"
18+
#include "subspace/iter/iterator_concept.h"
19+
#include "subspace/iter/iterator_defn.h"
20+
#include "subspace/iter/sized_iterator.h"
21+
#include "subspace/mem/move.h"
22+
#include "subspace/mem/relocate.h"
23+
#include "subspace/mem/size_of.h"
24+
25+
namespace sus::iter {
26+
27+
using ::sus::mem::relocate_by_memcpy;
28+
29+
/// An iterator that iterates over another iterator but in reverse.
30+
///
31+
/// The iterator wrapped by Reverse must be a DoubleEndedIterator.
32+
template <class ItemT, class InnerIter>
33+
class Reverse final : public IteratorImpl<Reverse<ItemT, InnerIter>, ItemT> {
34+
using InnerSizedIter = SizedIterator<ItemT, ::sus::mem::size_of<InnerIter>(),
35+
alignof(InnerIter)>;
36+
37+
public:
38+
using Item = ItemT;
39+
40+
static Reverse with(InnerSizedIter&& next_iter) noexcept
41+
requires(sus::iter::DoubleEndedIterator<InnerIter, Item>)
42+
{
43+
return Reverse(::sus::move(next_iter));
44+
}
45+
46+
Option<Item> next() noexcept final {
47+
// This class must know the full type of the inner iterator in order to
48+
// access its next_back() method.
49+
//
50+
// TODO: We could consider storing an object pointer and method pointer
51+
// instead to erase the type, but needs to be a properly casted pointer
52+
// into the SizedIterator's storage to deal with the case where the Iterator
53+
// implementation has another virtual base class which comes before Iterator
54+
// and thus casting moves the pointer.
55+
//
56+
// TODO: If SizedIterator stored the method pointer for next_back and a
57+
// `static constexpr` tag to say if `next_back()` is callable, then all
58+
// composible iterators could preserve DoubleEndedIterator the same way
59+
// Reverse does.
60+
return static_cast<InnerIter&>(next_iter_.iterator_mut()).next_back();
61+
}
62+
63+
// Reverse is reversible, and implements DoubleEndedIterator.
64+
Option<Item> next_back() noexcept {
65+
return static_cast<InnerIter&>(next_iter_.iterator_mut()).next();
66+
}
67+
68+
private:
69+
Reverse(InnerSizedIter&& next_iter) : next_iter_(::sus::move(next_iter)) {}
70+
71+
InnerSizedIter next_iter_;
72+
73+
// The InnerSizedIter is trivially relocatable.
74+
sus_class_trivially_relocatable(::sus::marker::unsafe_fn,
75+
decltype(next_iter_));
76+
};
77+
78+
} // namespace sus::iter

0 commit comments

Comments
 (0)