Skip to content

[libc++] Make drop_view::begin constant time (#72883) #72929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion libcxx/include/__ranges/drop_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class drop_view : public view_interface<drop_view<_View>> {
_LIBCPP_HIDE_FROM_ABI constexpr auto begin()
requires(!(__simple_view<_View> && random_access_range<const _View> && sized_range<const _View>))
{
if constexpr (random_access_range<_View> && sized_range<_View>) {
const auto __dist = std::min(ranges::distance(__base_), __count_);
return ranges::begin(__base_) + __dist;
}
if constexpr (_UseCache)
if (__cached_begin_.__has_value())
return *__cached_begin_;
Expand All @@ -103,7 +107,8 @@ class drop_view : public view_interface<drop_view<_View>> {
_LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
requires random_access_range<const _View> && sized_range<const _View>
{
return ranges::next(ranges::begin(__base_), __count_, ranges::end(__base_));
const auto __dist = std::min(ranges::distance(__base_), __count_);
return ranges::begin(__base_) + __dist;
}

_LIBCPP_HIDE_FROM_ABI constexpr auto end()
Expand Down
38 changes: 38 additions & 0 deletions libcxx/test/std/ranges/range.adaptors/range.drop/begin.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,44 @@ constexpr bool test() {

static_assert(!BeginInvocable<const ForwardView>);

{
// non-common non-simple view,
// The wording of the standard is:
// Returns: ranges::next(ranges::begin(base_), count_, ranges::end(base_))
// Note that "Returns" is used here, meaning that we don't have to do it this way.
// In fact, this will use ranges::advance that has O(n) on non-common range.
// but [range.range] requires "amortized constant time" for ranges::begin and ranges::end
// Here, we test that begin() is indeed constant time, by creating a customized
// sentinel and counting how many times the sentinel eq function is called.
// It should be 0 times, but since this test (or any test under libcxx/test/std) is
// also used by other implementations, we relax the condition to that
// sentinel_cmp_calls is a constant number.
int sentinel_cmp_calls_1 = 0;
int sentinel_cmp_calls_2 = 0;
using NonCommonView = MaybeSimpleNonCommonView<false>;
static_assert(std::ranges::random_access_range<NonCommonView>);
static_assert(std::ranges::sized_range<NonCommonView>);
std::ranges::drop_view dropView9_1(NonCommonView{{}, 0, &sentinel_cmp_calls_1}, 4);
std::ranges::drop_view dropView9_2(NonCommonView{{}, 0, &sentinel_cmp_calls_2}, 6);
assert(dropView9_1.begin() == globalBuff + 4);
assert(dropView9_2.begin() == globalBuff + 6);
assert(sentinel_cmp_calls_1 == sentinel_cmp_calls_2);
}

{
// non-common simple view, same as above.
int sentinel_cmp_calls_1 = 0;
int sentinel_cmp_calls_2 = 0;
using NonCommonView = MaybeSimpleNonCommonView<true>;
static_assert(std::ranges::random_access_range<NonCommonView>);
static_assert(std::ranges::sized_range<NonCommonView>);
std::ranges::drop_view dropView10_1(NonCommonView{{}, 0, &sentinel_cmp_calls_1}, 4);
std::ranges::drop_view dropView10_2(NonCommonView{{}, 0, &sentinel_cmp_calls_2}, 6);
assert(dropView10_1.begin() == globalBuff + 4);
assert(dropView10_2.begin() == globalBuff + 6);
assert(sentinel_cmp_calls_1 == sentinel_cmp_calls_2);
}

{
static_assert(std::ranges::random_access_range<const SimpleView>);
static_assert(std::ranges::sized_range<const SimpleView>);
Expand Down
32 changes: 32 additions & 0 deletions libcxx/test/std/ranges/range.adaptors/range.drop/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,38 @@

int globalBuff[8];

template <class T>
struct sentinel {
T* ptr_;
int* num_of_sentinel_cmp_calls;

public:
friend constexpr bool operator==(sentinel const s, T* const ptr) noexcept {
++(*s.num_of_sentinel_cmp_calls);
return {s.ptr_ == ptr};
}
friend constexpr bool operator==(T* const ptr, sentinel const s) noexcept {
++(*s.num_of_sentinel_cmp_calls);
return {s.ptr_ == ptr};
}
friend constexpr bool operator!=(sentinel const s, T* const ptr) noexcept { return !(s == ptr); }
friend constexpr bool operator!=(T* const ptr, sentinel const s) noexcept { return !(s == ptr); }
};

template <bool IsSimple>
struct MaybeSimpleNonCommonView : std::ranges::view_base {
int start_;
int* num_of_sentinel_cmp_calls;
constexpr std::size_t size() const { return 8; }
constexpr int* begin() { return globalBuff + start_; }
constexpr std::conditional_t<IsSimple, int*, const int*> begin() const { return globalBuff + start_; }
constexpr sentinel<int> end() { return sentinel<int>{globalBuff + size(), num_of_sentinel_cmp_calls}; }
constexpr auto end() const {
return std::conditional_t<IsSimple, sentinel<int>, sentinel<const int>>{
globalBuff + size(), num_of_sentinel_cmp_calls};
}
};

struct MoveOnlyView : std::ranges::view_base {
int start_;
constexpr explicit MoveOnlyView(int start = 0) : start_(start) {}
Expand Down