Skip to content

Commit 9f7e107

Browse files
committed
Make Vec and Slice iterators be ExactSizeIterator
1 parent 9a8e288 commit 9f7e107

File tree

12 files changed

+167
-41
lines changed

12 files changed

+167
-41
lines changed

subspace/containers/__private/array_iter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
namespace sus::containers {
2828

2929
template <class T, size_t N>
30-
requires(N <= PTRDIFF_MAX)
30+
requires(N <= size_t{PTRDIFF_MAX})
3131
class Array;
3232

3333
template <::sus::mem::Move ItemT, size_t N>

subspace/containers/__private/slice_iter.h

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "subspace/mem/nonnull.h"
2525
#include "subspace/mem/relocate.h"
2626
#include "subspace/mem/replace.h"
27+
#include "subspace/num/signed_integer.h"
2728
#include "subspace/num/unsigned_integer.h"
2829

2930
namespace sus::containers {
@@ -52,34 +53,46 @@ struct [[sus_trivial_abi]] SliceIter final
5253
Option<Item> next() noexcept final {
5354
if (ptr_ == end_) [[unlikely]]
5455
return Option<Item>::none();
55-
// SAFETY: Since end_ > ptr_, which is checked in the constructor, ptr_ + 1
56-
// will never be null.
56+
// SAFETY: end_ is always > ptr_ when we get here (this was checked by the
57+
// constructor) so ptr_ will be inside the allocation, not pointing just
58+
// after it (like end_ may be).
5759
return Option<Item>::some(*::sus::mem::replace_ptr(mref(ptr_), ptr_ + 1u));
5860
}
5961

6062
// sus::iter::DoubleEndedIterator trait.
6163
Option<Item> next_back() noexcept {
6264
if (ptr_ == end_) [[unlikely]]
6365
return Option<Item>::none();
64-
// SAFETY: Since end_ > ptr_, which is checked in the constructor, end_ - 1
65-
// will never be null.
66+
// SAFETY: end_ is always > ptr_ when we get here (this was checked by the
67+
// constructor) so subtracting one and dereffing will be inside the
68+
// allocation.
6669
end_ -= 1u;
6770
return Option<Item>::some(*end_);
6871
}
6972

70-
::sus::iter::SizeHint size_hint() noexcept final {
71-
// SAFETY: end_ is always larger than ptr_ which is only incremented until
72-
// end_, so this static cast does not drop a negative sign bit. That ptr_
73-
// starts at or before end_ is checked in the constructor.
74-
const usize remaining = static_cast<size_t>(end_ - ptr_);
73+
::sus::iter::SizeHint size_hint() const noexcept final {
74+
// SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can
75+
// not exceed isize::MAX.
76+
// TODO: Use from_unchecked()
77+
const auto remaining = ::sus::num::usize(static_cast<size_t>(end_ - ptr_));
7578
return ::sus::iter::SizeHint(
7679
remaining, ::sus::Option<::sus::num::usize>::some(remaining));
7780
}
7881

82+
/// sus::iter::ExactSizeIterator trait.
83+
::sus::num::usize exact_size_hint() const noexcept {
84+
// SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can
85+
// not exceed isize::MAX.
86+
// TODO: Use from_unchecked()
87+
return ::sus::num::usize::from(static_cast<size_t>(end_ - ptr_));
88+
}
89+
7990
private:
8091
constexpr SliceIter(const RawItem* start, usize len) noexcept
81-
: ptr_(start), end_(start + len.primitive_value) {
82-
check(end_ >= ptr_ || !end_); // end_ may wrap around to 0, but not past 0.
92+
: ptr_(start), end_(start + size_t{len}) {
93+
// Wrap-around would be an invalid allocation and would break our distance
94+
// functions.
95+
check(end_ >= ptr_);
8396
}
8497

8598
const RawItem* ptr_;
@@ -111,8 +124,9 @@ struct [[sus_trivial_abi]] SliceIterMut final
111124
Option<Item> next() noexcept final {
112125
if (ptr_ == end_) [[unlikely]]
113126
return Option<Item>::none();
114-
// SAFETY: Since end_ > ptr_, which is checked in the constructor, ptr_ + 1
115-
// will never be null.
127+
// SAFETY: end_ is always > ptr_ when we get here (this was checked by the
128+
// constructor) so ptr_ will be inside the allocation, not pointing just
129+
// after it (like end_ may be).
116130
return Option<Item>::some(
117131
mref(*::sus::mem::replace_ptr(mref(ptr_), ptr_ + 1u)));
118132
}
@@ -121,25 +135,36 @@ struct [[sus_trivial_abi]] SliceIterMut final
121135
Option<Item> next_back() noexcept {
122136
if (ptr_ == end_) [[unlikely]]
123137
return Option<Item>::none();
124-
// SAFETY: Since end_ > ptr_, which is checked in the constructor, end_ - 1
125-
// will never be null.
138+
// SAFETY: end_ is always > ptr_ when we get here (this was checked by the
139+
// constructor) so subtracting one and dereffing will be inside the
140+
// allocation.
126141
end_ -= 1u;
127142
return Option<Item>::some(mref(*end_));
128143
}
129144

130-
::sus::iter::SizeHint size_hint() noexcept final {
131-
// SAFETY: end_ is always larger than ptr_ which is only incremented until
132-
// end_, so this static cast does not drop a negative sign bit. That ptr_
133-
// starts at or before end_ is checked in the constructor.
134-
const usize remaining = static_cast<size_t>(end_ - ptr_);
145+
::sus::iter::SizeHint size_hint() const noexcept final {
146+
// SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can
147+
// not exceed isize::MAX.
148+
// TODO: Use from_unchecked()
149+
const auto remaining = ::sus::num::usize(static_cast<size_t>(end_ - ptr_));
135150
return ::sus::iter::SizeHint(
136151
remaining, ::sus::Option<::sus::num::usize>::some(remaining));
137152
}
138153

154+
/// sus::iter::ExactSizeIterator trait.
155+
::sus::num::usize exact_size_hint() const noexcept {
156+
// SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can
157+
// not exceed isize::MAX.
158+
// TODO: Use from_unchecked()
159+
return ::sus::num::usize::from(static_cast<size_t>(end_ - ptr_));
160+
}
161+
139162
private:
140163
constexpr SliceIterMut(RawItem* start, usize len) noexcept
141-
: ptr_(start), end_(start + len.primitive_value) {
142-
check(end_ >= ptr_ || !end_); // end_ may wrap around to 0, but not past 0.
164+
: ptr_(start), end_(start + size_t{len}) {
165+
// Wrap-around would be an invalid allocation and would break our distance
166+
// functions.
167+
check(end_ >= ptr_);
143168
}
144169

145170
RawItem* ptr_;

subspace/containers/__private/vec_iter.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ struct VecIntoIter final
3535
public:
3636
using Item = ItemT;
3737

38+
/// Constructs `VecIntoIter` from a `Vec`.
3839
static constexpr auto with(Vec<Item>&& vec) noexcept {
3940
return VecIntoIter(::sus::move(vec));
4041
}
4142

42-
// sus::iter::Iterator trait.
43+
/// sus::iter::Iterator trait.
4344
Option<Item> next() noexcept final {
4445
if (front_index_ == back_index_) [[unlikely]]
4546
return Option<Item>::none();
@@ -52,7 +53,7 @@ struct VecIntoIter final
5253
return Option<Item>::some(move(item));
5354
}
5455

55-
// sus::iter::DoubleEndedIterator trait.
56+
/// sus::iter::DoubleEndedIterator trait.
5657
Option<Item> next_back() noexcept {
5758
if (front_index_ == back_index_) [[unlikely]]
5859
return Option<Item>::none();
@@ -64,18 +65,21 @@ struct VecIntoIter final
6465
return Option<Item>::some(move(item));
6566
}
6667

67-
::sus::iter::SizeHint size_hint() noexcept final {
68+
/// sus::iter::Iterator method.
69+
::sus::iter::SizeHint size_hint() const noexcept final {
6870
const usize remaining = back_index_ - front_index_;
6971
return ::sus::iter::SizeHint(
7072
remaining, ::sus::Option<::sus::num::usize>::some(remaining));
7173
}
7274

75+
/// sus::iter::ExactSizeIterator trait.
76+
::sus::num::usize exact_size_hint() const noexcept {
77+
return back_index_ - front_index_;
78+
}
79+
7380
private:
7481
VecIntoIter(Vec<Item>&& vec) noexcept : vec_(::sus::move(vec)) {}
7582

76-
// TODO: Decompose the Vec (with a Vec::into_raw_parts) into a (pointer, len,
77-
// cap) and just store the pointer here, as the front_index_ and back_index_
78-
// are all we need to retain wrt the Vec's size.
7983
Vec<Item> vec_;
8084
usize front_index_ = 0_usize;
8185
usize back_index_ = vec_.len();

subspace/containers/array.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ struct Storage<T, 0> final {};
5757

5858
/// A container of objects of type T, with a fixed size N.
5959
///
60-
/// An Array can not be larger than PTRDIFF_MAX, as subtracting a pointer at a
60+
/// An Array can not be larger than `isize::MAX`, as subtracting a pointer at a
6161
/// greater distance results in Undefined Behaviour.
6262
template <class T, size_t N>
63-
requires(N <= PTRDIFF_MAX)
63+
requires(N <= size_t{PTRDIFF_MAX})
6464
class Array final {
6565
static_assert(!std::is_const_v<T>,
6666
"`Array<const T, N>` should be written `const Array<T, N>`, as "

subspace/containers/slice.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ class Slice {
5151
static constexpr inline Slice from_raw_parts(::sus::marker::UnsafeFnMarker,
5252
T* data,
5353
::sus::usize len) noexcept {
54-
::sus::check(len.primitive_value <= PTRDIFF_MAX);
54+
::sus::check(len.primitive_value <= static_cast<size_t>(isize::MAX_PRIMITIVE));
5555
return Slice(data, len);
5656
}
5757

5858
// sus::construct::From<Slice<T>, T[]> trait.
5959
template <size_t N>
60-
requires(N <= PTRDIFF_MAX)
60+
requires(N <= size_t{PTRDIFF_MAX})
6161
static constexpr inline Slice from(T (&data)[N]) {
6262
return Slice(data, N);
6363
}

subspace/containers/slice_unittest.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ TEST(Slice, DoubleEndedIterator) {
300300
auto slice = Slice<const usize>::from(ar);
301301

302302
auto it = slice.iter();
303+
static_assert(sus::iter::DoubleEndedIterator<decltype(it), const usize&>);
303304
EXPECT_EQ(it.next_back(), sus::some(3_usize).construct());
304305
EXPECT_EQ(it.next_back(), sus::some(2_usize).construct());
305306
EXPECT_EQ(it.next_back(), sus::some(1_usize).construct());
@@ -310,6 +311,47 @@ TEST(Slice, DoubleEndedIterator) {
310311
auto slice = Slice<usize>::from(ar);
311312

312313
auto it = slice.iter_mut();
314+
static_assert(sus::iter::DoubleEndedIterator<decltype(it), usize&>);
315+
EXPECT_EQ(it.next_back(), sus::some(3_usize).construct());
316+
EXPECT_EQ(it.next_back(), sus::some(2_usize).construct());
317+
EXPECT_EQ(it.next_back(), sus::some(1_usize).construct());
318+
EXPECT_EQ(it.next_back(), sus::None);
319+
}
320+
}
321+
322+
TEST(Slice, ExactSizeIterator) {
323+
{
324+
const usize ar[] = {1u, 2u, 3u};
325+
auto slice = Slice<const usize>::from(ar);
326+
327+
auto it = slice.iter();
328+
static_assert(sus::iter::ExactSizeIterator<decltype(it), const usize&>);
329+
EXPECT_EQ(it.size_hint().lower, 3u);
330+
EXPECT_EQ(it.size_hint().upper, sus::Option<usize>::some(3u));
331+
EXPECT_EQ(it.exact_size_hint(), 3u);
332+
EXPECT_EQ(it.next_back(), sus::some(3_usize).construct());
333+
EXPECT_EQ(it.size_hint().lower, 2u);
334+
EXPECT_EQ(it.size_hint().upper, sus::Option<usize>::some(2u));
335+
EXPECT_EQ(it.exact_size_hint(), 2u);
336+
EXPECT_EQ(it.next_back(), sus::some(2_usize).construct());
337+
EXPECT_EQ(it.size_hint().lower, 1u);
338+
EXPECT_EQ(it.size_hint().upper, sus::Option<usize>::some(1u));
339+
EXPECT_EQ(it.exact_size_hint(), 1u);
340+
EXPECT_EQ(it.next_back(), sus::some(1_usize).construct());
341+
EXPECT_EQ(it.size_hint().lower, 0u);
342+
EXPECT_EQ(it.size_hint().upper, sus::Option<usize>::some(0u));
343+
EXPECT_EQ(it.exact_size_hint(), 0u);
344+
EXPECT_EQ(it.next_back(), sus::None);
345+
EXPECT_EQ(it.size_hint().lower, 0u);
346+
EXPECT_EQ(it.size_hint().upper, sus::Option<usize>::some(0u));
347+
EXPECT_EQ(it.exact_size_hint(), 0u);
348+
}
349+
{
350+
usize ar[] = {1u, 2u, 3u};
351+
auto slice = Slice<usize>::from(ar);
352+
353+
auto it = slice.iter_mut();
354+
static_assert(sus::iter::ExactSizeIterator<decltype(it), usize&>);
313355
EXPECT_EQ(it.next_back(), sus::some(3_usize).construct());
314356
EXPECT_EQ(it.next_back(), sus::some(2_usize).construct());
315357
EXPECT_EQ(it.next_back(), sus::some(1_usize).construct());

subspace/containers/vec_unittest.cc

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ TEST(Vec, IntoIterDoubleEnded) {
323323
v.push(3_i32);
324324

325325
auto it = sus::move(v).into_iter();
326+
static_assert(sus::iter::DoubleEndedIterator<decltype(it), i32>);
326327
EXPECT_EQ(it.next_back(), sus::some(3_i32).construct());
327328
EXPECT_EQ(it.next_back(), sus::some(2_i32).construct());
328329
EXPECT_EQ(it.next_back(), sus::some(1_i32).construct());
@@ -515,9 +516,54 @@ TEST(Vec, SizeHint) {
515516
v.push(1_i32);
516517
v.push(2_i32);
517518
v.push(3_i32);
518-
auto [lower, upper] = sus::move(v).into_iter().size_hint();
519-
EXPECT_EQ(lower, 3_usize);
520-
EXPECT_EQ(upper, sus::Option<usize>::some(3_usize));
519+
auto it = sus::move(v).into_iter();
520+
{
521+
auto [lower, upper] = it.size_hint();
522+
EXPECT_EQ(lower, 3_usize);
523+
EXPECT_EQ(upper, sus::Option<usize>::some(3_usize));
524+
}
525+
EXPECT_EQ(it.next(), sus::Some);
526+
{
527+
auto [lower, upper] = it.size_hint();
528+
EXPECT_EQ(lower, 2_usize);
529+
EXPECT_EQ(upper, sus::Option<usize>::some(2_usize));
530+
}
531+
EXPECT_EQ(it.next(), sus::Some);
532+
{
533+
auto [lower, upper] = it.size_hint();
534+
EXPECT_EQ(lower, 1_usize);
535+
EXPECT_EQ(upper, sus::Option<usize>::some(1_usize));
536+
}
537+
EXPECT_EQ(it.next(), sus::Some);
538+
{
539+
auto [lower, upper] = it.size_hint();
540+
EXPECT_EQ(lower, 0_usize);
541+
EXPECT_EQ(upper, sus::Option<usize>::some(0_usize));
542+
}
543+
EXPECT_EQ(it.next(), sus::None);
544+
{
545+
auto [lower, upper] = it.size_hint();
546+
EXPECT_EQ(lower, 0_usize);
547+
EXPECT_EQ(upper, sus::Option<usize>::some(0_usize));
548+
}
549+
}
550+
551+
TEST(Vec, ExactSizeIterator) {
552+
auto v = Vec<i32>();
553+
v.push(1_i32);
554+
v.push(2_i32);
555+
v.push(3_i32);
556+
auto it = sus::move(v).into_iter();
557+
static_assert(sus::iter::ExactSizeIterator<decltype(it), i32>);
558+
EXPECT_EQ(it.exact_size_hint(), 3_usize);
559+
EXPECT_EQ(it.next(), sus::Some);
560+
EXPECT_EQ(it.exact_size_hint(), 2_usize);
561+
EXPECT_EQ(it.next(), sus::Some);
562+
EXPECT_EQ(it.exact_size_hint(), 1_usize);
563+
EXPECT_EQ(it.next(), sus::Some);
564+
EXPECT_EQ(it.exact_size_hint(), 0_usize);
565+
EXPECT_EQ(it.next(), sus::None);
566+
EXPECT_EQ(it.exact_size_hint(), 0_usize);
521567
}
522568

523569
TEST(Vec, Destroy) {

subspace/iter/iterator_concept.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "subspace/convert/subclass.h"
2121
#include "subspace/mem/forward.h"
2222

23+
namespace sus::num {
24+
struct usize;
25+
}
26+
2327
namespace sus::option {
2428
template <class T>
2529
class Option;
@@ -76,7 +80,12 @@ concept Iterator = requires(T& t) {
7680
/// interchangeable for this purpose.
7781
template <class T, class Item>
7882
concept DoubleEndedIterator = Iterator<T, Item> && requires(T& t) {
79-
{ t.next_back() } -> std::same_as<::sus::option::Option<Item>>;
83+
{ t.next_back() } noexcept -> std::same_as<::sus::option::Option<Item>>;
84+
};
85+
86+
template <class T, class Item>
87+
concept ExactSizeIterator = Iterator<T, Item> && requires(const T& t) {
88+
{ t.exact_size_hint() } noexcept -> std::same_as<::sus::num::usize>;
8089
};
8190

8291
/// Conversion into an `Iterator`.

subspace/iter/iterator_defn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class IteratorBase {
9898
///
9999
/// The default implementation returns `lower = 0` and `upper = None` which is
100100
/// correct for any iterator.
101-
virtual SizeHint size_hint() noexcept {
101+
virtual SizeHint size_hint() const noexcept {
102102
return SizeHint(0_usize, ::sus::Option<::sus::num::usize>::none());
103103
}
104104

subspace/num/__private/float_macros.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
namespace sus::containers {
3131
template <class T, size_t N>
32-
requires(N <= PTRDIFF_MAX)
32+
requires(N <= size_t{PTRDIFF_MAX})
3333
class Array;
3434
}
3535

0 commit comments

Comments
 (0)