// Copyright 2017 Elias Kosunen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This file is a part of scnlib: // https://github.com/eliaskosunen/scnlib #ifndef SCN_DETAIL_RANGE_H #define SCN_DETAIL_RANGE_H #include "../ranges/ranges.h" #include "../util/algorithm.h" #include "../util/memory.h" #include "error.h" #include "vectored.h" namespace scn { SCN_BEGIN_NAMESPACE namespace detail { namespace _reset_begin_iterator { struct fn { private: template static auto impl(Iterator& it, priority_tag<1>) noexcept( noexcept(it.reset_begin_iterator())) -> decltype(it.reset_begin_iterator()) { return it.reset_begin_iterator(); } template static void impl(Iterator&, size_t, priority_tag<0>) noexcept { } public: template auto operator()(Iterator& it) const noexcept(noexcept(fn::impl(it, priority_tag<1>{}))) -> decltype(fn::impl(it, priority_tag<1>{})) { return fn::impl(it, priority_tag<1>{}); } }; } // namespace _reset_begin_iterator namespace { static constexpr auto& reset_begin_iterator = static_const::value; } template struct extract_char_type; template struct extract_char_type< Iterator, typename std::enable_if>::value>::type> { using type = polyfill_2a::iter_value_t; }; template struct extract_char_type< Iterator, void_t< typename std::enable_if>::value>::type, typename polyfill_2a::iter_value_t::success_type>> { using type = typename polyfill_2a::iter_value_t::success_type; }; template struct is_direct_impl : std::is_integral> { }; template struct reconstruct_tag { }; template < typename Range, typename Iterator, typename Sentinel, typename = typename std::enable_if< std::is_constructible::value>::type> Range reconstruct(reconstruct_tag, Iterator begin, Sentinel end) { return {begin, end}; } #if SCN_HAS_STRING_VIEW // std::string_view is not reconstructible pre-C++20 template std::basic_string_view reconstruct( reconstruct_tag>, Iterator begin, Sentinel end) { // On MSVC, string_view can't even be constructed from its // iterators! return {::scn::detail::to_address(begin), static_cast(ranges::distance(begin, end))}; } #endif // SCN_HAS_STRING_VIEW template struct range_wrapper_storage; template struct range_wrapper_storage { using type = remove_cvref_t; using range_type = const type&; const type* value{nullptr}; range_wrapper_storage() = default; range_wrapper_storage(const type& v, dummy_type) : value(std::addressof(v)) { } const type& get() const& noexcept { return *value; } type&& get() && noexcept { return *value; } }; template struct range_wrapper_storage { using range_type = T; T value{}; range_wrapper_storage() = default; template range_wrapper_storage(U&& v, dummy_type) : value(SCN_FWD(v)) { } const T& get() const& noexcept { return value; } T&& get() && noexcept { return value; } }; template using _range_wrapper_marker = typename T::range_wrapper_marker; template struct _has_range_wrapper_marker : custom_ranges::detail::exists<_range_wrapper_marker, T> { }; /** * Wraps a source range for more consistent behavior */ template class range_wrapper { public: using range_type = Range; using range_nocvref_type = remove_cvref_t; using iterator = ranges::iterator_t; using sentinel = ranges::sentinel_t; using char_type = typename extract_char_type::type; using difference_type = ranges::range_difference_t; using storage_type = range_wrapper_storage::value>; using storage_range_type = typename storage_type::range_type; using range_wrapper_marker = void; template < typename R, typename = typename std::enable_if< !_has_range_wrapper_marker>::value>::type> range_wrapper(R&& r) : m_range(SCN_FWD(r), dummy_type{}), m_begin(ranges::cbegin(m_range.get())) { } range_wrapper(const range_wrapper& o) : m_range(o.m_range) { const auto n = ranges::distance(o.begin_underlying(), o.m_begin); m_begin = ranges::cbegin(m_range.get()); ranges::advance(m_begin, n); m_read = o.m_read; } range_wrapper& operator=(const range_wrapper& o) { const auto n = ranges::distance(o.begin_underlying(), o.m_begin); m_range = o.m_range; m_begin = ranges::cbegin(m_range.get()); ranges::advance(m_begin, n); m_read = o.m_read; return *this; } range_wrapper(range_wrapper&& o) noexcept { const auto n = ranges::distance(o.begin_underlying(), o.m_begin); m_range = SCN_MOVE(o.m_range); m_begin = ranges::cbegin(m_range.get()); ranges::advance(m_begin, n); m_read = exchange(o.m_read, 0); } range_wrapper& operator=(range_wrapper&& o) noexcept { reset_to_rollback_point(); const auto n = ranges::distance(o.begin_underlying(), o.m_begin); m_range = SCN_MOVE(o.m_range); m_begin = ranges::cbegin(m_range.get()); ranges::advance(m_begin, n); m_read = exchange(o.m_read, 0); return *this; } ~range_wrapper() = default; iterator begin() const noexcept { return m_begin; } SCN_GCC_PUSH SCN_GCC_IGNORE("-Wnoexcept") sentinel end() const noexcept( noexcept(ranges::end(SCN_DECLVAL(const storage_type&).get()))) { return ranges::end(m_range.get()); } SCN_GCC_POP struct dummy { }; /** * Returns `true` if `begin() == end()`. */ bool empty() const { return begin() == end(); } /** * Advance the begin iterator by `n` characters. */ iterator advance(difference_type n = 1) noexcept { SCN_EXPECT(_advance_check( n, std::integral_constant{})); m_read += n; ranges::advance(m_begin, n); return m_begin; } /// @{ /** * Advance the begin iterator, until it's equal to `it`. * Assumes that `it` is reachable by repeatedly incrementing begin, * will hang otherwise. */ template )>::type* = nullptr> void advance_to(iterator it) noexcept { const auto diff = ranges::distance(m_begin, it); m_read += diff; m_begin = it; } template )>::type* = nullptr> void advance_to(iterator it) noexcept { while (m_begin != it) { ++m_read; ++m_begin; } } /// @} /** * Returns the begin iterator of the underlying source range, is not * necessarily equal to `begin()`. */ iterator begin_underlying() const noexcept(noexcept( ranges::cbegin(SCN_DECLVAL(const range_nocvref_type&)))) { return ranges::cbegin(m_range.get()); } /** * Returns the underlying source range. * Note that `range_underlying().begin()` may not be equal to * `begin()`. */ const range_type& range_underlying() const noexcept { return m_range.get(); } /** * Returns a pointer to the beginning of the range. * `*this` must be contiguous. */ template )>::type* = nullptr> auto data() const noexcept(noexcept(*SCN_DECLVAL(ranges::iterator_t))) -> decltype(std::addressof( *SCN_DECLVAL(ranges::iterator_t))) { return std::addressof(*m_begin); } SCN_GCC_PUSH SCN_GCC_IGNORE("-Wnoexcept") /** * Returns `end() - begin()`. * `*this` must be sized. */ template )>::type* = nullptr> auto size() const noexcept(noexcept( ranges::distance(SCN_DECLVAL(ranges::iterator_t), SCN_DECLVAL(ranges::sentinel_t)))) -> decltype(ranges::distance( SCN_DECLVAL(ranges::iterator_t), SCN_DECLVAL(ranges::sentinel_t))) { return ranges::distance(m_begin, end()); } SCN_GCC_POP struct dummy2 { }; template ::value>::type* = nullptr> span get_buffer_and_advance( size_t max_size = std::numeric_limits::max()) { auto buf = get_buffer(m_range.get(), begin(), max_size); if (buf.size() == 0) { return buf; } advance(buf.ssize()); return buf; } /** * Reset `begin()` to the rollback point, as if by repeatedly * calling `operator--()` on the begin iterator. * * Returns `error::unrecoverable_source_error` on failure. * * \see set_rollback_point() */ error reset_to_rollback_point() { for (; m_read != 0; --m_read) { --m_begin; if (m_begin == end()) { return {error::unrecoverable_source_error, "Putback failed"}; } } return {}; } /** * Sets the rollback point equal to the current `begin()` iterator. * * \see reset_to_rollback_point() */ void set_rollback_point() { m_read = 0; } void reset_begin_iterator() { detail::reset_begin_iterator(m_begin); } /** * Construct a new source range from `begin()` and `end()`, and wrap * it in a new `range_wrapper`. */ template auto reconstruct_and_rewrap() && -> range_wrapper { auto reconstructed = reconstruct(reconstruct_tag{}, begin(), end()); return {SCN_MOVE(reconstructed)}; } /** * `true` if `value_type` is a character type (`char` or `wchar_t`) * `false` if it's an `expected` containing a character */ static constexpr bool is_direct = is_direct_impl::value; // can call .data() and memcpy /** * `true` if `this->data()` can be called, and `memcpy` can be * performed on it. */ static constexpr bool is_contiguous = SCN_CHECK_CONCEPT(ranges::contiguous_range); /** * `true` if the range provides a way to access a contiguous buffer * on it (`detail::get_buffer()`), which may not provide the entire * source data, e.g. a `span` of `span`s (vectored I/O). */ static constexpr bool provides_buffer_access = provides_buffer_access_impl::value; private: template bool _advance_check(std::ptrdiff_t n, std::true_type) { SCN_CLANG_PUSH SCN_CLANG_IGNORE("-Wzero-as-null-pointer-constant") return m_begin + n <= end(); SCN_CLANG_POP } template bool _advance_check(std::ptrdiff_t, std::false_type) { return true; } storage_type m_range; iterator m_begin; mutable difference_type m_read{0}; }; namespace _wrap { struct fn { private: template static range_wrapper impl(const range_wrapper& r, priority_tag<4>) noexcept { return r; } template static range_wrapper impl(range_wrapper&& r, priority_tag<4>) noexcept { return SCN_MOVE(r); } template static auto impl(Range&& r, priority_tag<3>) noexcept( noexcept(SCN_FWD(r).wrap())) -> decltype(SCN_FWD(r).wrap()) { return SCN_FWD(r).wrap(); } template static auto impl(CharT (&str)[N], priority_tag<2>) noexcept -> range_wrapper< basic_string_view::type>> { return { basic_string_view::type>( str, str + N - 1)}; } template static auto impl( const std::basic_string, Allocator>& str, priority_tag<2>) noexcept -> range_wrapper> { return {basic_string_view{str.data(), str.size()}}; } template static auto impl( std::basic_string, Allocator>&& str, priority_tag<2>) noexcept(std:: is_nothrow_move_constructible< decltype(str)>::value) -> range_wrapper, Allocator>> { return {SCN_MOVE(str)}; } #if SCN_HAS_STRING_VIEW template static auto impl(const std::basic_string_view& str, priority_tag<1>) noexcept -> range_wrapper> { return {basic_string_view{str.data(), str.size()}}; } #endif template ::type> static auto impl(span s, priority_tag<2>) noexcept -> range_wrapper> { return {basic_string_view{s.data(), s.size()}}; } template )>::type> static auto impl(Range r, priority_tag<1>) noexcept -> range_wrapper { return {r}; } template static auto impl(const Range& r, priority_tag<0>) noexcept -> range_wrapper { static_assert(SCN_CHECK_CONCEPT(ranges::range), "Input needs to be a Range"); return {r}; } template ::value>::type> static auto impl(Range&& r, priority_tag<0>) noexcept -> range_wrapper { static_assert(SCN_CHECK_CONCEPT(ranges::range), "Input needs to be a Range"); return {SCN_MOVE(r)}; } public: template auto operator()(Range&& r) const noexcept(noexcept(fn::impl(SCN_FWD(r), priority_tag<4>{}))) -> decltype(fn::impl(SCN_FWD(r), priority_tag<4>{})) { return fn::impl(SCN_FWD(r), priority_tag<4>{}); } }; } // namespace _wrap } // namespace detail namespace { /** * Create a `range_wrapper` for any supported source range. */ static constexpr auto& wrap = detail::static_const::value; } // namespace template struct range_wrapper_for { using type = decltype(wrap(SCN_DECLVAL(Range))); }; template using range_wrapper_for_t = typename range_wrapper_for::type; SCN_END_NAMESPACE } // namespace scn #endif // SCN_DETAIL_RANGE_H