// Copyright (C) 2020 T. Zachary Laine // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_PARSER_DETAIL_TEXT_TRANSCODE_VIEW_HPP #define BOOST_PARSER_DETAIL_TEXT_TRANSCODE_VIEW_HPP #include #include #include #include #include #include namespace boost::parser::detail { namespace text { namespace detail { template constexpr auto iterator_to_tag() { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS if constexpr (std::random_access_iterator) { return std::random_access_iterator_tag{}; } else if constexpr (std::bidirectional_iterator) { return std::bidirectional_iterator_tag{}; } else if constexpr (std::forward_iterator) { #else if constexpr (detail::random_access_iterator_v) { return std::random_access_iterator_tag{}; } else if constexpr (detail::bidirectional_iterator_v) { return std::bidirectional_iterator_tag{}; } else if constexpr (detail::forward_iterator_v) { #endif return std::forward_iterator_tag{}; } else { return std::input_iterator_tag{}; } } template using iterator_to_tag_t = decltype(iterator_to_tag()); #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template using with_reference = T &; template concept can_reference = requires { typename with_reference; }; #endif #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template struct cast_to_charn { constexpr Char operator()(Char c) const { return c; } }; #else struct cast_to_char8; struct cast_to_char16; struct cast_to_char32; template auto function_for_tag(Arg arg) { #if defined(__cpp_char8_t) if constexpr (std::is_same_v) { return (char8_t)arg; } else #endif if constexpr (std::is_same_v) { return (char16_t)arg; } else if constexpr (std::is_same_v) { return (char32_t)arg; } } #endif } #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::regular_invocable> && detail::can_reference>> #else template // F is a tag type in c++17 #endif class project_view : public stl_interfaces::view_interface> { V base_ = V(); // HACK: SentType is here to work around irritating big-3 // implementation inconsistencies. template class sentinel; template> class iterator; public: constexpr project_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr explicit project_view(V base) : base_(std::move(base)) {} constexpr V& base() & { return base_; } constexpr const V& base() const& { return base_; } constexpr V base() && { return std::move(base_); } constexpr iterator begin() { return iterator{detail::begin(base_)}; } constexpr iterator begin() const #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::ranges::range #endif { return iterator{detail::begin(base_)}; } constexpr sentinel end() { return sentinel{detail::end(base_)}; } #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS constexpr iterator end() requires std::ranges::common_range { return iterator{detail::end(base_)}; } #endif constexpr sentinel end() const #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::ranges::range { return sentinel{detail::end(base_)}; } constexpr iterator end() const requires std::ranges::common_range #endif { return iterator{detail::end(base_)}; } #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS constexpr auto size() requires std::ranges::sized_range { return std::ranges::size(base_); } constexpr auto size() const requires std::ranges::sized_range { return std::ranges::size(base_); } #endif }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::regular_invocable> && detail::can_reference>> #else template #endif template class project_view::iterator : public boost::parser::detail::stl_interfaces::proxy_iterator_interface< iterator, detail::iterator_to_tag_t>>, #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS std::invoke_result_t> #else decltype(detail::function_for_tag(0)) #endif > { using iterator_type = detail::iterator_t>; using sentinel_type = detail::sentinel_t>; using reference_type = #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS std::invoke_result_t> #else decltype(detail::function_for_tag(0)) #endif ; using sentinel = SentType; friend boost::parser::detail::stl_interfaces::access; iterator_type & base_reference() noexcept { return it_; } iterator_type base_reference() const { return it_; } iterator_type it_ = iterator_type(); friend project_view::template sentinel; template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sentinel_for>> #endif friend constexpr bool operator==(const iterator & x, const sentinel & y) { return x.it_ == y.end_; } template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sized_sentinel_for>> #endif friend constexpr detail::range_difference_t> operator-(const iterator & x, const sentinel & y) { return x.it_ - y.end_; } template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sized_sentinel_for>> #endif friend constexpr detail::range_difference_t> operator-(const sentinel & y, const iterator & x) { return y.end_ - x.it_; } public: constexpr iterator() = default; constexpr iterator(iterator_type it) : it_(std::move(it)) {} #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS constexpr reference_type operator*() const { return F(*it_); } #else constexpr reference_type operator*() const { return detail::function_for_tag(*it_); } #endif }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::regular_invocable> && detail::can_reference>> #else template #endif template class project_view::sentinel { using Base = detail::maybe_const; using sentinel_type = detail::sentinel_t; sentinel_type end_ = sentinel_type(); public: constexpr sentinel() = default; constexpr explicit sentinel(sentinel_type end) : end_(std::move(end)) {} #if !BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template> #endif constexpr sentinel(sentinel i) #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires Const && std::convertible_to, detail::sentinel_t> #endif : end_(std::move(i.end_)) {} constexpr sentinel_type base() const { return end_; } template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sentinel_for>> #endif friend constexpr bool operator==(const iterator & x, const sentinel & y) { return x.it_ == y.end_; } template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sized_sentinel_for>> #endif friend constexpr detail::range_difference_t> operator-(const iterator & x, const sentinel & y) { return x.it_ - y.end_; } template #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::sized_sentinel_for>> #endif friend constexpr detail::range_difference_t> operator-(const sentinel & y, const iterator & x) { return y.end_ - x.it_; } }; namespace detail { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template #else template #endif struct project_impl : stl_interfaces::range_adaptor_closure> { template using project_view_type = project_view; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::viewable_range && std::ranges::input_range && std::regular_invocable> && detail::can_reference>> #else template #endif [[nodiscard]] constexpr auto operator()(R && r) const { return project_view_type(std::forward(r)); } }; } #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template #else template #endif constexpr detail::project_impl project{}; #if defined(__cpp_char8_t) #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::convertible_to, char8_t> class char8_view : public project_view{}> #else template class char8_view : public project_view #endif { public: constexpr char8_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr char8_view(V base) : #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS project_view{}>{std::move(base)} #else project_view{std::move(base)} #endif {} }; #endif #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::convertible_to, char16_t> class char16_view : public project_view{}> #else template class char16_view : public project_view #endif { public: constexpr char16_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr char16_view(V base) : #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS project_view{}>{std::move(base)} #else project_view{std::move(base)} #endif {} }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::convertible_to, char32_t> class char32_view : public project_view{}> #else template class char32_view : public project_view #endif { public: constexpr char32_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr char32_view(V base) : #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS project_view{}>{std::move(base)} #else project_view{std::move(base)} #endif {} }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template char8_view(R &&) -> char8_view>; template char16_view(R &&) -> char16_view>; template char32_view(R &&) -> char32_view>; #endif namespace detail { template class View, format Format> struct as_charn_impl : stl_interfaces::range_adaptor_closure> { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires (std::ranges::viewable_range && std::ranges::input_range && std::convertible_to, format_to_type_t>) #else template #endif [[nodiscard]] constexpr auto operator()(R && r) const { using T = remove_cv_ref_t; if constexpr (detail::is_empty_view) { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS return std::ranges::empty_view>{}; #else return 42; // Never gonna happen. #endif } else { return View(std::forward(r)); } } }; template constexpr bool is_charn_view = false; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template constexpr bool is_charn_view> = true; #endif template constexpr bool is_charn_view> = true; template constexpr bool is_charn_view> = true; } #if defined(__cpp_char8_t) inline constexpr detail::as_charn_impl as_char8_t; #endif inline constexpr detail::as_charn_impl as_char16_t; inline constexpr detail::as_charn_impl as_char32_t; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view && std::ranges::forward_range #else template #endif class unpacking_view : public stl_interfaces::view_interface> { V base_ = V(); public: constexpr unpacking_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr unpacking_view(V base) : base_(std::move(base)) {} constexpr V base() const & #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::copy_constructible #endif { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto code_units() const noexcept { auto unpacked = boost::parser::detail::text::unpack_iterator_and_sentinel( detail::begin(base_), detail::end(base_)); return BOOST_PARSER_DETAIL_TEXT_SUBRANGE( unpacked.first, unpacked.last); } constexpr auto begin() { return code_units().begin(); } constexpr auto begin() const { return code_units().begin(); } constexpr auto end() { return code_units().end(); } constexpr auto end() const { return code_units().end(); } }; template unpacking_view(R &&) -> unpacking_view>; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view #else template #endif class utf_view : public stl_interfaces::view_interface> { V base_ = V(); template static constexpr auto make_begin(I first, S last) { if constexpr (detail::bidirectional_iterator_v) { return utf_iterator{first, first, last}; } else { return utf_iterator{first, last}; } } template static constexpr auto make_end(I first, S last) { if constexpr (!std::is_same_v) { return last; } else if constexpr (detail::bidirectional_iterator_v) { return utf_iterator{first, last, last}; } else { return utf_iterator{last, last}; } } public: #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS constexpr utf_view() requires std::default_initializable = default; #endif constexpr utf_view(V base) : base_{std::move(base)} {} constexpr V base() const & #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::copy_constructible #endif { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() { constexpr format from_format = detail::format_of>(); if constexpr(detail::is_charn_view) { return make_begin(detail::begin(base_.base()), detail::end(base_.base())); } else { return make_begin(detail::begin(base_), detail::end(base_)); } } constexpr auto begin() const { constexpr format from_format = detail::format_of>(); if constexpr(detail::is_charn_view) { return make_begin(detail::begin(base_.base()), detail::end(base_.base())); } else { return make_begin(detail::begin(base_), detail::end(base_)); } } constexpr auto end() { constexpr format from_format = detail::format_of>(); if constexpr(detail::is_charn_view) { return make_end(detail::begin(base_.base()), detail::end(base_.base())); } else { return make_end(detail::begin(base_), detail::end(base_)); } } constexpr auto end() const { constexpr format from_format = detail::format_of>(); if constexpr(detail::is_charn_view) { return make_end(detail::begin(base_.base()), detail::end(base_.base())); } else { return make_end(detail::begin(base_), detail::end(base_)); } } /** Stream inserter; performs unformatted output, in UTF-8 encoding. */ friend std::ostream & operator<<(std::ostream & os, utf_view v) { if constexpr (Format == format::utf8) { auto out = std::ostreambuf_iterator(os); for (auto it = v.begin(); it != v.end(); ++it, ++out) { *out = *it; } } else { boost::parser::detail::text::transcode_to_utf8( v.begin(), v.end(), std::ostreambuf_iterator(os)); } return os; } #if defined(BOOST_TEXT_DOXYGEN) || defined(_MSC_VER) /** Stream inserter; performs unformatted output, in UTF-16 encoding. Defined on Windows only. */ friend std::wostream & operator<<(std::wostream & os, utf_view v) { if constexpr (Format == format::utf16) { auto out = std::ostreambuf_iterator(os); for (auto it = v.begin(); it != v.end(); ++it, ++out) { *out = *it; } } else { boost::parser::detail::text::transcode_to_utf16( v.begin(), v.end(), std::ostreambuf_iterator(os)); } return os; } #endif }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view #else template #endif class utf8_view : public utf_view { public: constexpr utf8_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr utf8_view(V base) : utf_view{std::move(base)} {} }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view #else template #endif class utf16_view : public utf_view { public: constexpr utf16_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr utf16_view(V base) : utf_view{std::move(base)} {} }; #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires std::ranges::view #else template #endif class utf32_view : public utf_view { public: constexpr utf32_view() #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS requires std::default_initializable #endif = default; constexpr utf32_view(V base) : utf_view{std::move(base)} {} }; template utf8_view(R &&) -> utf8_view>; template utf16_view(R &&) -> utf16_view>; template utf32_view(R &&) -> utf32_view>; #if defined(BOOST_TEXT_DOXYGEN) /** A view adaptor that produces a UTF-8 view of the given view. */ constexpr detail::unspecified as_utf8; /** A view adaptor that produces a UTF-16 view of the given view. */ constexpr detail::unspecified as_utf16; /** A view adaptor that produces a UTF-32 view of the given view. */ constexpr detail::unspecified as_utf32; #endif namespace detail { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template class View> concept can_utf_view = requires(R && r) { View((R &&)r); }; #else template using can_utf_view_expr = decltype(View(std::declval())); template class View> constexpr bool can_utf_view = is_detected_v>; #endif template constexpr bool is_utf_view = false; template constexpr bool is_utf_view> = true; template constexpr bool is_utf_view> = true; template constexpr bool is_utf_view> = true; template constexpr bool is_utf_view> = true; template constexpr bool is_bounded_array_v = false; template constexpr bool is_bounded_array_v = true; template constexpr decltype(auto) unpack_range(R && r) { using T = detail::remove_cv_ref_t; if constexpr (forward_range_v) { auto unpacked = boost::parser::detail::text::unpack_iterator_and_sentinel(detail::begin(r), detail::end(r)); if constexpr (is_bounded_array_v) { constexpr auto n = std::extent_v; if (n && !r[n - 1]) --unpacked.last; return BOOST_PARSER_DETAIL_TEXT_SUBRANGE(unpacked.first, unpacked.last); } else if constexpr ( !std::is_same_v> || !std::is_same_v>) { return unpacking_view(std::forward(r)); } else { return std::forward(r); } } else { return std::forward(r); } } template using unpacked_range = decltype(detail::unpack_range(std::declval())); template class View, format Format> struct as_utf_impl : stl_interfaces::range_adaptor_closure> { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template requires is_utf_view> || (std::ranges::viewable_range && can_utf_view, View>) #else template #endif [[nodiscard]] constexpr auto operator()(R && r) const { using T = detail::remove_cv_ref_t; if constexpr (detail::is_empty_view) { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS return std::ranges::empty_view>{}; #else return 42; // Never gonna happen. #endif } else if constexpr (is_utf_view) { return View(std::forward(r).base()); } else if constexpr (detail::is_charn_view) { return View(std::forward(r)); } else { return View(detail::unpack_range(std::forward(r))); } } }; template constexpr bool is_utf32_view = false; template constexpr bool is_utf32_view> = true; } inline constexpr detail::as_utf_impl as_utf8; inline constexpr detail::as_utf_impl as_utf16; inline constexpr detail::as_utf_impl as_utf32; }} #if BOOST_PARSER_USE_CONCEPTS && defined(__cpp_lib_ranges) namespace std::ranges { #if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; #endif template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; } #endif #endif