From 56a65e0471f73b234784046ca09ae0ed3d86c97f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 17 Mar 2023 03:42:16 +0100 Subject: [PATCH 1/9] P2321R2: `views::adjacent_transform`, `views::pairwise_transform` (#3546) Co-authored-by: Stephan T. Lavavej --- stl/inc/ranges | 394 ++++++- stl/inc/yvals_core.h | 2 +- tests/libcxx/expected_results.txt | 4 - tests/std/test.lst | 1 + .../std/tests/P2321R2_views_adjacent/test.cpp | 28 +- .../P2321R2_views_adjacent_transform/env.lst | 4 + .../P2321R2_views_adjacent_transform/test.cpp | 973 ++++++++++++++++++ .../test.compile.pass.cpp | 14 + 8 files changed, 1393 insertions(+), 27 deletions(-) create mode 100644 tests/std/tests/P2321R2_views_adjacent_transform/env.lst create mode 100644 tests/std/tests/P2321R2_views_adjacent_transform/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index cf7d9e016bd..ab03fe9702f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -8778,20 +8778,47 @@ namespace ranges { _EXPORT_STD inline constexpr _Zip_transform_fn zip_transform{}; } // namespace views + template + using _Repeat_type = _Ty; + template struct _Repeated_tuple_impl; template struct _Repeated_tuple_impl<_Ty, index_sequence<_Indices...>> { - template - using _Repeat_type = _Ty; - - using type = tuple<_Repeat_type<_Indices>...>; + using type = tuple<_Repeat_type<_Ty, _Indices>...>; }; template using _Repeated_tuple = typename _Repeated_tuple_impl<_Ty, make_index_sequence<_Nx>>::type; + template + inline constexpr bool _Regular_invocable_with_repeated_type_impl = false; + + template + inline constexpr bool _Regular_invocable_with_repeated_type_impl<_Fn, _Ty, index_sequence<_Indices...>> = + regular_invocable<_Fn, _Repeat_type<_Ty, _Indices>...>; + + template + concept _Regular_invocable_with_repeated_type = + _Regular_invocable_with_repeated_type_impl<_Fn, _Ty, make_index_sequence<_Nx>>; + + template + struct _Invoke_result_with_repeated_type_impl; + + template + struct _Invoke_result_with_repeated_type_impl<_Fn, _Ty, index_sequence<_Indices...>> { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1761088 + using type = invoke_result_t<_Fn, _Repeat_type<_Ty, _Indices>...>; +#else // ^^^ no workaround / workaround vvv + using type = decltype(_STD invoke(_STD declval<_Fn>(), _STD declval<_Repeat_type<_Ty, _Indices>>()...)); +#endif // ^^^ workaround ^^^ + }; + + template + using _Invoke_result_with_repeated_type = + typename _Invoke_result_with_repeated_type_impl<_Fn, _Ty, make_index_sequence<_Nx>>::type; + _EXPORT_STD template requires view<_Vw> && (_Nx > 0) class adjacent_view : public view_interface> { @@ -8807,6 +8834,12 @@ namespace ranges { private: friend adjacent_view; + template + requires view<_Vw2> && (_Nx2 > 0) + && is_object_v<_Fn> && _Regular_invocable_with_repeated_type<_Fn&, range_reference_t<_Vw2>, _Nx2> + && _Can_reference<_Invoke_result_with_repeated_type<_Fn&, range_reference_t<_Vw2>, _Nx2>> + friend class adjacent_transform_view; + using _Base = _Maybe_const<_Const, _Vw>; using _Base_iterator = iterator_t<_Base>; @@ -9167,6 +9200,359 @@ namespace ranges { _EXPORT_STD inline constexpr _Adjacent_fn<2> pairwise; } // namespace views + _EXPORT_STD template + requires view<_Vw> && (_Nx > 0) + && is_object_v<_Fn> && _Regular_invocable_with_repeated_type<_Fn&, range_reference_t<_Vw>, _Nx> + && _Can_reference<_Invoke_result_with_repeated_type<_Fn&, range_reference_t<_Vw>, _Nx>> + class adjacent_transform_view : public view_interface> { + private: + using _Inner_view = adjacent_view<_Vw, _Nx>; + template + using _Inner_iterator = iterator_t<_Maybe_const<_Const, _Inner_view>>; + template + using _Inner_sentinel = sentinel_t<_Maybe_const<_Const, _Inner_view>>; + + /* [[no_unique_address]] */ _Movable_box<_Fn> _Func; + /* [[no_unique_address]] */ _Inner_view _Inner; + + template + class _Iterator { + private: + friend adjacent_transform_view; + + using _Parent_t = _Maybe_const<_Const, adjacent_transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + _Parent_t* _Parent = nullptr; + _Inner_iterator<_Const> _Inner; + + constexpr _Iterator(_Parent_t& _Parent_, _Inner_iterator<_Const> _Inner_) noexcept( + is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + : _Parent(_STD addressof(_Parent_)), _Inner(_STD move(_Inner_)) {} + + _NODISCARD static consteval auto _Get_iterator_category() noexcept { + if constexpr (!is_reference_v<_Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, + range_reference_t<_Base>, _Nx>>) { + return input_iterator_tag{}; + } else { + using _Cat = typename iterator_traits>::iterator_category; + + if constexpr (derived_from<_Cat, random_access_iterator_tag>) { + return random_access_iterator_tag{}; + } else if constexpr (derived_from<_Cat, bidirectional_iterator_tag>) { + return bidirectional_iterator_tag{}; + } else if constexpr (derived_from<_Cat, forward_iterator_tag>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + } + + template + _NODISCARD static consteval bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { + return noexcept(_STD invoke(_STD declval<_Maybe_const<_Const, _Fn>&>(), + *_STD get<_Indices>(_STD declval&>()._Current)...)); + } + + public: + using iterator_category = decltype(_Get_iterator_category()); + using iterator_concept = typename _Inner_iterator<_Const>::iterator_concept; + using value_type = remove_cvref_t< + _Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>, _Nx>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _Other) + requires _Const && convertible_to<_Inner_iterator, _Inner_iterator<_Const>> + : _Parent(_Other._Parent), _Inner(_STD move(_Other._Inner)) {} + + _NODISCARD constexpr decltype(auto) operator*() const + noexcept(_Is_indirection_nothrow(make_index_sequence<_Nx>{})) { + return _STD apply( + [&](const auto&... _Iters) -> decltype(auto) { return _STD invoke(*_Parent->_Func, *_Iters...); }, + _Inner._Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept(++_Inner)) /* strengthened */ { + ++_Inner; + return *this; + } + + constexpr _Iterator operator++(int) noexcept( + noexcept(++*this) && is_nothrow_copy_constructible_v<_Iterator>) /* strengthened */ { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() noexcept(noexcept(--_Inner)) /* strengthened */ + requires bidirectional_range<_Base> + { + --_Inner; + return *this; + } + + constexpr _Iterator operator--(int) noexcept( + noexcept(--*this) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires bidirectional_range<_Base> + { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) noexcept( + noexcept(_Inner += _Off)) // strengthened + requires random_access_range<_Base> + { + _Inner += _Off; + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) noexcept( + noexcept(_Inner -= _Off)) // strengthened + requires random_access_range<_Base> + { + _Inner -= _Off; + return *this; + } + + _NODISCARD constexpr decltype(auto) operator[](const difference_type _Off) const + requires random_access_range<_Base> + { + return _STD apply( + [&](const auto&... _Iters) -> decltype(auto) { + return _STD invoke(*_Parent->_Func, _Iters[_Off]...); + }, + _Inner._Current); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner == _Right._Inner)) /* strengthened */ { + return _Left._Inner == _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner < _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner < _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner > _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner > _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner <= _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner <= _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner >= _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner >= _Right._Inner; + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner <=> _Right._Inner)) // strengthened + requires random_access_range<_Base> && three_way_comparable<_Inner_iterator<_Const>> + { + return _Left._Inner <=> _Right._Inner; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_It._Inner + _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner + _Off}; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) noexcept( + noexcept(_It._Inner + _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner + _Off}; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_It._Inner - _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner - _Off}; + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _Left, const _Iterator& _Right) // + noexcept(noexcept(_Left._Inner - _Right._Inner)) // strengthened + requires sized_sentinel_for<_Inner_iterator<_Const>, _Inner_iterator<_Const>> + { + return _Left._Inner - _Right._Inner; + } + }; + + template + class _Sentinel { + private: + friend adjacent_transform_view; + + /* [[no_unique_address]] */ _Inner_sentinel<_Const> _Inner; + + constexpr explicit _Sentinel(_Inner_sentinel<_Const> _Inner_) : _Inner(_Inner_) {} + + template + requires sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const + noexcept(noexcept(_It._Inner == _Inner)) { + return _It._Inner == _Inner; + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> _Distance_from( + const _Iterator<_OtherConst>& _It) const noexcept(noexcept(_Inner - _It._Inner)) { + return _Inner - _It._Inner; + } + + public: + _Sentinel() = default; + + constexpr _Sentinel(_Sentinel _Other) + requires _Const && convertible_to<_Inner_sentinel, _Inner_sentinel<_Const>> + : _Inner(_STD move(_Other._Inner)) {} + + template + requires sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator<_OtherConst>& _It, + const _Sentinel& _Se) noexcept(noexcept(_Se._Equal(_It))) /* strengthened */ { + return _Se._Equal(_It); + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> operator-( + const _Iterator<_OtherConst>& _It, const _Sentinel& _Se) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return -_Se._Distance_from(_It); + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> operator-( + const _Sentinel& _Se, const _Iterator<_OtherConst>& _It) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return _Se._Distance_from(_It); + } + }; + + public: + adjacent_transform_view() = default; + + constexpr explicit adjacent_transform_view(_Vw _Range_, _Fn _Func_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Fn>) // strengthened + : _Func(in_place, _STD move(_Func_)), _Inner(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept(noexcept(_Inner.base())) // strengthened + requires copy_constructible<_Inner_view> + { + return _Inner.base(); + } + + _NODISCARD constexpr _Vw base() && noexcept(noexcept(_STD move(_Inner).base())) /* strengthened */ { + return _STD move(_Inner).base(); + } + + _NODISCARD constexpr auto begin() { + return _Iterator{*this, _Inner.begin()}; + } + + _NODISCARD constexpr auto begin() const + requires range + && _Regular_invocable_with_repeated_type, _Nx> + { + return _Iterator{*this, _Inner.begin()}; + } + + _NODISCARD constexpr auto end() { + if constexpr (common_range<_Inner_view>) { + return _Iterator{*this, _Inner.end()}; + } else { + return _Sentinel{_Inner.end()}; + } + } + + _NODISCARD constexpr auto end() const + requires range + && _Regular_invocable_with_repeated_type, _Nx> + { + if constexpr (common_range) { + return _Iterator{*this, _Inner.end()}; + } else { + return _Sentinel{_Inner.end()}; + } + } + + _NODISCARD constexpr auto size() noexcept(noexcept(_Inner.size())) // strengthened + requires sized_range<_Inner_view> + { + return _Inner.size(); + } + + _NODISCARD constexpr auto size() const noexcept(noexcept(_Inner.size())) // strengthened + requires sized_range + { + return _Inner.size(); + } + }; + + namespace views { + template + class _Adjacent_transform_fn { + public: + template + _NODISCARD constexpr auto operator()(_Rng&&, _Fn&& _Func) const + noexcept(noexcept(views::zip_transform(_STD forward<_Fn>(_Func)))) + requires (_Nx == 0) +#ifndef __clang__ // TRANSITION, Clang 16 + && requires { views::zip_transform(_STD forward<_Fn>(_Func)); } +#endif // __clang__ + { + return views::zip_transform(_STD forward<_Fn>(_Func)); + } + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn&& _Func) const + noexcept(noexcept(adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)))) + requires requires { + adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)); + } + { + return adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)); + } + + template + requires constructible_from, _Fn> + _NODISCARD constexpr auto operator()(_Fn&& _Func) const + noexcept(is_nothrow_constructible_v, _Fn>) { + return _Range_closure<_Adjacent_transform_fn, decay_t<_Fn>>{_STD forward<_Fn>(_Func)}; + } + }; + + _EXPORT_STD template + inline constexpr _Adjacent_transform_fn<_Nx> adjacent_transform; + _EXPORT_STD inline constexpr _Adjacent_transform_fn<2> pairwise_transform; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1a4168eba2d..03eeca0afb9 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -335,7 +335,6 @@ // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip -// (missing views::adjacent_transform) // P2322R6 ranges::fold_left, ranges::fold_right, Etc. // P2387R3 Pipe Support For User-Defined Range Adaptors // P2404R3 Move-Only Types For Comparison Concepts @@ -1739,6 +1738,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_ranges_starts_ends_with 202106L #define __cpp_lib_ranges_stride 202207L #define __cpp_lib_ranges_to_container 202202L +#define __cpp_lib_ranges_zip 202110L #endif // __cpp_lib_concepts #define __cpp_lib_spanstream 202106L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 88a05cc2ad7..2d41f7f11b8 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -311,10 +311,6 @@ std/utilities/format/format.tuple/parse.pass.cpp FAIL std/utilities/format/format.tuple/set_brackets.pass.cpp FAIL std/utilities/format/format.tuple/set_separator.pass.cpp FAIL -# P2321R2 zip -std/language.support/support.limits/support.limits.general/tuple.version.compile.pass.cpp FAIL -std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp FAIL - # *** MISSING COMPILER FEATURES *** # MSVC doesn't properly support [[no_unique_address]] diff --git a/tests/std/test.lst b/tests/std/test.lst index fc8574d2405..f15f54508b0 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -569,6 +569,7 @@ tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference tests\P2321R2_views_adjacent +tests\P2321R2_views_adjacent_transform tests\P2321R2_views_zip tests\P2321R2_views_zip_transform tests\P2322R6_ranges_alg_fold diff --git a/tests/std/tests/P2321R2_views_adjacent/test.cpp b/tests/std/tests/P2321R2_views_adjacent/test.cpp index bc993cf73bc..bc8b9693fcb 100644 --- a/tests/std/tests/P2321R2_views_adjacent/test.cpp +++ b/tests/std/tests/P2321R2_views_adjacent/test.cpp @@ -142,19 +142,15 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // Check view_interface::empty and operator bool STATIC_ASSERT(CanMemberEmpty); - STATIC_ASSERT(CanBool == CanEmpty); - if constexpr (CanMemberEmpty) { - assert(r.empty() == is_empty); - assert(static_cast(r) == !is_empty); - } + STATIC_ASSERT(CanBool); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); // Check view_interface::empty and operator bool (const) STATIC_ASSERT(CanMemberEmpty); - STATIC_ASSERT(CanBool == CanEmpty); - if constexpr (CanMemberEmpty) { - assert(as_const(r).empty() == is_empty); - assert(static_cast(as_const(r)) == !is_empty); - } + STATIC_ASSERT(CanBool); + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); // Check content assert(ranges::equal(r, expected)); @@ -306,16 +302,12 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } // Check view_interface::front - STATIC_ASSERT(CanMemberFront == forward_range); - if constexpr (CanMemberFront) { - assert(r.front() == *begin(expected)); - } + STATIC_ASSERT(CanMemberFront); + assert(r.front() == *begin(expected)); // Check view_interface::front (const) - STATIC_ASSERT(CanMemberFront == forward_range); - if constexpr (CanMemberFront) { - assert(as_const(r).front() == *begin(expected)); - } + STATIC_ASSERT(CanMemberFront); + assert(as_const(r).front() == *begin(expected)); // Check view_interface::back STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); diff --git a/tests/std/tests/P2321R2_views_adjacent_transform/env.lst b/tests/std/tests/P2321R2_views_adjacent_transform/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent_transform/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp b/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp new file mode 100644 index 00000000000..f5aa39fc497 --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp @@ -0,0 +1,973 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +template +concept CanViewAdjacentTransform = + requires(R&& r, F&& f) { views::adjacent_transform(forward(r), forward(f)); }; + +template +struct repeated_tuple_impl; + +template +struct repeated_tuple_impl> { + template + using repeat_type = T; + + using type = tuple...>; +}; + +template +using repeated_tuple = typename repeated_tuple_impl>::type; + +STATIC_ASSERT(same_as, tuple<>>); +STATIC_ASSERT(same_as, tuple>); +STATIC_ASSERT(same_as, tuple>); + +template +struct regular_invocable_with_repeated_type_impl; + +template +struct regular_invocable_with_repeated_type_impl> { + template + using repeat_type = T; + + static constexpr bool value = regular_invocable...>; +}; + +template +concept regular_invocable_with_repeated_type = + regular_invocable_with_repeated_type_impl>::value; + +static_assert(regular_invocable_with_repeated_type, int, 2>); +static_assert(!regular_invocable_with_repeated_type, int, 3>); +static_assert(!regular_invocable_with_repeated_type, span, 2>); + +template +struct invoke_result_with_repeated_type_impl : invoke_result_with_repeated_type_impl { +}; + +template +struct invoke_result_with_repeated_type_impl { + using type = invoke_result_t; +}; + +template +using invoke_result_with_repeated_type = typename invoke_result_with_repeated_type_impl::type; + +static_assert(same_as, int, 2>, int>); +static_assert(same_as, int, 2>, bool>); + +template +constexpr bool test_one(Rng&& rng, Fn func, Expected&& expected_rng) { + using ranges::adjacent_transform_view, ranges::adjacent_view, ranges::forward_range, ranges::bidirectional_range, + ranges::random_access_range, ranges::sized_range, ranges::common_range, ranges::iterator_t, + ranges::const_iterator_t, ranges::sentinel_t, ranges::const_sentinel_t, ranges::range_reference_t, + ranges::range_difference_t, ranges::begin, ranges::end; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using R = adjacent_transform_view, N>; + + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!ranges::contiguous_range); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable); + + // Check borrowed_range + STATIC_ASSERT(!ranges::borrowed_range); + + // Check range closure object + const auto closure = views::adjacent_transform(func); + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacentTransform == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform) { + constexpr bool is_noexcept = + !is_view || (is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as(rng, func)), R>); + STATIC_ASSERT(noexcept(views::adjacent_transform(rng, func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform&, Fn, N> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform&, Fn, N>) { + using RC = adjacent_transform_view&>, Fn, N>; + constexpr bool is_noexcept = + !is_view || (is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as(as_const(rng), func)), RC>); + STATIC_ASSERT(noexcept(views::adjacent_transform(as_const(rng), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, N> == (is_view || movable>) ); + if constexpr (CanViewAdjacentTransform, Fn, N>) { + using RS = adjacent_transform_view>, Fn, N>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(move(rng), func)), RS>); + STATIC_ASSERT(noexcept(views::adjacent_transform(move(rng), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, N> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacentTransform, Fn, N>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(move(as_const(rng)), func)), R>); + STATIC_ASSERT(noexcept(views::adjacent_transform(move(as_const(rng)), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng)))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); + } + + // Check views::pairwise_transform + if constexpr (N == 2) { + using Pairwised = decltype(views::pairwise_transform(forward(rng), func)); + using Adjacented = decltype(views::adjacent_transform<2>(forward(rng), func)); + STATIC_ASSERT(same_as); + } + + R r{forward(rng), func}; + + // Check adjacent_transform_view::size + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = r.size(); + assert(_To_unsigned_like(s) == ranges::size(expected_rng)); + STATIC_ASSERT(noexcept(r.size()) == noexcept(ranges::size(rng))); // strengthened + } + + // Check adjacent_transform_view::size (const) + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = as_const(r).size(); + assert(_To_unsigned_like(s) == ranges::size(expected_rng)); + STATIC_ASSERT(noexcept(as_const(r).size()) == noexcept(ranges::size(as_const(rng)))); // strengthened + } + + const bool is_empty = ranges::empty(expected_rng); + + // Check view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + + // Check view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool); + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + + // Check content + assert(ranges::equal(r, expected_rng)); + + // Check adjacent_transform_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected_rng)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Check adjacent_transform_view::begin (const) + STATIC_ASSERT( + CanMemberBegin + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected_rng)); + } + + if constexpr (copy_constructible) { + const auto cr2 = r; + const same_as> auto ci2 = cr2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Check adjacent_transform_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((as_const(r).begin() == s) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*ranges::prev(s) == *ranges::prev(end(expected_rng))); + } + + if constexpr (copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*ranges::prev(r2.end()) == *ranges::prev(end(expected_rng))); + } + } + } + } + + // Check adjacent_transform_view::end (const) + STATIC_ASSERT( + CanMemberEnd + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((as_const(r).begin() == cs) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((r.begin() == cs) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*ranges::prev(cs) == *ranges::prev(end(expected_rng))); + } + + if constexpr (copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*ranges::prev(r2.end()) == *ranges::prev(end(expected_rng))); + } + } + } + } + + // Check view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT( + CanMemberCBegin + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + { + const same_as> auto i = r.cbegin(); + if (!is_empty) { + assert(*i == *cbegin(expected_rng)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.cbegin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanCBegin) { + const same_as> auto i3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Check view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT( + CanMemberCEnd + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if (!is_empty) { + same_as> auto i = r.cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*ranges::prev(i) == *ranges::prev(cend(expected_rng))); + } + + if constexpr (CanCEnd) { + same_as> auto i2 = as_const(r).cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*ranges::prev(i2) == *ranges::prev(cend(expected_rng))); + } + } + } + + // Check view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + if (is_empty) { + return true; + } + + // Check view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == expected_rng[0]); + } + + // Check view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected_rng[0]); + } + + // Check view_interface::front + STATIC_ASSERT(CanMemberFront); + assert(r.front() == *begin(expected_rng)); + + // Check view_interface::front (const) + STATIC_ASSERT(CanMemberFront); + assert(as_const(r).front() == *begin(expected_rng)); + + // Check view_interface::back + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected_rng))); + } + + // Check view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected_rng))); + } + + { // Check adjacent_transform_view::iterator + using BI = iterator_t; + using I = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + using IterCat = typename I::iterator_category; + if constexpr (!is_reference_v, N>>) { + STATIC_ASSERT(same_as); + } else { + using BaseCat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + } + + // Check iterator_concept + using IterConcept = typename I::iterator_concept; + STATIC_ASSERT(same_as>::iterator_concept>); + + // Check value_type + STATIC_ASSERT(same_as, N>>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + auto i = r.begin(); + + { // Check dereference + same_as, N>> decltype(auto) v = *as_const(i); + assert(v == expected_rng[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) i2 = ++i; + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + i = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) i2 = i++; + assert(*i2 == expected_rng[0]); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + i = r.begin(); + } + + { // Check equality comparisons + auto i2 = i; + same_as auto b1 = i == i2; + assert(b1); + ++i2; + same_as auto b2 = i != i2; + assert(b2); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = --i; + assert(&i2 == &i); + assert(*i2 == expected_rng[0]); + } + + { // Check post-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = i--; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected_rng[1]); + } + assert(*i == expected_rng[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) i2 = (i += 1); + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + + same_as decltype(auto) i3 = (i -= 1); + assert(&i3 == &i); + assert(*i == expected_rng[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = i[0]; + assert(v == expected_rng[0]); + } + + { // Check other comparisons + auto i2 = ranges::next(i); + same_as auto b1 = i < i2; + assert(b1); + same_as auto b2 = i2 > i; + assert(b2); + same_as auto b3 = i <= i2; + assert(b3); + same_as auto b4 = i2 >= i; + assert(b4); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto i2 = i; + same_as auto cmp1 = i <=> i2; + assert(cmp1 == Cat::equivalent); + ++i2; + assert((i <=> i2) == Cat::less); + assert((i2 <=> i) == Cat::greater); + } + + { // Check operator+ + same_as auto i2 = i + 1; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected_rng[1]); + } + + same_as auto i3 = 1 + i; + if (i3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i3 == expected_rng[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto i2 = ranges::next(i) - 1; + assert(*i2 == expected_rng[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = i - i; + assert(diff == 0); + assert((i - ranges::next(i)) == -1); + assert((ranges::next(i) - i) == 1); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + } + + // Check adjacent_transform_view::iterator + if constexpr (CanMemberBegin) { + using CBI = iterator_t; + using CI = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + using IterCat = typename CI::iterator_category; + if constexpr (!is_reference_v, N>>) { + STATIC_ASSERT(same_as); + } else { + using BaseCat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + } + + // Check iterator_concept + using IterConcept = typename CI::iterator_concept; + STATIC_ASSERT(same_as>::iterator_concept>); + + // Check value_type + STATIC_ASSERT(same_as, N>>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + iterator_t i = r.begin(); + + // Check construction from non-const iterator + CI ci = i; + + { // Check dereference + same_as, N>> decltype(auto) v = + *as_const(ci); + assert(v == expected_rng[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) ci2 = ++ci; + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + ci = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) ci2 = ci++; + assert(*ci2 == expected_rng[0]); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + ci = r.begin(); + } + + { // Check equality comparisons + auto ci2 = ci; + same_as auto b1 = ci == ci2; + assert(b1); + ++ci2; + same_as auto b2 = ci != ci2; + assert(b2); + } + + { // Check equality comparisons with non-const iterators + same_as auto b1 = ci == i; + assert(b1); + ++i; + same_as auto b2 = ci != i; + assert(b2); + i = r.begin(); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = --ci; + assert(&ci2 == &ci); + assert(*ci2 == expected_rng[0]); + } + + { // Check post-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = ci--; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected_rng[1]); + } + assert(*ci == expected_rng[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) ci2 = (ci += 1); + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + + same_as decltype(auto) ci3 = (ci -= 1); + assert(&ci3 == &ci); + assert(*ci == expected_rng[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = + ci[0]; + assert(v == expected_rng[0]); + } + + { // Check comparisons + auto ci2 = ranges::next(ci); + same_as auto b1 = ci < ci2; + assert(b1); + same_as auto b2 = ci2 > ci; + assert(b2); + same_as auto b3 = ci <= ci2; + assert(b3); + same_as auto b4 = ci2 >= ci; + assert(b4); + } + + { // Check comparisons with non-const iterators + ++i; + same_as auto b1 = ci < i; + assert(b1); + same_as auto b2 = i > ci; + assert(b2); + same_as auto b3 = ci <= i; + assert(b3); + same_as auto b4 = i >= ci; + assert(b4); + --i; + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto ci2 = ci; + same_as auto cmp1 = ci <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((ci <=> ci2) == Cat::less); + assert((ci2 <=> ci) == Cat::greater); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons with non-const iterators + using Cat = compare_three_way_result_t; + same_as auto cmp1 = ci <=> i; + assert(cmp1 == Cat::equivalent); + ++i; + assert((ci <=> i) == Cat::less); + assert((i <=> ci) == Cat::greater); + --i; + } + + { // Check operator+ + same_as auto ci2 = ci + 1; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected_rng[1]); + } + + same_as auto ci3 = 1 + ci; + if (ci3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci3 == expected_rng[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto ci2 = ranges::next(ci) - 1; + assert(*ci2 == expected_rng[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = ci - ci; + assert(diff == 0); + assert((ci - ranges::next(ci)) == -1); + assert((ranges::next(ci) - ci) == 1); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + } + + // Check adjacent_transform_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + [[maybe_unused]] same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); // strengthened + } + + // Check adjacent_transform_view::base() && + [[maybe_unused]] same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); // strengthened + + return true; +} + +template +constexpr void test_adjacent0(Rng&& rng) { + auto func = [] { return 602; }; + using Fn = decltype(func); + + using V = views::all_t; + using E = ranges::empty_view; + + // Check range closure object + constexpr bool is_view = ranges::view>; + const auto closure = views::adjacent_transform<0>(func); + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacentTransform == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform) { + STATIC_ASSERT(same_as(rng, func)), E>); + STATIC_ASSERT(noexcept(views::adjacent_transform<0>(rng, func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_nothrow_copy_constructible_v); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform&, Fn, 0> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform&, Fn, 0>) { + STATIC_ASSERT(same_as(as_const(rng), func)), E>); + STATIC_ASSERT( + noexcept(views::adjacent_transform<0>(as_const(rng), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_nothrow_copy_constructible_v); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, 0> == (is_view || movable>) ); + if constexpr (CanViewAdjacentTransform, Fn, 0>) { + STATIC_ASSERT(same_as(move(rng), func)), E>); + STATIC_ASSERT(noexcept(views::adjacent_transform<0>(move(rng), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_nothrow_copy_constructible_v); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, 0> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacentTransform, Fn, 0>) { + STATIC_ASSERT(same_as(move(as_const(rng)), func)), E>); + STATIC_ASSERT( + noexcept(views::adjacent_transform<0>(move(as_const(rng)), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng)))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_nothrow_copy_constructible_v); + } +} + +// We have to use std::array instead of C-array, see LLVM-61025 +constexpr auto some_ints = array{1, 3, 5, 7, 11, 13}; +constexpr auto adjacent1_fn = [](auto&& val) -> const int& { return val; }; +constexpr auto adjacent1_result = array{1, 3, 5, 7, 11, 13}; +constexpr auto pairwise_fn = plus{}; +constexpr auto pairwise_result = array{4, 8, 12, 18, 24}; +constexpr auto adjacent3_fn = [](auto... vals) { return (vals * ...); }; +constexpr auto adjacent3_result = array{15, 105, 385, 1001}; +constexpr auto adjacent4_fn = [](auto... vals) { return (vals | ...); }; +constexpr auto adjacent4_result = array{7, 15, 15}; +constexpr auto adjacent5_fn = [](auto... vals) { return (vals - ...); }; +constexpr auto adjacent5_result = array{7, 7}; +constexpr auto adjacent6_fn = [](auto... vals) { return (... - vals); }; +constexpr auto adjacent6_result = array{-38}; +constexpr auto adjacent7_fn = [](auto...) { return 0; }; +constexpr array adjacent7_result{}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one<1>(r, adjacent1_fn, adjacent1_result); + test_one<2>(r, pairwise_fn, pairwise_result); +#ifndef _M_IX86 // fatal error C1060: compiler is out of heap space + test_one<4>(r, adjacent4_fn, adjacent4_result); +#endif // !defined(_M_IX86) + test_one<7>(r, adjacent7_fn, adjacent7_result); + test_adjacent0(r); + } +}; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_fwd(); +#else // ^^^ test all forward range permutations / test only "interesting" permutations vvv + using test::Common, test::Sized; + + // The view is sensitive to category, commonality, and size, but oblivious to proxyness and differencing + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template > +using move_only_view = test::range}, test::CanView::yes, + test::Copyability::move_only>; + +int main() { + { // Check views + // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one<4>(s, adjacent4_fn, adjacent4_result)); + test_one<4>(s, adjacent4_fn, adjacent4_result); + } + + { // ... move-only + test_one<1>(move_only_view{some_ints}, adjacent1_fn, adjacent1_result); + test_one<2>(move_only_view{some_ints}, pairwise_fn, pairwise_result); + test_one<3>( + move_only_view{some_ints}, adjacent3_fn, adjacent3_result); + test_one<4>( + move_only_view{some_ints}, adjacent4_fn, adjacent4_result); + test_one<5>( + move_only_view{some_ints}, adjacent5_fn, adjacent5_result); + test_one<6>( + move_only_view{some_ints}, adjacent6_fn, adjacent6_result); + } + + { // Check non-views + STATIC_ASSERT(test_one<2>(some_ints, pairwise_fn, pairwise_result)); + test_one<2>(some_ints, pairwise_fn, pairwise_result); + + auto vec = some_ints | ranges::to(); + test_one<3>(vec, adjacent3_fn, adjacent3_result); + + auto lst = some_ints | ranges::to(); + test_one<4>(lst, adjacent4_fn, adjacent4_result); + } + + { // Check empty range + constexpr auto to_float = [](int x, int y, int z) { return static_cast(x + y + z); }; + STATIC_ASSERT(test_one<3>(span{}, to_float, span{})); + test_one<3>(span{}, to_float, span{}); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 1e545ab4f63..0aa3e9b765a 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1762,6 +1762,20 @@ STATIC_ASSERT(__cpp_lib_ranges_to_container == 202202L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_zip +#error __cpp_lib_ranges_zip is not defined +#elif __cpp_lib_ranges_zip != 202110L +#error __cpp_lib_ranges_zip is not 202110L +#else +STATIC_ASSERT(__cpp_lib_ranges_zip == 202110L); +#endif +#else +#ifdef __cpp_lib_ranges_zip +#error __cpp_lib_ranges_zip is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_raw_memory_algorithms #error __cpp_lib_raw_memory_algorithms is not defined From cf89890218906a46175d29a08aada5a5fbb746dd Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 17 Mar 2023 03:50:27 +0100 Subject: [PATCH 2/9] Unicode 15 (#3556) --- stl/inc/__msvc_format_ucd_tables.hpp | 368 +++++++++--------- stl/inc/yvals_core.h | 1 + .../test.cpp | 2 + .../grapheme_break_property_data_gen.py | 41 +- .../grapheme_break_test_data_gen.py | 34 +- 5 files changed, 237 insertions(+), 209 deletions(-) diff --git a/stl/inc/__msvc_format_ucd_tables.hpp b/stl/inc/__msvc_format_ucd_tables.hpp index 577627ed039..bc5c7c355ea 100644 --- a/stl/inc/__msvc_format_ucd_tables.hpp +++ b/stl/inc/__msvc_format_ucd_tables.hpp @@ -124,6 +124,8 @@ struct _Unicode_property_data { // Codepoint ranges may not overlap, and, within one property, a codepoint may only appear once. Furthermore the // codepoint lower bounds appear in sorted (ascending) order. +// GraphemeBreakProperty-15.0.0.txt +// Date: 2022-04-27, 17:07:38 GMT enum class _Grapheme_Break_property_values : uint8_t { _CR_value, _Control_value, @@ -141,7 +143,9 @@ enum class _Grapheme_Break_property_values : uint8_t { _No_value = 255 }; -inline constexpr _Unicode_property_data<_Grapheme_Break_property_values, 1355> _Grapheme_Break_property_data{ +// GraphemeBreakProperty-15.0.0.txt +// Date: 2022-04-27, 17:07:38 GMT +inline constexpr _Unicode_property_data<_Grapheme_Break_property_values, 1371> _Grapheme_Break_property_data{ {0x0, 0xa, 0xb, 0xd, 0xe, 0x7f, 0xad, 0x300, 0x483, 0x591, 0x5bf, 0x5c1, 0x5c4, 0x5c7, 0x600, 0x610, 0x61c, 0x64b, 0x670, 0x6d6, 0x6dd, 0x6df, 0x6e7, 0x6ea, 0x70f, 0x711, 0x730, 0x7a6, 0x7eb, 0x7fd, 0x816, 0x81b, 0x825, 0x829, 0x859, 0x890, 0x898, 0x8ca, 0x8e2, 0x8e3, 0x903, 0x93a, 0x93b, 0x93c, 0x93e, 0x941, 0x949, 0x94d, 0x94e, 0x951, @@ -149,97 +153,98 @@ inline constexpr _Unicode_property_data<_Grapheme_Break_property_values, 1355> _ 0xa3e, 0xa41, 0xa47, 0xa4b, 0xa51, 0xa70, 0xa75, 0xa81, 0xa83, 0xabc, 0xabe, 0xac1, 0xac7, 0xac9, 0xacb, 0xacd, 0xae2, 0xafa, 0xb01, 0xb02, 0xb3c, 0xb3e, 0xb40, 0xb41, 0xb47, 0xb4b, 0xb4d, 0xb55, 0xb62, 0xb82, 0xbbe, 0xbbf, 0xbc0, 0xbc1, 0xbc6, 0xbca, 0xbcd, 0xbd7, 0xc00, 0xc01, 0xc04, 0xc3c, 0xc3e, 0xc41, 0xc46, 0xc4a, 0xc55, 0xc62, - 0xc81, 0xc82, 0xcbc, 0xcbe, 0xcbf, 0xcc0, 0xcc2, 0xcc3, 0xcc6, 0xcc7, 0xcca, 0xccc, 0xcd5, 0xce2, 0xd00, 0xd02, - 0xd3b, 0xd3e, 0xd3f, 0xd41, 0xd46, 0xd4a, 0xd4d, 0xd4e, 0xd57, 0xd62, 0xd81, 0xd82, 0xdca, 0xdcf, 0xdd0, 0xdd2, - 0xdd6, 0xdd8, 0xddf, 0xdf2, 0xe31, 0xe33, 0xe34, 0xe47, 0xeb1, 0xeb3, 0xeb4, 0xec8, 0xf18, 0xf35, 0xf37, 0xf39, - 0xf3e, 0xf71, 0xf7f, 0xf80, 0xf86, 0xf8d, 0xf99, 0xfc6, 0x102d, 0x1031, 0x1032, 0x1039, 0x103b, 0x103d, 0x1056, - 0x1058, 0x105e, 0x1071, 0x1082, 0x1084, 0x1085, 0x108d, 0x109d, 0x1100, 0x1160, 0x11a8, 0x135d, 0x1712, 0x1715, - 0x1732, 0x1734, 0x1752, 0x1772, 0x17b4, 0x17b6, 0x17b7, 0x17be, 0x17c6, 0x17c7, 0x17c9, 0x17dd, 0x180b, 0x180e, - 0x180f, 0x1885, 0x18a9, 0x1920, 0x1923, 0x1927, 0x1929, 0x1930, 0x1932, 0x1933, 0x1939, 0x1a17, 0x1a19, 0x1a1b, - 0x1a55, 0x1a56, 0x1a57, 0x1a58, 0x1a60, 0x1a62, 0x1a65, 0x1a6d, 0x1a73, 0x1a7f, 0x1ab0, 0x1b00, 0x1b04, 0x1b34, - 0x1b3b, 0x1b3c, 0x1b3d, 0x1b42, 0x1b43, 0x1b6b, 0x1b80, 0x1b82, 0x1ba1, 0x1ba2, 0x1ba6, 0x1ba8, 0x1baa, 0x1bab, - 0x1be6, 0x1be7, 0x1be8, 0x1bea, 0x1bed, 0x1bee, 0x1bef, 0x1bf2, 0x1c24, 0x1c2c, 0x1c34, 0x1c36, 0x1cd0, 0x1cd4, - 0x1ce1, 0x1ce2, 0x1ced, 0x1cf4, 0x1cf7, 0x1cf8, 0x1dc0, 0x200b, 0x200c, 0x200d, 0x200e, 0x2028, 0x2060, 0x20d0, - 0x2cef, 0x2d7f, 0x2de0, 0x302a, 0x3099, 0xa66f, 0xa674, 0xa69e, 0xa6f0, 0xa802, 0xa806, 0xa80b, 0xa823, 0xa825, - 0xa827, 0xa82c, 0xa880, 0xa8b4, 0xa8c4, 0xa8e0, 0xa8ff, 0xa926, 0xa947, 0xa952, 0xa960, 0xa980, 0xa983, 0xa9b3, - 0xa9b4, 0xa9b6, 0xa9ba, 0xa9bc, 0xa9be, 0xa9e5, 0xaa29, 0xaa2f, 0xaa31, 0xaa33, 0xaa35, 0xaa43, 0xaa4c, 0xaa4d, - 0xaa7c, 0xaab0, 0xaab2, 0xaab7, 0xaabe, 0xaac1, 0xaaeb, 0xaaec, 0xaaee, 0xaaf5, 0xaaf6, 0xabe3, 0xabe5, 0xabe6, - 0xabe8, 0xabe9, 0xabec, 0xabed, 0xac00, 0xac01, 0xac1c, 0xac1d, 0xac38, 0xac39, 0xac54, 0xac55, 0xac70, 0xac71, - 0xac8c, 0xac8d, 0xaca8, 0xaca9, 0xacc4, 0xacc5, 0xace0, 0xace1, 0xacfc, 0xacfd, 0xad18, 0xad19, 0xad34, 0xad35, - 0xad50, 0xad51, 0xad6c, 0xad6d, 0xad88, 0xad89, 0xada4, 0xada5, 0xadc0, 0xadc1, 0xaddc, 0xaddd, 0xadf8, 0xadf9, - 0xae14, 0xae15, 0xae30, 0xae31, 0xae4c, 0xae4d, 0xae68, 0xae69, 0xae84, 0xae85, 0xaea0, 0xaea1, 0xaebc, 0xaebd, - 0xaed8, 0xaed9, 0xaef4, 0xaef5, 0xaf10, 0xaf11, 0xaf2c, 0xaf2d, 0xaf48, 0xaf49, 0xaf64, 0xaf65, 0xaf80, 0xaf81, - 0xaf9c, 0xaf9d, 0xafb8, 0xafb9, 0xafd4, 0xafd5, 0xaff0, 0xaff1, 0xb00c, 0xb00d, 0xb028, 0xb029, 0xb044, 0xb045, - 0xb060, 0xb061, 0xb07c, 0xb07d, 0xb098, 0xb099, 0xb0b4, 0xb0b5, 0xb0d0, 0xb0d1, 0xb0ec, 0xb0ed, 0xb108, 0xb109, - 0xb124, 0xb125, 0xb140, 0xb141, 0xb15c, 0xb15d, 0xb178, 0xb179, 0xb194, 0xb195, 0xb1b0, 0xb1b1, 0xb1cc, 0xb1cd, - 0xb1e8, 0xb1e9, 0xb204, 0xb205, 0xb220, 0xb221, 0xb23c, 0xb23d, 0xb258, 0xb259, 0xb274, 0xb275, 0xb290, 0xb291, - 0xb2ac, 0xb2ad, 0xb2c8, 0xb2c9, 0xb2e4, 0xb2e5, 0xb300, 0xb301, 0xb31c, 0xb31d, 0xb338, 0xb339, 0xb354, 0xb355, - 0xb370, 0xb371, 0xb38c, 0xb38d, 0xb3a8, 0xb3a9, 0xb3c4, 0xb3c5, 0xb3e0, 0xb3e1, 0xb3fc, 0xb3fd, 0xb418, 0xb419, - 0xb434, 0xb435, 0xb450, 0xb451, 0xb46c, 0xb46d, 0xb488, 0xb489, 0xb4a4, 0xb4a5, 0xb4c0, 0xb4c1, 0xb4dc, 0xb4dd, - 0xb4f8, 0xb4f9, 0xb514, 0xb515, 0xb530, 0xb531, 0xb54c, 0xb54d, 0xb568, 0xb569, 0xb584, 0xb585, 0xb5a0, 0xb5a1, - 0xb5bc, 0xb5bd, 0xb5d8, 0xb5d9, 0xb5f4, 0xb5f5, 0xb610, 0xb611, 0xb62c, 0xb62d, 0xb648, 0xb649, 0xb664, 0xb665, - 0xb680, 0xb681, 0xb69c, 0xb69d, 0xb6b8, 0xb6b9, 0xb6d4, 0xb6d5, 0xb6f0, 0xb6f1, 0xb70c, 0xb70d, 0xb728, 0xb729, - 0xb744, 0xb745, 0xb760, 0xb761, 0xb77c, 0xb77d, 0xb798, 0xb799, 0xb7b4, 0xb7b5, 0xb7d0, 0xb7d1, 0xb7ec, 0xb7ed, - 0xb808, 0xb809, 0xb824, 0xb825, 0xb840, 0xb841, 0xb85c, 0xb85d, 0xb878, 0xb879, 0xb894, 0xb895, 0xb8b0, 0xb8b1, - 0xb8cc, 0xb8cd, 0xb8e8, 0xb8e9, 0xb904, 0xb905, 0xb920, 0xb921, 0xb93c, 0xb93d, 0xb958, 0xb959, 0xb974, 0xb975, - 0xb990, 0xb991, 0xb9ac, 0xb9ad, 0xb9c8, 0xb9c9, 0xb9e4, 0xb9e5, 0xba00, 0xba01, 0xba1c, 0xba1d, 0xba38, 0xba39, - 0xba54, 0xba55, 0xba70, 0xba71, 0xba8c, 0xba8d, 0xbaa8, 0xbaa9, 0xbac4, 0xbac5, 0xbae0, 0xbae1, 0xbafc, 0xbafd, - 0xbb18, 0xbb19, 0xbb34, 0xbb35, 0xbb50, 0xbb51, 0xbb6c, 0xbb6d, 0xbb88, 0xbb89, 0xbba4, 0xbba5, 0xbbc0, 0xbbc1, - 0xbbdc, 0xbbdd, 0xbbf8, 0xbbf9, 0xbc14, 0xbc15, 0xbc30, 0xbc31, 0xbc4c, 0xbc4d, 0xbc68, 0xbc69, 0xbc84, 0xbc85, - 0xbca0, 0xbca1, 0xbcbc, 0xbcbd, 0xbcd8, 0xbcd9, 0xbcf4, 0xbcf5, 0xbd10, 0xbd11, 0xbd2c, 0xbd2d, 0xbd48, 0xbd49, - 0xbd64, 0xbd65, 0xbd80, 0xbd81, 0xbd9c, 0xbd9d, 0xbdb8, 0xbdb9, 0xbdd4, 0xbdd5, 0xbdf0, 0xbdf1, 0xbe0c, 0xbe0d, - 0xbe28, 0xbe29, 0xbe44, 0xbe45, 0xbe60, 0xbe61, 0xbe7c, 0xbe7d, 0xbe98, 0xbe99, 0xbeb4, 0xbeb5, 0xbed0, 0xbed1, - 0xbeec, 0xbeed, 0xbf08, 0xbf09, 0xbf24, 0xbf25, 0xbf40, 0xbf41, 0xbf5c, 0xbf5d, 0xbf78, 0xbf79, 0xbf94, 0xbf95, - 0xbfb0, 0xbfb1, 0xbfcc, 0xbfcd, 0xbfe8, 0xbfe9, 0xc004, 0xc005, 0xc020, 0xc021, 0xc03c, 0xc03d, 0xc058, 0xc059, - 0xc074, 0xc075, 0xc090, 0xc091, 0xc0ac, 0xc0ad, 0xc0c8, 0xc0c9, 0xc0e4, 0xc0e5, 0xc100, 0xc101, 0xc11c, 0xc11d, - 0xc138, 0xc139, 0xc154, 0xc155, 0xc170, 0xc171, 0xc18c, 0xc18d, 0xc1a8, 0xc1a9, 0xc1c4, 0xc1c5, 0xc1e0, 0xc1e1, - 0xc1fc, 0xc1fd, 0xc218, 0xc219, 0xc234, 0xc235, 0xc250, 0xc251, 0xc26c, 0xc26d, 0xc288, 0xc289, 0xc2a4, 0xc2a5, - 0xc2c0, 0xc2c1, 0xc2dc, 0xc2dd, 0xc2f8, 0xc2f9, 0xc314, 0xc315, 0xc330, 0xc331, 0xc34c, 0xc34d, 0xc368, 0xc369, - 0xc384, 0xc385, 0xc3a0, 0xc3a1, 0xc3bc, 0xc3bd, 0xc3d8, 0xc3d9, 0xc3f4, 0xc3f5, 0xc410, 0xc411, 0xc42c, 0xc42d, - 0xc448, 0xc449, 0xc464, 0xc465, 0xc480, 0xc481, 0xc49c, 0xc49d, 0xc4b8, 0xc4b9, 0xc4d4, 0xc4d5, 0xc4f0, 0xc4f1, - 0xc50c, 0xc50d, 0xc528, 0xc529, 0xc544, 0xc545, 0xc560, 0xc561, 0xc57c, 0xc57d, 0xc598, 0xc599, 0xc5b4, 0xc5b5, - 0xc5d0, 0xc5d1, 0xc5ec, 0xc5ed, 0xc608, 0xc609, 0xc624, 0xc625, 0xc640, 0xc641, 0xc65c, 0xc65d, 0xc678, 0xc679, - 0xc694, 0xc695, 0xc6b0, 0xc6b1, 0xc6cc, 0xc6cd, 0xc6e8, 0xc6e9, 0xc704, 0xc705, 0xc720, 0xc721, 0xc73c, 0xc73d, - 0xc758, 0xc759, 0xc774, 0xc775, 0xc790, 0xc791, 0xc7ac, 0xc7ad, 0xc7c8, 0xc7c9, 0xc7e4, 0xc7e5, 0xc800, 0xc801, - 0xc81c, 0xc81d, 0xc838, 0xc839, 0xc854, 0xc855, 0xc870, 0xc871, 0xc88c, 0xc88d, 0xc8a8, 0xc8a9, 0xc8c4, 0xc8c5, - 0xc8e0, 0xc8e1, 0xc8fc, 0xc8fd, 0xc918, 0xc919, 0xc934, 0xc935, 0xc950, 0xc951, 0xc96c, 0xc96d, 0xc988, 0xc989, - 0xc9a4, 0xc9a5, 0xc9c0, 0xc9c1, 0xc9dc, 0xc9dd, 0xc9f8, 0xc9f9, 0xca14, 0xca15, 0xca30, 0xca31, 0xca4c, 0xca4d, - 0xca68, 0xca69, 0xca84, 0xca85, 0xcaa0, 0xcaa1, 0xcabc, 0xcabd, 0xcad8, 0xcad9, 0xcaf4, 0xcaf5, 0xcb10, 0xcb11, - 0xcb2c, 0xcb2d, 0xcb48, 0xcb49, 0xcb64, 0xcb65, 0xcb80, 0xcb81, 0xcb9c, 0xcb9d, 0xcbb8, 0xcbb9, 0xcbd4, 0xcbd5, - 0xcbf0, 0xcbf1, 0xcc0c, 0xcc0d, 0xcc28, 0xcc29, 0xcc44, 0xcc45, 0xcc60, 0xcc61, 0xcc7c, 0xcc7d, 0xcc98, 0xcc99, - 0xccb4, 0xccb5, 0xccd0, 0xccd1, 0xccec, 0xcced, 0xcd08, 0xcd09, 0xcd24, 0xcd25, 0xcd40, 0xcd41, 0xcd5c, 0xcd5d, - 0xcd78, 0xcd79, 0xcd94, 0xcd95, 0xcdb0, 0xcdb1, 0xcdcc, 0xcdcd, 0xcde8, 0xcde9, 0xce04, 0xce05, 0xce20, 0xce21, - 0xce3c, 0xce3d, 0xce58, 0xce59, 0xce74, 0xce75, 0xce90, 0xce91, 0xceac, 0xcead, 0xcec8, 0xcec9, 0xcee4, 0xcee5, - 0xcf00, 0xcf01, 0xcf1c, 0xcf1d, 0xcf38, 0xcf39, 0xcf54, 0xcf55, 0xcf70, 0xcf71, 0xcf8c, 0xcf8d, 0xcfa8, 0xcfa9, - 0xcfc4, 0xcfc5, 0xcfe0, 0xcfe1, 0xcffc, 0xcffd, 0xd018, 0xd019, 0xd034, 0xd035, 0xd050, 0xd051, 0xd06c, 0xd06d, - 0xd088, 0xd089, 0xd0a4, 0xd0a5, 0xd0c0, 0xd0c1, 0xd0dc, 0xd0dd, 0xd0f8, 0xd0f9, 0xd114, 0xd115, 0xd130, 0xd131, - 0xd14c, 0xd14d, 0xd168, 0xd169, 0xd184, 0xd185, 0xd1a0, 0xd1a1, 0xd1bc, 0xd1bd, 0xd1d8, 0xd1d9, 0xd1f4, 0xd1f5, - 0xd210, 0xd211, 0xd22c, 0xd22d, 0xd248, 0xd249, 0xd264, 0xd265, 0xd280, 0xd281, 0xd29c, 0xd29d, 0xd2b8, 0xd2b9, - 0xd2d4, 0xd2d5, 0xd2f0, 0xd2f1, 0xd30c, 0xd30d, 0xd328, 0xd329, 0xd344, 0xd345, 0xd360, 0xd361, 0xd37c, 0xd37d, - 0xd398, 0xd399, 0xd3b4, 0xd3b5, 0xd3d0, 0xd3d1, 0xd3ec, 0xd3ed, 0xd408, 0xd409, 0xd424, 0xd425, 0xd440, 0xd441, - 0xd45c, 0xd45d, 0xd478, 0xd479, 0xd494, 0xd495, 0xd4b0, 0xd4b1, 0xd4cc, 0xd4cd, 0xd4e8, 0xd4e9, 0xd504, 0xd505, - 0xd520, 0xd521, 0xd53c, 0xd53d, 0xd558, 0xd559, 0xd574, 0xd575, 0xd590, 0xd591, 0xd5ac, 0xd5ad, 0xd5c8, 0xd5c9, - 0xd5e4, 0xd5e5, 0xd600, 0xd601, 0xd61c, 0xd61d, 0xd638, 0xd639, 0xd654, 0xd655, 0xd670, 0xd671, 0xd68c, 0xd68d, - 0xd6a8, 0xd6a9, 0xd6c4, 0xd6c5, 0xd6e0, 0xd6e1, 0xd6fc, 0xd6fd, 0xd718, 0xd719, 0xd734, 0xd735, 0xd750, 0xd751, - 0xd76c, 0xd76d, 0xd788, 0xd789, 0xd7b0, 0xd7cb, 0xfb1e, 0xfe00, 0xfe20, 0xfeff, 0xff9e, 0xfff0, 0x101fd, - 0x102e0, 0x10376, 0x10a01, 0x10a05, 0x10a0c, 0x10a38, 0x10a3f, 0x10ae5, 0x10d24, 0x10eab, 0x10f46, 0x10f82, - 0x11000, 0x11001, 0x11002, 0x11038, 0x11070, 0x11073, 0x1107f, 0x11082, 0x110b0, 0x110b3, 0x110b7, 0x110b9, - 0x110bd, 0x110c2, 0x110cd, 0x11100, 0x11127, 0x1112c, 0x1112d, 0x11145, 0x11173, 0x11180, 0x11182, 0x111b3, - 0x111b6, 0x111bf, 0x111c2, 0x111c9, 0x111ce, 0x111cf, 0x1122c, 0x1122f, 0x11232, 0x11234, 0x11235, 0x11236, - 0x1123e, 0x112df, 0x112e0, 0x112e3, 0x11300, 0x11302, 0x1133b, 0x1133e, 0x1133f, 0x11340, 0x11341, 0x11347, - 0x1134b, 0x11357, 0x11362, 0x11366, 0x11370, 0x11435, 0x11438, 0x11440, 0x11442, 0x11445, 0x11446, 0x1145e, - 0x114b0, 0x114b1, 0x114b3, 0x114b9, 0x114ba, 0x114bb, 0x114bd, 0x114be, 0x114bf, 0x114c1, 0x114c2, 0x115af, - 0x115b0, 0x115b2, 0x115b8, 0x115bc, 0x115be, 0x115bf, 0x115dc, 0x11630, 0x11633, 0x1163b, 0x1163d, 0x1163e, - 0x1163f, 0x116ab, 0x116ac, 0x116ad, 0x116ae, 0x116b0, 0x116b6, 0x116b7, 0x1171d, 0x11722, 0x11726, 0x11727, - 0x1182c, 0x1182f, 0x11838, 0x11839, 0x11930, 0x11931, 0x11937, 0x1193b, 0x1193d, 0x1193e, 0x1193f, 0x11940, - 0x11941, 0x11942, 0x11943, 0x119d1, 0x119d4, 0x119da, 0x119dc, 0x119e0, 0x119e4, 0x11a01, 0x11a33, 0x11a39, - 0x11a3a, 0x11a3b, 0x11a47, 0x11a51, 0x11a57, 0x11a59, 0x11a84, 0x11a8a, 0x11a97, 0x11a98, 0x11c2f, 0x11c30, - 0x11c38, 0x11c3e, 0x11c3f, 0x11c92, 0x11ca9, 0x11caa, 0x11cb1, 0x11cb2, 0x11cb4, 0x11cb5, 0x11d31, 0x11d3a, - 0x11d3c, 0x11d3f, 0x11d46, 0x11d47, 0x11d8a, 0x11d90, 0x11d93, 0x11d95, 0x11d96, 0x11d97, 0x11ef3, 0x11ef5, - 0x13430, 0x16af0, 0x16b30, 0x16f4f, 0x16f51, 0x16f8f, 0x16fe4, 0x16ff0, 0x1bc9d, 0x1bca0, 0x1cf00, 0x1cf30, - 0x1d165, 0x1d166, 0x1d167, 0x1d16d, 0x1d16e, 0x1d173, 0x1d17b, 0x1d185, 0x1d1aa, 0x1d242, 0x1da00, 0x1da3b, - 0x1da75, 0x1da84, 0x1da9b, 0x1daa1, 0x1e000, 0x1e008, 0x1e01b, 0x1e023, 0x1e026, 0x1e130, 0x1e2ae, 0x1e2ec, - 0x1e8d0, 0x1e944, 0x1f1e6, 0x1f3fb, 0xe0000, 0xe0020, 0xe0080, 0xe0100, 0xe01f0}, + 0xc81, 0xc82, 0xcbc, 0xcbe, 0xcbf, 0xcc0, 0xcc2, 0xcc3, 0xcc6, 0xcc7, 0xcca, 0xccc, 0xcd5, 0xce2, 0xcf3, 0xd00, + 0xd02, 0xd3b, 0xd3e, 0xd3f, 0xd41, 0xd46, 0xd4a, 0xd4d, 0xd4e, 0xd57, 0xd62, 0xd81, 0xd82, 0xdca, 0xdcf, 0xdd0, + 0xdd2, 0xdd6, 0xdd8, 0xddf, 0xdf2, 0xe31, 0xe33, 0xe34, 0xe47, 0xeb1, 0xeb3, 0xeb4, 0xec8, 0xf18, 0xf35, 0xf37, + 0xf39, 0xf3e, 0xf71, 0xf7f, 0xf80, 0xf86, 0xf8d, 0xf99, 0xfc6, 0x102d, 0x1031, 0x1032, 0x1039, 0x103b, 0x103d, + 0x1056, 0x1058, 0x105e, 0x1071, 0x1082, 0x1084, 0x1085, 0x108d, 0x109d, 0x1100, 0x1160, 0x11a8, 0x135d, 0x1712, + 0x1715, 0x1732, 0x1734, 0x1752, 0x1772, 0x17b4, 0x17b6, 0x17b7, 0x17be, 0x17c6, 0x17c7, 0x17c9, 0x17dd, 0x180b, + 0x180e, 0x180f, 0x1885, 0x18a9, 0x1920, 0x1923, 0x1927, 0x1929, 0x1930, 0x1932, 0x1933, 0x1939, 0x1a17, 0x1a19, + 0x1a1b, 0x1a55, 0x1a56, 0x1a57, 0x1a58, 0x1a60, 0x1a62, 0x1a65, 0x1a6d, 0x1a73, 0x1a7f, 0x1ab0, 0x1b00, 0x1b04, + 0x1b34, 0x1b3b, 0x1b3c, 0x1b3d, 0x1b42, 0x1b43, 0x1b6b, 0x1b80, 0x1b82, 0x1ba1, 0x1ba2, 0x1ba6, 0x1ba8, 0x1baa, + 0x1bab, 0x1be6, 0x1be7, 0x1be8, 0x1bea, 0x1bed, 0x1bee, 0x1bef, 0x1bf2, 0x1c24, 0x1c2c, 0x1c34, 0x1c36, 0x1cd0, + 0x1cd4, 0x1ce1, 0x1ce2, 0x1ced, 0x1cf4, 0x1cf7, 0x1cf8, 0x1dc0, 0x200b, 0x200c, 0x200d, 0x200e, 0x2028, 0x2060, + 0x20d0, 0x2cef, 0x2d7f, 0x2de0, 0x302a, 0x3099, 0xa66f, 0xa674, 0xa69e, 0xa6f0, 0xa802, 0xa806, 0xa80b, 0xa823, + 0xa825, 0xa827, 0xa82c, 0xa880, 0xa8b4, 0xa8c4, 0xa8e0, 0xa8ff, 0xa926, 0xa947, 0xa952, 0xa960, 0xa980, 0xa983, + 0xa9b3, 0xa9b4, 0xa9b6, 0xa9ba, 0xa9bc, 0xa9be, 0xa9e5, 0xaa29, 0xaa2f, 0xaa31, 0xaa33, 0xaa35, 0xaa43, 0xaa4c, + 0xaa4d, 0xaa7c, 0xaab0, 0xaab2, 0xaab7, 0xaabe, 0xaac1, 0xaaeb, 0xaaec, 0xaaee, 0xaaf5, 0xaaf6, 0xabe3, 0xabe5, + 0xabe6, 0xabe8, 0xabe9, 0xabec, 0xabed, 0xac00, 0xac01, 0xac1c, 0xac1d, 0xac38, 0xac39, 0xac54, 0xac55, 0xac70, + 0xac71, 0xac8c, 0xac8d, 0xaca8, 0xaca9, 0xacc4, 0xacc5, 0xace0, 0xace1, 0xacfc, 0xacfd, 0xad18, 0xad19, 0xad34, + 0xad35, 0xad50, 0xad51, 0xad6c, 0xad6d, 0xad88, 0xad89, 0xada4, 0xada5, 0xadc0, 0xadc1, 0xaddc, 0xaddd, 0xadf8, + 0xadf9, 0xae14, 0xae15, 0xae30, 0xae31, 0xae4c, 0xae4d, 0xae68, 0xae69, 0xae84, 0xae85, 0xaea0, 0xaea1, 0xaebc, + 0xaebd, 0xaed8, 0xaed9, 0xaef4, 0xaef5, 0xaf10, 0xaf11, 0xaf2c, 0xaf2d, 0xaf48, 0xaf49, 0xaf64, 0xaf65, 0xaf80, + 0xaf81, 0xaf9c, 0xaf9d, 0xafb8, 0xafb9, 0xafd4, 0xafd5, 0xaff0, 0xaff1, 0xb00c, 0xb00d, 0xb028, 0xb029, 0xb044, + 0xb045, 0xb060, 0xb061, 0xb07c, 0xb07d, 0xb098, 0xb099, 0xb0b4, 0xb0b5, 0xb0d0, 0xb0d1, 0xb0ec, 0xb0ed, 0xb108, + 0xb109, 0xb124, 0xb125, 0xb140, 0xb141, 0xb15c, 0xb15d, 0xb178, 0xb179, 0xb194, 0xb195, 0xb1b0, 0xb1b1, 0xb1cc, + 0xb1cd, 0xb1e8, 0xb1e9, 0xb204, 0xb205, 0xb220, 0xb221, 0xb23c, 0xb23d, 0xb258, 0xb259, 0xb274, 0xb275, 0xb290, + 0xb291, 0xb2ac, 0xb2ad, 0xb2c8, 0xb2c9, 0xb2e4, 0xb2e5, 0xb300, 0xb301, 0xb31c, 0xb31d, 0xb338, 0xb339, 0xb354, + 0xb355, 0xb370, 0xb371, 0xb38c, 0xb38d, 0xb3a8, 0xb3a9, 0xb3c4, 0xb3c5, 0xb3e0, 0xb3e1, 0xb3fc, 0xb3fd, 0xb418, + 0xb419, 0xb434, 0xb435, 0xb450, 0xb451, 0xb46c, 0xb46d, 0xb488, 0xb489, 0xb4a4, 0xb4a5, 0xb4c0, 0xb4c1, 0xb4dc, + 0xb4dd, 0xb4f8, 0xb4f9, 0xb514, 0xb515, 0xb530, 0xb531, 0xb54c, 0xb54d, 0xb568, 0xb569, 0xb584, 0xb585, 0xb5a0, + 0xb5a1, 0xb5bc, 0xb5bd, 0xb5d8, 0xb5d9, 0xb5f4, 0xb5f5, 0xb610, 0xb611, 0xb62c, 0xb62d, 0xb648, 0xb649, 0xb664, + 0xb665, 0xb680, 0xb681, 0xb69c, 0xb69d, 0xb6b8, 0xb6b9, 0xb6d4, 0xb6d5, 0xb6f0, 0xb6f1, 0xb70c, 0xb70d, 0xb728, + 0xb729, 0xb744, 0xb745, 0xb760, 0xb761, 0xb77c, 0xb77d, 0xb798, 0xb799, 0xb7b4, 0xb7b5, 0xb7d0, 0xb7d1, 0xb7ec, + 0xb7ed, 0xb808, 0xb809, 0xb824, 0xb825, 0xb840, 0xb841, 0xb85c, 0xb85d, 0xb878, 0xb879, 0xb894, 0xb895, 0xb8b0, + 0xb8b1, 0xb8cc, 0xb8cd, 0xb8e8, 0xb8e9, 0xb904, 0xb905, 0xb920, 0xb921, 0xb93c, 0xb93d, 0xb958, 0xb959, 0xb974, + 0xb975, 0xb990, 0xb991, 0xb9ac, 0xb9ad, 0xb9c8, 0xb9c9, 0xb9e4, 0xb9e5, 0xba00, 0xba01, 0xba1c, 0xba1d, 0xba38, + 0xba39, 0xba54, 0xba55, 0xba70, 0xba71, 0xba8c, 0xba8d, 0xbaa8, 0xbaa9, 0xbac4, 0xbac5, 0xbae0, 0xbae1, 0xbafc, + 0xbafd, 0xbb18, 0xbb19, 0xbb34, 0xbb35, 0xbb50, 0xbb51, 0xbb6c, 0xbb6d, 0xbb88, 0xbb89, 0xbba4, 0xbba5, 0xbbc0, + 0xbbc1, 0xbbdc, 0xbbdd, 0xbbf8, 0xbbf9, 0xbc14, 0xbc15, 0xbc30, 0xbc31, 0xbc4c, 0xbc4d, 0xbc68, 0xbc69, 0xbc84, + 0xbc85, 0xbca0, 0xbca1, 0xbcbc, 0xbcbd, 0xbcd8, 0xbcd9, 0xbcf4, 0xbcf5, 0xbd10, 0xbd11, 0xbd2c, 0xbd2d, 0xbd48, + 0xbd49, 0xbd64, 0xbd65, 0xbd80, 0xbd81, 0xbd9c, 0xbd9d, 0xbdb8, 0xbdb9, 0xbdd4, 0xbdd5, 0xbdf0, 0xbdf1, 0xbe0c, + 0xbe0d, 0xbe28, 0xbe29, 0xbe44, 0xbe45, 0xbe60, 0xbe61, 0xbe7c, 0xbe7d, 0xbe98, 0xbe99, 0xbeb4, 0xbeb5, 0xbed0, + 0xbed1, 0xbeec, 0xbeed, 0xbf08, 0xbf09, 0xbf24, 0xbf25, 0xbf40, 0xbf41, 0xbf5c, 0xbf5d, 0xbf78, 0xbf79, 0xbf94, + 0xbf95, 0xbfb0, 0xbfb1, 0xbfcc, 0xbfcd, 0xbfe8, 0xbfe9, 0xc004, 0xc005, 0xc020, 0xc021, 0xc03c, 0xc03d, 0xc058, + 0xc059, 0xc074, 0xc075, 0xc090, 0xc091, 0xc0ac, 0xc0ad, 0xc0c8, 0xc0c9, 0xc0e4, 0xc0e5, 0xc100, 0xc101, 0xc11c, + 0xc11d, 0xc138, 0xc139, 0xc154, 0xc155, 0xc170, 0xc171, 0xc18c, 0xc18d, 0xc1a8, 0xc1a9, 0xc1c4, 0xc1c5, 0xc1e0, + 0xc1e1, 0xc1fc, 0xc1fd, 0xc218, 0xc219, 0xc234, 0xc235, 0xc250, 0xc251, 0xc26c, 0xc26d, 0xc288, 0xc289, 0xc2a4, + 0xc2a5, 0xc2c0, 0xc2c1, 0xc2dc, 0xc2dd, 0xc2f8, 0xc2f9, 0xc314, 0xc315, 0xc330, 0xc331, 0xc34c, 0xc34d, 0xc368, + 0xc369, 0xc384, 0xc385, 0xc3a0, 0xc3a1, 0xc3bc, 0xc3bd, 0xc3d8, 0xc3d9, 0xc3f4, 0xc3f5, 0xc410, 0xc411, 0xc42c, + 0xc42d, 0xc448, 0xc449, 0xc464, 0xc465, 0xc480, 0xc481, 0xc49c, 0xc49d, 0xc4b8, 0xc4b9, 0xc4d4, 0xc4d5, 0xc4f0, + 0xc4f1, 0xc50c, 0xc50d, 0xc528, 0xc529, 0xc544, 0xc545, 0xc560, 0xc561, 0xc57c, 0xc57d, 0xc598, 0xc599, 0xc5b4, + 0xc5b5, 0xc5d0, 0xc5d1, 0xc5ec, 0xc5ed, 0xc608, 0xc609, 0xc624, 0xc625, 0xc640, 0xc641, 0xc65c, 0xc65d, 0xc678, + 0xc679, 0xc694, 0xc695, 0xc6b0, 0xc6b1, 0xc6cc, 0xc6cd, 0xc6e8, 0xc6e9, 0xc704, 0xc705, 0xc720, 0xc721, 0xc73c, + 0xc73d, 0xc758, 0xc759, 0xc774, 0xc775, 0xc790, 0xc791, 0xc7ac, 0xc7ad, 0xc7c8, 0xc7c9, 0xc7e4, 0xc7e5, 0xc800, + 0xc801, 0xc81c, 0xc81d, 0xc838, 0xc839, 0xc854, 0xc855, 0xc870, 0xc871, 0xc88c, 0xc88d, 0xc8a8, 0xc8a9, 0xc8c4, + 0xc8c5, 0xc8e0, 0xc8e1, 0xc8fc, 0xc8fd, 0xc918, 0xc919, 0xc934, 0xc935, 0xc950, 0xc951, 0xc96c, 0xc96d, 0xc988, + 0xc989, 0xc9a4, 0xc9a5, 0xc9c0, 0xc9c1, 0xc9dc, 0xc9dd, 0xc9f8, 0xc9f9, 0xca14, 0xca15, 0xca30, 0xca31, 0xca4c, + 0xca4d, 0xca68, 0xca69, 0xca84, 0xca85, 0xcaa0, 0xcaa1, 0xcabc, 0xcabd, 0xcad8, 0xcad9, 0xcaf4, 0xcaf5, 0xcb10, + 0xcb11, 0xcb2c, 0xcb2d, 0xcb48, 0xcb49, 0xcb64, 0xcb65, 0xcb80, 0xcb81, 0xcb9c, 0xcb9d, 0xcbb8, 0xcbb9, 0xcbd4, + 0xcbd5, 0xcbf0, 0xcbf1, 0xcc0c, 0xcc0d, 0xcc28, 0xcc29, 0xcc44, 0xcc45, 0xcc60, 0xcc61, 0xcc7c, 0xcc7d, 0xcc98, + 0xcc99, 0xccb4, 0xccb5, 0xccd0, 0xccd1, 0xccec, 0xcced, 0xcd08, 0xcd09, 0xcd24, 0xcd25, 0xcd40, 0xcd41, 0xcd5c, + 0xcd5d, 0xcd78, 0xcd79, 0xcd94, 0xcd95, 0xcdb0, 0xcdb1, 0xcdcc, 0xcdcd, 0xcde8, 0xcde9, 0xce04, 0xce05, 0xce20, + 0xce21, 0xce3c, 0xce3d, 0xce58, 0xce59, 0xce74, 0xce75, 0xce90, 0xce91, 0xceac, 0xcead, 0xcec8, 0xcec9, 0xcee4, + 0xcee5, 0xcf00, 0xcf01, 0xcf1c, 0xcf1d, 0xcf38, 0xcf39, 0xcf54, 0xcf55, 0xcf70, 0xcf71, 0xcf8c, 0xcf8d, 0xcfa8, + 0xcfa9, 0xcfc4, 0xcfc5, 0xcfe0, 0xcfe1, 0xcffc, 0xcffd, 0xd018, 0xd019, 0xd034, 0xd035, 0xd050, 0xd051, 0xd06c, + 0xd06d, 0xd088, 0xd089, 0xd0a4, 0xd0a5, 0xd0c0, 0xd0c1, 0xd0dc, 0xd0dd, 0xd0f8, 0xd0f9, 0xd114, 0xd115, 0xd130, + 0xd131, 0xd14c, 0xd14d, 0xd168, 0xd169, 0xd184, 0xd185, 0xd1a0, 0xd1a1, 0xd1bc, 0xd1bd, 0xd1d8, 0xd1d9, 0xd1f4, + 0xd1f5, 0xd210, 0xd211, 0xd22c, 0xd22d, 0xd248, 0xd249, 0xd264, 0xd265, 0xd280, 0xd281, 0xd29c, 0xd29d, 0xd2b8, + 0xd2b9, 0xd2d4, 0xd2d5, 0xd2f0, 0xd2f1, 0xd30c, 0xd30d, 0xd328, 0xd329, 0xd344, 0xd345, 0xd360, 0xd361, 0xd37c, + 0xd37d, 0xd398, 0xd399, 0xd3b4, 0xd3b5, 0xd3d0, 0xd3d1, 0xd3ec, 0xd3ed, 0xd408, 0xd409, 0xd424, 0xd425, 0xd440, + 0xd441, 0xd45c, 0xd45d, 0xd478, 0xd479, 0xd494, 0xd495, 0xd4b0, 0xd4b1, 0xd4cc, 0xd4cd, 0xd4e8, 0xd4e9, 0xd504, + 0xd505, 0xd520, 0xd521, 0xd53c, 0xd53d, 0xd558, 0xd559, 0xd574, 0xd575, 0xd590, 0xd591, 0xd5ac, 0xd5ad, 0xd5c8, + 0xd5c9, 0xd5e4, 0xd5e5, 0xd600, 0xd601, 0xd61c, 0xd61d, 0xd638, 0xd639, 0xd654, 0xd655, 0xd670, 0xd671, 0xd68c, + 0xd68d, 0xd6a8, 0xd6a9, 0xd6c4, 0xd6c5, 0xd6e0, 0xd6e1, 0xd6fc, 0xd6fd, 0xd718, 0xd719, 0xd734, 0xd735, 0xd750, + 0xd751, 0xd76c, 0xd76d, 0xd788, 0xd789, 0xd7b0, 0xd7cb, 0xfb1e, 0xfe00, 0xfe20, 0xfeff, 0xff9e, 0xfff0, 0x101fd, + 0x102e0, 0x10376, 0x10a01, 0x10a05, 0x10a0c, 0x10a38, 0x10a3f, 0x10ae5, 0x10d24, 0x10eab, 0x10efd, 0x10f46, + 0x10f82, 0x11000, 0x11001, 0x11002, 0x11038, 0x11070, 0x11073, 0x1107f, 0x11082, 0x110b0, 0x110b3, 0x110b7, + 0x110b9, 0x110bd, 0x110c2, 0x110cd, 0x11100, 0x11127, 0x1112c, 0x1112d, 0x11145, 0x11173, 0x11180, 0x11182, + 0x111b3, 0x111b6, 0x111bf, 0x111c2, 0x111c9, 0x111ce, 0x111cf, 0x1122c, 0x1122f, 0x11232, 0x11234, 0x11235, + 0x11236, 0x1123e, 0x11241, 0x112df, 0x112e0, 0x112e3, 0x11300, 0x11302, 0x1133b, 0x1133e, 0x1133f, 0x11340, + 0x11341, 0x11347, 0x1134b, 0x11357, 0x11362, 0x11366, 0x11370, 0x11435, 0x11438, 0x11440, 0x11442, 0x11445, + 0x11446, 0x1145e, 0x114b0, 0x114b1, 0x114b3, 0x114b9, 0x114ba, 0x114bb, 0x114bd, 0x114be, 0x114bf, 0x114c1, + 0x114c2, 0x115af, 0x115b0, 0x115b2, 0x115b8, 0x115bc, 0x115be, 0x115bf, 0x115dc, 0x11630, 0x11633, 0x1163b, + 0x1163d, 0x1163e, 0x1163f, 0x116ab, 0x116ac, 0x116ad, 0x116ae, 0x116b0, 0x116b6, 0x116b7, 0x1171d, 0x11722, + 0x11726, 0x11727, 0x1182c, 0x1182f, 0x11838, 0x11839, 0x11930, 0x11931, 0x11937, 0x1193b, 0x1193d, 0x1193e, + 0x1193f, 0x11940, 0x11941, 0x11942, 0x11943, 0x119d1, 0x119d4, 0x119da, 0x119dc, 0x119e0, 0x119e4, 0x11a01, + 0x11a33, 0x11a39, 0x11a3a, 0x11a3b, 0x11a47, 0x11a51, 0x11a57, 0x11a59, 0x11a84, 0x11a8a, 0x11a97, 0x11a98, + 0x11c2f, 0x11c30, 0x11c38, 0x11c3e, 0x11c3f, 0x11c92, 0x11ca9, 0x11caa, 0x11cb1, 0x11cb2, 0x11cb4, 0x11cb5, + 0x11d31, 0x11d3a, 0x11d3c, 0x11d3f, 0x11d46, 0x11d47, 0x11d8a, 0x11d90, 0x11d93, 0x11d95, 0x11d96, 0x11d97, + 0x11ef3, 0x11ef5, 0x11f00, 0x11f02, 0x11f03, 0x11f34, 0x11f36, 0x11f3e, 0x11f40, 0x11f41, 0x11f42, 0x13430, + 0x13440, 0x13447, 0x16af0, 0x16b30, 0x16f4f, 0x16f51, 0x16f8f, 0x16fe4, 0x16ff0, 0x1bc9d, 0x1bca0, 0x1cf00, + 0x1cf30, 0x1d165, 0x1d166, 0x1d167, 0x1d16d, 0x1d16e, 0x1d173, 0x1d17b, 0x1d185, 0x1d1aa, 0x1d242, 0x1da00, + 0x1da3b, 0x1da75, 0x1da84, 0x1da9b, 0x1daa1, 0x1e000, 0x1e008, 0x1e01b, 0x1e023, 0x1e026, 0x1e08f, 0x1e130, + 0x1e2ae, 0x1e2ec, 0x1e4ec, 0x1e8d0, 0x1e944, 0x1f1e6, 0x1f3fb, 0xe0000, 0xe0020, 0xe0080, 0xe0100, 0xe01f0}, {0x100a, 0x4001, 0x1002, 0x1, 0x1012, 0x1021, 0x1001, 0x2070, 0x2007, 0x202d, 0x2001, 0x2002, 0x2002, 0x2001, 0x7006, 0x200b, 0x1001, 0x2015, 0x2001, 0x2007, 0x7001, 0x2006, 0x2002, 0x2004, 0x7001, 0x2001, 0x201b, 0x200b, 0x2009, 0x2001, 0x2004, 0x2009, 0x2003, 0x2005, 0x2003, 0x7002, 0x2008, 0x2018, 0x7001, 0x2020, 0x9001, 0x2001, @@ -249,97 +254,102 @@ inline constexpr _Unicode_property_data<_Grapheme_Break_property_values, 1355> _ 0x2001, 0x9002, 0x2001, 0x2002, 0x9001, 0x2004, 0x9002, 0x9002, 0x2001, 0x2003, 0x2002, 0x2001, 0x2001, 0x9001, 0x2001, 0x9002, 0x9003, 0x9003, 0x2001, 0x2001, 0x2001, 0x9003, 0x2001, 0x2001, 0x2003, 0x9004, 0x2003, 0x2004, 0x2002, 0x2002, 0x2001, 0x9002, 0x2001, 0x9001, 0x2001, 0x9002, 0x2001, 0x9002, 0x2001, 0x9002, 0x9002, 0x2002, - 0x2002, 0x2002, 0x2002, 0x9002, 0x2002, 0x2001, 0x9002, 0x2004, 0x9003, 0x9003, 0x2001, 0x7001, 0x2001, 0x2002, - 0x2001, 0x9002, 0x2001, 0x2001, 0x9002, 0x2003, 0x2001, 0x9007, 0x2001, 0x9002, 0x2001, 0x9001, 0x2007, 0x2008, - 0x2001, 0x9001, 0x2009, 0x2006, 0x2002, 0x2001, 0x2001, 0x2001, 0x9002, 0x200e, 0x9001, 0x2005, 0x2002, 0x200b, - 0x2024, 0x2001, 0x2004, 0x9001, 0x2006, 0x2002, 0x9002, 0x2002, 0x9002, 0x2002, 0x2003, 0x2004, 0x2001, 0x9001, - 0x2002, 0x2001, 0x2001, 0x3060, 0xb048, 0xa058, 0x2003, 0x2003, 0x9001, 0x2002, 0x9001, 0x2002, 0x2002, 0x2002, - 0x9001, 0x2007, 0x9008, 0x2001, 0x9002, 0x200b, 0x2001, 0x2003, 0x1001, 0x2001, 0x2002, 0x2001, 0x2003, 0x9004, - 0x2002, 0x9003, 0x9002, 0x2001, 0x9006, 0x2003, 0x2002, 0x9002, 0x2001, 0x9001, 0x2001, 0x9001, 0x2007, 0x2001, - 0x2001, 0x2008, 0x9006, 0x200a, 0x2001, 0x201f, 0x2004, 0x9001, 0x2007, 0x9001, 0x2001, 0x9005, 0x2001, 0x9002, - 0x2009, 0x2002, 0x9001, 0x9001, 0x2004, 0x9002, 0x2002, 0x9001, 0x2003, 0x2001, 0x9001, 0x2002, 0x9003, 0x2001, - 0x9001, 0x2003, 0x9002, 0x9008, 0x2008, 0x9002, 0x2002, 0x2003, 0x200d, 0x9001, 0x2007, 0x2001, 0x2001, 0x9001, - 0x2002, 0x2040, 0x1001, 0x2001, 0xc001, 0x1002, 0x1007, 0x1010, 0x2021, 0x2003, 0x2001, 0x2020, 0x2006, 0x2002, - 0x2004, 0x200a, 0x2002, 0x2002, 0x2001, 0x2001, 0x2001, 0x9002, 0x2002, 0x9001, 0x2001, 0x9002, 0x9010, 0x2002, - 0x2012, 0x2001, 0x2008, 0x200b, 0x9002, 0x301d, 0x2003, 0x9001, 0x2001, 0x9002, 0x2004, 0x9002, 0x2002, 0x9003, - 0x2001, 0x2006, 0x9002, 0x2002, 0x9002, 0x2002, 0x2001, 0x2001, 0x9001, 0x2001, 0x2001, 0x2003, 0x2002, 0x2002, - 0x2001, 0x9001, 0x2002, 0x9002, 0x9001, 0x2001, 0x9002, 0x2001, 0x9002, 0x2001, 0x9002, 0x9001, 0x2001, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, - 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0xb017, - 0xa031, 0x2001, 0x2010, 0x2010, 0x1001, 0x2002, 0x100c, 0x2001, 0x2001, 0x2005, 0x2003, 0x2002, 0x2004, 0x2003, - 0x2001, 0x2002, 0x2004, 0x2002, 0x200b, 0x2004, 0x9001, 0x2001, 0x9001, 0x200f, 0x2001, 0x2002, 0x2003, 0x9001, - 0x9003, 0x2004, 0x9002, 0x2002, 0x7001, 0x2001, 0x7001, 0x2003, 0x2005, 0x9001, 0x2008, 0x9002, 0x2001, 0x2002, - 0x9001, 0x9003, 0x2009, 0x9002, 0x7002, 0x2004, 0x9001, 0x2001, 0x9003, 0x2003, 0x9002, 0x2001, 0x9001, 0x2002, - 0x2001, 0x2001, 0x9003, 0x2008, 0x2002, 0x9002, 0x2002, 0x2001, 0x9001, 0x2001, 0x9004, 0x9002, 0x9003, 0x2001, - 0x9002, 0x2007, 0x2005, 0x9003, 0x2008, 0x9002, 0x2003, 0x9001, 0x2001, 0x2001, 0x2001, 0x9002, 0x2006, 0x9001, - 0x2001, 0x9002, 0x2001, 0x9001, 0x2002, 0x9001, 0x2002, 0x2001, 0x9002, 0x2004, 0x9004, 0x2002, 0x9001, 0x2002, - 0x2002, 0x9003, 0x2008, 0x9002, 0x2001, 0x9001, 0x2002, 0x2001, 0x9001, 0x2001, 0x9002, 0x2006, 0x9001, 0x2001, - 0x2003, 0x2004, 0x9001, 0x2005, 0x9003, 0x2009, 0x9001, 0x2002, 0x2001, 0x9005, 0x9002, 0x2002, 0x9001, 0x2001, - 0x7001, 0x9001, 0x7001, 0x9001, 0x2001, 0x9003, 0x2004, 0x2002, 0x9004, 0x2001, 0x9001, 0x200a, 0x2006, 0x9001, - 0x7001, 0x2004, 0x2001, 0x2006, 0x9002, 0x2003, 0x7006, 0x200d, 0x9001, 0x2002, 0x9001, 0x2007, 0x2006, 0x9001, - 0x2001, 0x2016, 0x9001, 0x2007, 0x9001, 0x2002, 0x9001, 0x2002, 0x2006, 0x2001, 0x2002, 0x2007, 0x7001, 0x2001, - 0x9005, 0x2002, 0x9002, 0x2001, 0x9001, 0x2001, 0x2002, 0x9002, 0x1009, 0x2005, 0x2007, 0x2001, 0x9037, 0x2004, + 0x2002, 0x2002, 0x9001, 0x2002, 0x9002, 0x2002, 0x2001, 0x9002, 0x2004, 0x9003, 0x9003, 0x2001, 0x7001, 0x2001, + 0x2002, 0x2001, 0x9002, 0x2001, 0x2001, 0x9002, 0x2003, 0x2001, 0x9007, 0x2001, 0x9002, 0x2001, 0x9001, 0x2007, + 0x2008, 0x2001, 0x9001, 0x2009, 0x2007, 0x2002, 0x2001, 0x2001, 0x2001, 0x9002, 0x200e, 0x9001, 0x2005, 0x2002, + 0x200b, 0x2024, 0x2001, 0x2004, 0x9001, 0x2006, 0x2002, 0x9002, 0x2002, 0x9002, 0x2002, 0x2003, 0x2004, 0x2001, + 0x9001, 0x2002, 0x2001, 0x2001, 0x3060, 0xb048, 0xa058, 0x2003, 0x2003, 0x9001, 0x2002, 0x9001, 0x2002, 0x2002, + 0x2002, 0x9001, 0x2007, 0x9008, 0x2001, 0x9002, 0x200b, 0x2001, 0x2003, 0x1001, 0x2001, 0x2002, 0x2001, 0x2003, + 0x9004, 0x2002, 0x9003, 0x9002, 0x2001, 0x9006, 0x2003, 0x2002, 0x9002, 0x2001, 0x9001, 0x2001, 0x9001, 0x2007, + 0x2001, 0x2001, 0x2008, 0x9006, 0x200a, 0x2001, 0x201f, 0x2004, 0x9001, 0x2007, 0x9001, 0x2001, 0x9005, 0x2001, + 0x9002, 0x2009, 0x2002, 0x9001, 0x9001, 0x2004, 0x9002, 0x2002, 0x9001, 0x2003, 0x2001, 0x9001, 0x2002, 0x9003, + 0x2001, 0x9001, 0x2003, 0x9002, 0x9008, 0x2008, 0x9002, 0x2002, 0x2003, 0x200d, 0x9001, 0x2007, 0x2001, 0x2001, + 0x9001, 0x2002, 0x2040, 0x1001, 0x2001, 0xc001, 0x1002, 0x1007, 0x1010, 0x2021, 0x2003, 0x2001, 0x2020, 0x2006, + 0x2002, 0x2004, 0x200a, 0x2002, 0x2002, 0x2001, 0x2001, 0x2001, 0x9002, 0x2002, 0x9001, 0x2001, 0x9002, 0x9010, + 0x2002, 0x2012, 0x2001, 0x2008, 0x200b, 0x9002, 0x301d, 0x2003, 0x9001, 0x2001, 0x9002, 0x2004, 0x9002, 0x2002, + 0x9003, 0x2001, 0x2006, 0x9002, 0x2002, 0x9002, 0x2002, 0x2001, 0x2001, 0x9001, 0x2001, 0x2001, 0x2003, 0x2002, + 0x2002, 0x2001, 0x9001, 0x2002, 0x9002, 0x9001, 0x2001, 0x9002, 0x2001, 0x9002, 0x2001, 0x9002, 0x9001, 0x2001, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, 0x5001, 0x601b, + 0xb017, 0xa031, 0x2001, 0x2010, 0x2010, 0x1001, 0x2002, 0x100c, 0x2001, 0x2001, 0x2005, 0x2003, 0x2002, 0x2004, + 0x2003, 0x2001, 0x2002, 0x2004, 0x2002, 0x2003, 0x200b, 0x2004, 0x9001, 0x2001, 0x9001, 0x200f, 0x2001, 0x2002, + 0x2003, 0x9001, 0x9003, 0x2004, 0x9002, 0x2002, 0x7001, 0x2001, 0x7001, 0x2003, 0x2005, 0x9001, 0x2008, 0x9002, + 0x2001, 0x2002, 0x9001, 0x9003, 0x2009, 0x9002, 0x7002, 0x2004, 0x9001, 0x2001, 0x9003, 0x2003, 0x9002, 0x2001, + 0x9001, 0x2002, 0x2001, 0x2001, 0x2001, 0x9003, 0x2008, 0x2002, 0x9002, 0x2002, 0x2001, 0x9001, 0x2001, 0x9004, + 0x9002, 0x9003, 0x2001, 0x9002, 0x2007, 0x2005, 0x9003, 0x2008, 0x9002, 0x2003, 0x9001, 0x2001, 0x2001, 0x2001, + 0x9002, 0x2006, 0x9001, 0x2001, 0x9002, 0x2001, 0x9001, 0x2002, 0x9001, 0x2002, 0x2001, 0x9002, 0x2004, 0x9004, + 0x2002, 0x9001, 0x2002, 0x2002, 0x9003, 0x2008, 0x9002, 0x2001, 0x9001, 0x2002, 0x2001, 0x9001, 0x2001, 0x9002, + 0x2006, 0x9001, 0x2001, 0x2003, 0x2004, 0x9001, 0x2005, 0x9003, 0x2009, 0x9001, 0x2002, 0x2001, 0x9005, 0x9002, + 0x2002, 0x9001, 0x2001, 0x7001, 0x9001, 0x7001, 0x9001, 0x2001, 0x9003, 0x2004, 0x2002, 0x9004, 0x2001, 0x9001, + 0x200a, 0x2006, 0x9001, 0x7001, 0x2004, 0x2001, 0x2006, 0x9002, 0x2003, 0x7006, 0x200d, 0x9001, 0x2002, 0x9001, + 0x2007, 0x2006, 0x9001, 0x2001, 0x2016, 0x9001, 0x2007, 0x9001, 0x2002, 0x9001, 0x2002, 0x2006, 0x2001, 0x2002, + 0x2007, 0x7001, 0x2001, 0x9005, 0x2002, 0x9002, 0x2001, 0x9001, 0x2001, 0x2002, 0x9002, 0x2002, 0x7001, 0x9001, + 0x9002, 0x2005, 0x9002, 0x2001, 0x9001, 0x2001, 0x1010, 0x2001, 0x200f, 0x2005, 0x2007, 0x2001, 0x9037, 0x2004, 0x2001, 0x9002, 0x2002, 0x1004, 0x202e, 0x2017, 0x2001, 0x9001, 0x2003, 0x9001, 0x2005, 0x1008, 0x2008, 0x2007, - 0x2004, 0x2003, 0x2037, 0x2032, 0x2001, 0x2001, 0x2005, 0x200f, 0x2007, 0x2011, 0x2007, 0x2002, 0x2005, 0x2007, - 0x2001, 0x2004, 0x2007, 0x2007, 0x801a, 0x2005, 0x1020, 0x2060, 0x1080, 0x20f0, 0x1e10}}; + 0x2004, 0x2003, 0x2037, 0x2032, 0x2001, 0x2001, 0x2005, 0x200f, 0x2007, 0x2011, 0x2007, 0x2002, 0x2005, 0x2001, + 0x2007, 0x2001, 0x2004, 0x2004, 0x2007, 0x2007, 0x801a, 0x2005, 0x1020, 0x2060, 0x1080, 0x20f0, 0x1e10}}; +// emoji-data.txt +// Date: 2022-08-02, 00:26:10 GMT enum class _Extended_Pictographic_property_values : uint8_t { _Extended_Pictographic_value, _No_value = 255 }; +// emoji-data.txt +// Date: 2022-08-02, 00:26:10 GMT inline constexpr _Unicode_property_data<_Extended_Pictographic_property_values, 78> _Extended_Pictographic_property_data{ {0xa9, 0xae, 0x203c, 0x2049, 0x2122, 0x2139, 0x2194, 0x21a9, 0x231a, 0x2328, 0x2388, 0x23cf, 0x23e9, 0x23f8, diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 03eeca0afb9..da3870dedf5 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -290,6 +290,7 @@ // P2609R3 Relaxing Ranges Just A Smidge // P2655R3 common_reference_t Of reference_wrapper Should Be A Reference Type // P2711R1 Making Multi-Param Constructors Of Views explicit +// P2736R2 Referencing The Unicode Standard // P2770R0 Stashing Stashing Iterators For Proper Flattening // _HAS_CXX20 indirectly controls: diff --git a/tests/std/tests/P0645R10_text_formatting_grapheme_clusterization/test.cpp b/tests/std/tests/P0645R10_text_formatting_grapheme_clusterization/test.cpp index 1d7c9073c2f..c93985f0955 100644 --- a/tests/std/tests/P0645R10_text_formatting_grapheme_clusterization/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_grapheme_clusterization/test.cpp @@ -15,6 +15,8 @@ using namespace std; // Generator: tools/unicode_properties_parse/grapheme_break_test_data_gen.py // Beginning of generated data - DO NOT EDIT manually! +// GraphemeBreakTest-15.0.0.txt +// Date: 2022-02-26, 00:38:37 GMT template struct test_case_data { vector code_points; diff --git a/tools/unicode_properties_parse/grapheme_break_property_data_gen.py b/tools/unicode_properties_parse/grapheme_break_property_data_gen.py index 1a029cb6477..6002a40f343 100644 --- a/tools/unicode_properties_parse/grapheme_break_property_data_gen.py +++ b/tools/unicode_properties_parse/grapheme_break_property_data_gen.py @@ -24,7 +24,7 @@ class PropertyTable: LINE_REGEX = re.compile( r"^(?P[0-9A-F]{4,5})(?:\.\.(?P[0-9A-F]{4,5}))?\s*;\s*(?P\w+)") -def parsePropertyLine(inputLine: str) -> Optional[PropertyRange]: +def parse_property_line(inputLine: str) -> Optional[PropertyRange]: result = PropertyRange() if m := LINE_REGEX.match(inputLine): lower_str, upper_str, result.prop = m.group("lower", "upper", "prop") @@ -37,7 +37,7 @@ def parsePropertyLine(inputLine: str) -> Optional[PropertyRange]: return None -def compactPropertyRanges(input: list[PropertyRange]) -> list[PropertyRange]: +def compact_property_ranges(input: list[PropertyRange]) -> list[PropertyRange]: """ Merges consecutive ranges with the same property to one range. @@ -59,6 +59,8 @@ def compactPropertyRanges(input: list[PropertyRange]) -> list[PropertyRange]: PROP_VALUE_ENUMERATOR_TEMPLATE = "_{}_value" PROP_VALUE_ENUM_TEMPLATE = """ +{filename} +{timestamp} enum class _{prop_name}_property_values : uint8_t {{ {enumerators}, _No_value = 255 @@ -66,6 +68,8 @@ def compactPropertyRanges(input: list[PropertyRange]) -> list[PropertyRange]: """ DATA_ARRAY_TEMPLATE = """ +{filename} +{timestamp} inline constexpr _Unicode_property_data<_{prop_name}_property_values, {size}> _{prop_name}_property_data{{ {{{lower_bounds}}}, {{{props_and_size}}} @@ -222,18 +226,18 @@ def property_ranges_to_table(ranges: list[PropertyRange], props: list[str]) -> P return result -def generate_cpp_data(prop_name: str, ranges: list[PropertyRange]) -> str: +def generate_cpp_data(filename: str, timestamp: str, prop_name: str, ranges: list[PropertyRange]) -> str: result = StringIO() prop_values = sorted(set(x.prop for x in ranges)) table = property_ranges_to_table(ranges, prop_values) enumerator_values = [PROP_VALUE_ENUMERATOR_TEMPLATE.format( x) for x in prop_values] result.write(PROP_VALUE_ENUM_TEMPLATE.lstrip().format( - prop_name=prop_name, enumerators=",".join(enumerator_values))) + filename=filename, timestamp=timestamp, prop_name=prop_name, enumerators=",".join(enumerator_values))) result.write("\n") - result.write(DATA_ARRAY_TEMPLATE.lstrip().format(prop_name=prop_name, size=len(table.lower_bounds), - lower_bounds=",".join(["0x" + format(x, 'x') - for x in table.lower_bounds]), + result.write(DATA_ARRAY_TEMPLATE.lstrip().format(filename=filename, timestamp=timestamp, prop_name=prop_name, + size=len(table.lower_bounds), + lower_bounds=",".join(["0x" + format(x, 'x') for x in table.lower_bounds]), props_and_size=",".join(["0x" + format(x, 'x') for x in table.props_and_range]))) return result.getvalue() @@ -244,24 +248,31 @@ def generate_data_tables() -> str: GraphemeBreakProperty.txt and emoji-data.txt. GraphemeBreakProperty.txt can be found at - https://www.unicode.org/Public/14.0.0/ucd/auxiliary/GraphemeBreakProperty.txt + https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt emoji-data.txt can be found at - https://www.unicode.org/Public/14.0.0/ucd/emoji/emoji-data.txt + https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt Both files are expected to be in the same directory as this script. """ - gbp_data_path = Path(__file__).absolute( - ).with_name("GraphemeBreakProperty.txt") + gbp_data_path = Path(__file__).absolute().with_name("GraphemeBreakProperty.txt") emoji_data_path = Path(__file__).absolute().with_name("emoji-data.txt") + gbp_filename = "" + gbp_timestamp = "" + emoji_filename = "" + emoji_timestamp = "" gbp_ranges = list() emoji_ranges = list() with gbp_data_path.open(encoding='utf-8') as f: - gbp_ranges = compactPropertyRanges([x for line in f if (x := parsePropertyLine(line))]) + gbp_filename = f.readline().replace("#", "//").rstrip() + gbp_timestamp = f.readline().replace("#", "//").rstrip() + gbp_ranges = compact_property_ranges([x for line in f if (x := parse_property_line(line))]) with emoji_data_path.open(encoding='utf-8') as f: - emoji_ranges = compactPropertyRanges([x for line in f if (x := parsePropertyLine(line))]) - gpb_cpp_data = generate_cpp_data("Grapheme_Break", gbp_ranges) - emoji_cpp_data = generate_cpp_data("Extended_Pictographic", [ + emoji_filename = f.readline().replace("#", "//").rstrip() + emoji_timestamp = f.readline().replace("#", "//").rstrip() + emoji_ranges = compact_property_ranges([x for line in f if (x := parse_property_line(line))]) + gpb_cpp_data = generate_cpp_data(gbp_filename, gbp_timestamp, "Grapheme_Break", gbp_ranges) + emoji_cpp_data = generate_cpp_data(emoji_filename, emoji_timestamp, "Extended_Pictographic", [ x for x in emoji_ranges if x.prop == "Extended_Pictographic"]) return "\n".join([gpb_cpp_data, emoji_cpp_data]) diff --git a/tools/unicode_properties_parse/grapheme_break_test_data_gen.py b/tools/unicode_properties_parse/grapheme_break_test_data_gen.py index 66394493174..c7f85bac602 100644 --- a/tools/unicode_properties_parse/grapheme_break_test_data_gen.py +++ b/tools/unicode_properties_parse/grapheme_break_test_data_gen.py @@ -21,7 +21,7 @@ class EOF: pass -def parseBreakTestLine(input: TextIO) -> Optional[BreakTestItem]: +def parse_break_test_line(input: TextIO) -> Optional[BreakTestItem]: result = BreakTestItem() while True: c = input.read(1) @@ -52,6 +52,8 @@ def parseBreakTestLine(input: TextIO) -> Optional[BreakTestItem]: cpp_template = """ +{0} +{1} template struct test_case_data {{ vector code_points; @@ -59,28 +61,28 @@ def parseBreakTestLine(input: TextIO) -> Optional[BreakTestItem]: }}; template -const test_case_data test_data[{0}]; +const test_case_data test_data[{2}]; template <> -const test_case_data test_data[{0}] = {{ - {1} +const test_case_data test_data[{2}] = {{ + {3} }}; template <> -const test_case_data test_data[{0}] = {{ - {2} +const test_case_data test_data[{2}] = {{ + {4} }}; """ cpp_test_data_line_template = "{{ {{{}}}, {{{}}} }}" -def lineToCppDataLineUtf32(line: BreakTestItem) -> str: +def line_to_cpp_data_line_utf32(line: BreakTestItem) -> str: return cpp_test_data_line_template.format(','.join( [f"U'\\x{x:x}'" for x in line.code_points]), ','.join( [str(x) for x in line.breaks])) -def lineToCppDataLineUtf8(line: BreakTestItem) -> str: +def line_to_cpp_data_line_utf8(line: BreakTestItem) -> str: utf8_rep = str(array('L', line.code_points), encoding='utf-32').encode('utf-8') return cpp_test_data_line_template.format(','.join( @@ -90,20 +92,22 @@ def lineToCppDataLineUtf8(line: BreakTestItem) -> str: """ Generate test data from "GraphemeBreakText.txt" -This file can be downloaded from: https://www.unicode.org/Public/14.0.0/ucd/auxiliary/GraphemeBreakTest.txt +This file can be downloaded from: https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt This script looks for GraphemeBreakTest.txt in same directory as this script """ def generate_all() -> str: - test_data_path = Path(__file__) - test_data_path = test_data_path.absolute() - test_data_path = test_data_path.with_name("GraphemeBreakTest.txt") + test_data_path = Path(__file__).absolute().with_name("GraphemeBreakTest.txt") + filename = "" + timestamp = "" lines = list() with open(test_data_path, mode='rt', encoding='utf-8') as file: - while line := parseBreakTestLine(file): + filename = file.readline().replace("#", "//").rstrip() + timestamp = file.readline().replace("#", "//").rstrip() + while line := parse_break_test_line(file): if len(line.code_points) > 0: lines.append(line) - return cpp_template.format(len(lines), ','.join(map(lineToCppDataLineUtf32, lines)), - ','.join(map(lineToCppDataLineUtf8, lines))) + return cpp_template.format(filename, timestamp, len(lines), ','.join(map(line_to_cpp_data_line_utf32, lines)), + ','.join(map(line_to_cpp_data_line_utf8, lines))) if __name__ == "__main__": From 5116678ea11245b232a6e43824aad25fb60b9c6e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 16 Mar 2023 19:59:08 -0700 Subject: [PATCH 3/9] `_Weakly_unwrappable` should use `_Allow_inheriting_unwrap_v` (#3566) --- stl/inc/xutility | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 4353b17e043..ee954bc9c69 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2287,9 +2287,8 @@ namespace ranges { using sentinel_t = decltype(_RANGES end(_STD declval<_Rng&>())); template - concept _Weakly_unwrappable = - same_as::_Prevent_inheriting_unwrap, remove_cvref_t<_Wrapped>> - && requires(_Wrapped&& _Wr) { _STD forward<_Wrapped>(_Wr)._Unwrapped(); }; + concept _Weakly_unwrappable = _Allow_inheriting_unwrap_v> + && requires(_Wrapped&& _Wr) { _STD forward<_Wrapped>(_Wr)._Unwrapped(); }; template concept _Weakly_unwrappable_sentinel = _Weakly_unwrappable&>; From 8a6ec5a5fccdc4562d228d4c5f4440e88aec9970 Mon Sep 17 00:00:00 2001 From: Tyler Nichols Date: Thu, 23 Mar 2023 18:27:55 -0700 Subject: [PATCH 4/9] P2093R14: Formatted Output (#3337) Co-authored-by: S. B. Tam Co-authored-by: Stephan T. Lavavej --- stl/CMakeLists.txt | 37 +- stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/__msvc_filebuf.hpp | 821 ++++++++++++++++++ stl/inc/__msvc_iter_core.hpp | 4 + stl/inc/__msvc_print.hpp | 75 ++ stl/inc/chrono | 15 - stl/inc/format | 48 + stl/inc/fstream | 785 +---------------- stl/inc/header-units.json | 3 + stl/inc/ostream | 212 +++++ stl/inc/print | 171 ++++ stl/inc/system_error | 24 + stl/inc/xutility | 4 - stl/inc/yvals_core.h | 3 + stl/modules/std.ixx | 1 + .../stl_base/stl.files.settings.targets | 1 + stl/src/msvcp_atomic_wait.src | 4 + stl/src/print.cpp | 274 ++++++ tests/std/include/temp_file_name.hpp | 21 + .../include/test_header_units_and_modules.hpp | 11 + tests/std/test.lst | 1 + .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../tests/P2093R14_formatted_output/env.lst | 7 + .../tests/P2093R14_formatted_output/test.cpp | 703 +++++++++++++++ .../test.compile.pass.cpp | 14 + .../include_each_header_alone_matrix.lst | 1 + 27 files changed, 2429 insertions(+), 814 deletions(-) create mode 100644 stl/inc/__msvc_filebuf.hpp create mode 100644 stl/inc/__msvc_print.hpp create mode 100644 stl/inc/print create mode 100644 stl/src/print.cpp create mode 100644 tests/std/include/temp_file_name.hpp create mode 100644 tests/std/tests/P2093R14_formatted_output/env.lst create mode 100644 tests/std/tests/P2093R14_formatted_output/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 4bc93c646ca..6fff5b44da2 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -9,9 +9,11 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_all_public_headers.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_chrono.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_cxx_stdatomic.hpp + ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_filebuf.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_format_ucd_tables.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_int128.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_iter_core.hpp + ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_print.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_sanitizer_annotate_container.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_system_error_abi.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_tzdb.hpp @@ -182,6 +184,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/numeric ${CMAKE_CURRENT_LIST_DIR}/inc/optional ${CMAKE_CURRENT_LIST_DIR}/inc/ostream + ${CMAKE_CURRENT_LIST_DIR}/inc/print ${CMAKE_CURRENT_LIST_DIR}/inc/queue ${CMAKE_CURRENT_LIST_DIR}/inc/random ${CMAKE_CURRENT_LIST_DIR}/inc/ranges @@ -285,6 +288,7 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/format.cpp ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/print.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp ${CMAKE_CURRENT_LIST_DIR}/src/stacktrace.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp @@ -529,6 +533,24 @@ function(target_stl_compile_options tgt rel_or_dbg) endif() endfunction() +function(generate_satellite_def SATELLITE_NAME D_SUFFIX) + set(full_satellite_name "msvcp140${D_SUFFIX}_${SATELLITE_NAME}${VCLIBS_SUFFIX}") + string(TOUPPER "${full_satellite_name}" upper_full_satellite_name) + set(satellite_input_src_file_path "${CMAKE_CURRENT_LIST_DIR}/src/msvcp_${SATELLITE_NAME}.src") + set(satellite_output_def_file_path "${CMAKE_BINARY_DIR}/msvcp_${SATELLITE_NAME}${D_SUFFIX}.def") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${satellite_input_src_file_path}") + + # We use the placeholder name "LIBRARYNAME" in the corresponding .src file of a satellite DLL + # (i.e., we write "LIBRARY LIBRARYNAME" as the first non-commented line in the file). + # + # Here, we dynamically replace this placeholder name with the name of the satellite DLL for + # the current build configuration. Then, we write out the new .def file to the binary output + # directory. + file(READ "${satellite_input_src_file_path}" satellite_def_file_contents) + string(REPLACE "LIBRARYNAME" "${upper_full_satellite_name}" satellite_def_file_contents "${satellite_def_file_contents}") + file(GENERATE OUTPUT "${satellite_output_def_file_path}" CONTENT "${satellite_def_file_contents}") +endfunction() + function(add_stl_dlls D_SUFFIX REL_OR_DBG) set(link_options_Release "/LTCG;/opt:ref,icf") set(link_options_Debug "/opt:ref,noicf") @@ -604,20 +626,13 @@ function(add_stl_dlls D_SUFFIX REL_OR_DBG) target_stl_compile_options(msvcp${D_SUFFIX}_atomic_wait_objects ${REL_OR_DBG}) # generate the .def for msvcp140_atomic_wait.dll - set(_ATOMIC_WAIT_OUTPUT_NAME "msvcp140${D_SUFFIX}_atomic_wait${VCLIBS_SUFFIX}") - string(TOUPPER "${_ATOMIC_WAIT_OUTPUT_NAME}" _ATOMIC_WAIT_OUTPUT_NAME_UPPER) - set(_ATOMIC_WAIT_DEF_NAME "${CMAKE_BINARY_DIR}/msvcp_atomic_wait${D_SUFFIX}.def") - set(_ATOMIC_WAIT_DEF_FILE_SRC "${CMAKE_CURRENT_LIST_DIR}/src/msvcp_atomic_wait.src") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_ATOMIC_WAIT_DEF_FILE_SRC}") - file(READ "${_ATOMIC_WAIT_DEF_FILE_SRC}" _ATOMIC_WAIT_SRC_CONTENTS) - string(REPLACE "LIBRARYNAME" "${_ATOMIC_WAIT_OUTPUT_NAME_UPPER}" _ATOMIC_WAIT_DEF_CONTENTS "${_ATOMIC_WAIT_SRC_CONTENTS}") - file(GENERATE OUTPUT "${_ATOMIC_WAIT_DEF_NAME}" CONTENT "${_ATOMIC_WAIT_DEF_CONTENTS}") - - add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") + generate_satellite_def("atomic_wait" "${D_SUFFIX}") + + add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${CMAKE_BINARY_DIR}/msvcp_atomic_wait${D_SUFFIX}.def") target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib" "advapi32.lib") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "${_ATOMIC_WAIT_OUTPUT_NAME}") + set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "msvcp140${D_SUFFIX}_atomic_wait${VCLIBS_SUFFIX}") target_link_options(msvcp${D_SUFFIX}_atomic_wait PRIVATE ${link_options_${REL_OR_DBG}}) # msvcp140_codecvt_ids.dll diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 1734fa07b26..7b63085c8ba 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -112,6 +112,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/__msvc_filebuf.hpp b/stl/inc/__msvc_filebuf.hpp new file mode 100644 index 00000000000..7e420846cfa --- /dev/null +++ b/stl/inc/__msvc_filebuf.hpp @@ -0,0 +1,821 @@ +// __msvc_filebuf.hpp internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef __MSVC_FILEBUF_HPP +#define __MSVC_FILEBUF_HPP +#include +#if _STL_COMPILER_PREPROCESSOR + +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +// TRANSITION, ABI: The _Path_ish functions accepting filesystem::path or experimental::filesystem::path are templates +// which always use the same types as a workaround for user code deriving from iostreams types and +// __declspec(dllexport)ing the derived types. Adding member functions to iostreams broke the ABI of such DLLs. +// Deriving and __declspec(dllexport)ing standard library types is not supported, but in this particular case +// the workaround was inexpensive. The workaround will be removed in the next ABI breaking release of the +// Visual C++ Libraries. +_STD_BEGIN +#if _HAS_CXX17 +namespace filesystem { + _EXPORT_STD class path; +} +#endif // _HAS_CXX17 + +#ifndef _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM +#ifdef _M_CEE +#define _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM 0 +#else // _M_CEE +#define _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM 1 +#endif // _M_CEE +#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM + +#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM +namespace experimental { + namespace filesystem { + inline namespace v1 { + class path; + } + } // namespace filesystem +} // namespace experimental +#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM + +// clang-format off +template +_INLINE_VAR constexpr bool _Is_any_path = _Is_any_of_v<_Ty +#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM + , experimental::filesystem::path +#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM +#if _HAS_CXX17 + , filesystem::path +#endif // _HAS_CXX17 + >; +// clang-format on + +extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const char*, ios_base::openmode, int); +extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const wchar_t*, ios_base::openmode, int); + +#ifdef _CRTBLD +extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const unsigned short*, ios_base::openmode, int); +#endif // _CRTBLD + +template +bool _Fgetc(_Elem& _Ch, FILE* _File) { // get an element from a C stream + return _CSTD fread(&_Ch, sizeof(_Elem), 1, _File) == 1; +} + +template <> +inline bool _Fgetc(char& _Byte, FILE* _File) { // get a char element from a C stream + int _Meta; + if ((_Meta = _CSTD fgetc(_File)) == EOF) { + return false; + } else { // got one, convert to char + _Byte = static_cast(_Meta); + return true; + } +} + +template <> +inline bool _Fgetc(wchar_t& _Wchar, FILE* _File) { // get a wchar_t element from a C stream + wint_t _Meta; + if ((_Meta = _CSTD fgetwc(_File)) == WEOF) { + return false; + } else { // got one, convert to wchar_t + _Wchar = static_cast(_Meta); + return true; + } +} + +#ifdef _CRTBLD +template <> +inline bool _Fgetc(unsigned short& _Wchar, FILE* _File) { // get an unsigned short element from a C stream + wint_t _Meta; + if ((_Meta = _CSTD fgetwc(_File)) == WEOF) { + return false; + } else { // got one, convert to unsigned short + _Wchar = static_cast(_Meta); + return true; + } +} +#endif // _CRTBLD + +template +bool _Fputc(_Elem _Ch, FILE* _File) { // put an element to a C stream + return _CSTD fwrite(&_Ch, 1, sizeof(_Elem), _File) == sizeof(_Elem); +} + +template <> +inline bool _Fputc(char _Byte, FILE* _File) { // put a char element to a C stream + return _CSTD fputc(_Byte, _File) != EOF; +} + +template <> +inline bool _Fputc(wchar_t _Wchar, FILE* _File) { // put a wchar_t element to a C stream + return _CSTD fputwc(_Wchar, _File) != WEOF; +} + +#ifdef _CRTBLD +template <> +inline bool _Fputc(unsigned short _Wchar, FILE* _File) { // put an unsigned short element to a C stream + return _CSTD fputwc(_Wchar, _File) != WEOF; +} +#endif // _CRTBLD + +template +bool _Ungetc(const _Elem&, FILE*) { // put back an arbitrary element to a C stream (always fail) + return false; +} + +template <> +inline bool _Ungetc(const char& _Byte, FILE* _File) { // put back a char element to a C stream + return _CSTD ungetc(static_cast(_Byte), _File) != EOF; +} + +template <> +inline bool _Ungetc(const signed char& _Byte, FILE* _File) { // put back a signed char element to a C stream + return _CSTD ungetc(static_cast(_Byte), _File) != EOF; +} + +template <> +inline bool _Ungetc(const unsigned char& _Byte, FILE* _File) { // put back an unsigned char element to a C stream + return _CSTD ungetc(_Byte, _File) != EOF; +} + +template <> +inline bool _Ungetc(const wchar_t& _Wchar, FILE* _File) { // put back a wchar_t element to a C stream + return _CSTD ungetwc(_Wchar, _File) != WEOF; +} + +#ifdef _CRTBLD +template <> +inline bool _Ungetc(const unsigned short& _Wchar, FILE* _File) { // put back an unsigned short element to a C stream + return _CSTD ungetwc(_Wchar, _File) != WEOF; +} +#endif // _CRTBLD + +_EXPORT_STD template +class basic_filebuf : public basic_streambuf<_Elem, _Traits> { // stream buffer associated with a C stream +public: + using _Mysb = basic_streambuf<_Elem, _Traits>; + using _Cvt = codecvt<_Elem, char, typename _Traits::state_type>; + + basic_filebuf() : _Mysb() { + _Init(nullptr, _Newfl); + } + + explicit basic_filebuf(FILE* const _File) : _Mysb() { // extension + _Init(_File, _Newfl); + } + + __CLR_OR_THIS_CALL ~basic_filebuf() noexcept override { + if (_Myfile) { + _Reset_back(); // revert from _Mychar buffer + } + + if (_Closef) { + close(); + } + } + + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + + basic_filebuf(_Uninitialized) noexcept : _Mysb(_Noinit) {} + + basic_filebuf(basic_filebuf&& _Right) { + _Init(_Right._Myfile, _Newfl); // match buffering styles + _Init(static_cast(nullptr), _Closefl); // then make *this look closed + _Assign_rv(_STD move(_Right)); + } + + basic_filebuf& operator=(basic_filebuf&& _Right) { + _Assign_rv(_STD move(_Right)); + return *this; + } + + void _Assign_rv(basic_filebuf&& _Right) { + if (this != _STD addressof(_Right)) { + close(); + this->swap(_Right); + } + } + + void swap(basic_filebuf& _Right) noexcept /* strengthened */ { + if (this != _STD addressof(_Right)) { + FILE* _Myfile_sav = _Myfile; + const _Cvt* _Pcvt_sav = _Pcvt; + typename _Traits::state_type _State_sav = _State; + bool _Wrotesome_sav = _Wrotesome; + bool _Closef_sav = _Closef; + bool _Set_eback_sav = _Mysb::eback() == &_Mychar; + bool _Set_eback_live = _Mysb::gptr() == &_Mychar; + + _Elem* _Pfirst0 = _Mysb::pbase(); + _Elem* _Pnext0 = _Mysb::pptr(); + _Elem* _Pend = _Mysb::epptr(); + _Elem* _Gfirst0 = _Mysb::eback(); + _Elem* _Gnext0 = _Mysb::gptr(); + _Elem* _Gend = _Mysb::egptr(); + + // reinitialize *this + _Init(_Right._Myfile, _Right._Myfile ? _Openfl : _Newfl); + _Mysb::setp(_Right.pbase(), _Right.pptr(), _Right.epptr()); + if (_Right.eback() != &_Right._Mychar) { + _Mysb::setg(_Right.eback(), _Right.gptr(), _Right.egptr()); + } else if (_Right.gptr() != &_Right._Mychar) { + _Mysb::setg(&_Mychar, &_Mychar + 1, &_Mychar + 1); + } else { + _Mysb::setg(&_Mychar, &_Mychar, &_Mychar + 1); + } + + _Pcvt = _Right._Pcvt; + _State = _Right._State; + _Wrotesome = _Right._Wrotesome; + _Closef = _Right._Closef; + + // reinitialize _Right + _Right._Init(_Myfile_sav, _Myfile_sav ? _Openfl : _Newfl); + _Right.setp(_Pfirst0, _Pnext0, _Pend); + if (!_Set_eback_sav) { + _Right.setg(_Gfirst0, _Gnext0, _Gend); + } else if (!_Set_eback_live) { + _Right.setg(&_Right._Mychar, &_Right._Mychar + 1, &_Right._Mychar + 1); + } else { + _Right.setg(&_Right._Mychar, &_Right._Mychar, &_Right._Mychar + 1); + } + + _Right._Pcvt = _Pcvt_sav; + _Right._State = _State_sav; + _Right._Wrotesome = _Wrotesome_sav; + _Right._Closef = _Closef_sav; + + // swap ancillary data + _STD swap(_Set_eback, _Right._Set_eback); + _STD swap(_Set_egptr, _Right._Set_egptr); + + _STD swap(_Mychar, _Right._Mychar); + _STD swap(_Mysb::_Plocale, _Right._Plocale); + } + } + + basic_filebuf(const basic_filebuf&) = delete; + basic_filebuf& operator=(const basic_filebuf&) = delete; + + enum _Initfl { // reasons for a call to _Init + _Newfl, + _Openfl, + _Closefl + }; + + _NODISCARD bool is_open() const noexcept /* strengthened */ { + return static_cast(_Myfile); + } + + basic_filebuf* open(const char* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // _Prot is an extension + if (_Myfile) { + return nullptr; + } + + const auto _File = _Fiopen(_Filename, _Mode, _Prot); + if (!_File) { + return nullptr; // open failed + } + + _Init(_File, _Openfl); + _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); + return this; // open succeeded + } + + basic_filebuf* open(const string& _Str, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // _Prot is an extension + return open(_Str.c_str(), _Mode, _Prot); + } + +#if _HAS_OLD_IOSTREAMS_MEMBERS + basic_filebuf* open(const char* _Filename, ios_base::open_mode _Mode) { + return open(_Filename, static_cast(_Mode)); + } +#endif // _HAS_OLD_IOSTREAMS_MEMBERS + + basic_filebuf* open(const wchar_t* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // in standard as const std::filesystem::path::value_type *; _Prot is an extension + if (_Myfile) { + return nullptr; + } + + const auto _File = _Fiopen(_Filename, _Mode, _Prot); + if (!_File) { + return nullptr; // open failed + } + + _Init(_File, _Openfl); + _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); + return this; // open succeeded + } + + basic_filebuf* open(const wstring& _Str, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // extension + return open(_Str.c_str(), _Mode, _Prot); + } + +#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM + template + basic_filebuf* open( + const _Identity_t<_Path_ish>& _Path, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // _Prot is an extension + return open(_Path.c_str(), _Mode, _Prot); + } +#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM + +#if _HAS_CXX17 + template + basic_filebuf* open( + const _Identity_t<_Path_ish>& _Path, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // _Prot is an extension + return open(_Path.c_str(), _Mode, _Prot); + } +#endif // _HAS_CXX17 + +#if _HAS_OLD_IOSTREAMS_MEMBERS + basic_filebuf* open(const wchar_t* _Filename, ios_base::open_mode _Mode) { + // in standard as const std::filesystem::path::value_type * + return open(_Filename, static_cast(_Mode)); + } +#endif // _HAS_OLD_IOSTREAMS_MEMBERS + +#ifdef _CRTBLD + basic_filebuf* open( + const unsigned short* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { + // in standard as const std::filesystem::path::value_type *; _Prot is an extension + if (_Myfile) { + return nullptr; + } + + const auto _File = _Fiopen(_Filename, _Mode, _Prot); + if (!_File) { + return nullptr; // open failed + } + + _Init(_File, _Openfl); + _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); + return this; // open succeeded + } + +#if _HAS_OLD_IOSTREAMS_MEMBERS + basic_filebuf* open(const unsigned short* _Filename, ios_base::open_mode _Mode) { + // in standard as const std::filesystem::path::value_type * + return open(_Filename, static_cast(_Mode)); + } +#endif // _HAS_OLD_IOSTREAMS_MEMBERS +#endif // _CRTBLD + + basic_filebuf* close() { + basic_filebuf* _Ans; + if (_Myfile) { // put any homing sequence and close file + _Reset_back(); // revert from _Mychar buffer + + _Ans = this; + if (!_Endwrite()) { + _Ans = nullptr; + } + + if (_CSTD fclose(_Myfile) != 0) { + _Ans = nullptr; + } + } else { + _Ans = nullptr; + } + + _Init(nullptr, _Closefl); + return _Ans; + } + + void __CLR_OR_THIS_CALL _Lock() override { // lock file instead of stream buffer + if (_Myfile) { + _CSTD _lock_file(_Myfile); + } + } + + void __CLR_OR_THIS_CALL _Unlock() override { // unlock file instead of stream buffer + if (_Myfile) { + _CSTD _unlock_file(_Myfile); + } + } + +#if defined(__cpp_lib_print) && defined(_CPPRTTI) + template + friend ios_base::iostate _Print_noformat_unicode(ostream&, string_view); +#endif + +protected: + int_type __CLR_OR_THIS_CALL overflow(int_type _Meta = _Traits::eof()) override { // put an element to stream + if (_Traits::eq_int_type(_Traits::eof(), _Meta)) { + return _Traits::not_eof(_Meta); // EOF, return success code + } + + if (_Mysb::pptr() && _Mysb::pptr() < _Mysb::epptr()) { // room in buffer, store it + *_Mysb::_Pninc() = _Traits::to_char_type(_Meta); + return _Meta; + } + + if (!_Myfile) { + return _Traits::eof(); // no open C stream, fail + } + + _Reset_back(); // revert from _Mychar buffer + if (!_Pcvt) { // no codecvt facet, put as is + return _Fputc(_Traits::to_char_type(_Meta), _Myfile) ? _Meta : _Traits::eof(); + } + + // put using codecvt facet + constexpr size_t _Codecvt_temp_buf = 32; + char _Str[_Codecvt_temp_buf]; + const _Elem _Ch = _Traits::to_char_type(_Meta); + const _Elem* _Src; + char* _Dest; + + // test result of converting one element + switch (_Pcvt->out(_State, &_Ch, &_Ch + 1, _Src, _Str, _Str + _Codecvt_temp_buf, _Dest)) { + case codecvt_base::partial: + case codecvt_base::ok: + { // converted something, try to put it out + const auto _Count = static_cast(_Dest - _Str); + if (0 < _Count && _Count != static_cast(_CSTD fwrite(_Str, 1, _Count, _Myfile))) { + return _Traits::eof(); // write failed + } + + _Wrotesome = true; // write succeeded + if (_Src != &_Ch) { + return _Meta; // converted whole element + } + + return _Traits::eof(); // conversion failed + } + + case codecvt_base::noconv: + // no conversion, put as is + return _Fputc(_Ch, _Myfile) ? _Meta : _Traits::eof(); + + default: + return _Traits::eof(); // conversion failed + } + } + + int_type __CLR_OR_THIS_CALL pbackfail(int_type _Meta = _Traits::eof()) override { + // put an element back to stream + if (_Mysb::gptr() && _Mysb::eback() < _Mysb::gptr() + && (_Traits::eq_int_type(_Traits::eof(), _Meta) + || _Traits::eq_int_type(_Traits::to_int_type(_Mysb::gptr()[-1]), + _Meta))) { // just back up position + _Mysb::_Gndec(); + return _Traits::not_eof(_Meta); + } else if (!_Myfile || _Traits::eq_int_type(_Traits::eof(), _Meta)) { + return _Traits::eof(); // no open C stream or EOF, fail + } else if (!_Pcvt && _Ungetc(_Traits::to_char_type(_Meta), _Myfile)) { + return _Meta; // no facet and unget succeeded, return + } else if (_Mysb::gptr() != &_Mychar) { // putback to _Mychar + _Mychar = _Traits::to_char_type(_Meta); + _Set_back(); // switch to _Mychar buffer + return _Meta; + } else { + return _Traits::eof(); // nowhere to put back + } + } + + int_type __CLR_OR_THIS_CALL underflow() override { // get an element from stream, but don't point past it + int_type _Meta; + if (_Mysb::gptr() && _Mysb::gptr() < _Mysb::egptr()) { + return _Traits::to_int_type(*_Mysb::gptr()); // return buffered + } else if (_Traits::eq_int_type(_Traits::eof(), _Meta = uflow())) { + return _Meta; // uflow failed, return EOF + } else { // get a char, don't point past it + pbackfail(_Meta); + return _Meta; + } + } + + int_type __CLR_OR_THIS_CALL uflow() override { // get an element from stream, point past it + if (_Mysb::gptr() && _Mysb::gptr() < _Mysb::egptr()) { + return _Traits::to_int_type(*_Mysb::_Gninc()); // return buffered + } + + if (!_Myfile) { + return _Traits::eof(); // no open C stream, fail + } + + _Reset_back(); // revert from _Mychar buffer + if (!_Pcvt) { // no codecvt facet, just get it + _Elem _Ch; + return _Fgetc(_Ch, _Myfile) ? _Traits::to_int_type(_Ch) : _Traits::eof(); + } + + // build string until codecvt succeeds + string _Str; + + for (;;) { // get using codecvt facet + const char* _Src; + int _Meta = _CSTD fgetc(_Myfile); + + if (_Meta == EOF) { + return _Traits::eof(); // partial char? + } + + _Str.push_back(static_cast(_Meta)); // append byte and convert + + _Elem _Ch; + _Elem* _Dest; + + // test result of converting one element + switch (_Pcvt->in(_State, _Str.data(), _Str.data() + _Str.size(), _Src, &_Ch, &_Ch + 1, _Dest)) { + case codecvt_base::partial: + case codecvt_base::ok: + if (_Dest != &_Ch) { // got an element, put back excess and deliver it + auto _Nleft = _Str.data() + _Str.size() - _Src; + while (0 < _Nleft) { + _CSTD ungetc(_Src[--_Nleft], _Myfile); + } + + return _Traits::to_int_type(_Ch); + } + + _Str.erase(0, static_cast(_Src - _Str.data())); // partial, discard used input + break; + + case codecvt_base::noconv: + // noconv is only possible if _Elem is char, so we can use it directly + return static_cast(_Str.front()); + + default: + return _Traits::eof(); // conversion failed + } + } + } + + streamsize __CLR_OR_THIS_CALL xsgetn(_Elem* _Ptr, streamsize _Count) override { + // get _Count characters from stream + if constexpr (sizeof(_Elem) == 1) { + if (_Count <= 0) { + return 0; + } + + if (_Pcvt) { // if we need a nontrivial codecvt transform, do the default expensive thing + return _Mysb::xsgetn(_Ptr, _Count); + } + + // assuming this is OK because _Ptr + _Count must be valid + auto _Count_s = static_cast(_Count); + const auto _Start_count = _Count; + const auto _Available = static_cast(_Mysb::_Gnavail()); + if (0 < _Available) { // copy from get area + const auto _Read_size = (_STD min)(_Count_s, _Available); + _Traits::copy(_Ptr, _Mysb::gptr(), _Read_size); + _Ptr += _Read_size; + _Count_s -= _Read_size; + _Mysb::gbump(static_cast(_Read_size)); + } + + if (_Myfile) { // open C stream, attempt read + _Reset_back(); // revert from _Mychar buffer + // process in 4k - 1 chunks to avoid tripping over fread's clobber-the-end behavior when + // doing \r\n -> \n translation + constexpr size_t _Read_size = 4095; // _INTERNAL_BUFSIZ - 1 + while (_Read_size < _Count_s) { + const auto _Actual_read = _CSTD fread(_Ptr, sizeof(_Elem), _Read_size, _Myfile); + _Ptr += _Actual_read; + _Count_s -= _Actual_read; + if (_Actual_read != _Read_size) { + return static_cast(_Start_count - _Count_s); + } + } + + if (0 < _Count_s) { + _Count_s -= _CSTD fread(_Ptr, sizeof(_Elem), _Count_s, _Myfile); + } + } + + return static_cast(_Start_count - _Count_s); + } else { // non-chars always get element-by-element processing + return _Mysb::xsgetn(_Ptr, _Count); + } + } + + streamsize __CLR_OR_THIS_CALL xsputn(const _Elem* _Ptr, streamsize _Count) override { + // put _Count characters to stream + if constexpr (sizeof(_Elem) == 1) { + if (_Pcvt) { // if we need a nontrivial codecvt transform, do the default expensive thing + return _Mysb::xsputn(_Ptr, _Count); + } + + const streamsize _Start_count = _Count; + streamsize _Size = _Mysb::_Pnavail(); + if (0 < _Count && 0 < _Size) { // copy to write buffer + if (_Count < _Size) { + _Size = _Count; + } + + _Traits::copy(_Mysb::pptr(), _Ptr, static_cast(_Size)); + _Ptr += _Size; + _Count -= _Size; + _Mysb::pbump(static_cast(_Size)); + } + + if (0 < _Count && _Myfile) { // open C stream, attempt write + _Count -= _CSTD fwrite(_Ptr, sizeof(_Elem), static_cast(_Count), _Myfile); + } + + return _Start_count - _Count; + } else { // non-chars always get element-by-element processing + return _Mysb::xsputn(_Ptr, _Count); + } + } + + pos_type __CLR_OR_THIS_CALL seekoff(off_type _Off, ios_base::seekdir _Way, + ios_base::openmode = ios_base::in | ios_base::out) override { // change position by _Off + fpos_t _Fileposition; + + if (_Mysb::gptr() == &_Mychar // something putback + && _Way == ios_base::cur // a relative seek + && !_Pcvt) { // not converting + _Off -= static_cast(sizeof(_Elem)); // back up over _Elem bytes + } + + if (!_Myfile || !_Endwrite() + || ((_Off != 0 || _Way != ios_base::cur) && _CSTD _fseeki64(_Myfile, _Off, _Way) != 0) + || _CSTD fgetpos(_Myfile, &_Fileposition) != 0) { + return pos_type{off_type{-1}}; // report failure + } + + _Reset_back(); // revert from _Mychar buffer, discarding any putback + return pos_type{_State, _Fileposition}; // return new position + } + + pos_type __CLR_OR_THIS_CALL seekpos(pos_type _Pos, ios_base::openmode = ios_base::in | ios_base::out) override { + // change position to _Pos + off_type _Off = static_cast(_Pos); + + if (!_Myfile || !_Endwrite() || _CSTD fsetpos(_Myfile, &_Off) != 0) { + return pos_type{off_type{-1}}; // report failure + } + + _State = _Pos.state(); + _Reset_back(); // revert from _Mychar buffer, discarding any putback + return pos_type{_State, _Off}; // return new position + } + + _Mysb* __CLR_OR_THIS_CALL setbuf(_Elem* _Buffer, streamsize _Count) override { // offer _Buffer to C stream + int _Mode; + if (!_Buffer && _Count == 0) { + _Mode = _IONBF; + } else { + _Mode = _IOFBF; + } + + const size_t _Size = static_cast(_Count) * sizeof(_Elem); + + if (!_Myfile || _CSTD setvbuf(_Myfile, reinterpret_cast(_Buffer), _Mode, _Size) != 0) { + return nullptr; // failed + } + + // new buffer, reinitialize pointers + _Init(_Myfile, _Openfl); + return this; + } + + int __CLR_OR_THIS_CALL sync() override { // synchronize C stream with external file + if (!_Myfile || _Traits::eq_int_type(_Traits::eof(), overflow()) || 0 <= _CSTD fflush(_Myfile)) { + return 0; + } + + return -1; + } + + void __CLR_OR_THIS_CALL imbue(const locale& _Loc) override { + // set locale to argument (capture nontrivial codecvt facet) + _Initcvt(_STD use_facet<_Cvt>(_Loc)); + } + + void _Init(FILE* _File, _Initfl _Which) noexcept { // initialize to C stream _File after {new, open, close} + using _State_type = typename _Traits::state_type; + + __PURE_APPDOMAIN_GLOBAL static _State_type _Stinit; // initial state + + _Closef = _Which == _Openfl; + _Wrotesome = false; + + _Mysb::_Init(); // initialize stream buffer base object + + if (_File && sizeof(_Elem) == 1) { // point inside C stream with [first, first + count) buffer + _Elem** _Pb = nullptr; + _Elem** _Pn = nullptr; + int* _Nr = nullptr; + + ::_get_stream_buffer_pointers( + _File, reinterpret_cast(&_Pb), reinterpret_cast(&_Pn), &_Nr); + int* _Nw = _Nr; + + _Mysb::_Init(_Pb, _Pn, _Nr, _Pb, _Pn, _Nw); + } + + _Myfile = _File; + _State = _Stinit; + _Pcvt = nullptr; // pointer to codecvt facet + } + + bool _Endwrite() { // put shift to initial conversion state, as needed + if (!_Pcvt || !_Wrotesome) { + return true; + } + + // may have to put + if (_Traits::eq_int_type(_Traits::eof(), overflow())) { + return false; + } + + constexpr size_t _Codecvt_temp_buf = 32; + char _Str[_Codecvt_temp_buf]; + char* _Dest; + switch (_Pcvt->unshift(_State, _Str, _Str + _Codecvt_temp_buf, _Dest)) { // test result of homing conversion + case codecvt_base::ok: + _Wrotesome = false; // homed successfully + _FALLTHROUGH; + + case codecvt_base::partial: + { // put any generated bytes + const auto _Count = static_cast(_Dest - _Str); + if (0 < _Count && _Count != static_cast(_CSTD fwrite(_Str, 1, _Count, _Myfile))) { + return false; // write failed + } + + return !_Wrotesome; + } + + case codecvt_base::noconv: + _Wrotesome = false; // homed successfully + return true; // nothing else to do + + default: + return false; // conversion failed + } + } + + void _Initcvt(const _Cvt& _Newcvt) noexcept { // initialize codecvt pointer + if (_Newcvt.always_noconv()) { + _Pcvt = nullptr; // nothing to do + } else { // set up for nontrivial codecvt facet + _Pcvt = _STD addressof(_Newcvt); + _Mysb::_Init(); // reset any buffering + } + } + +private: + const _Cvt* _Pcvt; // pointer to codecvt facet (may be null) + _Elem _Mychar; // putback character, when _Ungetc fails + bool _Wrotesome; // true if homing sequence may be needed + typename _Traits::state_type _State; // current conversion state + bool _Closef; // true if C stream must be closed + FILE* _Myfile; // pointer to C stream + + void _Reset_back() noexcept { // restore buffer after putback + if (_Mysb::eback() == &_Mychar) { + _Mysb::setg(_Set_eback, _Set_eback, _Set_egptr); + } + } + + void _Set_back() noexcept { // set up putback area + if (_Mysb::eback() != &_Mychar) { // save current get buffer + _Set_eback = _Mysb::eback(); + _Set_egptr = _Mysb::egptr(); + } + _Mysb::setg(&_Mychar, &_Mychar, &_Mychar + 1); + } + + _Elem* _Set_eback; // saves eback() during one-element putback + _Elem* _Set_egptr; // saves egptr() +}; + +_EXPORT_STD template +void swap(basic_filebuf<_Elem, _Traits>& _Left, basic_filebuf<_Elem, _Traits>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // __MSVC_FILEBUF_HPP diff --git a/stl/inc/__msvc_iter_core.hpp b/stl/inc/__msvc_iter_core.hpp index aeacf38ca31..e94bd7183e6 100644 --- a/stl/inc/__msvc_iter_core.hpp +++ b/stl/inc/__msvc_iter_core.hpp @@ -410,6 +410,10 @@ concept sized_sentinel_for = sentinel_for<_Se, _It> }; // clang-format on +_EXPORT_STD struct default_sentinel_t {}; + +_EXPORT_STD inline constexpr default_sentinel_t default_sentinel{}; + namespace ranges { _EXPORT_STD enum class subrange_kind : bool { unsized, sized }; diff --git a/stl/inc/__msvc_print.hpp b/stl/inc/__msvc_print.hpp new file mode 100644 index 00000000000..9168dac9b17 --- /dev/null +++ b/stl/inc/__msvc_print.hpp @@ -0,0 +1,75 @@ +// __msvc_print.hpp internal header (core) + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef __MSVC_PRINT_HPP +#define __MSVC_PRINT_HPP +#include +#if _STL_COMPILER_PREPROCESSOR + +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_EXTERN_C + +enum class __std_unicode_console_handle : intptr_t { _Invalid = -1 }; + +struct __std_unicode_console_retrieval_result { + __std_unicode_console_handle _Console_handle; + + // For this, we have a few potential return values: + // + // - __std_win_error::_Success: The operation completed successfully. This is the only value for which the + // _Console_handle field has a well-defined value. + // + // - __std_win_error::_File_not_found: The FILE* provided is valid, but it is determined to not be associated + // with a unicode console. In this case, printing should fall back to vprint_nonunicode(). + // + // - __std_win_error::_Not_supported: The FILE* provided does not actually have an associated output stream. In + // this case, the entire print can safely be elided, thanks to the "as-if" rule. + // (We haven't observed this happening in practice. Console applications with stdout redirected to NUL + // and Windows applications both appear to activate the __std_win_error::_File_not_found "valid, but + // not a unicode console" codepath.) + // + // - __std_win_error::_Invalid_parameter: The FILE* provided is invalid. A std::system_error exception should be + // thrown if this value is returned within the FILE* overload of vprint_unicode(). + __std_win_error _Error; +}; + +_NODISCARD _Success_(return._Error == __std_win_error::_Success) __std_unicode_console_retrieval_result + __stdcall __std_get_unicode_console_handle_from_file_stream(_In_ FILE* _Stream) noexcept; + +_NODISCARD _Success_(return == __std_win_error::_Success) __std_win_error + __stdcall __std_print_to_unicode_console(_In_ __std_unicode_console_handle _Console_handle, + _In_reads_(_Str_size) const char* _Str, _In_ size_t _Str_size) noexcept; + +_END_EXTERN_C + +_STD_BEGIN + +_NODISCARD consteval bool _Is_ordinary_literal_encoding_utf8() { + // See: https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers +#if defined(_MSVC_EXECUTION_CHARACTER_SET) && _MSVC_EXECUTION_CHARACTER_SET == 65001 // Unicode (UTF-8) == 65001 + return true; +#else + return false; +#endif +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // __MSVC_PRINT_HPP diff --git a/stl/inc/chrono b/stl/inc/chrono index e6bfa99bb77..c81b284dbed 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -49,21 +49,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #if _HAS_CXX17 -// We would really love to use the proper way of building error_code by specializing -// is_error_code_enum and make_error_code for __std_win_error, but because: -// 1. We would like to keep the definition of __std_win_error in xfilesystem_abi.h -// 2. and xfilesystem_abi.h cannot include -// 3. and specialization of is_error_code_enum and overload of make_error_code -// need to be kept together with the enum (see limerick in N4810 [temp.expl.spec]/7) -// we resort to using this _Make_ec helper. -_NODISCARD inline error_code _Make_ec(__std_win_error _Errno) noexcept { // make an error_code - return {static_cast(_Errno), _STD system_category()}; -} - -[[noreturn]] inline void _Throw_system_error_from_std_win_error(const __std_win_error _Errno) { - _THROW(system_error{_Make_ec(_Errno)}); -} - _NODISCARD inline int _Check_convert_result(const __std_fs_convert_result _Result) { if (_Result._Err != __std_win_error::_Success) { _Throw_system_error_from_std_win_error(_Result._Err); diff --git a/stl/inc/format b/stl/inc/format index 8c5eec979cb..9c0121b7fe3 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3614,6 +3614,54 @@ _NODISCARD size_t formatted_size(const locale& _Loc, const wformat_string<_Types return _Buf._Count(); } +#if _HAS_CXX23 +enum class _Add_newline : bool { _Nope, _Yes }; + +_NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const string_view _Old_str) { + string _Unescaped_str; + + if (_Old_str.empty()) { + if (_Add_nl == _Add_newline::_Yes) { + _Unescaped_str.push_back('\n'); + } + + return _Unescaped_str; + } + + size_t _Unescaped_str_expected_size = _Old_str.size(); + + if (_Add_nl == _Add_newline::_Yes) { + ++_Unescaped_str_expected_size; + } + + _Unescaped_str.resize_and_overwrite(_Unescaped_str_expected_size, [_Add_nl, _Old_str](char* _Dest_ptr, size_t) { + char _Prev_char = _Old_str.front(); + size_t _Num_chars_written = 1; + *_Dest_ptr++ = _Prev_char; + + for (const auto _Curr_char : _Old_str.substr(1)) { + if ((_Curr_char == '{' && _Prev_char == '{') || (_Curr_char == '}' && _Prev_char == '}')) { + _Prev_char = '\0'; + } else { + *_Dest_ptr++ = _Curr_char; + ++_Num_chars_written; + + _Prev_char = _Curr_char; + } + } + + if (_Add_nl == _Add_newline::_Yes) { + *_Dest_ptr = '\n'; + ++_Num_chars_written; + } + + return _Num_chars_written; + }); + + return _Unescaped_str; +} +#endif // _HAS_CXX23 + _STD_END #pragma pop_macro("new") diff --git a/stl/inc/fstream b/stl/inc/fstream index ecf2b7509b3..6d19f9800eb 100644 --- a/stl/inc/fstream +++ b/stl/inc/fstream @@ -8,6 +8,7 @@ #define _FSTREAM_ #include #if _STL_COMPILER_PREPROCESSOR +#include <__msvc_filebuf.hpp> #include #pragma pack(push, _CRT_PACKING) @@ -17,791 +18,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -// TRANSITION, ABI: The _Path_ish functions accepting filesystem::path or experimental::filesystem::path are templates -// which always use the same types as a workaround for user code deriving from iostreams types and -// __declspec(dllexport)ing the derived types. Adding member functions to iostreams broke the ABI of such DLLs. -// Deriving and __declspec(dllexport)ing standard library types is not supported, but in this particular case -// the workaround was inexpensive. The workaround will be removed in the next ABI breaking release of the -// Visual C++ Libraries. _STD_BEGIN -#if _HAS_CXX17 -namespace filesystem { - _EXPORT_STD class path; -} -#endif // _HAS_CXX17 - -#ifndef _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM -#ifdef _M_CEE -#define _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM 0 -#else // _M_CEE -#define _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM 1 -#endif // _M_CEE -#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM - -#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM -namespace experimental { - namespace filesystem { - inline namespace v1 { - class path; - } - } // namespace filesystem -} // namespace experimental -#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM - -// clang-format off -template -_INLINE_VAR constexpr bool _Is_any_path = _Is_any_of_v<_Ty -#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM - , experimental::filesystem::path -#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM -#if _HAS_CXX17 - , filesystem::path -#endif // _HAS_CXX17 - >; -// clang-format on - -extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const char*, ios_base::openmode, int); -extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const wchar_t*, ios_base::openmode, int); - -#ifdef _CRTBLD -extern "C++" _CRTIMP2_PURE FILE* __CLRCALL_PURE_OR_CDECL _Fiopen(const unsigned short*, ios_base::openmode, int); -#endif // _CRTBLD - -template -bool _Fgetc(_Elem& _Ch, FILE* _File) { // get an element from a C stream - return _CSTD fread(&_Ch, sizeof(_Elem), 1, _File) == 1; -} - -template <> -inline bool _Fgetc(char& _Byte, FILE* _File) { // get a char element from a C stream - int _Meta; - if ((_Meta = _CSTD fgetc(_File)) == EOF) { - return false; - } else { // got one, convert to char - _Byte = static_cast(_Meta); - return true; - } -} - -template <> -inline bool _Fgetc(wchar_t& _Wchar, FILE* _File) { // get a wchar_t element from a C stream - wint_t _Meta; - if ((_Meta = _CSTD fgetwc(_File)) == WEOF) { - return false; - } else { // got one, convert to wchar_t - _Wchar = static_cast(_Meta); - return true; - } -} - -#ifdef _CRTBLD -template <> -inline bool _Fgetc(unsigned short& _Wchar, FILE* _File) { // get an unsigned short element from a C stream - wint_t _Meta; - if ((_Meta = _CSTD fgetwc(_File)) == WEOF) { - return false; - } else { // got one, convert to unsigned short - _Wchar = static_cast(_Meta); - return true; - } -} -#endif // _CRTBLD - -template -bool _Fputc(_Elem _Ch, FILE* _File) { // put an element to a C stream - return _CSTD fwrite(&_Ch, 1, sizeof(_Elem), _File) == sizeof(_Elem); -} - -template <> -inline bool _Fputc(char _Byte, FILE* _File) { // put a char element to a C stream - return _CSTD fputc(_Byte, _File) != EOF; -} - -template <> -inline bool _Fputc(wchar_t _Wchar, FILE* _File) { // put a wchar_t element to a C stream - return _CSTD fputwc(_Wchar, _File) != WEOF; -} - -#ifdef _CRTBLD -template <> -inline bool _Fputc(unsigned short _Wchar, FILE* _File) { // put an unsigned short element to a C stream - return _CSTD fputwc(_Wchar, _File) != WEOF; -} -#endif // _CRTBLD - -template -bool _Ungetc(const _Elem&, FILE*) { // put back an arbitrary element to a C stream (always fail) - return false; -} - -template <> -inline bool _Ungetc(const char& _Byte, FILE* _File) { // put back a char element to a C stream - return _CSTD ungetc(static_cast(_Byte), _File) != EOF; -} - -template <> -inline bool _Ungetc(const signed char& _Byte, FILE* _File) { // put back a signed char element to a C stream - return _CSTD ungetc(static_cast(_Byte), _File) != EOF; -} - -template <> -inline bool _Ungetc(const unsigned char& _Byte, FILE* _File) { // put back an unsigned char element to a C stream - return _CSTD ungetc(_Byte, _File) != EOF; -} - -template <> -inline bool _Ungetc(const wchar_t& _Wchar, FILE* _File) { // put back a wchar_t element to a C stream - return _CSTD ungetwc(_Wchar, _File) != WEOF; -} - -#ifdef _CRTBLD -template <> -inline bool _Ungetc(const unsigned short& _Wchar, FILE* _File) { // put back an unsigned short element to a C stream - return _CSTD ungetwc(_Wchar, _File) != WEOF; -} -#endif // _CRTBLD - -_EXPORT_STD template -class basic_filebuf : public basic_streambuf<_Elem, _Traits> { // stream buffer associated with a C stream -public: - using _Mysb = basic_streambuf<_Elem, _Traits>; - using _Cvt = codecvt<_Elem, char, typename _Traits::state_type>; - - basic_filebuf() : _Mysb() { - _Init(nullptr, _Newfl); - } - - explicit basic_filebuf(FILE* const _File) : _Mysb() { // extension - _Init(_File, _Newfl); - } - - __CLR_OR_THIS_CALL ~basic_filebuf() noexcept override { - if (_Myfile) { - _Reset_back(); // revert from _Mychar buffer - } - - if (_Closef) { - close(); - } - } - - using int_type = typename _Traits::int_type; - using pos_type = typename _Traits::pos_type; - using off_type = typename _Traits::off_type; - - basic_filebuf(_Uninitialized) noexcept : _Mysb(_Noinit) {} - - basic_filebuf(basic_filebuf&& _Right) { - _Init(_Right._Myfile, _Newfl); // match buffering styles - _Init(static_cast(nullptr), _Closefl); // then make *this look closed - _Assign_rv(_STD move(_Right)); - } - - basic_filebuf& operator=(basic_filebuf&& _Right) { - _Assign_rv(_STD move(_Right)); - return *this; - } - - void _Assign_rv(basic_filebuf&& _Right) { - if (this != _STD addressof(_Right)) { - close(); - this->swap(_Right); - } - } - - void swap(basic_filebuf& _Right) noexcept /* strengthened */ { - if (this != _STD addressof(_Right)) { - FILE* _Myfile_sav = _Myfile; - const _Cvt* _Pcvt_sav = _Pcvt; - typename _Traits::state_type _State_sav = _State; - bool _Wrotesome_sav = _Wrotesome; - bool _Closef_sav = _Closef; - bool _Set_eback_sav = _Mysb::eback() == &_Mychar; - bool _Set_eback_live = _Mysb::gptr() == &_Mychar; - - _Elem* _Pfirst0 = _Mysb::pbase(); - _Elem* _Pnext0 = _Mysb::pptr(); - _Elem* _Pend = _Mysb::epptr(); - _Elem* _Gfirst0 = _Mysb::eback(); - _Elem* _Gnext0 = _Mysb::gptr(); - _Elem* _Gend = _Mysb::egptr(); - - // reinitialize *this - _Init(_Right._Myfile, _Right._Myfile ? _Openfl : _Newfl); - _Mysb::setp(_Right.pbase(), _Right.pptr(), _Right.epptr()); - if (_Right.eback() != &_Right._Mychar) { - _Mysb::setg(_Right.eback(), _Right.gptr(), _Right.egptr()); - } else if (_Right.gptr() != &_Right._Mychar) { - _Mysb::setg(&_Mychar, &_Mychar + 1, &_Mychar + 1); - } else { - _Mysb::setg(&_Mychar, &_Mychar, &_Mychar + 1); - } - - _Pcvt = _Right._Pcvt; - _State = _Right._State; - _Wrotesome = _Right._Wrotesome; - _Closef = _Right._Closef; - - // reinitialize _Right - _Right._Init(_Myfile_sav, _Myfile_sav ? _Openfl : _Newfl); - _Right.setp(_Pfirst0, _Pnext0, _Pend); - if (!_Set_eback_sav) { - _Right.setg(_Gfirst0, _Gnext0, _Gend); - } else if (!_Set_eback_live) { - _Right.setg(&_Right._Mychar, &_Right._Mychar + 1, &_Right._Mychar + 1); - } else { - _Right.setg(&_Right._Mychar, &_Right._Mychar, &_Right._Mychar + 1); - } - - _Right._Pcvt = _Pcvt_sav; - _Right._State = _State_sav; - _Right._Wrotesome = _Wrotesome_sav; - _Right._Closef = _Closef_sav; - - // swap ancillary data - _STD swap(_Set_eback, _Right._Set_eback); - _STD swap(_Set_egptr, _Right._Set_egptr); - - _STD swap(_Mychar, _Right._Mychar); - _STD swap(_Mysb::_Plocale, _Right._Plocale); - } - } - - basic_filebuf(const basic_filebuf&) = delete; - basic_filebuf& operator=(const basic_filebuf&) = delete; - - enum _Initfl { // reasons for a call to _Init - _Newfl, - _Openfl, - _Closefl - }; - - _NODISCARD bool is_open() const noexcept /* strengthened */ { - return static_cast(_Myfile); - } - - basic_filebuf* open(const char* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // _Prot is an extension - if (_Myfile) { - return nullptr; - } - - const auto _File = _Fiopen(_Filename, _Mode, _Prot); - if (!_File) { - return nullptr; // open failed - } - - _Init(_File, _Openfl); - _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); - return this; // open succeeded - } - - basic_filebuf* open(const string& _Str, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // _Prot is an extension - return open(_Str.c_str(), _Mode, _Prot); - } - -#if _HAS_OLD_IOSTREAMS_MEMBERS - basic_filebuf* open(const char* _Filename, ios_base::open_mode _Mode) { - return open(_Filename, static_cast(_Mode)); - } -#endif // _HAS_OLD_IOSTREAMS_MEMBERS - - basic_filebuf* open(const wchar_t* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // in standard as const std::filesystem::path::value_type *; _Prot is an extension - if (_Myfile) { - return nullptr; - } - - const auto _File = _Fiopen(_Filename, _Mode, _Prot); - if (!_File) { - return nullptr; // open failed - } - - _Init(_File, _Openfl); - _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); - return this; // open succeeded - } - - basic_filebuf* open(const wstring& _Str, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // extension - return open(_Str.c_str(), _Mode, _Prot); - } - -#if _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM - template - basic_filebuf* open( - const _Identity_t<_Path_ish>& _Path, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // _Prot is an extension - return open(_Path.c_str(), _Mode, _Prot); - } -#endif // _FSTREAM_SUPPORTS_EXPERIMENTAL_FILESYSTEM - -#if _HAS_CXX17 - template - basic_filebuf* open( - const _Identity_t<_Path_ish>& _Path, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // _Prot is an extension - return open(_Path.c_str(), _Mode, _Prot); - } -#endif // _HAS_CXX17 - -#if _HAS_OLD_IOSTREAMS_MEMBERS - basic_filebuf* open(const wchar_t* _Filename, ios_base::open_mode _Mode) { - // in standard as const std::filesystem::path::value_type * - return open(_Filename, static_cast(_Mode)); - } -#endif // _HAS_OLD_IOSTREAMS_MEMBERS - -#ifdef _CRTBLD - basic_filebuf* open( - const unsigned short* _Filename, ios_base::openmode _Mode, int _Prot = ios_base::_Default_open_prot) { - // in standard as const std::filesystem::path::value_type *; _Prot is an extension - if (_Myfile) { - return nullptr; - } - - const auto _File = _Fiopen(_Filename, _Mode, _Prot); - if (!_File) { - return nullptr; // open failed - } - - _Init(_File, _Openfl); - _Initcvt(_STD use_facet<_Cvt>(_Mysb::getloc())); - return this; // open succeeded - } - -#if _HAS_OLD_IOSTREAMS_MEMBERS - basic_filebuf* open(const unsigned short* _Filename, ios_base::open_mode _Mode) { - // in standard as const std::filesystem::path::value_type * - return open(_Filename, static_cast(_Mode)); - } -#endif // _HAS_OLD_IOSTREAMS_MEMBERS -#endif // _CRTBLD - - basic_filebuf* close() { - basic_filebuf* _Ans; - if (_Myfile) { // put any homing sequence and close file - _Reset_back(); // revert from _Mychar buffer - - _Ans = this; - if (!_Endwrite()) { - _Ans = nullptr; - } - - if (_CSTD fclose(_Myfile) != 0) { - _Ans = nullptr; - } - } else { - _Ans = nullptr; - } - - _Init(nullptr, _Closefl); - return _Ans; - } - - void __CLR_OR_THIS_CALL _Lock() override { // lock file instead of stream buffer - if (_Myfile) { - _CSTD _lock_file(_Myfile); - } - } - - void __CLR_OR_THIS_CALL _Unlock() override { // unlock file instead of stream buffer - if (_Myfile) { - _CSTD _unlock_file(_Myfile); - } - } - -protected: - int_type __CLR_OR_THIS_CALL overflow(int_type _Meta = _Traits::eof()) override { // put an element to stream - if (_Traits::eq_int_type(_Traits::eof(), _Meta)) { - return _Traits::not_eof(_Meta); // EOF, return success code - } - - if (_Mysb::pptr() && _Mysb::pptr() < _Mysb::epptr()) { // room in buffer, store it - *_Mysb::_Pninc() = _Traits::to_char_type(_Meta); - return _Meta; - } - - if (!_Myfile) { - return _Traits::eof(); // no open C stream, fail - } - - _Reset_back(); // revert from _Mychar buffer - if (!_Pcvt) { // no codecvt facet, put as is - return _Fputc(_Traits::to_char_type(_Meta), _Myfile) ? _Meta : _Traits::eof(); - } - - // put using codecvt facet - constexpr size_t _Codecvt_temp_buf = 32; - char _Str[_Codecvt_temp_buf]; - const _Elem _Ch = _Traits::to_char_type(_Meta); - const _Elem* _Src; - char* _Dest; - - // test result of converting one element - switch (_Pcvt->out(_State, &_Ch, &_Ch + 1, _Src, _Str, _Str + _Codecvt_temp_buf, _Dest)) { - case codecvt_base::partial: - case codecvt_base::ok: - { // converted something, try to put it out - const auto _Count = static_cast(_Dest - _Str); - if (0 < _Count && _Count != static_cast(_CSTD fwrite(_Str, 1, _Count, _Myfile))) { - return _Traits::eof(); // write failed - } - - _Wrotesome = true; // write succeeded - if (_Src != &_Ch) { - return _Meta; // converted whole element - } - - return _Traits::eof(); // conversion failed - } - - case codecvt_base::noconv: - // no conversion, put as is - return _Fputc(_Ch, _Myfile) ? _Meta : _Traits::eof(); - - default: - return _Traits::eof(); // conversion failed - } - } - - int_type __CLR_OR_THIS_CALL pbackfail(int_type _Meta = _Traits::eof()) override { - // put an element back to stream - if (_Mysb::gptr() && _Mysb::eback() < _Mysb::gptr() - && (_Traits::eq_int_type(_Traits::eof(), _Meta) - || _Traits::eq_int_type(_Traits::to_int_type(_Mysb::gptr()[-1]), - _Meta))) { // just back up position - _Mysb::_Gndec(); - return _Traits::not_eof(_Meta); - } else if (!_Myfile || _Traits::eq_int_type(_Traits::eof(), _Meta)) { - return _Traits::eof(); // no open C stream or EOF, fail - } else if (!_Pcvt && _Ungetc(_Traits::to_char_type(_Meta), _Myfile)) { - return _Meta; // no facet and unget succeeded, return - } else if (_Mysb::gptr() != &_Mychar) { // putback to _Mychar - _Mychar = _Traits::to_char_type(_Meta); - _Set_back(); // switch to _Mychar buffer - return _Meta; - } else { - return _Traits::eof(); // nowhere to put back - } - } - - int_type __CLR_OR_THIS_CALL underflow() override { // get an element from stream, but don't point past it - int_type _Meta; - if (_Mysb::gptr() && _Mysb::gptr() < _Mysb::egptr()) { - return _Traits::to_int_type(*_Mysb::gptr()); // return buffered - } else if (_Traits::eq_int_type(_Traits::eof(), _Meta = uflow())) { - return _Meta; // uflow failed, return EOF - } else { // get a char, don't point past it - pbackfail(_Meta); - return _Meta; - } - } - - int_type __CLR_OR_THIS_CALL uflow() override { // get an element from stream, point past it - if (_Mysb::gptr() && _Mysb::gptr() < _Mysb::egptr()) { - return _Traits::to_int_type(*_Mysb::_Gninc()); // return buffered - } - - if (!_Myfile) { - return _Traits::eof(); // no open C stream, fail - } - - _Reset_back(); // revert from _Mychar buffer - if (!_Pcvt) { // no codecvt facet, just get it - _Elem _Ch; - return _Fgetc(_Ch, _Myfile) ? _Traits::to_int_type(_Ch) : _Traits::eof(); - } - - // build string until codecvt succeeds - string _Str; - - for (;;) { // get using codecvt facet - const char* _Src; - int _Meta = _CSTD fgetc(_Myfile); - - if (_Meta == EOF) { - return _Traits::eof(); // partial char? - } - - _Str.push_back(static_cast(_Meta)); // append byte and convert - - _Elem _Ch; - _Elem* _Dest; - - // test result of converting one element - switch (_Pcvt->in(_State, _Str.data(), _Str.data() + _Str.size(), _Src, &_Ch, &_Ch + 1, _Dest)) { - case codecvt_base::partial: - case codecvt_base::ok: - if (_Dest != &_Ch) { // got an element, put back excess and deliver it - auto _Nleft = _Str.data() + _Str.size() - _Src; - while (0 < _Nleft) { - _CSTD ungetc(_Src[--_Nleft], _Myfile); - } - - return _Traits::to_int_type(_Ch); - } - - _Str.erase(0, static_cast(_Src - _Str.data())); // partial, discard used input - break; - - case codecvt_base::noconv: - // noconv is only possible if _Elem is char, so we can use it directly - return static_cast(_Str.front()); - - default: - return _Traits::eof(); // conversion failed - } - } - } - - streamsize __CLR_OR_THIS_CALL xsgetn(_Elem* _Ptr, streamsize _Count) override { - // get _Count characters from stream - if constexpr (sizeof(_Elem) == 1) { - if (_Count <= 0) { - return 0; - } - - if (_Pcvt) { // if we need a nontrivial codecvt transform, do the default expensive thing - return _Mysb::xsgetn(_Ptr, _Count); - } - - // assuming this is OK because _Ptr + _Count must be valid - auto _Count_s = static_cast(_Count); - const auto _Start_count = _Count; - const auto _Available = static_cast(_Mysb::_Gnavail()); - if (0 < _Available) { // copy from get area - const auto _Read_size = (_STD min)(_Count_s, _Available); - _Traits::copy(_Ptr, _Mysb::gptr(), _Read_size); - _Ptr += _Read_size; - _Count_s -= _Read_size; - _Mysb::gbump(static_cast(_Read_size)); - } - - if (_Myfile) { // open C stream, attempt read - _Reset_back(); // revert from _Mychar buffer - // process in 4k - 1 chunks to avoid tripping over fread's clobber-the-end behavior when - // doing \r\n -> \n translation - constexpr size_t _Read_size = 4095; // _INTERNAL_BUFSIZ - 1 - while (_Read_size < _Count_s) { - const auto _Actual_read = _CSTD fread(_Ptr, sizeof(_Elem), _Read_size, _Myfile); - _Ptr += _Actual_read; - _Count_s -= _Actual_read; - if (_Actual_read != _Read_size) { - return static_cast(_Start_count - _Count_s); - } - } - - if (0 < _Count_s) { - _Count_s -= _CSTD fread(_Ptr, sizeof(_Elem), _Count_s, _Myfile); - } - } - - return static_cast(_Start_count - _Count_s); - } else { // non-chars always get element-by-element processing - return _Mysb::xsgetn(_Ptr, _Count); - } - } - - streamsize __CLR_OR_THIS_CALL xsputn(const _Elem* _Ptr, streamsize _Count) override { - // put _Count characters to stream - if constexpr (sizeof(_Elem) == 1) { - if (_Pcvt) { // if we need a nontrivial codecvt transform, do the default expensive thing - return _Mysb::xsputn(_Ptr, _Count); - } - - const streamsize _Start_count = _Count; - streamsize _Size = _Mysb::_Pnavail(); - if (0 < _Count && 0 < _Size) { // copy to write buffer - if (_Count < _Size) { - _Size = _Count; - } - - _Traits::copy(_Mysb::pptr(), _Ptr, static_cast(_Size)); - _Ptr += _Size; - _Count -= _Size; - _Mysb::pbump(static_cast(_Size)); - } - - if (0 < _Count && _Myfile) { // open C stream, attempt write - _Count -= _CSTD fwrite(_Ptr, sizeof(_Elem), static_cast(_Count), _Myfile); - } - - return _Start_count - _Count; - } else { // non-chars always get element-by-element processing - return _Mysb::xsputn(_Ptr, _Count); - } - } - - pos_type __CLR_OR_THIS_CALL seekoff(off_type _Off, ios_base::seekdir _Way, - ios_base::openmode = ios_base::in | ios_base::out) override { // change position by _Off - fpos_t _Fileposition; - - if (_Mysb::gptr() == &_Mychar // something putback - && _Way == ios_base::cur // a relative seek - && !_Pcvt) { // not converting - _Off -= static_cast(sizeof(_Elem)); // back up over _Elem bytes - } - - if (!_Myfile || !_Endwrite() - || ((_Off != 0 || _Way != ios_base::cur) && _CSTD _fseeki64(_Myfile, _Off, _Way) != 0) - || _CSTD fgetpos(_Myfile, &_Fileposition) != 0) { - return pos_type{off_type{-1}}; // report failure - } - - _Reset_back(); // revert from _Mychar buffer, discarding any putback - return pos_type{_State, _Fileposition}; // return new position - } - - pos_type __CLR_OR_THIS_CALL seekpos(pos_type _Pos, ios_base::openmode = ios_base::in | ios_base::out) override { - // change position to _Pos - off_type _Off = static_cast(_Pos); - - if (!_Myfile || !_Endwrite() || _CSTD fsetpos(_Myfile, &_Off) != 0) { - return pos_type{off_type{-1}}; // report failure - } - - _State = _Pos.state(); - _Reset_back(); // revert from _Mychar buffer, discarding any putback - return pos_type{_State, _Off}; // return new position - } - - _Mysb* __CLR_OR_THIS_CALL setbuf(_Elem* _Buffer, streamsize _Count) override { // offer _Buffer to C stream - int _Mode; - if (!_Buffer && _Count == 0) { - _Mode = _IONBF; - } else { - _Mode = _IOFBF; - } - - const size_t _Size = static_cast(_Count) * sizeof(_Elem); - - if (!_Myfile || _CSTD setvbuf(_Myfile, reinterpret_cast(_Buffer), _Mode, _Size) != 0) { - return nullptr; // failed - } - - // new buffer, reinitialize pointers - _Init(_Myfile, _Openfl); - return this; - } - - int __CLR_OR_THIS_CALL sync() override { // synchronize C stream with external file - if (!_Myfile || _Traits::eq_int_type(_Traits::eof(), overflow()) || 0 <= _CSTD fflush(_Myfile)) { - return 0; - } - - return -1; - } - - void __CLR_OR_THIS_CALL imbue(const locale& _Loc) override { - // set locale to argument (capture nontrivial codecvt facet) - _Initcvt(_STD use_facet<_Cvt>(_Loc)); - } - - void _Init(FILE* _File, _Initfl _Which) noexcept { // initialize to C stream _File after {new, open, close} - using _State_type = typename _Traits::state_type; - - __PURE_APPDOMAIN_GLOBAL static _State_type _Stinit; // initial state - - _Closef = _Which == _Openfl; - _Wrotesome = false; - - _Mysb::_Init(); // initialize stream buffer base object - - if (_File && sizeof(_Elem) == 1) { // point inside C stream with [first, first + count) buffer - _Elem** _Pb = nullptr; - _Elem** _Pn = nullptr; - int* _Nr = nullptr; - - ::_get_stream_buffer_pointers( - _File, reinterpret_cast(&_Pb), reinterpret_cast(&_Pn), &_Nr); - int* _Nw = _Nr; - - _Mysb::_Init(_Pb, _Pn, _Nr, _Pb, _Pn, _Nw); - } - - _Myfile = _File; - _State = _Stinit; - _Pcvt = nullptr; // pointer to codecvt facet - } - - bool _Endwrite() { // put shift to initial conversion state, as needed - if (!_Pcvt || !_Wrotesome) { - return true; - } - - // may have to put - if (_Traits::eq_int_type(_Traits::eof(), overflow())) { - return false; - } - - constexpr size_t _Codecvt_temp_buf = 32; - char _Str[_Codecvt_temp_buf]; - char* _Dest; - switch (_Pcvt->unshift(_State, _Str, _Str + _Codecvt_temp_buf, _Dest)) { // test result of homing conversion - case codecvt_base::ok: - _Wrotesome = false; // homed successfully - _FALLTHROUGH; - - case codecvt_base::partial: - { // put any generated bytes - const auto _Count = static_cast(_Dest - _Str); - if (0 < _Count && _Count != static_cast(_CSTD fwrite(_Str, 1, _Count, _Myfile))) { - return false; // write failed - } - - return !_Wrotesome; - } - - case codecvt_base::noconv: - _Wrotesome = false; // homed successfully - return true; // nothing else to do - - default: - return false; // conversion failed - } - } - - void _Initcvt(const _Cvt& _Newcvt) noexcept { // initialize codecvt pointer - if (_Newcvt.always_noconv()) { - _Pcvt = nullptr; // nothing to do - } else { // set up for nontrivial codecvt facet - _Pcvt = _STD addressof(_Newcvt); - _Mysb::_Init(); // reset any buffering - } - } - -private: - const _Cvt* _Pcvt; // pointer to codecvt facet (may be null) - _Elem _Mychar; // putback character, when _Ungetc fails - bool _Wrotesome; // true if homing sequence may be needed - typename _Traits::state_type _State; // current conversion state - bool _Closef; // true if C stream must be closed - FILE* _Myfile; // pointer to C stream - - void _Reset_back() noexcept { // restore buffer after putback - if (_Mysb::eback() == &_Mychar) { - _Mysb::setg(_Set_eback, _Set_eback, _Set_egptr); - } - } - - void _Set_back() noexcept { // set up putback area - if (_Mysb::eback() != &_Mychar) { // save current get buffer - _Set_eback = _Mysb::eback(); - _Set_egptr = _Mysb::egptr(); - } - _Mysb::setg(&_Mychar, &_Mychar, &_Mychar + 1); - } - - _Elem* _Set_eback; // saves eback() during one-element putback - _Elem* _Set_egptr; // saves egptr() -}; - -_EXPORT_STD template -void swap(basic_filebuf<_Elem, _Traits>& _Left, basic_filebuf<_Elem, _Traits>& _Right) noexcept /* strengthened */ { - _Left.swap(_Right); -} _EXPORT_STD template class basic_ifstream : public basic_istream<_Elem, _Traits> { // input stream associated with a C stream diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 612fae95022..0bc5e0c9636 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -7,9 +7,11 @@ // "__msvc_all_public_headers.hpp", // for testing, not production "__msvc_chrono.hpp", "__msvc_cxx_stdatomic.hpp", + "__msvc_filebuf.hpp", "__msvc_format_ucd_tables.hpp", "__msvc_int128.hpp", "__msvc_iter_core.hpp", + "__msvc_print.hpp", "__msvc_sanitizer_annotate_container.hpp", "__msvc_system_error_abi.hpp", "__msvc_tzdb.hpp", @@ -88,6 +90,7 @@ "numeric", "optional", "ostream", + "print", "queue", "random", "ranges", diff --git a/stl/inc/ostream b/stl/inc/ostream index cc112046fe0..7ac90ecf305 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -10,6 +10,12 @@ #if _STL_COMPILER_PREPROCESSOR #include +#if _HAS_CXX23 +#include <__msvc_filebuf.hpp> +#include <__msvc_print.hpp> +#include +#endif // _HAS_CXX23 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1076,6 +1082,212 @@ basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, // display error code return _Ostr << _Errcode.category().name() << ':' << _Errcode.value(); } + +#ifdef __cpp_lib_print +#ifdef _CPPRTTI +template +ios_base::iostate _Print_noformat_nonunicode(ostream& _Ostr, const string_view _Str) { + // *LOCKED* + // + // This function is called from a context in which an ostream::sentry has been + // constructed. + ios_base::iostate _State = ios_base::goodbit; + + _TRY_IO_BEGIN + const auto _Characters_written = _Ostr.rdbuf()->sputn(_Str.data(), static_cast(_Str.size())); + const bool _Was_insertion_successful = static_cast(_Characters_written) == _Str.size(); + if (!_Was_insertion_successful) [[unlikely]] { + _State |= ios_base::badbit; + } + _CATCH_IO_(ios_base, _Ostr) + + return _State; +} + +template +void _Vprint_nonunicode_impl( + const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { + const ostream::sentry _Ok(_Ostr); + ios_base::iostate _State = ios_base::goodbit; + + if (!_Ok) [[unlikely]] { + _State |= ios_base::badbit; + } else [[likely]] { + // This is intentionally kept outside of the try/catch block in _Print_noformat_nonunicode() + // (see N4928 [ostream.formatted.print]/3.2). + string _Output_str = _STD vformat(_Ostr.getloc(), _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Output_str.push_back('\n'); + } + + _State |= _STD _Print_noformat_nonunicode(_Ostr, _Output_str); + } + + _Ostr.setstate(_State); +} + +template +ios_base::iostate _Print_noformat_unicode(ostream& _Ostr, const string_view _Str) { + // *LOCKED* + // + // This function is called from a context in which an ostream::sentry has been + // constructed. + ios_base::iostate _State = ios_base::goodbit; + + // The std::ostream& overload of vprint_unicode() expects you to determine whether the stream refers + // to a unicode console or not based solely on the std::ostream& provided. That class simply doesn't + // provide enough information to know this in every case. The best we can do (without breaking ABI) + // is check if the underlying std::streambuf object of the std::ostream& actually refers to a std::filebuf + // object. This requires the use of a dynamic_cast. (This is also why the std::ostream& overloads of + // std::print() et al. are deleted when RTTI is disabled.) + streambuf* const _Streambuf = _Ostr.rdbuf(); + const auto _Filebuf = dynamic_cast<_Filebuf_type*>(_Streambuf); + + // If we got nullptr, then it probably isn't being used for a unicode console... + if (_Filebuf == nullptr) { + _State |= _STD _Print_noformat_nonunicode(_Ostr, _Str); + return _State; + } + + FILE* const _File_stream = _Filebuf->_Myfile; + const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ + __std_get_unicode_console_handle_from_file_stream(_File_stream)}; + + // See the documentation for __std_unicode_console_retrieval_result to understand why we do this. + bool _Is_unicode_console; + +#pragma warning(push) +#pragma warning(disable : 4061) // enumerator not explicitly handled by switch label + switch (_Unicode_console_retrieval_result._Error) { + case __std_win_error::_Success: + _Is_unicode_console = true; + break; + + case __std_win_error::_File_not_found: + _Is_unicode_console = false; + break; + + case __std_win_error::_Not_supported: + [[unlikely]] return _State; + + default: + [[unlikely]] return ios_base::failbit; + } +#pragma warning(pop) + + if (_Is_unicode_console) { + _TRY_IO_BEGIN + const bool _Was_flush_successful = _Ostr.rdbuf()->pubsync() != -1; + if (!_Was_flush_successful) [[unlikely]] { + _State |= ios_base::badbit; + return _State; + } + + const __std_win_error _Unicode_console_print_result = + __std_print_to_unicode_console(_Unicode_console_retrieval_result._Console_handle, _Str.data(), _Str.size()); + if (_Unicode_console_print_result != __std_win_error::_Success) [[unlikely]] { + _State |= ios_base::badbit; + } + _CATCH_IO_(ios_base, _Ostr) + } else { + _State |= _STD _Print_noformat_nonunicode(_Ostr, _Str); + } + + return _State; +} + +template +void _Vprint_unicode_impl( + const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { + const ostream::sentry _Ok(_Ostr); + ios_base::iostate _State = ios_base::goodbit; + + if (!_Ok) [[unlikely]] { + _State |= ios_base::badbit; + } else [[likely]] { + // This is intentionally kept outside of the try/catch block in _Print_noformat_unicode() + // (see N4928 [ostream.formatted.print]/3.2). + string _Output_str = _STD vformat(_Ostr.getloc(), _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Output_str.push_back('\n'); + } + + _State |= _STD _Print_noformat_unicode(_Ostr, _Output_str); + } + + _Ostr.setstate(_State); +} + +template +void _Print_noformat(ostream& _Ostr, const string_view _Str) { + const ostream::sentry _Ok(_Ostr); + ios_base::iostate _State = ios_base::goodbit; + + if (!_Ok) [[unlikely]] { + _State |= ios_base::badbit; + } else [[likely]] { + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _State |= _STD _Print_noformat_unicode(_Ostr, _Str); + } else { + _State |= _STD _Print_noformat_nonunicode(_Ostr, _Str); + } + } + + _Ostr.setstate(_State); +} + +template +void _Print_impl(const _Add_newline _Add_nl, ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _Args) { + constexpr bool _Has_format_args = sizeof...(_Types) > 0; + + // This is intentionally kept outside of the try/catch block in _Print_noformat_*() + // (see N4928 [ostream.formatted.print]/3.2). + + if constexpr (_Has_format_args) { + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _STD _Vprint_unicode_impl( + _Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + } else { + _STD _Vprint_nonunicode_impl( + _Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + } + } else { + const string _Unescaped_str{_Unescape_braces(_Add_nl, _Fmt.get())}; + _STD _Print_noformat(_Ostr, _Unescaped_str); + } +} + +_EXPORT_STD template +void print(ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD _Print_impl(_Add_newline::_Nope, _Ostr, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template +void println(ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD _Print_impl(_Add_newline::_Yes, _Ostr, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_unicode(ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_unicode_impl(_Add_newline::_Nope, _Ostr, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_nonunicode(ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_nonunicode_impl(_Add_newline::_Nope, _Ostr, _Fmt_str, _Fmt_args); +} +#else // ^^^ _CPPRTTI / !_CPPRTTI vvv +_EXPORT_STD template +void print(ostream&, format_string<_Types...>, _Types&&...) = delete; // requires /GR option + +_EXPORT_STD template +void println(ostream&, format_string<_Types...>, _Types&&...) = delete; // requires /GR option + +_EXPORT_STD void vprint_unicode(ostream&, string_view, format_args) = delete; // requires /GR option +_EXPORT_STD void vprint_nonunicode(ostream&, string_view, format_args) = delete; // requires /GR option +#endif // !_CPPRTTI +#endif // __cpp_lib_print + _STD_END #pragma pop_macro("new") diff --git a/stl/inc/print b/stl/inc/print new file mode 100644 index 00000000000..184392ccb3e --- /dev/null +++ b/stl/inc/print @@ -0,0 +1,171 @@ +// print standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _PRINT_ +#define _PRINT_ +#include +#if _STL_COMPILER_PREPROCESSOR +#ifndef __cpp_lib_print +_EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); +#else // ^^^ !defined(__cpp_lib_print) / defined(__cpp_lib_print) vvv + +#include <__msvc_print.hpp> +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +inline void _Print_noformat_nonunicode(FILE* const _Stream, const string_view _Str) { + const bool _Was_write_successful = _CSTD fwrite(_Str.data(), 1, _Str.size(), _Stream) == _Str.size(); + + if (!_Was_write_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } +} + +inline void _Vprint_nonunicode_impl( + const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + string _Output_str = _STD vformat(_Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Output_str.push_back('\n'); + } + + _STD _Print_noformat_nonunicode(_Stream, _Output_str); +} + +inline void _Print_noformat_unicode(FILE* const _Stream, const string_view _Str) { + const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ + __std_get_unicode_console_handle_from_file_stream(_Stream)}; + + // See the documentation for __std_unicode_console_retrieval_result to understand why we do this. + bool _Is_unicode_console; + +#pragma warning(push) +#pragma warning(disable : 4061) // enumerator not explicitly handled by switch label + switch (_Unicode_console_retrieval_result._Error) { + case __std_win_error::_Success: + _Is_unicode_console = true; + break; + + case __std_win_error::_File_not_found: + _Is_unicode_console = false; + break; + + case __std_win_error::_Not_supported: + [[unlikely]] return; + + default: + [[unlikely]] _STD _Throw_system_error_from_std_win_error(_Unicode_console_retrieval_result._Error); + } +#pragma warning(pop) + + if (_Is_unicode_console) { + const bool _Was_flush_successful = _CSTD fflush(_Stream) == 0; + if (!_Was_flush_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } + + const __std_win_error _Console_print_result = + __std_print_to_unicode_console(_Unicode_console_retrieval_result._Console_handle, _Str.data(), _Str.size()); + if (_Console_print_result != __std_win_error::_Success) [[unlikely]] { + _STD _Throw_system_error_from_std_win_error(_Console_print_result); + } + } else { + _STD _Print_noformat_nonunicode(_Stream, _Str); + } +} + +inline void _Vprint_unicode_impl( + const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + string _Output_str = _STD vformat(_Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Output_str.push_back('\n'); + } + + _STD _Print_noformat_unicode(_Stream, _Output_str); +} + +template +void _Print_impl( + const _Add_newline _Add_nl, FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { + constexpr bool _Has_format_args = sizeof...(_Types) > 0; + + if constexpr (_Has_format_args) { + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _STD _Vprint_unicode_impl( + _Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + } else { + _STD _Vprint_nonunicode_impl( + _Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + } + } else { + const string _Unescaped_str{_Unescape_braces(_Add_nl, _Fmt.get())}; + + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _STD _Print_noformat_unicode(_Stream, _Unescaped_str); + } else { + _STD _Print_noformat_nonunicode(_Stream, _Unescaped_str); + } + } +} + +_EXPORT_STD template +void print(FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD _Print_impl(_Add_newline::_Nope, _Stream, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template +void print(const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD print(stdout, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template +void println(FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD _Print_impl(_Add_newline::_Yes, _Stream, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template +void println(const format_string<_Types...> _Fmt, _Types&&... _Args) { + _STD println(stdout, _Fmt, _STD forward<_Types>(_Args)...); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_unicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_unicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_unicode(const string_view _Fmt_str, const format_args _Fmt_args) { + _STD vprint_unicode(stdout, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_nonunicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_nonunicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_nonunicode(const string_view _Fmt_str, const format_args _Fmt_args) { + _STD vprint_nonunicode(stdout, _Fmt_str, _Fmt_args); +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // __cpp_lib_print +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _PRINT_ diff --git a/stl/inc/system_error b/stl/inc/system_error index 60ccf897eb2..04e227fc531 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -702,6 +702,30 @@ _EXPORT_STD _NODISCARD inline const error_category& system_category() noexcept { return _Immortalize_memcpy_image<_System_error_category>(); } _STD_END + +#if _HAS_CXX17 +_EXTERN_C +enum class __std_win_error : unsigned long; +_END_EXTERN_C + +_STD_BEGIN +// We would really love to use the proper way of building error_code by specializing +// is_error_code_enum and make_error_code for __std_win_error, but because: +// 1. We would like to keep the definition of __std_win_error in xfilesystem_abi.h +// 2. and xfilesystem_abi.h cannot include +// 3. and specialization of is_error_code_enum and overload of make_error_code +// need to be kept together with the enum (see limerick in N4810 [temp.expl.spec]/7) +// we resort to using this _Make_ec helper. +_NODISCARD inline error_code _Make_ec(__std_win_error _Errno) noexcept { // make an error_code + return {static_cast(_Errno), _STD system_category()}; +} + +[[noreturn]] inline void _Throw_system_error_from_std_win_error(const __std_win_error _Errno) { + _THROW(system_error{_Make_ec(_Errno)}); +} +_STD_END +#endif // _HAS_CXX17 + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/xutility b/stl/inc/xutility index ee954bc9c69..8b9046287cc 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4292,10 +4292,6 @@ template requires (!sized_sentinel_for<_Iter1, _Iter2>) inline constexpr bool disable_sized_sentinel_for, move_iterator<_Iter2>> = true; -_EXPORT_STD struct default_sentinel_t {}; - -_EXPORT_STD inline constexpr default_sentinel_t default_sentinel{}; - _EXPORT_STD struct unreachable_sentinel_t; namespace _Unreachable_sentinel_detail { struct _Base { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index da3870dedf5..b0c34181d72 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -326,6 +326,7 @@ // P1951R1 Default Template Arguments For pair's Forwarding Constructor // P1989R2 Range Constructor For string_view // P2077R3 Heterogeneous Erasure Overloads For Associative Containers +// P2093R14 : Formatted Output // P2136R3 invoke_r() // P2164R9 views::enumerate // P2165R4 Compatibility Between tuple, pair, And tuple-like Objects @@ -353,6 +354,7 @@ // P2494R2 Relaxing Range Adaptors To Allow Move-Only Types // P2499R0 string_view Range Constructor Should Be explicit // P2505R5 Monadic Functions For expected +// P2539R4 Synchronizing print() With The Underlying Stream // P2549R1 unexpected::error() // P2652R2 Disallowing User Specialization Of allocator_traits @@ -1724,6 +1726,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L +#define __cpp_lib_print 202207L #define __cpp_lib_ranges_as_const 202207L #define __cpp_lib_ranges_as_rvalue 202207L #define __cpp_lib_ranges_chunk 202202L diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index dc43b5722e6..5dd3f99d1aa 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -84,6 +84,7 @@ export module std; #include #include #include +#include #include #include #include diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 977feaf0ea7..d9453cc46d7 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -166,6 +166,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\format.cpp; $(CrtRoot)\github\stl\src\locale0_implib.cpp; $(CrtRoot)\github\stl\src\nothrow.cpp; + $(CrtRoot)\github\stl\src\print.cpp; $(CrtRoot)\github\stl\src\sharedmutex.cpp; $(CrtRoot)\github\stl\src\stacktrace.cpp; $(CrtRoot)\github\stl\src\syserror_import_lib.cpp; diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 0ea7194ccc4..d156a3c4ce4 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -3,6 +3,10 @@ ; atomic wait satellite DLL definition +; NOTE: "LIBRARYNAME" is just a placeholder. It gets dynamically replaced with the generated +; DLL name during the CMake build. (See the generate_satellite_def() function in +; stl\CMakeLists.txt.) Doing this instead of using, e.g., CMake variable substitution allows +; for file preprocessing via "cl /DLIBRARYNAME=..." LIBRARY LIBRARYNAME EXPORTS diff --git a/stl/src/print.cpp b/stl/src/print.cpp new file mode 100644 index 00000000000..02ae5d6fa41 --- /dev/null +++ b/stl/src/print.cpp @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// print.cpp -- C++23 implementation + +// This must be as small as possible, because its contents are +// injected into the msvcprt.lib and msvcprtd.lib import libraries. +// Do not include or define anything else here. +// In particular, basic_string must not be included here. + +#include <__msvc_print.hpp> +#include +#include +#include +#include +#include + +#include + +_EXTERN_C + +[[nodiscard]] _Success_(return._Error == __std_win_error::_Success) __std_unicode_console_retrieval_result + __stdcall __std_get_unicode_console_handle_from_file_stream(_In_ FILE* const _Stream) noexcept { + if (_Stream == nullptr) [[unlikely]] { + return __std_unicode_console_retrieval_result{._Error = __std_win_error::_Invalid_parameter}; + } + + const int _Fd = _fileno(_Stream); + + if (_Fd == -2) [[unlikely]] { + // According to https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fileno?view=msvc-170 , + // _fileno() returns -2 if _Stream refers to either stdout or stderr and there is no associated output stream. + // In that case, there is also no associated console HANDLE. (We haven't observed this happening in practice.) + return __std_unicode_console_retrieval_result{._Error = __std_win_error::_Not_supported}; + } else if (_Fd == -1) [[unlikely]] { + return __std_unicode_console_retrieval_result{._Error = __std_win_error::_Invalid_parameter}; + } + + const HANDLE _Console_handle = reinterpret_cast(_get_osfhandle(_Fd)); + + if (_Console_handle == INVALID_HANDLE_VALUE) [[unlikely]] { + return __std_unicode_console_retrieval_result{._Error = __std_win_error::_Invalid_parameter}; + } + + // We can check if _Console_handle actually refers to a console or not by checking the + // return value of GetConsoleMode(). + DWORD _Console_mode; + const bool _Is_unicode_console = GetConsoleMode(_Console_handle, &_Console_mode) != 0; + + if (!_Is_unicode_console) { + return __std_unicode_console_retrieval_result{._Error = __std_win_error::_File_not_found}; + } + + return __std_unicode_console_retrieval_result{ + ._Console_handle = static_cast<__std_unicode_console_handle>( + reinterpret_cast<_STD underlying_type_t<__std_unicode_console_handle>>(_Console_handle)), + ._Error = __std_win_error::_Success}; +} + +_END_EXTERN_C + +namespace { + class _Allocated_string { + public: + _Allocated_string() = default; + + explicit _Allocated_string(__crt_unique_heap_ptr&& _Other_str, const size_t _Other_capacity) noexcept + : _Str(_STD move(_Other_str)), _Str_capacity(_Other_capacity) {} + + [[nodiscard]] wchar_t* _Data() const noexcept { + return _Str.get(); + } + + [[nodiscard]] size_t _Capacity() const noexcept { + return _Str_capacity; + } + + void _Reset() noexcept { + _Str.release(); + _Str_capacity = 0; + } + + private: + __crt_unique_heap_ptr _Str; + size_t _Str_capacity = 0; + }; + + template + class _Really_basic_string_view { + public: + _Really_basic_string_view() = default; + + explicit _Really_basic_string_view(const _Char_type* const _Other_str, const size_t _Other_size) noexcept + : _Str(_Other_str), _Str_size(_Other_size) {} + + [[nodiscard]] const _Char_type* _Data() const noexcept { + return _Str; + } + + [[nodiscard]] size_t _Size() const noexcept { + return _Str_size; + } + + [[nodiscard]] bool _Empty() const noexcept { + return _Str_size == 0; + } + + private: + const _Char_type* _Str = nullptr; + size_t _Str_size = 0; + }; + + using _Minimal_string_view = _Really_basic_string_view; + using _Minimal_wstring_view = _Really_basic_string_view; + + [[nodiscard]] _Minimal_string_view _Get_next_utf8_string_segment( + const char* const _Str, const size_t _Str_size) noexcept { + constexpr size_t _Max_str_segment_size = 8192; + + if (_Str_size <= _Max_str_segment_size) [[likely]] { + return _Minimal_string_view{_Str, _Str_size}; + } + + // Let's refer to _Max_str_segment_size as M. + // Now we know _Str_size > M, so we can read _Str[M]. + // We might need to shrink this segment down to M - 3 bytes, in this worst case scenario: + + // Values: [byte1][byte2][byte3] | [byte4] + // Indices: [M - 4][M - 3][M - 2][M - 1] | [ M ] + // Sizes: [M - 3][M - 2][M - 1][ M ] | [M + 1] + // Maximum segment boundary ^ + + for (size_t _Shrink = 0; _Shrink < 3; ++_Shrink) { + const size_t _Kx = _Max_str_segment_size - _Shrink; // consider a segment of _Kx bytes + + // The first byte after the segment is at index _Kx, which we can read (see above). + // If _Str[_Kx] is a non-trailing byte, then it's the beginning of a code point. + const bool _Trailing_byte = (static_cast(_Str[_Kx]) >> 6) == 0b10; + + if (!_Trailing_byte) { + return _Minimal_string_view{_Str, _Kx}; // found a boundary between code points + } + } + + return _Minimal_string_view{_Str, _Max_str_segment_size - 3}; // worst case scenario + } + + class _Transcode_result { + public: + _Transcode_result() noexcept : _Transcoded_str(), _Successful(true) {} + + _Transcode_result(_Minimal_wstring_view _Result_str) noexcept + : _Transcoded_str(_Result_str), _Successful(true) {} + + _Transcode_result(__std_win_error _Result_error) noexcept : _Win_error(_Result_error), _Successful(false) {} + + [[nodiscard]] bool _Has_value() const noexcept { + return _Successful; + } + + [[nodiscard]] _Minimal_wstring_view _Value() const noexcept { + return _Transcoded_str; + } + + [[nodiscard]] __std_win_error _Error() const noexcept { + return _Win_error; + } + + private: + union { + _Minimal_wstring_view _Transcoded_str; + __std_win_error _Win_error; + }; + + bool _Successful; + }; + + [[nodiscard]] _Transcode_result _Transcode_utf8_string( + _Allocated_string& _Dst_str, const _Minimal_string_view _Src_str) noexcept { + // MultiByteToWideChar() fails if strLength == 0. + if (_Src_str._Empty()) [[unlikely]] { + return {}; + } + + // For vprint_unicode(), N4928 [ostream.formatted.print]/4 suggests replacing invalid code units with U+FFFD. + // This is done automatically by MultiByteToWideChar(), so long as we do not use the MB_ERR_INVALID_CHARS flag. + // We transcode up to 8,192 bytes per segment, which easily fits in an int. + const int32_t _Num_chars_required = + MultiByteToWideChar(CP_UTF8, 0, _Src_str._Data(), static_cast(_Src_str._Size()), nullptr, 0); + + if (_Num_chars_required == 0) [[unlikely]] { + return static_cast<__std_win_error>(GetLastError()); + } + + if (static_cast(_Num_chars_required) > _Dst_str._Capacity()) { + _Dst_str._Reset(); + + __crt_unique_heap_ptr _Wide_str{_malloc_crt_t(wchar_t, _Num_chars_required)}; + if (!_Wide_str) [[unlikely]] { + return __std_win_error::_Not_enough_memory; + } + + _Dst_str = _Allocated_string{_STD move(_Wide_str), static_cast(_Num_chars_required)}; + } + + const int32_t _Conversion_result = MultiByteToWideChar(CP_UTF8, 0, _Src_str._Data(), + static_cast(_Src_str._Size()), _Dst_str._Data(), static_cast(_Dst_str._Capacity())); + + if (_Conversion_result == 0) [[unlikely]] { + // This shouldn't happen... + _CSTD abort(); + } + + return _Minimal_wstring_view{_Dst_str._Data(), static_cast(_Conversion_result)}; + } + + [[nodiscard]] __std_win_error _Write_console( + const HANDLE _Console_handle, const _Minimal_wstring_view _Wide_str) noexcept { + const BOOL _Write_result = + WriteConsoleW(_Console_handle, _Wide_str._Data(), static_cast(_Wide_str._Size()), nullptr, nullptr); + + if (!_Write_result) [[unlikely]] { + return static_cast<__std_win_error>(GetLastError()); + } + + return __std_win_error::_Success; + } +} // unnamed namespace + +_EXTERN_C + +[[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error + __stdcall __std_print_to_unicode_console(_In_ const __std_unicode_console_handle _Console_handle, + _In_reads_(_Str_size) const char* const _Str, _In_ const size_t _Str_size) noexcept { + if (_Console_handle == __std_unicode_console_handle::_Invalid || _Str == nullptr) [[unlikely]] { + return __std_win_error::_Invalid_parameter; + } + + const HANDLE _Actual_console_handle = reinterpret_cast(_Console_handle); + + // We transcode in fairly large segments of 8,192 bytes per segment, + // so one iteration should handle the vast majority of strings. + const char* _Remaining_str = _Str; + size_t _Remaining_str_size = _Str_size; + + _Minimal_string_view _Curr_str_segment{}; + _Allocated_string _Allocated_str{}; + _Transcode_result _Transcoded_str{}; + + while (true) { + _Curr_str_segment = _Get_next_utf8_string_segment(_Remaining_str, _Remaining_str_size); + _Transcoded_str = _Transcode_utf8_string(_Allocated_str, _Curr_str_segment); + + if (!_Transcoded_str._Has_value()) [[unlikely]] { + return _Transcoded_str._Error(); + } + + const __std_win_error _Write_result = _Write_console(_Actual_console_handle, _Transcoded_str._Value()); + + if (_Write_result != __std_win_error::_Success) [[unlikely]] { + return _Write_result; + } + + _Remaining_str_size -= _Curr_str_segment._Size(); + + if (_Remaining_str_size == 0) { + return __std_win_error::_Success; + } + + _Remaining_str += _Curr_str_segment._Size(); + } +} + +_END_EXTERN_C diff --git a/tests/std/include/temp_file_name.hpp b/tests/std/include/temp_file_name.hpp new file mode 100644 index 00000000000..79991f56f20 --- /dev/null +++ b/tests/std/include/temp_file_name.hpp @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include + +[[nodiscard]] inline std::string temp_file_name() { + std::string ret{"temp_file_"}; + std::uniform_int_distribution dist{0, 15}; + std::random_device rd; + + for (int i = 0; i < 64; ++i) { // 64 hexits = 256 bits of entropy + ret.push_back("0123456789ABCDEF"[dist(rd)]); + } + + ret += ".tmp"; + + return ret; +} diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index 9b73391c185..51e07d54d28 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -492,6 +492,16 @@ void test_ostream() { assert(os.rdbuf() == nullptr); } +void test_print() { + using namespace std; + puts("Testing ."); + println("Hello, world!"); + +#ifdef _CPPRTTI + println(cout, "The answer to life, the universe, and everything: {}", 42); +#endif // _CPPRTTI +} + void test_queue() { using namespace std; puts("Testing ."); @@ -1093,6 +1103,7 @@ void all_cpp_header_tests() { test_numeric(); test_optional(); test_ostream(); + test_print(); test_queue(); test_random(); test_ranges(); diff --git a/tests/std/test.lst b/tests/std/test.lst index f15f54508b0..1a916d8d640 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -549,6 +549,7 @@ tests\P1682R3_to_underlying tests\P1899R3_views_stride tests\P1899R3_views_stride_death tests\P1951R1_default_arguments_pair_forward_ctor +tests\P2093R14_formatted_output tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant tests\P2164R9_views_enumerate diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index ad8b6c8ab9e..585535d3ca1 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -48,6 +48,7 @@ "numeric", "optional", "ostream", + "print", "queue", "random", "ranges", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index e9ec1a36cf0..c82a77835a5 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -54,6 +54,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/P2093R14_formatted_output/env.lst b/tests/std/tests/P2093R14_formatted_output/env.lst new file mode 100644 index 00000000000..0f108cca756 --- /dev/null +++ b/tests/std/tests/P2093R14_formatted_output/env.lst @@ -0,0 +1,7 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst +RUNALL_CROSSLIST +PM_CL="" +PM_CL="/utf-8" diff --git a/tests/std/tests/P2093R14_formatted_output/test.cpp b/tests/std/tests/P2093R14_formatted_output/test.cpp new file mode 100644 index 00000000000..92eb0884942 --- /dev/null +++ b/tests/std/tests/P2093R14_formatted_output/test.cpp @@ -0,0 +1,703 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "temp_file_name.hpp" + +using namespace std; + +namespace test { + class win_console { + public: + win_console() { + // There's a trick you can do to create a FILE stream for a Win32 console: + // + // 1. Call _open_osfhandle() to create a file descriptor for the console screen + // buffer handle. + // + // 2. Call _fdopen() to get a FILE* for the file descriptor. + constexpr DWORD screen_buffer_access = GENERIC_READ | GENERIC_WRITE; + console_handle = + CreateConsoleScreenBuffer(screen_buffer_access, 0, nullptr, CONSOLE_TEXTMODE_BUFFER, nullptr); + + if (console_handle == INVALID_HANDLE_VALUE) [[unlikely]] { + return; + } + + const int console_fd = _open_osfhandle(reinterpret_cast(console_handle), _O_TEXT); + + if (console_fd == -1) [[unlikely]] { + return; + } + + file_stream_ptr = _fdopen(console_fd, "w"); + } + + ~win_console() { + delete_console(); + } + + win_console(const win_console&) = delete; + win_console& operator=(const win_console&) = delete; + + win_console(win_console&& rhs) noexcept + : console_handle(rhs.console_handle), file_stream_ptr(rhs.file_stream_ptr) { + rhs.console_handle = nullptr; + rhs.file_stream_ptr = nullptr; + } + + win_console& operator=(win_console&& rhs) noexcept { + delete_console(); + + console_handle = rhs.console_handle; + rhs.console_handle = nullptr; + + file_stream_ptr = rhs.file_stream_ptr; + rhs.file_stream_ptr = nullptr; + + return *this; + } + + FILE* get_file_stream() const { + assert(is_console_valid()); + return file_stream_ptr; + } + + wstring get_console_line(const size_t line_number) const { + // We use the ReadConsoleOutputCharacterW() function to read lines of text which were written to + // the console. The neat thing here is that if we write to the console using the FILE* returned by + // win_console::get_file_stream() with std::print(), we can still use ReadConsoleOutputCharacterW() + // to get that text! This allows us to verify the output being written to the console and check for, + // e.g., invalid code point replacement. + const size_t line_char_width = get_line_character_width(); + const COORD read_coords{.X = 0, .Y = static_cast(line_number)}; + + wstring output_str; + output_str.resize_and_overwrite( + line_char_width, [this, read_coords](wchar_t* const dest_ptr, const size_t allocated_size) { + DWORD num_chars_read; + const BOOL read_output_result = ReadConsoleOutputCharacterW( + console_handle, dest_ptr, static_cast(allocated_size), read_coords, &num_chars_read); + + assert(read_output_result && "ERROR: ReadConsoleOutputCharacterW() failed!"); + return num_chars_read; + }); + + // By default, Windows fills console character cells with space characters. We want to remove + // those space characters which appear after the user's text. + const size_t lastValidChar = output_str.find_last_not_of(' '); + + if (lastValidChar == wstring::npos) [[unlikely]] { + output_str.clear(); + } else [[likely]] { + output_str = output_str.substr(0, lastValidChar + 1); + } + + return output_str; + } + + private: + void delete_console() { + if (is_console_valid()) [[likely]] { + // According to the MSDN, we don't call CloseHandle() on handles passed to _open_osfhandle(), + // and we don't call _close() on file descriptors passed to _fdopen(). So, our only clean-up + // task is to call fclose(). + fclose(file_stream_ptr); + + console_handle = nullptr; + file_stream_ptr = nullptr; + } + } + + size_t get_line_character_width() const { + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + + { + const BOOL get_screen_buffer_info_result = + GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); + assert(get_screen_buffer_info_result && "ERROR: GetConsoleScreenBufferInfo() failed!"); + } + + return static_cast(screen_buffer_info.dwSize.X); + } + + bool is_console_valid() const { + return console_handle != nullptr && console_handle != INVALID_HANDLE_VALUE && file_stream_ptr != nullptr; + } + + private: + HANDLE console_handle; + FILE* file_stream_ptr; + }; + + // We use Windows semaphores to synchronize the parent and child processes. + class win_semaphore { + public: + // Construct a new semaphore in the parent process. + win_semaphore() { + SECURITY_ATTRIBUTES semaphore_attributes{ + .nLength = sizeof(SECURITY_ATTRIBUTES), + .bInheritHandle = TRUE, // The child process will inherit this handle. + }; + m_handle = CreateSemaphoreW(&semaphore_attributes, 0, 1, nullptr); + assert(m_handle != nullptr); + } + + // Construct an inherited semaphore in the child process. + explicit win_semaphore(const uintptr_t val) : m_handle(reinterpret_cast(val)) { + assert(m_handle != nullptr); + } + + win_semaphore(const win_semaphore&) = delete; + win_semaphore& operator=(const win_semaphore&) = delete; + + ~win_semaphore() { + const BOOL close_handle_succeeded = CloseHandle(m_handle); + assert(close_handle_succeeded); + } + + // The parent process has to tell the child process what its inherited handles are. + [[nodiscard]] uintptr_t to_uintptr() const { + return reinterpret_cast(m_handle); + } + + // win_semaphore imitates std::counting_semaphore's interface with release() and acquire(). + void release() { + const BOOL release_semaphore_succeeded = ReleaseSemaphore(m_handle, 1, nullptr); + assert(release_semaphore_succeeded); + } + + void acquire() { + const DWORD wait_result = WaitForSingleObject(m_handle, INFINITE); + assert(wait_result == WAIT_OBJECT_0); + } + + private: + HANDLE m_handle; + }; +} // namespace test + +const locale& get_utf8_locale() { +#pragma warning(push) +#pragma warning(disable : 4640) // construction of local static object is not thread-safe + static const locale utf8_locale{"en-US.UTF-8"}; +#pragma warning(pop) + + return utf8_locale; +} + +wstring string_to_wstring(const string_view str) { + using facet_type = codecvt; + const facet_type& facet{use_facet(get_utf8_locale())}; + + mbstate_t conversion_state{}; + const size_t num_chars_required = static_cast( + facet.length(conversion_state, str.data(), str.data() + str.size(), (numeric_limits::max)())); + + wstring output_str; + output_str.resize_and_overwrite(num_chars_required, [&](wchar_t* const dest_str_ptr, const size_t output_size) { + const char* src_end_ptr; + wchar_t* dest_end_ptr; + const codecvt_base::result conversion_result = facet.in(conversion_state, str.data(), str.data() + str.size(), + src_end_ptr, dest_str_ptr, dest_str_ptr + output_size, dest_end_ptr); + + assert(conversion_result == codecvt_base::ok); + + return dest_end_ptr - dest_str_ptr; + }); + + return output_str; +} + +string wstring_to_string(const wstring_view wide_str) { + using facet_type = codecvt; + const facet_type& facet{use_facet(get_utf8_locale())}; + + mbstate_t conversion_state{}; + const size_t max_chars_required = wide_str.size() * facet.max_length(); + + string output_str; + output_str.resize_and_overwrite(max_chars_required, [&](char* const dest_str_ptr, const size_t output_size) { + const wchar_t* src_end_ptr; + char* dest_end_ptr; + const codecvt_base::result conversion_result = facet.out(conversion_state, wide_str.data(), + wide_str.data() + wide_str.size(), src_end_ptr, dest_str_ptr, dest_str_ptr + output_size, dest_end_ptr); + + assert(conversion_result == codecvt_base::ok); + + return dest_end_ptr - dest_str_ptr; + }); + + return output_str; +} + +void maybe_flush_console_file_stream(const test::win_console& console) { + // std::print() and std::println() should automatically flush the stream if the Unicode + // API is being used, according to N4928 [print.fun]/7. So, as an additional check, + // we'll only call std::fflush() if the ordinary literal encoding is *NOT* UTF-8. This + // should work fine, assuming that our implementation is correct. + if constexpr (!_Is_ordinary_literal_encoding_utf8()) { + fflush(console.get_file_stream()); + } +} + +void test_print_optimizations() { + test::win_console test_console{}; + FILE* const console_file_stream = test_console.get_file_stream(); + + size_t curr_line_number = 0; + + const auto get_last_console_line_closure = [&test_console, &curr_line_number]() { + maybe_flush_console_file_stream(test_console); + + return wstring_to_string(test_console.get_console_line(curr_line_number++)); + }; + + stringstream test_str_stream{}; + + // Even if a string has no formatting arguments, std::format() will still replace escaped + // brace characters (i.e., {{ and }}) with a single brace character. We need to make sure + // that we do the same. + { + constexpr string_view escaped_braces_str{"[{{a{{{{b c}}}}d}}]"}; + constexpr string_view expected_str{"[{a{{b c}}d}][{a{{b c}}d}]"}; + + print(console_file_stream, escaped_braces_str); + println(console_file_stream, escaped_braces_str); + + const string last_console_line = get_last_console_line_closure(); + assert(last_console_line == expected_str); + + print(test_str_stream, escaped_braces_str); + println(test_str_stream, escaped_braces_str); + + string str_stream_line; + getline(test_str_stream, str_stream_line); + + assert(str_stream_line == expected_str); + } + + // When writing out to a Unicode console, we transcode the string in segments of 8,192 bytes + // each. Splitting up the strings into segments requires ending each segment on a valid code + // point (if applicable). We need to make sure that the actual string is getting printed + // appropriately. Manual test: + + /********** + #include + #include + #include + using namespace std; + + int main() { + string str; + for (int i = 10; i < 8190; i += 10) { + str += format("[{:.<8}]", i); + } + str += "[8189...]"; + println("{}\xF0\x9F\x90\x88", str); + } + **********/ +} + +void test_invalid_code_points_console() { + if constexpr (!_Is_ordinary_literal_encoding_utf8()) { + if (GetConsoleOutputCP() != CP_UTF8) { + return; // With neither `/utf-8` nor a UTF-8 console output codepage, we can't run this part of the test. + } + } + + test::win_console test_console{}; + FILE* const console_file_stream = test_console.get_file_stream(); + size_t curr_line_number = 0; + + using printed_string_type = format_string<>; + + const auto test_valid_sequence_closure = [&](const printed_string_type printed_str) { + println(console_file_stream, printed_str); + maybe_flush_console_file_stream(test_console); + + const wstring console_line{test_console.get_console_line(curr_line_number++)}; + assert(wstring_to_string(console_line) == printed_str.get()); + }; + + const auto test_invalid_sequence_closure = [&](const printed_string_type printed_str) { + println(console_file_stream, printed_str); + maybe_flush_console_file_stream(test_console); + + const wstring console_line{test_console.get_console_line(curr_line_number++)}; + const bool contains_replacement_character = console_line.contains(L'\uFFFD'); + + if constexpr (_Is_ordinary_literal_encoding_utf8()) { + // It isn't necessarily well-documented how MultiByteToWideChar() (used internally by + // __std_print_to_unicode_console()) replaces invalid code points, except for the fact + // that if an invalid code point is encountered, then some amount of characters are + // replaced with a single U+FFFD character. So, we instead check that the string written + // to the console has at least one U+FFFD character if it is invalid. (The documentation + // for the function also implies that if the string provided to MultiByteToWideChar() isn't + // empty, then the string created by it also won't be empty.) + assert(printed_str.get().empty() || !console_line.empty()); + assert(contains_replacement_character); + } + + // There seems to be some inconsistent behavior with what happens when std::fputs() (used internally + // by std::print() et al. when writing to files or when the ordinary literal encoding isn't UTF-8) writes + // invalid code sequences to a FILE stream associated with a console. On some systems, it seems that + // these characters are dropped; on others, they seem to be replaced with U+FFFD. So, a check like + // "assert(!contains_replacement_character)" might pass on some systems and fail on others. + }; + + // Example UTF-8 Code Sequences from https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 + + // Valid ASCII + test_valid_sequence_closure("a"); + + // Valid 2 Octet Sequence + test_valid_sequence_closure("\xC3\xB1"); + + // Invalid 2 Octet Sequence + test_invalid_sequence_closure("\xC3\x28"); + + // Invalid Sequence Identifier + test_invalid_sequence_closure("\xA0\xA1"); + + // Valid 3 Octet Sequence + test_valid_sequence_closure("\xE2\x82\xA1"); + + // Invalid 3 Octet Sequence (in 2nd Octet) + test_invalid_sequence_closure("\xE2\x28\xA1"); + + // Invalid 3 Octet Sequence (in 3rd Octet) + test_invalid_sequence_closure("\xE2\x82\x28"); + + // Skipped: 0xF0 0x90 0x8C 0xBC (MultiByteToWideChar() correctly finds that this is a valid + // UTF-8 code point, but the call to WriteConsoleW() seems to erroneously replace it with U+FFFD.) + + // Invalid 4 Octet Sequence (in 2nd Octet) + test_invalid_sequence_closure("\xF0\x28\x8C\xBC"); + + // Invalid 4 Octet Sequence (in 3rd Octet) + test_invalid_sequence_closure("\xF0\x90\x28\xBC"); + + // Invalid 4 Octet Sequence (in 4th Octet) + test_invalid_sequence_closure("\xF0\x28\x8C\x25"); +} + +void test_invalid_code_points_file() { + // Unlike for the console API when the ordinary literal encoding is UTF-8, invalid code points shouldn't + // be replaced when writing to a file. + const string temp_file_name_str = temp_file_name(); + + FILE* temp_file_stream; + + { + const errno_t fopen_result = fopen_s(&temp_file_stream, temp_file_name_str.c_str(), "w+b"); + assert(fopen_result == 0); + } + + using printed_string_type = format_string<>; + + const auto test_sequence_closure = [&](const printed_string_type printed_str) { + rewind(temp_file_stream); + print(temp_file_stream, printed_str); + rewind(temp_file_stream); + + string file_line_str; + file_line_str.resize_and_overwrite( + printed_str.get().size() + 1, [&](char* const dest_str_ptr, const size_t output_size) { + fgets(dest_str_ptr, static_cast(output_size), temp_file_stream); + return output_size - 1; + }); + + assert(file_line_str == printed_str.get()); + }; + + // Example UTF-8 Code Sequences from https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 + + // Valid ASCII + test_sequence_closure("a"); + + // Valid 2 Octet Sequence + test_sequence_closure("\xC3\xB1"); + + // Invalid 2 Octet Sequence + test_sequence_closure("\xC3\x28"); + + // Invalid Sequence Identifier + test_sequence_closure("\xA0\xA1"); + + // Valid 3 Octet Sequence + test_sequence_closure("\xE2\x82\xA1"); + + // Invalid 3 Octet Sequence (in 2nd Octet) + test_sequence_closure("\xE2\x28\xA1"); + + // Invalid 3 Octet Sequence (in 3rd Octet) + test_sequence_closure("\xE2\x82\x28"); + + // Valid 4 Octet Sequence + test_sequence_closure("\xF0\x90\x8C\xBC"); + + // Invalid 4 Octet Sequence (in 2nd Octet) + test_sequence_closure("\xF0\x28\x8C\xBC"); + + // Invalid 4 Octet Sequence (in 3rd Octet) + test_sequence_closure("\xF0\x90\x28\xBC"); + + // Invalid 4 Octet Sequence (in 4th Octet) + test_sequence_closure("\xF0\x28\x8C\x25"); + + fclose(temp_file_stream); + + filesystem::remove(temp_file_name_str); +} + +void test_stream_flush_console() { + // If the ordinary literal encoding is UTF-8, then the FILE stream associated with + // a console should always be flushed before writing output during a call to std::print() + // and std::println(). Otherwise, the stream should *NOT* be flushed. + test::win_console temp_console{}; + FILE* const console_file_stream = temp_console.get_file_stream(); + + print(console_file_stream, "Hello,"); + + { + const wstring extractedStr{temp_console.get_console_line(0)}; + + if constexpr (_Is_ordinary_literal_encoding_utf8()) { + assert(extractedStr == L"Hello,"); + } else { + assert(extractedStr.empty()); + } + } + + println(console_file_stream, " world!"); + + { + const wstring extractedStr{temp_console.get_console_line(0)}; + + if constexpr (_Is_ordinary_literal_encoding_utf8()) { + assert(extractedStr == L"Hello, world!"); + } else { + assert(extractedStr.empty()); + } + } + + maybe_flush_console_file_stream(temp_console); + + { + const wstring extractedStr{temp_console.get_console_line(0)}; + assert(extractedStr == L"Hello, world!"); + } +} + +void test_stream_flush_file() { + // Regardless of the ordinary literal encoding, neither std::print() nor std::println() + // should flush file streams which do not refer to consoles. + const string temp_file_name_str = temp_file_name(); + + { + ofstream output_file_stream{temp_file_name_str}; + + print(output_file_stream, "Hello, "); + + { + ifstream input_file_stream{temp_file_name_str}; + + string extracted_line_str; + getline(input_file_stream, extracted_line_str); + + assert(extracted_line_str.empty()); + } + + println(output_file_stream, "world!"); + + { + ifstream input_file_stream{temp_file_name_str}; + + string extracted_line_str; + getline(input_file_stream, extracted_line_str); + + assert(extracted_line_str.empty()); + } + + output_file_stream.flush(); + + { + ifstream input_file_stream{temp_file_name_str}; + + string extracted_line_str; + getline(input_file_stream, extracted_line_str); + + assert(extracted_line_str == "Hello, world!"); + } + } + + filesystem::remove(temp_file_name_str); +} + +void test_empty_strings_and_newlines() { + const string temp_file_name_str = temp_file_name(); + + { + ofstream output_file_stream{temp_file_name_str}; + + print(output_file_stream, "NCC-1701"); + print(output_file_stream, ""); + print(output_file_stream, "-D\n"); + print(output_file_stream, "{{}} for {}!\n", "impact"); + + println(output_file_stream, "I have {} cute {} kittens.", 1729, "fluffy"); + println(output_file_stream, ""); + println(output_file_stream, "What are an orthodontist's favorite characters? '{{' and '}}', of course!"); + println(output_file_stream, "ONE\nTWO\n"); + println(output_file_stream, "THREE"); + } + + { + ifstream input_file_stream{temp_file_name_str}; + + vector lines; + for (string str; getline(input_file_stream, str);) { + lines.push_back(str); + } + + const vector expected_lines{ + "NCC-1701-D", + "{} for impact!", + "I have 1729 cute fluffy kittens.", + "", + "What are an orthodontist's favorite characters? '{' and '}', of course!", + "ONE", + "TWO", + "", + "THREE", + }; + + assert(lines == expected_lines); + } + + filesystem::remove(temp_file_name_str); +} + +void all_tests() { + test_print_optimizations(); + + test_invalid_code_points_console(); + test_invalid_code_points_file(); + + test_stream_flush_console(); + test_stream_flush_file(); + + test_empty_strings_and_newlines(); +} + +int main(int argc, char* argv[]) { + // For clarity, we pass a --child option, instead of just detecting the semaphore values. + const bool is_child{argc == 4 && argv[1] == "--child"sv}; + + if (is_child) { + test::win_semaphore hello_semaphore{static_cast(stoull(argv[2]))}; + test::win_semaphore goodbye_semaphore{static_cast(stoull(argv[3]))}; + + // We use the hello_semaphore to tell the parent process that we're ready for it to attach to our console. + hello_semaphore.release(); + + // Then, we use the goodbye_semaphore to wait for the parent process to finish its work. + goodbye_semaphore.acquire(); + } else { + test::win_semaphore hello_semaphore{}; + test::win_semaphore goodbye_semaphore{}; + + // This will receive the child process ID. + PROCESS_INFORMATION process_information{}; + + { + // Get the absolute path of our executable. + // This assumes that our test infrastructure doesn't use long paths. + wstring module_filename; + module_filename.resize_and_overwrite(MAX_PATH, [](wchar_t* const ptr, const size_t n) { + // resize_and_overwrite() prepares a buffer of size n + 1. + // GetModuleFileNameW() takes that buffer size. + const DWORD len = GetModuleFileNameW(nullptr, ptr, static_cast(n + 1)); + assert(len != 0); + return len; + }); + + // CreateProcessW() requires the command line to be non-const, so we pass command_line.data() below. + // We use quotes to defend against module_filename containing spaces. + wstring command_line = format(LR"("{}" --child {} {})", module_filename, hello_semaphore.to_uintptr(), + goodbye_semaphore.to_uintptr()); + + // The entire purpose of this code is to hide the child process's console window, + // to avoid rapid flickering during test runs. + STARTUPINFOW startup_info{ + .cb = sizeof(STARTUPINFOW), + .dwFlags = STARTF_USESHOWWINDOW, + .wShowWindow = SW_HIDE, + }; + + constexpr BOOL inherit_handles = TRUE; + + // https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + // "The new process has a new console, instead of inheriting its parent's console (the default)." + constexpr DWORD creation_flags = CREATE_NEW_CONSOLE; + + const BOOL create_process_succeeded = CreateProcessW(module_filename.c_str(), command_line.data(), nullptr, + nullptr, inherit_handles, creation_flags, nullptr, nullptr, &startup_info, &process_information); + assert(create_process_succeeded); + } + + // Wait for the child process to be ready before attaching to its console. + hello_semaphore.acquire(); + + { + // We want to do things like alter the output code page of our console, but we don't want + // to affect other tests which run concurrently. So, we want to detach this process from + // its current console, attach to the child process's console, and modify that one instead. + + const BOOL free_console_succeeded = FreeConsole(); + assert(free_console_succeeded); + + const BOOL attach_console_succeeded = AttachConsole(process_information.dwProcessId); + assert(attach_console_succeeded); + } + + all_tests(); // Run tests with the original console output codepage. + + { + const BOOL set_console_output_cp_succeeded = SetConsoleOutputCP(CP_UTF8); + assert(set_console_output_cp_succeeded); + } + + all_tests(); // Run tests with the console output codepage set to UTF-8. + + // Tell the child process that we're done working with its console. + goodbye_semaphore.release(); + + { + const BOOL close_process_handle_succeeded = CloseHandle(process_information.hProcess); + assert(close_process_handle_succeeded); + + const BOOL close_thread_handle_succeeded = CloseHandle(process_information.hThread); + assert(close_thread_handle_succeeded); + } + } +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 0aa3e9b765a..83368cba70e 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1522,6 +1522,20 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_print +#error __cpp_lib_print is not defined +#elif __cpp_lib_print != 202207L +#error __cpp_lib_print is not 202207L +#else +STATIC_ASSERT(__cpp_lib_print == 202207L); +#endif +#else +#ifdef __cpp_lib_print +#error __cpp_lib_print is defined +#endif +#endif + #ifndef __cpp_lib_quoted_string_io #error __cpp_lib_quoted_string_io is not defined #elif __cpp_lib_quoted_string_io != 201304L diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index e9fdcfa9c7e..e65d2a64013 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -52,6 +52,7 @@ PM_CL="/DMEOW_HEADER=numbers" PM_CL="/DMEOW_HEADER=numeric" PM_CL="/DMEOW_HEADER=optional" PM_CL="/DMEOW_HEADER=ostream" +PM_CL="/DMEOW_HEADER=print" PM_CL="/DMEOW_HEADER=queue" PM_CL="/DMEOW_HEADER=random" PM_CL="/DMEOW_HEADER=ranges" From ecc7e5a2032a3937500d0bfdfa9615efde4c7097 Mon Sep 17 00:00:00 2001 From: Rose <83477269+AtariDreams@users.noreply.github.com> Date: Thu, 23 Mar 2023 21:33:41 -0400 Subject: [PATCH 5/9] Prefer fill_n over fill where applicable (#3578) --- stl/inc/deque | 7 +++---- stl/inc/vector | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/stl/inc/deque b/stl/inc/deque index a304a157dba..5667b19def2 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -1488,7 +1488,7 @@ private: } _Mid = begin() + static_cast(_Count); - _STD fill(_Mid, _Mid + static_cast(_Off), _Val); // fill in rest of values + _STD fill_n(_Mid, _Off, _Val); // fill in rest of values } else { // insert not longer than prefix for (_Num = _Count; _Num > 0; --_Num) { push_front(begin()[static_cast(_Count - 1)]); // push part of prefix @@ -1514,7 +1514,7 @@ private: } _Mid = begin() + static_cast(_Off); - _STD fill(_Mid, _Mid + static_cast(_Rem), _Val); // fill in rest of values + _STD fill_n(_Mid, _Rem, _Val); // fill in rest of values } else { // insert not longer than prefix for (_Num = 0; _Num < _Count; ++_Num) { _Emplace_back_internal( @@ -1525,8 +1525,7 @@ private: _Alloc_temporary2<_Alty> _Tmp(_Getal(), _Val); // in case _Val is in sequence _STD move_backward(_Mid, _Mid + static_cast(_Rem - _Count), _Mid + static_cast(_Rem)); // copy rest of prefix - _STD fill(_Mid, _Mid + static_cast(_Count), - _Tmp._Get_value()); // fill in values + _STD fill_n(_Mid, _Count, _Tmp._Get_value()); // fill in values } _Guard._Container = nullptr; } diff --git a/stl/inc/vector b/stl/inc/vector index 01574777fdb..dee6fb99578 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1076,7 +1076,7 @@ public: } else { // new stuff can all be assigned _Mylast = _Uninitialized_move(_Oldlast - _Count, _Oldlast, _Oldlast, _Al); _Move_backward_unchecked(_Whereptr, _Oldlast - _Count, _Oldlast); - _STD fill(_Whereptr, _Whereptr + _Count, _Tmp); + _STD fill_n(_Whereptr, _Count, _Tmp); } _ASAN_VECTOR_RELEASE_GUARD; } @@ -3458,7 +3458,7 @@ public: _CONSTEXPR20 iterator _Insert_n(const_iterator _Where, size_type _Count, const bool& _Val) { size_type _Off = _Insert_x(_Where, _Count); const auto _Result = begin() + static_cast(_Off); - _STD fill(_Result, _Result + static_cast(_Count), _Val); + _STD fill_n(_Result, _Count, _Val); return _Result; } From 581b4f089c303123071a173a16ca2f2260a3a891 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 24 Mar 2023 08:37:01 +0700 Subject: [PATCH 6/9] Don't warn when using operator<=> with 0 (#3581) Co-authored-by: Stephan T. Lavavej --- stl/inc/compare | 15 +++++++++++++-- .../std/tests/P0768R1_spaceship_operator/test.cpp | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index 76723bf8162..99e4a402cde 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -27,8 +27,19 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN -using _Literal_zero = decltype(nullptr); -using _Compare_t = signed char; +void _Literal_zero_is_expected(); + +struct _Literal_zero { + template , int> = 0> + consteval _Literal_zero(_Ty _Zero) noexcept { + // Can't use _STL_VERIFY because this is a core header + if (_Zero != 0) { + _Literal_zero_is_expected(); + } + } +}; + +using _Compare_t = signed char; // These "pretty" enumerator names are safe since they reuse names of user-facing entities. enum class _Compare_eq : _Compare_t { equal = 0, equivalent = equal }; diff --git a/tests/std/tests/P0768R1_spaceship_operator/test.cpp b/tests/std/tests/P0768R1_spaceship_operator/test.cpp index 33a1218f7d6..00cfcc17a5c 100644 --- a/tests/std/tests/P0768R1_spaceship_operator/test.cpp +++ b/tests/std/tests/P0768R1_spaceship_operator/test.cpp @@ -7,6 +7,11 @@ #include #include +// See GH-3581 for details +#ifdef __clang__ +#pragma clang diagnostic error "-Wzero-as-null-pointer-constant" +#endif // __clang__ + enum class comp { equal, less, greater, unordered }; template From c353b688ac9cfb2453de94e306e1e25aca894f86 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 23 Mar 2023 18:42:40 -0700 Subject: [PATCH 7/9] P1467R9 Extended Floating-Point Types (#3583) --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/header-units.json | 1 + stl/inc/stdfloat | 36 +++++++++++++++++++ stl/inc/yvals_core.h | 2 ++ stl/modules/std.ixx | 1 + .../include/test_header_units_and_modules.hpp | 7 ++++ .../test.cpp | 6 ++++ .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../include_each_header_alone_matrix.lst | 1 + 11 files changed, 58 insertions(+) create mode 100644 stl/inc/stdfloat diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 6fff5b44da2..3cd7e18dc6a 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -201,6 +201,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/stack ${CMAKE_CURRENT_LIST_DIR}/inc/stacktrace ${CMAKE_CURRENT_LIST_DIR}/inc/stdexcept + ${CMAKE_CURRENT_LIST_DIR}/inc/stdfloat ${CMAKE_CURRENT_LIST_DIR}/inc/stop_token ${CMAKE_CURRENT_LIST_DIR}/inc/streambuf ${CMAKE_CURRENT_LIST_DIR}/inc/string diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 7b63085c8ba..ccb3267943a 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 0bc5e0c9636..0d21964d524 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -107,6 +107,7 @@ "stack", "stacktrace", "stdexcept", + "stdfloat", "stop_token", "streambuf", "string", diff --git a/stl/inc/stdfloat b/stl/inc/stdfloat new file mode 100644 index 00000000000..f82ed8cc21d --- /dev/null +++ b/stl/inc/stdfloat @@ -0,0 +1,36 @@ +// stdfloat standard header (core) + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _STDFLOAT_ +#define _STDFLOAT_ +#include +#if _STL_COMPILER_PREPROCESSOR + +#if !_HAS_CXX23 +_EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); +#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +// We don't support any optional extended floating-point types, but we do need to provide an empty `namespace std`. +// We don't need to `export` it for modules, as the other headers will implicitly do so (N4928 [module.interface]/2.1). +_STD_BEGIN +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // ^^^ _HAS_CXX23 ^^^ + +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _STDFLOAT_ diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index b0c34181d72..b80a951e5ad 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -319,6 +319,8 @@ // P1272R4 byteswap() // P1328R1 constexpr type_info::operator==() // P1425R4 Iterator Pair Constructors For stack And queue +// P1467R9 Extended Floating-Point Types +// (only the header; we don't support any optional extended floating-point types) // P1659R3 ranges::starts_with, ranges::ends_with // P1679R3 contains() For basic_string/basic_string_view // P1682R3 to_underlying() For Enumerations diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index 5dd3f99d1aa..326d944d74f 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -101,6 +101,7 @@ export module std; #include #include #include +#include #include #include #include diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index 51e07d54d28..d8530032085 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -782,6 +782,12 @@ void test_stdexcept() { assert(caught_puppies); } +void test_stdfloat() { + using namespace std; + puts("Testing ."); + // `namespace std` is available, so we're done. +} + void test_stop_token() { using namespace std; puts("Testing ."); @@ -1120,6 +1126,7 @@ void all_cpp_header_tests() { test_stack(); test_stacktrace(); test_stdexcept(); + test_stdfloat(); test_stop_token(); test_streambuf(); test_string(); diff --git a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp index 4dbe0fed137..7c0efffbf09 100644 --- a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp +++ b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp @@ -292,4 +292,10 @@ int main() { assert((proj(-inf) == complex{inf, 0.0})); test_gh_2728(); + + // Also test N4928 [complex.numbers.general]/2: + // "Specializations of complex for cv-unqualified floating-point types are trivially-copyable literal types" + STATIC_ASSERT(is_trivially_copyable_v>); + STATIC_ASSERT(is_trivially_copyable_v>); + STATIC_ASSERT(is_trivially_copyable_v>); } diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index 585535d3ca1..70d0af803ec 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -65,6 +65,7 @@ "stack", "stacktrace", "stdexcept", + "stdfloat", "stop_token", "streambuf", "string", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index c82a77835a5..f9fa3090a20 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -71,6 +71,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index e65d2a64013..9b041e051e5 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -70,6 +70,7 @@ PM_CL="/DMEOW_HEADER=stack" PM_CL="/DMEOW_HEADER=stacktrace" PM_CL="/DMEOW_HEADER=stdatomic.h" PM_CL="/DMEOW_HEADER=stdexcept" +PM_CL="/DMEOW_HEADER=stdfloat" PM_CL="/DMEOW_HEADER=stop_token" PM_CL="/DMEOW_HEADER=streambuf" PM_CL="/DMEOW_HEADER=string" From f0bf77e549437dcc8a8344e6522b04a299f7b869 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 23 Mar 2023 18:52:16 -0700 Subject: [PATCH 8/9] Fix CodeQL warnings, part 2 (#3585) --- stl/inc/xbit_ops.h | 4 ++-- stl/inc/xtimec.h | 7 +++++-- stl/src/cond.cpp | 4 ++-- stl/src/cthread.cpp | 4 ++-- stl/src/mutex.cpp | 4 ++-- stl/src/vector_algorithms.cpp | 26 ++++++++++++++++++-------- stl/src/xtime.cpp | 8 +++++--- 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/stl/inc/xbit_ops.h b/stl/inc/xbit_ops.h index a46e3cdfb9c..74613792362 100644 --- a/stl/inc/xbit_ops.h +++ b/stl/inc/xbit_ops.h @@ -39,9 +39,9 @@ _NODISCARD inline unsigned long _Floor_of_log_2(size_t _Value) noexcept { // ret #else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv #ifdef _WIN64 - _BitScanReverse64(&_Result, _Value); + _BitScanReverse64(&_Result, _Value); // lgtm [cpp/conditionallyuninitializedvariable] #else // ^^^ 64-bit / 32-bit vvv - _BitScanReverse(&_Result, _Value); + _BitScanReverse(&_Result, _Value); // lgtm [cpp/conditionallyuninitializedvariable] #endif // 64 vs. 32-bit #endif // _M_CEE_PURE diff --git a/stl/inc/xtimec.h b/stl/inc/xtimec.h index 12419359719..49785d48768 100644 --- a/stl/inc/xtimec.h +++ b/stl/inc/xtimec.h @@ -25,11 +25,14 @@ struct xtime { // store time with nanosecond resolution long nsec; }; -_CRTIMP2_PURE int __cdecl xtime_get(xtime*, int); - _CRTIMP2_PURE long __cdecl _Xtime_diff_to_millis2(const xtime*, const xtime*); _CRTIMP2_PURE long long __cdecl _Xtime_get_ticks(); +#ifdef _CRTBLD +// Used by several src files, but not dllexported. +void _Xtime_get2(xtime*); +#endif // _CRTBLD + _CRTIMP2_PURE long long __cdecl _Query_perf_counter(); _CRTIMP2_PURE long long __cdecl _Query_perf_frequency(); diff --git a/stl/src/cond.cpp b/stl/src/cond.cpp index 46d82ae1132..bc3acb9dc7f 100644 --- a/stl/src/cond.cpp +++ b/stl/src/cond.cpp @@ -69,10 +69,10 @@ int _Cnd_timedwait(const _Cnd_t cond, const _Mtx_t mtx, const xtime* const targe _Mtx_reset_owner(mtx); } else { // target time specified, wait for it xtime now; - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); _Mtx_clear_owner(mtx); if (!cond->_get_cv()->wait_for(cs, _Xtime_diff_to_millis2(target, &now))) { // report timeout - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); if (_Xtime_diff_to_millis2(target, &now) == 0) { res = _Thrd_timedout; } diff --git a/stl/src/cthread.cpp b/stl/src/cthread.cpp index 20512fc6580..24ff401d428 100644 --- a/stl/src/cthread.cpp +++ b/stl/src/cthread.cpp @@ -74,10 +74,10 @@ int _Thrd_detach(_Thrd_t thr) { // tell OS to release thread's resources when it void _Thrd_sleep(const xtime* xt) { // suspend thread until time xt xtime now; - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); do { // sleep and check time Sleep(_Xtime_diff_to_millis2(xt, &now)); - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); } while (now.sec < xt->sec || now.sec == xt->sec && now.nsec < xt->nsec); } diff --git a/stl/src/mutex.cpp b/stl/src/mutex.cpp index 7b99c2b2920..543838b1d8a 100644 --- a/stl/src/mutex.cpp +++ b/stl/src/mutex.cpp @@ -116,7 +116,7 @@ static int mtx_do_lock(_Mtx_t mtx, const xtime* target) { // lock mutex } else { // check timeout xtime now; - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); while (now.sec < target->sec || now.sec == target->sec && now.nsec < target->nsec) { // time has not expired if (mtx->thread_id == static_cast(GetCurrentThreadId()) || mtx->_get_cs()->try_lock_for(_Xtime_diff_to_millis2(target, &now))) { // stop waiting @@ -126,7 +126,7 @@ static int mtx_do_lock(_Mtx_t mtx, const xtime* target) { // lock mutex res = WAIT_TIMEOUT; } - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); } } diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 82b13133411..a38d93c281f 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -879,7 +879,10 @@ namespace { // Select the smallest vertical indices from the smallest element mask _Mask &= _mm_movemask_epi8(_Traits::_Cmp_eq(_Idx_min, _Idx_min_val)); unsigned long _H_pos; - _BitScanForward(&_H_pos, _Mask); // Find the smallest horizontal index + + // Find the smallest horizontal index + _BitScanForward(&_H_pos, _Mask); // lgtm [cpp/conditionallyuninitializedvariable] + const auto _V_pos = _Traits::_Get_v_pos(_Cur_idx_min, _H_pos); // Extract its vertical index _Res._Min = _Base + _V_pos * 16 + _H_pos; // Finally, compute the pointer } @@ -908,7 +911,10 @@ namespace { const __m128i _Idx_max = _Traits::_H_max_u(_Idx_max_val); // The greatest indices // Select the greatest vertical indices from the largest element mask _Mask &= _mm_movemask_epi8(_Traits::_Cmp_eq(_Idx_max, _Idx_max_val)); - _BitScanReverse(&_H_pos, _Mask); // Find the largest horizontal index + + // Find the largest horizontal index + _BitScanReverse(&_H_pos, _Mask); // lgtm [cpp/conditionallyuninitializedvariable] + _H_pos -= sizeof(_Cur_max_val) - 1; // Correct from highest val bit to lowest } else { // Looking for the first occurrence of maximum @@ -918,7 +924,9 @@ namespace { const __m128i _Idx_max = _Traits::_H_min_u(_Idx_max_val); // The smallest indices // Select the smallest vertical indices from the largest element mask _Mask &= _mm_movemask_epi8(_Traits::_Cmp_eq(_Idx_max, _Idx_max_val)); - _BitScanForward(&_H_pos, _Mask); // Find the smallest horizontal index + + // Find the smallest horizontal index + _BitScanForward(&_H_pos, _Mask); // lgtm [cpp/conditionallyuninitializedvariable] } const auto _V_pos = _Traits::_Get_v_pos(_Cur_idx_max, _H_pos); // Extract its vertical index @@ -1206,7 +1214,8 @@ namespace { __m256i _Data = _mm256_load_si256(static_cast(_First)); unsigned int _Bingo = static_cast(_mm256_movemask_epi8(_Traits::_Cmp_avx(_Data, _Comparand))); - if ((_Bingo &= _Mask) != 0) { + _Bingo &= _Mask; + if (_Bingo != 0) { unsigned long _Offset = _tzcnt_u32(_Bingo); _Advance_bytes(_First, _Offset); return _First; @@ -1241,9 +1250,10 @@ namespace { __m128i _Data = _mm_load_si128(static_cast(_First)); unsigned int _Bingo = static_cast(_mm_movemask_epi8(_Traits::_Cmp_sse(_Data, _Comparand))); - if ((_Bingo &= _Mask) != 0) { + _Bingo &= _Mask; + if (_Bingo != 0) { unsigned long _Offset; - _BitScanForward(&_Offset, _Bingo); + _BitScanForward(&_Offset, _Bingo); // lgtm [cpp/conditionallyuninitializedvariable] _Advance_bytes(_First, _Offset); return _First; } @@ -1254,7 +1264,7 @@ namespace { if (_Bingo != 0) { unsigned long _Offset; - _BitScanForward(&_Offset, _Bingo); + _BitScanForward(&_Offset, _Bingo); // lgtm [cpp/conditionallyuninitializedvariable] _Advance_bytes(_First, _Offset); return _First; } @@ -1301,7 +1311,7 @@ namespace { if (_Bingo != 0) { unsigned long _Offset; - _BitScanForward(&_Offset, _Bingo); + _BitScanForward(&_Offset, _Bingo); // lgtm [cpp/conditionallyuninitializedvariable] _Advance_bytes(_First, _Offset); return _First; } diff --git a/stl/src/xtime.cpp b/stl/src/xtime.cpp index a6139b68683..36a97db159e 100644 --- a/stl/src/xtime.cpp +++ b/stl/src/xtime.cpp @@ -53,7 +53,8 @@ _CRTIMP2_PURE long long __cdecl _Xtime_get_ticks() { // get system time in 100-n return ((static_cast(ft.dwHighDateTime)) << 32) + static_cast(ft.dwLowDateTime) - _Epoch; } -static void sys_get_time(xtime* xt) { // get system time with nanosecond resolution +// Used by several src files, but not dllexported. +void _Xtime_get2(xtime* xt) { // get system time with nanosecond resolution unsigned long long now = _Xtime_get_ticks(); xt->sec = static_cast<__time64_t>(now / _Nsec100_per_sec); xt->nsec = static_cast(now % _Nsec100_per_sec) * 100; @@ -67,15 +68,16 @@ _CRTIMP2_PURE long __cdecl _Xtime_diff_to_millis2(const xtime* xt1, const xtime* // TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE long __cdecl _Xtime_diff_to_millis(const xtime* xt) { // convert time to milliseconds xtime now; - xtime_get(&now, TIME_UTC); + _Xtime_get2(&now); return _Xtime_diff_to_millis2(xt, &now); } +// TRANSITION, ABI: preserved for binary compatibility _CRTIMP2_PURE int __cdecl xtime_get(xtime* xt, int type) { // get current time if (type != TIME_UTC || xt == nullptr) { type = 0; } else { - sys_get_time(xt); + _Xtime_get2(xt); } return type; From 5e603826ea2f4bfbe3d72b33967193b2edf75ce5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 24 Mar 2023 12:02:36 -0700 Subject: [PATCH 9/9] Fix modules test by using `static constexpr` arrays (#3588) --- tests/std/include/test_header_units_and_modules.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index d8530032085..ebd07763d14 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -331,7 +331,7 @@ void test_istream() { void test_iterator() { using namespace std; puts("Testing ."); - constexpr int arr[]{10, 20, 30, 40, 50}; + static constexpr int arr[]{10, 20, 30, 40, 50}; constexpr reverse_iterator ri{end(arr)}; assert(*ri == 50); assert(*next(ri) == 40); @@ -685,7 +685,7 @@ void test_source_location() { void test_span() { using namespace std; puts("Testing ."); - constexpr int arr[]{11, 22, 33, 44, 55}; + static constexpr int arr[]{11, 22, 33, 44, 55}; constexpr span whole{arr}; constexpr span mid = whole.subspan<1, 3>(); assert(mid[0] == 22 && mid[1] == 33 && mid[2] == 44);