xref: /freebsd-src/contrib/llvm-project/libcxx/include/__ranges/chunk_by_view.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15f757f3fSDimitry Andric // -*- C++ -*-
25f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
35f757f3fSDimitry Andric //
45f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
55f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
65f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
75f757f3fSDimitry Andric //
85f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
95f757f3fSDimitry Andric 
105f757f3fSDimitry Andric #ifndef _LIBCPP___RANGES_CHUNK_BY_VIEW_H
115f757f3fSDimitry Andric #define _LIBCPP___RANGES_CHUNK_BY_VIEW_H
125f757f3fSDimitry Andric 
135f757f3fSDimitry Andric #include <__algorithm/ranges_adjacent_find.h>
145f757f3fSDimitry Andric #include <__assert>
155f757f3fSDimitry Andric #include <__concepts/constructible.h>
165f757f3fSDimitry Andric #include <__config>
175f757f3fSDimitry Andric #include <__functional/bind_back.h>
185f757f3fSDimitry Andric #include <__functional/invoke.h>
195f757f3fSDimitry Andric #include <__iterator/concepts.h>
205f757f3fSDimitry Andric #include <__iterator/default_sentinel.h>
215f757f3fSDimitry Andric #include <__iterator/iterator_traits.h>
225f757f3fSDimitry Andric #include <__iterator/next.h>
235f757f3fSDimitry Andric #include <__iterator/prev.h>
245f757f3fSDimitry Andric #include <__memory/addressof.h>
255f757f3fSDimitry Andric #include <__ranges/access.h>
265f757f3fSDimitry Andric #include <__ranges/all.h>
275f757f3fSDimitry Andric #include <__ranges/concepts.h>
285f757f3fSDimitry Andric #include <__ranges/movable_box.h>
295f757f3fSDimitry Andric #include <__ranges/non_propagating_cache.h>
305f757f3fSDimitry Andric #include <__ranges/range_adaptor.h>
315f757f3fSDimitry Andric #include <__ranges/reverse_view.h>
325f757f3fSDimitry Andric #include <__ranges/subrange.h>
335f757f3fSDimitry Andric #include <__ranges/view_interface.h>
345f757f3fSDimitry Andric #include <__type_traits/conditional.h>
355f757f3fSDimitry Andric #include <__type_traits/decay.h>
365f757f3fSDimitry Andric #include <__type_traits/is_nothrow_constructible.h>
375f757f3fSDimitry Andric #include <__type_traits/is_object.h>
385f757f3fSDimitry Andric #include <__utility/forward.h>
395f757f3fSDimitry Andric #include <__utility/in_place.h>
405f757f3fSDimitry Andric #include <__utility/move.h>
415f757f3fSDimitry Andric 
425f757f3fSDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
435f757f3fSDimitry Andric #  pragma GCC system_header
445f757f3fSDimitry Andric #endif
455f757f3fSDimitry Andric 
465f757f3fSDimitry Andric _LIBCPP_PUSH_MACROS
475f757f3fSDimitry Andric #include <__undef_macros>
485f757f3fSDimitry Andric 
495f757f3fSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
505f757f3fSDimitry Andric 
515f757f3fSDimitry Andric #if _LIBCPP_STD_VER >= 23
525f757f3fSDimitry Andric 
535f757f3fSDimitry Andric namespace ranges {
545f757f3fSDimitry Andric 
555f757f3fSDimitry Andric template <forward_range _View, indirect_binary_predicate<iterator_t<_View>, iterator_t<_View>> _Pred>
565f757f3fSDimitry Andric   requires view<_View> && is_object_v<_Pred>
577a6dacacSDimitry Andric class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS chunk_by_view : public view_interface<chunk_by_view<_View, _Pred>> {
585f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
595f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_;
605f757f3fSDimitry Andric 
615f757f3fSDimitry Andric   // We cache the result of begin() to allow providing an amortized O(1).
625f757f3fSDimitry Andric   using _Cache = __non_propagating_cache<iterator_t<_View>>;
635f757f3fSDimitry Andric   _Cache __cached_begin_;
645f757f3fSDimitry Andric 
655f757f3fSDimitry Andric   class __iterator;
665f757f3fSDimitry Andric 
675f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_next(iterator_t<_View> __current) {
681db9f3b2SDimitry Andric     // Note: this duplicates a check in `optional` but provides a better error message.
691db9f3b2SDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
705f757f3fSDimitry Andric         __pred_.__has_value(), "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate.");
715f757f3fSDimitry Andric     auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) -> bool {
725f757f3fSDimitry Andric       return !std::invoke(*__pred_, std::forward<_Tp>(__x), std::forward<_Up>(__y));
735f757f3fSDimitry Andric     };
745f757f3fSDimitry Andric     return ranges::next(
755f757f3fSDimitry Andric         ranges::adjacent_find(__current, ranges::end(__base_), __reversed_pred), 1, ranges::end(__base_));
765f757f3fSDimitry Andric   }
775f757f3fSDimitry Andric 
785f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_prev(iterator_t<_View> __current)
795f757f3fSDimitry Andric     requires bidirectional_range<_View>
805f757f3fSDimitry Andric   {
811db9f3b2SDimitry Andric     // Attempting to decrement a begin iterator is a no-op (`__find_prev` would return the same argument given to it).
821db9f3b2SDimitry Andric     _LIBCPP_ASSERT_PEDANTIC(__current != ranges::begin(__base_), "Trying to call __find_prev() on a begin iterator.");
831db9f3b2SDimitry Andric     // Note: this duplicates a check in `optional` but provides a better error message.
841db9f3b2SDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
855f757f3fSDimitry Andric         __pred_.__has_value(), "Trying to call __find_prev() on a chunk_by_view that does not have a valid predicate.");
865f757f3fSDimitry Andric 
875f757f3fSDimitry Andric     auto __first = ranges::begin(__base_);
885f757f3fSDimitry Andric     reverse_view __reversed{subrange{__first, __current}};
895f757f3fSDimitry Andric     auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) -> bool {
905f757f3fSDimitry Andric       return !std::invoke(*__pred_, std::forward<_Up>(__y), std::forward<_Tp>(__x));
915f757f3fSDimitry Andric     };
925f757f3fSDimitry Andric     return ranges::prev(ranges::adjacent_find(__reversed, __reversed_pred).base(), 1, std::move(__first));
935f757f3fSDimitry Andric   }
945f757f3fSDimitry Andric 
955f757f3fSDimitry Andric public:
965f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI chunk_by_view()
975f757f3fSDimitry Andric     requires default_initializable<_View> && default_initializable<_Pred>
985f757f3fSDimitry Andric   = default;
995f757f3fSDimitry Andric 
1005f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_by_view(_View __base, _Pred __pred)
1015f757f3fSDimitry Andric       : __base_(std::move(__base)), __pred_(in_place, std::move(__pred)) {}
1025f757f3fSDimitry Andric 
1035f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
1045f757f3fSDimitry Andric     requires copy_constructible<_View>
1055f757f3fSDimitry Andric   {
1065f757f3fSDimitry Andric     return __base_;
1075f757f3fSDimitry Andric   }
1085f757f3fSDimitry Andric 
1095f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
1105f757f3fSDimitry Andric 
1115f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr const _Pred& pred() const { return *__pred_; }
1125f757f3fSDimitry Andric 
1135f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() {
1141db9f3b2SDimitry Andric     // Note: this duplicates a check in `optional` but provides a better error message.
1151db9f3b2SDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1165f757f3fSDimitry Andric         __pred_.__has_value(), "Trying to call begin() on a chunk_by_view that does not have a valid predicate.");
1175f757f3fSDimitry Andric 
1185f757f3fSDimitry Andric     auto __first = ranges::begin(__base_);
1195f757f3fSDimitry Andric     if (!__cached_begin_.__has_value()) {
1205f757f3fSDimitry Andric       __cached_begin_.__emplace(__find_next(__first));
1215f757f3fSDimitry Andric     }
1225f757f3fSDimitry Andric     return {*this, std::move(__first), *__cached_begin_};
1235f757f3fSDimitry Andric   }
1245f757f3fSDimitry Andric 
1255f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
1265f757f3fSDimitry Andric     if constexpr (common_range<_View>) {
1275f757f3fSDimitry Andric       return __iterator{*this, ranges::end(__base_), ranges::end(__base_)};
1285f757f3fSDimitry Andric     } else {
1295f757f3fSDimitry Andric       return default_sentinel;
1305f757f3fSDimitry Andric     }
1315f757f3fSDimitry Andric   }
1325f757f3fSDimitry Andric };
1335f757f3fSDimitry Andric 
1345f757f3fSDimitry Andric template <class _Range, class _Pred>
1355f757f3fSDimitry Andric chunk_by_view(_Range&&, _Pred) -> chunk_by_view<views::all_t<_Range>, _Pred>;
1365f757f3fSDimitry Andric 
1375f757f3fSDimitry Andric template <forward_range _View, indirect_binary_predicate<iterator_t<_View>, iterator_t<_View>> _Pred>
1385f757f3fSDimitry Andric   requires view<_View> && is_object_v<_Pred>
1395f757f3fSDimitry Andric class chunk_by_view<_View, _Pred>::__iterator {
1405f757f3fSDimitry Andric   friend chunk_by_view;
1415f757f3fSDimitry Andric 
1425f757f3fSDimitry Andric   chunk_by_view* __parent_                               = nullptr;
1435f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
1445f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __next_    = iterator_t<_View>();
1455f757f3fSDimitry Andric 
1465f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
1475f757f3fSDimitry Andric       chunk_by_view& __parent, iterator_t<_View> __current, iterator_t<_View> __next)
1485f757f3fSDimitry Andric       : __parent_(std::addressof(__parent)), __current_(__current), __next_(__next) {}
1495f757f3fSDimitry Andric 
1505f757f3fSDimitry Andric public:
1515f757f3fSDimitry Andric   using value_type        = subrange<iterator_t<_View>>;
1525f757f3fSDimitry Andric   using difference_type   = range_difference_t<_View>;
1535f757f3fSDimitry Andric   using iterator_category = input_iterator_tag;
1545f757f3fSDimitry Andric   using iterator_concept  = conditional_t<bidirectional_range<_View>, bidirectional_iterator_tag, forward_iterator_tag>;
1555f757f3fSDimitry Andric 
1565f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
1575f757f3fSDimitry Andric 
1585f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
1591db9f3b2SDimitry Andric     // If the iterator is at end, this would return an empty range which can be checked by the calling code and doesn't
1601db9f3b2SDimitry Andric     // necessarily lead to a bad access.
1611db9f3b2SDimitry Andric     _LIBCPP_ASSERT_PEDANTIC(__current_ != __next_, "Trying to dereference past-the-end chunk_by_view iterator.");
1625f757f3fSDimitry Andric     return {__current_, __next_};
1635f757f3fSDimitry Andric   }
1645f757f3fSDimitry Andric 
1655f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
1661db9f3b2SDimitry Andric     // Attempting to increment an end iterator is a no-op (`__find_next` would return the same argument given to it).
1671db9f3b2SDimitry Andric     _LIBCPP_ASSERT_PEDANTIC(__current_ != __next_, "Trying to increment past end chunk_by_view iterator.");
1685f757f3fSDimitry Andric     __current_ = __next_;
1695f757f3fSDimitry Andric     __next_    = __parent_->__find_next(__current_);
1705f757f3fSDimitry Andric     return *this;
1715f757f3fSDimitry Andric   }
1725f757f3fSDimitry Andric 
1735f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
1745f757f3fSDimitry Andric     auto __tmp = *this;
1755f757f3fSDimitry Andric     ++*this;
1765f757f3fSDimitry Andric     return __tmp;
1775f757f3fSDimitry Andric   }
1785f757f3fSDimitry Andric 
1795f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
1805f757f3fSDimitry Andric     requires bidirectional_range<_View>
1815f757f3fSDimitry Andric   {
1825f757f3fSDimitry Andric     __next_    = __current_;
1835f757f3fSDimitry Andric     __current_ = __parent_->__find_prev(__next_);
1845f757f3fSDimitry Andric     return *this;
1855f757f3fSDimitry Andric   }
1865f757f3fSDimitry Andric 
1875f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
1885f757f3fSDimitry Andric     requires bidirectional_range<_View>
1895f757f3fSDimitry Andric   {
1905f757f3fSDimitry Andric     auto __tmp = *this;
1915f757f3fSDimitry Andric     --*this;
1925f757f3fSDimitry Andric     return __tmp;
1935f757f3fSDimitry Andric   }
1945f757f3fSDimitry Andric 
1955f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
1965f757f3fSDimitry Andric     return __x.__current_ == __y.__current_;
1975f757f3fSDimitry Andric   }
1985f757f3fSDimitry Andric 
1995f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
2005f757f3fSDimitry Andric     return __x.__current_ == __x.__next_;
2015f757f3fSDimitry Andric   }
2025f757f3fSDimitry Andric };
2035f757f3fSDimitry Andric 
2045f757f3fSDimitry Andric namespace views {
2055f757f3fSDimitry Andric namespace __chunk_by {
2065f757f3fSDimitry Andric struct __fn {
2075f757f3fSDimitry Andric   template <class _Range, class _Pred>
208*0fca6ea1SDimitry Andric   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
2095f757f3fSDimitry Andric       noexcept(noexcept(/**/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
2105f757f3fSDimitry Andric           -> decltype(/*--*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) {
2115f757f3fSDimitry Andric     return /*-------------*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred));
2125f757f3fSDimitry Andric   }
2135f757f3fSDimitry Andric 
2145f757f3fSDimitry Andric   template <class _Pred>
2155f757f3fSDimitry Andric     requires constructible_from<decay_t<_Pred>, _Pred>
216*0fca6ea1SDimitry Andric   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const
2175f757f3fSDimitry Andric       noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _Pred>) {
2185f757f3fSDimitry Andric     return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred)));
2195f757f3fSDimitry Andric   }
2205f757f3fSDimitry Andric };
2215f757f3fSDimitry Andric } // namespace __chunk_by
2225f757f3fSDimitry Andric 
2235f757f3fSDimitry Andric inline namespace __cpo {
2245f757f3fSDimitry Andric inline constexpr auto chunk_by = __chunk_by::__fn{};
2255f757f3fSDimitry Andric } // namespace __cpo
2265f757f3fSDimitry Andric } // namespace views
2275f757f3fSDimitry Andric } // namespace ranges
2285f757f3fSDimitry Andric 
2295f757f3fSDimitry Andric #endif // _LIBCPP_STD_VER >= 23
2305f757f3fSDimitry Andric 
2315f757f3fSDimitry Andric _LIBCPP_END_NAMESPACE_STD
2325f757f3fSDimitry Andric 
2335f757f3fSDimitry Andric _LIBCPP_POP_MACROS
2345f757f3fSDimitry Andric 
2355f757f3fSDimitry Andric #endif // _LIBCPP___RANGES_CHUNK_BY_VIEW_H
236