From 45c4f09bab59c05d2648eb2b30409065a98fcd6b Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 12:03:52 +0800 Subject: [PATCH 01/45] fast_io --- third-parties/fast_io/include/fast_io_core.h | 2 + .../fast_io_core_impl/manipulators/pack.h | 75 +++++++++ .../printimpl/print_freestanding_cxx20.h | 148 +++++++++++++++++- 3 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h diff --git a/third-parties/fast_io/include/fast_io_core.h b/third-parties/fast_io/include/fast_io_core.h index 5baeecc5b..f529d304d 100644 --- a/third-parties/fast_io/include/fast_io_core.h +++ b/third-parties/fast_io/include/fast_io_core.h @@ -66,6 +66,8 @@ #include "fast_io_core_impl/perms.h" #include "fast_io_dsal/impl/common.h" #include "fast_io_dsal/impl/span.h" +#include "fast_io_dsal/impl/tuple.h" +#include "fast_io_core_impl/manipulators/pack.h" #include "fast_io_core_impl/operations/impl.h" // This should provide an option macro to disable any generation for table in freestanding environments. diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h new file mode 100644 index 000000000..e8738951d --- /dev/null +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h @@ -0,0 +1,75 @@ +#pragma once + +namespace fast_io +{ + +namespace details +{ + +template +concept pack_value_transferable = ::std::is_trivially_copyable_v<::std::remove_cvref_t> && +#if (defined(_WIN32) && !defined(__WINE__)) || defined(__CYGWIN__) + sizeof(::std::remove_cvref_t) <= 8u +#else + sizeof(::std::remove_cvref_t) <= (sizeof(::std::size_t) * 2) +#endif + ; + +template +using pack_alias_type = + ::std::conditional_t<::fast_io::details::alias_return_lvalue_ref, + ::std::conditional_t<::fast_io::details::pack_value_transferable, + ::std::remove_cvref_t, ::std::remove_cvref_t const &>, + ::std::remove_cvref_t()))>>; + +} // namespace details + +namespace manipulators +{ + +struct pack_manip_tag_t +{}; + +template +struct pack_t +{ + using manip_tag = ::fast_io::manip_tag_t; + using semantic_tag = pack_manip_tag_t; + using storage_type = ::fast_io::containers::tuple; + inline static constexpr ::std::size_t size = sizeof...(Args); + + storage_type storage; +}; + +template +inline constexpr auto pack(Args &&...args) noexcept +{ +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + return pack_t<::fast_io::details::pack_alias_type...>{ + ::fast_io::containers::tuple<::fast_io::details::pack_alias_type...>{ + ::fast_io::io_print_alias(::std::forward(args))...}}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +} + +} // namespace manipulators + +namespace details +{ + +template +inline constexpr bool is_print_pack_v = false; + +template +inline constexpr bool is_print_pack_v<::fast_io::manipulators::pack_t> = true; + +template +concept print_pack = is_print_pack_v<::std::remove_cvref_t>; + +} // namespace details + +} // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index e7a7172a2..26761f175 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -1839,6 +1839,125 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } } +template +inline constexpr bool print_semantic_pack_parameter_v = false; + +template +inline constexpr bool print_semantic_pack_parameter_v<::fast_io::parameter> = + ::fast_io::details::print_pack<::std::remove_cvref_t>; + +template +concept print_semantic_pack_node = + ::fast_io::details::print_pack || + ::fast_io::details::decay::print_semantic_pack_parameter_v<::std::remove_cvref_t>; + +template +inline constexpr decltype(auto) print_semantic_pack_ref(T &&t) noexcept +{ + if constexpr (::fast_io::details::print_pack) + { + return ::std::forward(t); + } + else + { + return ::fast_io::details::decay::print_semantic_pack_ref(::std::forward(t).reference); + } +} + +template +inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuation &&cont, + ::std::index_sequence) +{ + auto &&pack_ref{::fast_io::details::decay::print_semantic_pack_ref(::std::forward(t))}; + return cont(::fast_io::containers::get(::std::forward(pack_ref).storage)...); +} + +template +inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation &&cont) +{ + using pack_type = + ::std::remove_cvref_t(t)))>; + return ::fast_io::details::decay::print_semantic_pack_apply_impl( + ::std::forward(t), ::std::forward(cont), ::std::make_index_sequence{}); +} + +template +inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont) +{ + return cont(); +} + +template +inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args) +{ + if constexpr (::fast_io::details::decay::print_semantic_pack_node) + { + return ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(t), [&](PackArgs &&...pack_args) -> decltype(auto) { + return ::fast_io::details::decay::print_semantic_pack_expand( + [&](ExpandedPackArgs &&...expanded_pack_args) -> decltype(auto) { + return ::fast_io::details::decay::print_semantic_pack_expand( + [&](TailArgs &&...tail_args) -> decltype(auto) { + return cont(::std::forward(expanded_pack_args)..., + ::std::forward(tail_args)...); + }, + ::std::forward(args)...); + }, + ::std::forward(pack_args)...); + }); + } + else if constexpr (already_forwarded) + { + return ::fast_io::details::decay::print_semantic_pack_expand( + [&](TailArgs &&...tail_args) -> decltype(auto) { + return cont(::std::forward(t), ::std::forward(tail_args)...); + }, + ::std::forward(args)...); + } + else + { + auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; + return ::fast_io::details::decay::print_semantic_pack_expand( + [&](TailArgs &&...tail_args) -> decltype(auto) { + return cont(forwarded, ::std::forward(tail_args)...); + }, + ::std::forward(args)...); + } +} + +template <::std::integral char_type, typename T> +struct print_freestanding_decay_param_okay_single; + +template <::std::integral char_type, typename T> +struct print_semantic_pack_params_okay : ::std::false_type +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_pack_params_okay> + : print_semantic_pack_params_okay> +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_pack_params_okay> + : ::std::bool_constant< + (print_freestanding_decay_param_okay_single< + char_type, + decltype(::fast_io::io_print_forward( + ::fast_io::io_print_alias(::std::declval())))>::value && + ...)> +{}; + +template <::std::integral char_type, typename T> +struct print_freestanding_decay_param_okay_single + : ::std::bool_constant< + ::fast_io::printable || ::fast_io::reserve_printable || + ::fast_io::dynamic_reserve_printable || ::fast_io::scatter_printable || + ::fast_io::reserve_scatters_printable || ::fast_io::context_printable || + ::fast_io::transcode_imaginary_printable || + ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || + print_semantic_pack_params_okay>::value> +{}; + } // namespace details::decay namespace operations @@ -1848,7 +1967,7 @@ namespace decay { template -inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, Args... args) +inline constexpr decltype(auto) print_freestanding_decay_no_pack(outputstmtype optstm, Args... args) { if constexpr (::fast_io::operations::decay::defines::has_status_print_define) { @@ -1870,7 +1989,7 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A { ::fast_io::operations::decay::stream_ref_decay_lock_guard lg{ ::fast_io::operations::decay::output_stream_mutex_ref_decay(optstm)}; - return ::fast_io::operations::decay::print_freestanding_decay( + return ::fast_io::operations::decay::print_freestanding_decay_no_pack( ::fast_io::operations::decay::output_stream_unlocked_ref_decay(optstm), args...); } else if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) @@ -1883,6 +2002,25 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A } } +template +inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, Args... args) +{ + if constexpr ((::fast_io::details::decay::print_semantic_pack_node || ...)) + { + using char_type = typename outputstmtype::output_char_type; + return ::fast_io::details::decay::print_semantic_pack_expand( + [optstm](FlatArgs &&...flat_args) constexpr -> decltype(auto) { + return ::fast_io::operations::decay::print_freestanding_decay_no_pack( + optstm, ::std::forward(flat_args)...); + }, + args...); + } + else + { + return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); + } +} + template #if __has_cpp_attribute(__gnu__::__cold__) [[__gnu__::__cold__]] @@ -1903,11 +2041,7 @@ namespace decay::defines template concept print_freestanding_params_okay = ::std::integral && - ((::fast_io::printable || ::fast_io::reserve_printable || - ::fast_io::dynamic_reserve_printable || ::fast_io::scatter_printable || - ::fast_io::reserve_scatters_printable || ::fast_io::context_printable || - ::fast_io::transcode_imaginary_printable || ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) && - ...); + (::fast_io::details::decay::print_freestanding_decay_param_okay_single::value && ...); template concept print_freestanding_okay = From 9e4262cb494ca30153bf76818b8d4ce386cdb4df Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 14:11:38 +0800 Subject: [PATCH 02/45] Optimize fast_io C++20 semantic print nodes --- .../include/fast_io_core_impl/buffer_view.h | 39 + .../fast_io_core_impl/codecvt/general.h | 10 +- .../fast_io_core_impl/concepts/operation.h | 26 + .../fast_io_core_impl/dynamic_output_buffer.h | 15 + .../fast_io_core_impl/integers/pointer.h | 116 +- .../fast_io_core_impl/manipulators/pack.h | 17 + .../printimpl/print_freestanding_cxx20.h | 1364 ++++++++++++++++- .../include/fast_io_dsal/impl/cstring_view.h | 33 + .../include/fast_io_dsal/impl/string.h | 34 + .../include/fast_io_dsal/impl/string_view.h | 33 + .../include/fast_io_freestanding_impl/cond.h | 56 +- .../include/fast_io_freestanding_impl/width.h | 2 +- 12 files changed, 1645 insertions(+), 100 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/buffer_view.h b/third-parties/fast_io/include/fast_io_core_impl/buffer_view.h index b23563457..1aad33acb 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/buffer_view.h +++ b/third-parties/fast_io/include/fast_io_core_impl/buffer_view.h @@ -188,6 +188,45 @@ struct basic_obuffer_view } }; +template <::std::integral char_type> +inline constexpr basic_io_scatter_t +print_alias_define(io_alias_t, basic_obuffer_view const &view) noexcept +{ + return {view.cbegin(), view.size()}; +} + +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_size(io_reserve_type_t>, + basic_obuffer_view const &view) noexcept +{ + return view.size(); +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_define(io_reserve_type_t>, + char_type *iter, basic_obuffer_view const &view) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(view.cbegin(), view.size(), iter); +} + +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_precise_size(io_reserve_type_t>, + basic_obuffer_view const &view) noexcept +{ + return view.size(); +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_precise_define(io_reserve_type_t>, + char_type *iter, ::std::size_t, basic_obuffer_view const &view) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(view.cbegin(), view.size(), iter); +} + template <::std::integral char_type> struct basic_obuffer_view_ref { diff --git a/third-parties/fast_io/include/fast_io_core_impl/codecvt/general.h b/third-parties/fast_io/include/fast_io_core_impl/codecvt/general.h index 631aefd1f..9dd500c97 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/codecvt/general.h +++ b/third-parties/fast_io/include/fast_io_core_impl/codecvt/general.h @@ -568,7 +568,8 @@ inline constexpr bool print_alias_test_codecvt_impl() noexcept if constexpr (type_has_value_type) { using value_type = typename alias_type::value_type; - return ::std::same_as>; + return ::std::same_as> || + ::std::convertible_to>; } else { @@ -616,6 +617,13 @@ inline constexpr auto code_cvt(small_scatter_t t) noexcept return code_cvt_t{{t.base, t.len}}; } +template +inline constexpr auto code_cvt(static_scatter_t t) noexcept +{ + return code_cvt_t{{t.base, N}}; +} + template inline constexpr auto code_cvt_os_c_str(char_type const *cstr) noexcept diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index 81ee0096e..b38503430 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -243,6 +243,24 @@ concept precise_reserve_printable = print_reserve_precise_define(io_reserve_type>, ptr, n, t); }; +/// @brief static_precise_reserve_printable +/// @details That a type is static precise reserve printable +/// is that its precise printing size is known as a compile-time +/// constant without inspecting an object. +/// @fn print_reserve_static_precise_size +/// @brief Returns the precise size of the printed object as a constant expression. +/// @tparam +/// @param ::fast_io::io_reserve_type_t> tag-invoke +/// @return ::std::size_t the precise size of the output +template +concept static_precise_reserve_printable = + ::std::integral && + (reserve_printable || dynamic_reserve_printable) && requires { + typename ::fast_io::details::reserve_static_stack_size_constant>)>; + requires(print_reserve_static_precise_size(io_reserve_type>) != SIZE_MAX); + }; + /// @brief reserve_scatters_printable /// @details That a type is reserve scatters printable /// is that it's composed of seperate parts @@ -444,6 +462,14 @@ inline constexpr ::std::size_t print_reserve_precise_size(io_reserve_type_t>, para.reference); } +template <::std::integral char_type, typename value_type> + requires static_precise_reserve_printable> +inline constexpr ::std::size_t +print_reserve_static_precise_size(io_reserve_type_t>) noexcept +{ + return print_reserve_static_precise_size(io_reserve_type>); +} + template <::std::integral char_type, typename value_type, typename Iter> requires precise_reserve_printable> inline constexpr void print_reserve_precise_define(io_reserve_type_t>, Iter begin, diff --git a/third-parties/fast_io/include/fast_io_core_impl/dynamic_output_buffer.h b/third-parties/fast_io/include/fast_io_core_impl/dynamic_output_buffer.h index d2c5b1557..85c61ca18 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/dynamic_output_buffer.h +++ b/third-parties/fast_io/include/fast_io_core_impl/dynamic_output_buffer.h @@ -55,6 +55,13 @@ output_stream_ref_define(basic_generic_dynamic_output_buffer +inline constexpr basic_dynamic_output_buffer_ref +output_stream_ref_define(basic_dynamic_output_buffer_ref diobref) noexcept +{ + return diobref; +} + namespace details { @@ -190,6 +197,14 @@ inline constexpr void obuffer_set_curr( bdobr.dob_ptr->curr_ptr = it; } +template <::std::integral char_type, ::std::size_t buffersize, typename allocatortype> +inline constexpr void output_stream_buffer_flush_define( + basic_dynamic_output_buffer_ref> + bdobr) noexcept +{ + ::fast_io::details::grow_twice_define_impl(*bdobr.dob_ptr); +} + template <::std::integral char_type, ::std::size_t buffersize, typename allocatortype> inline constexpr void obuffer_overflow( basic_dynamic_output_buffer_ref> bdobr, diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h index dc72b94e0..69bedcabd 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h @@ -32,6 +32,18 @@ struct small_scatter_t ::std::size_t len{}; }; +template <::std::integral ch_type, ::std::size_t N> +struct static_scatter_t +{ + using manip_tag = manip_tag_t; + using value_type = ch_type; + ch_type const *base{}; + inline constexpr operator ::fast_io::basic_io_scatter_t() const noexcept + { + return {base, N}; + } +}; + template <::std::integral T> inline constexpr chvw_t chvw(T ch) noexcept { @@ -91,28 +103,24 @@ template <::std::integral char_type, ::std::size_t n> requires(n != 0) inline constexpr auto small_scatter(char_type const (&s)[n]) noexcept { - constexpr bool not_char_literal{::std::is_const_v}; - if constexpr (not_char_literal) + using no_const_char_type = ::std::remove_const_t; + constexpr ::std::size_t nm1{n - 1}; + constexpr ::std::size_t boundary{64}, boundaryp1{boundary + 1}; + if constexpr (n == 1) + { + return basic_io_scatter_t{s, 0}; + } + else if constexpr (n == 2) { - constexpr ::std::size_t nm1{n - 1}; - constexpr ::std::size_t boundary{64}, boundaryp1{boundary + 1}; - if constexpr (n == 2) - { - return manipulators::chvw_t<::std::remove_const_t>{*s}; - } - else if constexpr (n < boundaryp1) - { - return ::fast_io::manipulators::small_scatter_t<::std::remove_const_t, boundary>{s, nm1}; - } - else - { - return basic_io_scatter_t<::std::remove_const_t>{s, nm1}; - } + return manipulators::chvw_t{*s}; + } + else if constexpr (n < boundaryp1) + { + return ::fast_io::manipulators::static_scatter_t{s}; } else { - static_assert(not_char_literal, "The type is an array but not char array literal. Reject."); - return; + return basic_io_scatter_t{s, nm1}; } } @@ -197,10 +205,18 @@ inline constexpr auto print_alias_define(io_alias_t, T const &s) noexcept static_assert(n != 0); static_assert(not_char_literal, "The type is an array but not char array literal. Reject."); - if constexpr (n == 2) + if constexpr (n == 1) + { + return basic_io_scatter_t{s, 0}; + } + else if constexpr (n == 2) { return manipulators::chvw_t{*s}; } + else if constexpr (n < 65) + { + return manipulators::static_scatter_t{s}; + } else { return basic_io_scatter_t{s, nm1}; @@ -222,6 +238,13 @@ print_reserve_size(io_reserve_type_t return 1; } +template <::std::integral char_type, ::std::integral pchar_type> +inline constexpr ::std::size_t +print_reserve_static_precise_size(io_reserve_type_t>) noexcept +{ + return 1; +} + template <::std::integral char_type, ::std::integral pchar_type, typename T> inline constexpr char_type *print_reserve_define(io_reserve_type_t>, char_type *iter, T ch) noexcept @@ -231,6 +254,61 @@ inline constexpr char_type *print_reserve_define(io_reserve_type_t +inline constexpr ::std::size_t +print_reserve_precise_size(io_reserve_type_t>, + manipulators::chvw_t) noexcept +{ + return 1; +} + +template <::std::integral char_type, ::std::integral pchar_type> +inline constexpr char_type * +print_reserve_precise_define(io_reserve_type_t>, + char_type *iter, ::std::size_t, manipulators::chvw_t ch) noexcept +{ + return print_reserve_define(io_reserve_type>, iter, ch); +} + +template <::std::integral char_type, ::std::size_t N> +inline constexpr ::std::size_t +print_reserve_size(io_reserve_type_t>) noexcept +{ + return N; +} + +template <::std::integral char_type, ::std::size_t N> +inline constexpr ::std::size_t +print_reserve_static_precise_size(io_reserve_type_t>) noexcept +{ + return N; +} + +template <::std::integral char_type, ::std::size_t N> +inline constexpr char_type * +print_reserve_define(io_reserve_type_t>, + char_type *iter, ::fast_io::manipulators::static_scatter_t scatter) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(scatter.base, N, iter); +} + +template <::std::integral char_type, ::std::size_t N> +inline constexpr ::std::size_t +print_reserve_precise_size(io_reserve_type_t>, + ::fast_io::manipulators::static_scatter_t) noexcept +{ + return N; +} + +template <::std::integral char_type, ::std::size_t N> +inline constexpr char_type * +print_reserve_precise_define(io_reserve_type_t>, + char_type *iter, ::std::size_t, + ::fast_io::manipulators::static_scatter_t scatter) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(scatter.base, N, iter); +} + template <::std::integral char_type, ::std::size_t N> inline constexpr ::std::size_t print_reserve_size(io_reserve_type_t>) noexcept diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h index e8738951d..a19269584 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h @@ -27,6 +27,23 @@ using pack_alias_type = namespace manipulators { +template +struct condition; + +enum class scalar_placement : char8_t; + +template +struct width_t; + +template +struct width_ch_t; + +template +struct width_runtime_t; + +template +struct width_runtime_ch_t; + struct pack_manip_tag_t {}; diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 26761f175..1dabeb2c1 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -269,6 +269,115 @@ inline auto prrsvsct_byte_common_impl(io_scatter_t *pscatters, char_type *buffer return ::fast_io::details::decay::prrsvsct_byte_common_rsvsc_impl(pscatters, buffer, t).scatters_pos_ptr; } +inline constexpr ::std::size_t print_small_scatter_coalesce_threshold_bytes{256u}; + +template +inline constexpr char_type *print_small_scatter_copy_n(char_type const *first, ::std::size_t count, + char_type *result) noexcept +{ + if (count <= 16u) + { + for (::std::size_t i{}; i != count; ++i) + { + result[i] = first[i]; + } + return result + count; + } + return ::fast_io::details::non_overlapped_copy_n(first, count, result); +} + +template +inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype outstm, + ::fast_io::io_scatter_t const *scatters, + ::std::size_t n) +{ + if (n == 0) + { + return; + } + if (n == 1) + { + ::std::size_t const len{scatters->len}; + if (len != 0) + { + auto first{static_cast<::std::byte const *>(scatters->base)}; + ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, first + len); + } + return; + } + ::std::byte buffer[::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes]; + ::std::byte *curr{buffer}; + ::std::size_t remaining{::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes}; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (remaining < len) + { + ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); + return; + } + if (len != 0) + { + curr = ::fast_io::details::decay::print_small_scatter_copy_n(static_cast<::std::byte const *>(iter->base), + len, curr); + remaining -= len; + } + } + if (curr != buffer) + { + ::fast_io::operations::decay::write_all_bytes_decay(outstm, buffer, curr); + } +} + +template +inline constexpr void print_scatter_write_all_maybe_coalesce( + outputstmtype outstm, ::fast_io::basic_io_scatter_t const *scatters, + ::std::size_t n) +{ + using char_type = typename outputstmtype::output_char_type; + if (n == 0) + { + return; + } + if (n == 1) + { + auto [base, len] = *scatters; + if (len != 0) + { + ::fast_io::operations::decay::write_all_decay(outstm, base, base + len); + } + return; + } + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes / sizeof(char_type)}; + if constexpr (threshold_chars != 0) + { + char_type buffer[threshold_chars]; + char_type *curr{buffer}; + ::std::size_t remaining{threshold_chars}; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (remaining < len) + { + ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); + return; + } + if (len != 0) + { + curr = ::fast_io::details::decay::print_small_scatter_copy_n(iter->base, len, curr); + remaining -= len; + } + } + if (curr != buffer) + { + ::fast_io::operations::decay::write_all_decay(outstm, buffer, curr); + } + return; + } + ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); +} + template requires(::std::is_trivially_copyable_v && ::std::is_trivially_copyable_v) inline constexpr void print_control_single(output outstm, T t) @@ -345,13 +454,13 @@ inline constexpr void print_control_single(output outstm, T t) ::fast_io::io_scatter_t scatters[2]{ {scatter_res.base, scatter_res.len * sizeof(char_type)}, {__builtin_addressof(char_literal_v), sizeof(char_type)}}; - ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, 2); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters, 2); } else { ::fast_io::basic_io_scatter_t scatters[2]{ scatter_res, {__builtin_addressof(char_literal_v), 1}}; - ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, 2); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters, 2); } } else @@ -609,21 +718,23 @@ inline constexpr void print_control_single(output outstm, T t) *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } - ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce( + outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); return; } } ::fast_io::basic_io_scatter_t scattersbuffer[scattersnum]; char_type buffer[sz.reserve_size]; auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type>, - scattersbuffer, buffer, t) - .scatters_pos_ptr}; + scattersbuffer, buffer, t) + .scatters_pos_ptr}; if constexpr (line) { *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } - ::fast_io::operations::decay::scatter_write_all_decay(outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce( + outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); } else if constexpr (::fast_io::transcode_imaginary_printable) { @@ -1499,11 +1610,13 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< outputstmtype>) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, scatterscount); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, + scatterscount); } else { - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, scatterscount); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, + scatterscount); } } if constexpr (res.position != n) @@ -1621,11 +1734,11 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< outputstmtype>) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, diff); } else { - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); } } else if constexpr (res.hasdynamicreserve) @@ -1663,11 +1776,12 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (::fast_io::operations::decay::defines:: has_any_of_write_or_seek_pwrite_bytes_operations) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, + diff); } else { - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); } } else @@ -1681,11 +1795,12 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (::fast_io::operations::decay::defines:: has_any_of_write_or_seek_pwrite_bytes_operations) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, + diff); } else { - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); } } } @@ -1700,11 +1815,12 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (::fast_io::operations::decay::defines:: has_any_of_write_or_seek_pwrite_bytes_operations) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, + diff); } else { - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, diff); + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); } } } @@ -1840,57 +1956,192 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } template -inline constexpr bool print_semantic_pack_parameter_v = false; +inline constexpr bool print_semantic_condition_v = false; + +template +inline constexpr bool + print_semantic_condition_v<::fast_io::manipulators::condition> = true; + +template +inline constexpr bool print_semantic_width_v = false; + +template <::fast_io::manipulators::scalar_placement placement, typename T> +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_t> = true; + +template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> +inline constexpr bool + print_semantic_width_v<::fast_io::manipulators::width_ch_t> = true; template -inline constexpr bool print_semantic_pack_parameter_v<::fast_io::parameter> = - ::fast_io::details::print_pack<::std::remove_cvref_t>; +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_t> = true; + +template +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_ch_t> = true; template -concept print_semantic_pack_node = +inline constexpr bool print_semantic_node_no_parameter_v = ::fast_io::details::print_pack || - ::fast_io::details::decay::print_semantic_pack_parameter_v<::std::remove_cvref_t>; + ::fast_io::details::decay::print_semantic_condition_v<::std::remove_cvref_t> || + ::fast_io::details::decay::print_semantic_width_v<::std::remove_cvref_t>; + +template +inline constexpr bool print_semantic_parameter_object_v = false; + +template +inline constexpr bool print_semantic_parameter_object_v<::fast_io::parameter> = true; + +template +inline constexpr bool print_semantic_parameter_v = false; + +template +inline constexpr bool print_semantic_parameter_v<::fast_io::parameter> = + ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t>; + +template +concept print_semantic_node = + ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t> || + ::fast_io::details::decay::print_semantic_parameter_v<::std::remove_cvref_t>; template -inline constexpr decltype(auto) print_semantic_pack_ref(T &&t) noexcept +struct print_semantic_width_traits +{}; + +template <::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_width_traits<::fast_io::manipulators::width_t> +{ + inline static constexpr bool runtime_placement = false; + inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; + inline static constexpr bool has_fill_char = false; +}; + +template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> +struct print_semantic_width_traits<::fast_io::manipulators::width_ch_t> +{ + using fill_char_type = ch_type; + inline static constexpr bool runtime_placement = false; + inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; + inline static constexpr bool has_fill_char = true; +}; + +template +struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_t> +{ + inline static constexpr bool runtime_placement = true; + inline static constexpr bool has_fill_char = false; +}; + +template +struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_ch_t> +{ + using fill_char_type = ch_type; + inline static constexpr bool runtime_placement = true; + inline static constexpr bool has_fill_char = true; +}; + +template +inline constexpr decltype(auto) print_semantic_node_ref(T &&t) noexcept { - if constexpr (::fast_io::details::print_pack) + if constexpr (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t>) + { + return ::fast_io::details::decay::print_semantic_node_ref(::std::forward(t).reference); + } + else { return ::std::forward(t); } +} + +template +inline constexpr bool print_semantic_pack_argument_v = + ::fast_io::details::print_pack || + (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t> && + ::fast_io::details::print_pack< + decltype(::fast_io::details::decay::print_semantic_node_ref(::std::declval()))>); + +template <::std::integral char_type, typename T> +using print_semantic_input_alias_t = decltype(::fast_io::io_print_alias(::std::declval())); + +template <::std::integral char_type, typename T> +using print_semantic_input_forward_t = + decltype(::fast_io::io_print_forward(::fast_io::io_print_alias(::std::declval()))); + +template <::std::integral char_type, typename T> +inline constexpr bool print_semantic_input_argument_v = + ::fast_io::details::decay::print_semantic_node_no_parameter_v< + ::std::remove_cvref_t<::fast_io::details::decay::print_semantic_input_alias_t>> || + ::fast_io::details::decay::print_semantic_node< + ::fast_io::details::decay::print_semantic_input_forward_t>; + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr decltype(auto) print_semantic_input_forward(T &&t) noexcept +{ + if constexpr (::fast_io::details::decay::print_semantic_node_no_parameter_v< + ::std::remove_cvref_t< + decltype(::fast_io::io_print_alias(::std::forward(t)))>>) + { + return ::fast_io::io_print_alias(::std::forward(t)); + } else { - return ::fast_io::details::decay::print_semantic_pack_ref(::std::forward(t).reference); + return ::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t))); } } template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuation &&cont, ::std::index_sequence) { - auto &&pack_ref{::fast_io::details::decay::print_semantic_pack_ref(::std::forward(t))}; + auto &&pack_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; return cont(::fast_io::containers::get(::std::forward(pack_ref).storage)...); } template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation &&cont) { using pack_type = - ::std::remove_cvref_t(t)))>; + ::std::remove_cvref_t(t)))>; return ::fast_io::details::decay::print_semantic_pack_apply_impl( ::std::forward(t), ::std::forward(cont), ::std::make_index_sequence{}); } template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont) { return cont(); } template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args) { - if constexpr (::fast_io::details::decay::print_semantic_pack_node) + if constexpr (::fast_io::details::print_pack || + (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t> && + ::fast_io::details::print_pack< + decltype(::fast_io::details::decay::print_semantic_node_ref(::std::forward(t)))>)) { return ::fast_io::details::decay::print_semantic_pack_apply( ::std::forward(t), [&](PackArgs &&...pack_args) -> decltype(auto) { @@ -1925,28 +2176,251 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, } } +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont) +{ + if constexpr (!has_value && had_null) + { + return cont(::fast_io::io_null); + } + else + { + return cont(); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont, T &&t, Args &&...args) +{ + if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) + { + return ::fast_io::details::decay::print_semantic_filter_nulls( + ::std::forward(cont), ::std::forward(args)...); + } + else + { + return ::fast_io::details::decay::print_semantic_filter_nulls( + [&](TailArgs &&...tail_args) -> decltype(auto) { + return cont(::std::forward(t), ::std::forward(tail_args)...); + }, + ::std::forward(args)...); + } +} + template <::std::integral char_type, typename T> struct print_freestanding_decay_param_okay_single; template <::std::integral char_type, typename T> -struct print_semantic_pack_params_okay : ::std::false_type +using print_semantic_forwarded_arg_t = + decltype(::fast_io::io_print_forward(::fast_io::io_print_alias(::std::declval()))); + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size_impl +{ + inline static constexpr bool available = ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>; + inline static constexpr ::std::size_t size = available ? 0u : SIZE_MAX; +}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size + : ::fast_io::details::decay::print_semantic_static_precise_size_impl> +{}; + +template <::std::integral char_type, typename T> + requires(!::fast_io::details::decay::print_semantic_parameter_object_v && + ::fast_io::static_precise_reserve_printable) +struct print_semantic_static_precise_size_impl +{ + inline static constexpr bool available = true; + inline static constexpr ::std::size_t size{ + print_reserve_static_precise_size(::fast_io::io_reserve_type)}; +}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size_impl> + : ::fast_io::details::decay::print_semantic_static_precise_size +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_static_precise_size_impl> +{ + inline static constexpr bool available = + (::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::available && + ...); + + static consteval ::std::size_t static_size() noexcept + { + if constexpr (available) + { + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t>::size)), + ...); + return total; + } + else + { + return SIZE_MAX; + } + } + + inline static constexpr ::std::size_t size{static_size()}; +}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_static_precise_size_impl> +{ + using first_size = ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + using second_size = ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + inline static constexpr bool available = + first_size::available && second_size::available && (first_size::size == second_size::size); + inline static constexpr ::std::size_t size = available ? first_size::size : SIZE_MAX; +}; + +template <::std::integral char_type, typename T> +inline constexpr bool print_semantic_precise_leaf_size_ok_v = + ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || + ::fast_io::static_precise_reserve_printable> || + ::fast_io::precise_reserve_printable> || + ::fast_io::scatter_printable> || + ::fast_io::reserve_scatters_printable>; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok + : ::fast_io::details::decay::print_semantic_precise_size_ok_impl> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + (::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && + ...)> +{}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && + ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, + ::std::integral ch_type> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && + ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, typename T, ::std::integral ch_type> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && + ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_params_okay : ::std::false_type {}; template <::std::integral char_type, typename T> -struct print_semantic_pack_params_okay> - : print_semantic_pack_params_okay> +struct print_semantic_params_okay> + : print_semantic_params_okay> {}; template <::std::integral char_type, typename... Args> -struct print_semantic_pack_params_okay> +struct print_semantic_params_okay> : ::std::bool_constant< (print_freestanding_decay_param_okay_single< - char_type, - decltype(::fast_io::io_print_forward( - ::fast_io::io_print_alias(::std::declval())))>::value && + char_type, print_semantic_forwarded_arg_t>::value && ...)> {}; +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_params_okay> + : ::std::bool_constant< + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_params_okay> + : print_freestanding_decay_param_okay_single> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, + ::std::integral ch_type> +struct print_semantic_params_okay> + : ::std::bool_constant< + ::std::same_as && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_params_okay> + : print_freestanding_decay_param_okay_single> +{}; + +template <::std::integral char_type, typename T, ::std::integral ch_type> +struct print_semantic_params_okay> + : ::std::bool_constant< + ::std::same_as && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + template <::std::integral char_type, typename T> struct print_freestanding_decay_param_okay_single : ::std::bool_constant< @@ -1955,7 +2429,7 @@ struct print_freestanding_decay_param_okay_single ::fast_io::reserve_scatters_printable || ::fast_io::context_printable || ::fast_io::transcode_imaginary_printable || ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || - print_semantic_pack_params_okay>::value> + print_semantic_params_okay>::value> {}; } // namespace details::decay @@ -2002,32 +2476,799 @@ inline constexpr decltype(auto) print_freestanding_decay_no_pack(outputstmtype o } } -template -inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, Args... args) +template +inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args); + +template <::std::integral char_type, typename outputstmtype> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void print_semantic_write_fill(outputstmtype optstm, ::std::size_t n, char_type ch) { - if constexpr ((::fast_io::details::decay::print_semantic_pack_node || ...)) + if (n == 0) { - using char_type = typename outputstmtype::output_char_type; - return ::fast_io::details::decay::print_semantic_pack_expand( - [optstm](FlatArgs &&...flat_args) constexpr -> decltype(auto) { - return ::fast_io::operations::decay::print_freestanding_decay_no_pack( - optstm, ::std::forward(flat_args)...); - }, - args...); + return; } - else + char_type buffer[64]; + ::fast_io::details::my_fill_n(buffer, static_cast<::std::size_t>(64u), ch); + while (static_cast<::std::size_t>(64u) < n) { - return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); + ::fast_io::operations::decay::write_all_decay(optstm, buffer, buffer + 64); + n -= static_cast<::std::size_t>(64u); } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, buffer + n); } -template -#if __has_cpp_attribute(__gnu__::__cold__) -[[__gnu__::__cold__]] +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t); + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] #endif -inline constexpr decltype(auto) print_freestanding_decay_cold(outputstmtype optstm, Args... args) +inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) { -#if !__has_cpp_attribute(__gnu__::__cold__) && __has_cpp_attribute(unlikely) + using value_type = ::std::remove_cvref_t; + if constexpr (::std::same_as) + { + return 0; + } + else if constexpr (::fast_io::static_precise_reserve_printable) + { + return print_reserve_static_precise_size(::fast_io::io_reserve_type); + } + else if constexpr (::fast_io::precise_reserve_printable) + { + return print_reserve_precise_size(::fast_io::io_reserve_type, ::std::forward(t)); + } + else if constexpr (::fast_io::scatter_printable) + { + return print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t)).len; + } + else if constexpr (::fast_io::reserve_scatters_printable) + { + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[sz.reserve_size == 0 ? 1 : sz.reserve_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + ::std::size_t len{}; + for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) + { + len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); + } + return len; + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_width_placement_code(placement_type placement) noexcept +{ + return static_cast<::std::size_t>(placement); +} + +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t); + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_internal_shift(T &&t) +{ + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using node_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::decay::print_semantic_condition_v) + { + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.t2); + } + else if constexpr (::fast_io::details::print_pack || + ::fast_io::details::decay::print_semantic_width_v) + { + return 0; + } + else + { + using value_type = ::std::remove_cvref_t; + if constexpr (::fast_io::printable_internal_shift) + { + return print_define_internal_shift(::fast_io::io_reserve_type, + ::std::forward(node_ref)); + } + else + { + return 0; + } + } +} + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t) +{ + decltype(auto) forwarded{ + ::fast_io::details::decay::print_semantic_input_forward(::std::forward(t))}; + return ::fast_io::operations::decay::print_semantic_internal_shift( + ::std::forward(forwarded)); +} + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_precise_size(T &&t) +{ + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using node_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::print_pack) + { + ::std::size_t total{}; + ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(node_ref), [&](PackArgs &&...pack_args) { + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::operations::decay::print_semantic_precise_size_arg( + ::std::forward(pack_args)))), + ...); + }); + return total; + } + else if constexpr (::fast_io::details::decay::print_semantic_condition_v) + { + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.t1); + } + else + { + return ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.t2); + } + } + else if constexpr (::fast_io::details::decay::print_semantic_width_v) + { + using width_traits = ::fast_io::details::decay::print_semantic_width_traits; + ::std::size_t const child_len{ + ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; + ::std::size_t const width{node_ref.width}; + auto const placement{[](width_reference_type &&wr) constexpr { + if constexpr (width_traits::runtime_placement) + { + return wr.placement; + } + else + { + return width_traits::static_placement; + } + }(node_ref)}; + ::std::size_t const placement_code{ + ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; + if (width <= child_len || placement_code == 0u) + { + return child_len; + } + return width; + } + else + { + return ::fast_io::operations::decay::print_semantic_precise_size_leaf( + ::std::forward(node_ref)); + } +} + +template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t) +{ + decltype(auto) forwarded{ + ::fast_io::details::decay::print_semantic_input_forward(::std::forward(t))}; + return ::fast_io::operations::decay::print_semantic_precise_size( + ::std::forward(forwarded)); +} + +template <::std::integral char_type, typename T> +inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t); + +template <::std::integral char_type, typename T> +inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T &&t) +{ + decltype(auto) forwarded{ + ::fast_io::details::decay::print_semantic_input_forward(::std::forward(t))}; + return ::fast_io::operations::decay::print_semantic_emit_unchecked( + iter, ::std::forward(forwarded)); +} + +template <::std::integral char_type, typename T> +inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, T &&t) +{ + using value_type = ::std::remove_cvref_t; + if constexpr (::std::same_as) + { + return iter; + } + else if constexpr (::fast_io::static_precise_reserve_printable) + { + return print_reserve_define(::fast_io::io_reserve_type, iter, ::std::forward(t)); + } + else if constexpr (::fast_io::precise_reserve_printable) + { + ::std::size_t const precise_size{ + print_reserve_precise_size(::fast_io::io_reserve_type, t)}; + if constexpr (requires { + { + print_reserve_precise_define(::fast_io::io_reserve_type, iter, + precise_size, ::std::forward(t)) + } -> ::std::same_as; + }) + { + return print_reserve_precise_define(::fast_io::io_reserve_type, iter, + precise_size, ::std::forward(t)); + } + else + { + print_reserve_precise_define(::fast_io::io_reserve_type, iter, + precise_size, ::std::forward(t)); + return iter + precise_size; + } + } + else if constexpr (::fast_io::scatter_printable) + { + auto scatter{print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t))}; + return ::fast_io::details::non_overlapped_copy_n(scatter.base, scatter.len, iter); + } + else if constexpr (::fast_io::reserve_scatters_printable) + { + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[sz.reserve_size == 0 ? 1 : sz.reserve_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) + { + iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); + } + return iter; + } +} + +template <::std::integral char_type, typename T> +inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t) +{ + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using node_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::print_pack) + { + ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(node_ref), [&](PackArgs &&...pack_args) { + ((iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( + iter, ::std::forward(pack_args))), + ...); + }); + return iter; + } + else if constexpr (::fast_io::details::decay::print_semantic_condition_v) + { + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); + } + else if constexpr (::fast_io::details::decay::print_semantic_width_v) + { + using width_traits = ::fast_io::details::decay::print_semantic_width_traits; + ::std::size_t const len{ + ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; + ::std::size_t const width{node_ref.width}; + char_type const fillch{[](width_reference_type &&wr) constexpr { + if constexpr (width_traits::has_fill_char) + { + return static_cast(wr.ch); + } + else + { + return ::fast_io::char_literal_v; + } + }(node_ref)}; + auto const placement{[](width_reference_type &&wr) constexpr { + if constexpr (width_traits::runtime_placement) + { + return wr.placement; + } + else + { + return width_traits::static_placement; + } + }(node_ref)}; + ::std::size_t const placement_code{ + ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; + if (width <= len || placement_code == 0u) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + } + ::std::size_t const padding{width - len}; + if (placement_code == 1u) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + return ::fast_io::details::my_fill_n(iter, padding, fillch); + } + if (placement_code == 2u) + { + ::std::size_t const left_padding{padding >> 1u}; + ::std::size_t const right_padding{padding - left_padding}; + iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + return ::fast_io::details::my_fill_n(iter, right_padding, fillch); + } + if (placement_code == 4u) + { + ::std::size_t const internal_len{ + ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.reference)}; + if (internal_len != 0) + { + char_type *const first{iter}; + char_type *const last{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference)}; + if (len < internal_len) + { + return last; + } + char_type *const shift_pos{first + internal_len}; + ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); + ::fast_io::details::my_fill_n(shift_pos, padding, fillch); + return first + width; + } + } + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + } + else + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_leaf( + iter, ::std::forward(node_ref)); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T &&reference, ::std::size_t width, + char_type fillch, ::std::size_t placement_code, + ::std::size_t len) +{ + if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) + { + ::std::size_t required{(width <= len || placement_code == 0u) ? len : width}; + if constexpr (line) + { + required = ::fast_io::details::intrinsics::add_or_overflow_die(required, static_cast<::std::size_t>(1u)); + } + char_type *const curr{obuffer_curr(optstm)}; + char_type *const end{obuffer_end(optstm)}; + if (static_cast<::std::size_t>(end - curr) >= required) + { + char_type *iter{curr}; + if (width <= len || placement_code == 0u) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + } + else + { + ::std::size_t const padding{width - len}; + if (placement_code == 1u) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + } + else if (placement_code == 2u) + { + ::std::size_t const left_padding{padding >> 1u}; + ::std::size_t const right_padding{padding - left_padding}; + iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + iter = ::fast_io::details::my_fill_n(iter, right_padding, fillch); + } + else if (placement_code == 4u) + { + ::std::size_t const internal_len{ + ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; + if (internal_len == 0) + { + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, + reference); + } + else + { + char_type *const first{iter}; + char_type *const last{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference)}; + if (len < internal_len) + { + iter = last; + } + else + { + char_type *const shift_pos{first + internal_len}; + ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); + ::fast_io::details::my_fill_n(shift_pos, padding, fillch); + iter = first + width; + } + } + } + else + { + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + } + } + if constexpr (line) + { + *iter++ = ::fast_io::char_literal_v; + } + obuffer_set_curr(optstm, iter); + return; + } + } + ::std::size_t required{(width <= len || placement_code == 0u) ? len : width}; + if constexpr (line) + { + required = ::fast_io::details::intrinsics::add_or_overflow_die(required, static_cast<::std::size_t>(1u)); + } + if (required <= static_cast<::std::size_t>(256u)) + { + ::fast_io::basic_dynamic_output_buffer buffer; + auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; + ::fast_io::operations::decay::print_semantic_emit_width_direct( + buffer_ref, reference, width, fillch, placement_code, len); + ::fast_io::operations::decay::write_all_decay(optstm, buffer.begin_ptr, buffer.curr_ptr); + return; + } + if (width <= len || placement_code == 0u) + { + ::fast_io::operations::decay::print_semantic_emit(optstm, reference); + } + else + { + ::std::size_t const padding{width - len}; + if (placement_code == 1u) + { + ::fast_io::operations::decay::print_semantic_emit(optstm, reference); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + } + else if (placement_code == 2u) + { + ::std::size_t const left_padding{padding >> 1u}; + ::std::size_t const right_padding{padding - left_padding}; + ::fast_io::operations::decay::print_semantic_write_fill(optstm, left_padding, fillch); + ::fast_io::operations::decay::print_semantic_emit(optstm, reference); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, right_padding, fillch); + } + else if (placement_code == 4u) + { + ::std::size_t const internal_len{ + ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; + if (internal_len == 0) + { + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::print_semantic_emit(optstm, reference); + } + else + { + ::fast_io::basic_dynamic_output_buffer buffer; + auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; + ::fast_io::operations::decay::print_semantic_emit(buffer_ref, reference); + char_type const *const first{buffer.begin_ptr}; + char_type const *const last{buffer.curr_ptr}; + if (len < internal_len) + { + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + } + else + { + ::fast_io::operations::decay::write_all_decay(optstm, first, first + internal_len); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::write_all_decay(optstm, first + internal_len, last); + } + } + } + else + { + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::print_semantic_emit(optstm, reference); + } + } + if constexpr (line) + { + ::fast_io::operations::decay::char_put_decay(optstm, ::fast_io::char_literal_v); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) +{ + auto &&width_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using width_type = ::std::remove_cvref_t; + using width_traits = ::fast_io::details::decay::print_semantic_width_traits; + using width_child_type = + ::fast_io::details::decay::print_semantic_forwarded_arg_t; + ::std::size_t const width{width_ref.width}; + char_type const fillch{[](width_reference_type &&wr) constexpr { + if constexpr (width_traits::has_fill_char) + { + return static_cast(wr.ch); + } + else + { + return ::fast_io::char_literal_v; + } + }(width_ref)}; + auto const placement{[](width_reference_type &&wr) constexpr { + if constexpr (width_traits::runtime_placement) + { + return wr.placement; + } + else + { + return width_traits::static_placement; + } + }(width_ref)}; + ::std::size_t const placement_code{ + ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; + if constexpr (::fast_io::details::decay::print_semantic_static_precise_size::available) + { + constexpr ::std::size_t len{ + ::fast_io::details::decay::print_semantic_static_precise_size::size}; + ::fast_io::operations::decay::print_semantic_emit_width_direct( + optstm, width_ref.reference, width, fillch, placement_code, len); + return; + } + else if constexpr (::fast_io::details::decay::print_semantic_precise_size_ok::value) + { + ::std::size_t const len{ + ::fast_io::operations::decay::print_semantic_precise_size_arg(width_ref.reference)}; + ::fast_io::operations::decay::print_semantic_emit_width_direct( + optstm, width_ref.reference, width, fillch, placement_code, len); + return; + } + ::fast_io::basic_dynamic_output_buffer buffer; + auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; + ::fast_io::operations::decay::print_semantic_emit(buffer_ref, width_ref.reference); + char_type const *const first{buffer.begin_ptr}; + char_type const *const last{buffer.curr_ptr}; + ::std::size_t const len{static_cast<::std::size_t>(last - first)}; + if (width <= len || placement_code == 0u) + { + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + } + else + { + ::std::size_t const padding{width - len}; + if (placement_code == 1u) + { + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + } + else if (placement_code == 2u) + { + ::std::size_t const left_padding{padding >> 1u}; + ::std::size_t const right_padding{padding - left_padding}; + ::fast_io::operations::decay::print_semantic_write_fill(optstm, left_padding, fillch); + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, right_padding, fillch); + } + else if (placement_code == 4u) + { + ::std::size_t const internal_len{ + ::fast_io::operations::decay::print_semantic_internal_shift_arg(width_ref.reference)}; + if (internal_len == 0) + { + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + } + else if (len < internal_len) + { + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + } + else + { + ::fast_io::operations::decay::write_all_decay(optstm, first, first + internal_len); + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::write_all_decay(optstm, first + internal_len, last); + } + } + else + { + ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + } + } + if constexpr (line) + { + ::fast_io::operations::decay::char_put_decay(optstm, ::fast_io::char_literal_v); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) +{ + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using node_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::print_pack) + { + ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(node_ref), [optstm](PackArgs &&...pack_args) { + ::fast_io::operations::decay::print_semantic_emit( + optstm, ::std::forward(pack_args)...); + }); + } + else if constexpr (::fast_io::details::decay::print_semantic_condition_v) + { + if (node_ref.pred) + { + ::fast_io::operations::decay::print_semantic_emit(optstm, node_ref.t1); + } + else + { + ::fast_io::operations::decay::print_semantic_emit(optstm, node_ref.t2); + } + } + else if constexpr (::fast_io::details::decay::print_semantic_width_v) + { + ::fast_io::operations::decay::print_semantic_emit_width(optstm, + ::std::forward(t)); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr void print_semantic_emit_flat_impl(outputstmtype, continuation &&cont) +{ + cont.template operator()(); +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, continuation &&cont, T &&t, Args &&...args) +{ + if constexpr (::fast_io::details::decay::print_semantic_node) + { + cont.template operator()(); + ::fast_io::operations::decay::print_semantic_emit_node(optstm, ::std::forward(t)); + ::fast_io::operations::decay::print_semantic_emit(optstm, + ::std::forward(args)...); + } + else + { + ::fast_io::operations::decay::print_semantic_emit_flat_impl( + optstm, + [&](Prefix &&...prefix) constexpr { + cont.template operator()(::std::forward(t), ::std::forward(prefix)...); + }, + ::std::forward(args)...); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args) +{ + auto emit_flat{[optstm](FilteredArgs &&...filtered_args) constexpr { + ::fast_io::operations::decay::print_semantic_emit_flat_impl( + optstm, + [optstm](Prefix &&...prefix) constexpr { + ::fast_io::operations::decay::print_freestanding_decay_no_pack( + optstm, ::std::forward(prefix)...); + }, + ::std::forward(filtered_args)...); + }}; + if constexpr (!((::fast_io::details::decay::print_semantic_pack_argument_v || + ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) || + ...)) + { + emit_flat(::std::forward(args)...); + } + else + { + ::fast_io::details::decay::print_semantic_pack_expand( + [&](FlatArgs &&...flat_args) constexpr { + ::fast_io::details::decay::print_semantic_filter_nulls( + emit_flat, ::std::forward(flat_args)...); + }, + ::std::forward(args)...); + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +#if __has_cpp_attribute(__gnu__::__flatten__) +[[__gnu__::__flatten__]] +#endif +inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, Args... args) +{ + if constexpr ((::fast_io::details::decay::print_semantic_node || ...)) + { + using char_type = typename outputstmtype::output_char_type; + return ::fast_io::operations::decay::print_semantic_emit(optstm, args...); + } + else + { + return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); + } +} + +template +#if __has_cpp_attribute(__gnu__::__cold__) +[[__gnu__::__cold__]] +#endif +inline constexpr decltype(auto) print_freestanding_decay_cold(outputstmtype optstm, Args... args) +{ +#if !__has_cpp_attribute(__gnu__::__cold__) && __has_cpp_attribute(unlikely) if (true) [[unlikely]] #endif return ::fast_io::operations::decay::print_freestanding_decay(optstm, args...); @@ -2072,10 +3313,21 @@ template #endif inline constexpr void print_freestanding(output &&outstm, Args &&...args) { - ::fast_io::operations::decay::print_freestanding_decay( - ::fast_io::operations::output_stream_ref(outstm), - io_print_forward( - io_print_alias(args))...); + auto outref{::fast_io::operations::output_stream_ref(outstm)}; + using char_type = typename decltype(outref)::output_char_type; + if constexpr ((false || ... || + ::fast_io::details::decay::print_semantic_input_argument_v)) + { + ::fast_io::operations::decay::print_semantic_emit( + outref, + ::fast_io::details::decay::print_semantic_input_forward( + ::std::forward(args))...); + } + else + { + ::fast_io::operations::decay::print_freestanding_decay( + outref, io_print_forward(io_print_alias(args))...); + } } } // namespace operations diff --git a/third-parties/fast_io/include/fast_io_dsal/impl/cstring_view.h b/third-parties/fast_io/include/fast_io_dsal/impl/cstring_view.h index 046b96cac..603326724 100644 --- a/third-parties/fast_io/include/fast_io_dsal/impl/cstring_view.h +++ b/third-parties/fast_io/include/fast_io_dsal/impl/cstring_view.h @@ -199,6 +199,39 @@ inline constexpr ::fast_io::basic_io_scatter_t print_alias_define(::f return {str.ptr, str.n}; } +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_size(::fast_io::io_reserve_type_t>, + ::fast_io::containers::basic_cstring_view str) noexcept +{ + return str.n; +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_define(::fast_io::io_reserve_type_t>, + char_type *iter, ::fast_io::containers::basic_cstring_view str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.ptr, str.n, iter); +} + +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_precise_size(::fast_io::io_reserve_type_t>, + ::fast_io::containers::basic_cstring_view str) noexcept +{ + return str.n; +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_precise_define(::fast_io::io_reserve_type_t>, + char_type *iter, ::std::size_t, + ::fast_io::containers::basic_cstring_view str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.ptr, str.n, iter); +} + template <::std::integral char_type> inline constexpr bool operator==(::fast_io::containers::basic_cstring_view a, ::fast_io::containers::basic_cstring_view b) noexcept { diff --git a/third-parties/fast_io/include/fast_io_dsal/impl/string.h b/third-parties/fast_io/include/fast_io_dsal/impl/string.h index d5292e9c0..b400a24a6 100644 --- a/third-parties/fast_io/include/fast_io_dsal/impl/string.h +++ b/third-parties/fast_io/include/fast_io_dsal/impl/string.h @@ -1698,6 +1698,40 @@ print_alias_define(io_alias_t, basic_string const &str) noexc return {str.imp.begin_ptr, static_cast<::std::size_t>(str.imp.curr_ptr - str.imp.begin_ptr)}; } +template <::std::integral char_type, typename alloctype> +inline constexpr ::std::size_t +print_reserve_size(::fast_io::io_reserve_type_t>, + basic_string const &str) noexcept +{ + return static_cast<::std::size_t>(str.imp.curr_ptr - str.imp.begin_ptr); +} + +template <::std::integral char_type, typename alloctype> +inline constexpr char_type * +print_reserve_define(::fast_io::io_reserve_type_t>, + char_type *iter, basic_string const &str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.imp.begin_ptr, + static_cast<::std::size_t>(str.imp.curr_ptr - str.imp.begin_ptr), iter); +} + +template <::std::integral char_type, typename alloctype> +inline constexpr ::std::size_t +print_reserve_precise_size(::fast_io::io_reserve_type_t>, + basic_string const &str) noexcept +{ + return static_cast<::std::size_t>(str.imp.curr_ptr - str.imp.begin_ptr); +} + +template <::std::integral char_type, typename alloctype> +inline constexpr char_type * +print_reserve_precise_define(::fast_io::io_reserve_type_t>, + char_type *iter, ::std::size_t, basic_string const &str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.imp.begin_ptr, + static_cast<::std::size_t>(str.imp.curr_ptr - str.imp.begin_ptr), iter); +} + template <::std::integral char_type, typename allocator_type> inline constexpr auto strlike_construct_define(io_strlike_type_t>, diff --git a/third-parties/fast_io/include/fast_io_dsal/impl/string_view.h b/third-parties/fast_io/include/fast_io_dsal/impl/string_view.h index 0b6f55b1e..278b9bd37 100644 --- a/third-parties/fast_io/include/fast_io_dsal/impl/string_view.h +++ b/third-parties/fast_io/include/fast_io_dsal/impl/string_view.h @@ -674,6 +674,39 @@ inline constexpr ::fast_io::basic_io_scatter_t print_alias_define(::f return {str.ptr, str.n}; } +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_size(::fast_io::io_reserve_type_t>, + ::fast_io::containers::basic_string_view str) noexcept +{ + return str.n; +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_define(::fast_io::io_reserve_type_t>, + char_type *iter, ::fast_io::containers::basic_string_view str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.ptr, str.n, iter); +} + +template <::std::integral char_type> +inline constexpr ::std::size_t +print_reserve_precise_size(::fast_io::io_reserve_type_t>, + ::fast_io::containers::basic_string_view str) noexcept +{ + return str.n; +} + +template <::std::integral char_type> +inline constexpr char_type * +print_reserve_precise_define(::fast_io::io_reserve_type_t>, + char_type *iter, ::std::size_t, + ::fast_io::containers::basic_string_view str) noexcept +{ + return ::fast_io::details::non_overlapped_copy_n(str.ptr, str.n, iter); +} + template <::std::integral char_type> inline constexpr bool operator==(::fast_io::containers::basic_string_view a, ::fast_io::containers::basic_string_view b) noexcept { diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h b/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h index 6a886333f..8c96901bd 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h @@ -19,15 +19,33 @@ concept cond_value_transferable = ::std::is_trivially_copyable_v<::std::remove_c namespace manipulators { +struct condition_manip_tag_t +{}; + template struct condition { using manip_tag = manip_tag_t; + using semantic_tag = condition_manip_tag_t; using alias_type1 = T1; using alias_type2 = T2; + bool pred; +#ifndef __INTELLISENSE__ +#if __has_cpp_attribute(msvc::no_unique_address) + [[msvc::no_unique_address]] +#elif __has_cpp_attribute(no_unique_address) + [[no_unique_address]] +#endif +#endif alias_type1 t1; +#ifndef __INTELLISENSE__ +#if __has_cpp_attribute(msvc::no_unique_address) + [[msvc::no_unique_address]] +#elif __has_cpp_attribute(no_unique_address) + [[no_unique_address]] +#endif +#endif alias_type2 t2; - bool pred; }; template @@ -44,30 +62,22 @@ inline constexpr auto cond(bool pred, T1 &&t1, T2 &&t2) noexcept ::std::remove_cvref_t, ::std::remove_cvref_t const &>, ::std::remove_cvref_t(t2)))>>; - constexpr bool type_match{::std::same_as}; - if constexpr (type_match) + if constexpr (::std::same_as && + ::std::same_as) { - if (pred) - { - return ::fast_io::io_print_alias(::std::forward(t1)); - } - else - { - return ::fast_io::io_print_alias(::std::forward(t2)); - } + return ::fast_io::io_null; + } + else if constexpr (sizeof(t1aliastype) < sizeof(t2aliastype)) + { + return condition{!pred, + ::fast_io::io_print_alias(::std::forward(t2)), + ::fast_io::io_print_alias(::std::forward(t1))}; } else { - if constexpr (sizeof(t1aliastype) < sizeof(t2aliastype)) - { - return condition{::fast_io::io_print_alias(::std::forward(t2)), - ::fast_io::io_print_alias(::std::forward(t1)), !pred}; - } - else - { - return condition{::fast_io::io_print_alias(::std::forward(t1)), - ::fast_io::io_print_alias(::std::forward(t2)), pred}; - } + return condition{pred, + ::fast_io::io_print_alias(::std::forward(t1)), + ::fast_io::io_print_alias(::std::forward(t2))}; } } @@ -87,8 +97,8 @@ inline constexpr auto cond(bool pred, T1 &&t1) noexcept } else { - return condition{::fast_io::io_print_alias(::std::forward(t1)), - ::fast_io::io_null, pred}; + return condition{ + pred, ::fast_io::io_print_alias(::std::forward(t1)), ::fast_io::io_null}; } } diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/width.h b/third-parties/fast_io/include/fast_io_freestanding_impl/width.h index e373d656e..d14ece9d0 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/width.h +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/width.h @@ -176,7 +176,7 @@ inline constexpr auto left(T &&t, ::std::size_t n, char_type ch) noexcept } else { - return width_ch_t{t, n}; + return width_ch_t{t, n, ch}; } } From 7e7c020361f880c6dbce347bca09e9e7fefd9efd Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 16:28:48 +0800 Subject: [PATCH 03/45] Tune fast_io C++20 stack scatter budget --- .../fast_io_core_impl/concepts/operation.h | 37 +- .../printimpl/print_freestanding_cxx20.h | 1406 +++++++++++------ .../include/fast_io_hosted/platforms/posix.h | 7 + 3 files changed, 996 insertions(+), 454 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index b38503430..dc1d0821c 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -125,11 +125,26 @@ template <::std::size_t> struct reserve_static_stack_size_constant {}; +inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{131072u}; + +inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept +{ +#if defined(FAST_IO_PRINT_STACK_BUFFER_MAX_BYTES) + return static_cast<::std::size_t>(FAST_IO_PRINT_STACK_BUFFER_MAX_BYTES); +#else + return ::fast_io::details::print_stack_buffer_default_max_bytes; +#endif +} + template <::std::integral char_type> inline constexpr ::std::size_t dynamic_reserve_default_static_stack_size() noexcept { - constexpr ::std::size_t bytes{4096u}; - if constexpr (sizeof(char_type) < bytes) + constexpr ::std::size_t bytes{::fast_io::details::print_stack_buffer_max_bytes()}; + if constexpr (bytes == 0) + { + return 0u; + } + else if constexpr (sizeof(char_type) < bytes) { return bytes / sizeof(char_type); } @@ -200,6 +215,24 @@ concept context_printable_with_static_buffer_size = requires(print_context_static_buffer_size(io_reserve_type>) != SIZE_MAX); }; +/// @brief scatter_fallback_full_output_threshold_stream +/// @details Customizes the maximum full-output size, in char_type units, +/// for coalescing scatter output into a contiguous buffer before +/// writing it once. The value must be a compile-time std::size_t. +/// Returning 0 disables this coalescing path for the stream. +/// The print layer may cap large values before allocating stack storage. +/// @fn scatter_fallback_full_output_threshold +/// @brief Returns the scatter coalescing threshold, in char_type units. +template +concept scatter_fallback_full_output_threshold_stream = + ::std::integral && requires { + typename ::fast_io::details::reserve_static_stack_size_constant< + scatter_fallback_full_output_threshold(io_reserve_type>)>; + { + scatter_fallback_full_output_threshold(io_reserve_type>) + } -> ::std::same_as<::std::size_t>; + }; + /// @brief printable_internal_shift /// @details Defines the behaviour when printed with ::fast_io::mnp::width<::fast_io::mnp::scalar_placement::internal> /// @fn print_define_internal_shift diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 1dabeb2c1..6869a1c5f 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -164,6 +164,39 @@ concept minimum_buffer_output_stream_require_size_impl = ::fast_io::operations::decay::defines::has_obuffer_minimum_size_operations && minimum_buffer_output_stream_require_size_constant_impl; +inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{ + ::fast_io::details::print_stack_buffer_default_max_bytes}; + +inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept +{ + return ::fast_io::details::print_stack_buffer_max_bytes(); +} + +template +inline constexpr ::std::size_t print_stack_buffer_max_element_count() noexcept +{ + constexpr ::std::size_t max_bytes{::fast_io::details::decay::print_stack_buffer_max_bytes()}; + if constexpr (max_bytes < sizeof(element_type)) + { + return 0u; + } + else + { + return max_bytes / sizeof(element_type); + } +} + +template <::std::integral char_type> +inline constexpr ::std::size_t print_stack_buffer_max_size() noexcept +{ + return ::fast_io::details::decay::print_stack_buffer_max_element_count(); +} + +template <::std::size_t requested_size, typename element_type> +inline constexpr bool print_stack_buffer_size_within_limit = + requested_size != 0 && + requested_size <= ::fast_io::details::decay::print_stack_buffer_max_element_count(); + template <::std::size_t static_stack_size, ::std::integral char_type> inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexcept { @@ -172,7 +205,7 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexc A print run can contain many dynamic producers, so directly merging all hints into one stack array can create a large frame and defeat the stack-safety purpose of this path. */ - constexpr ::std::size_t run_cap{::fast_io::details::dynamic_reserve_default_static_stack_size()}; + constexpr ::std::size_t run_cap{::fast_io::details::decay::print_stack_buffer_max_size()}; if constexpr (static_stack_size < run_cap) { return static_stack_size; @@ -202,7 +235,8 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_size() } template -inline constexpr ::std::size_t context_print_static_buffer_size_v = []() constexpr { +inline constexpr ::std::size_t context_print_static_buffer_size() noexcept +{ using value_type = ::std::remove_cvref_t; if constexpr (::fast_io::context_printable_with_static_buffer_size) { @@ -217,7 +251,11 @@ inline constexpr ::std::size_t context_print_static_buffer_size_v = []() constex { return 32u; } -}(); +} + +template +inline constexpr ::std::size_t context_print_static_buffer_size_v{ + ::fast_io::details::decay::context_print_static_buffer_size()}; template <::std::size_t sz> requires(sz != 0) @@ -269,7 +307,68 @@ inline auto prrsvsct_byte_common_impl(io_scatter_t *pscatters, char_type *buffer return ::fast_io::details::decay::prrsvsct_byte_common_rsvsc_impl(pscatters, buffer, t).scatters_pos_ptr; } -inline constexpr ::std::size_t print_small_scatter_coalesce_threshold_bytes{256u}; +template <::std::integral char_type> +inline constexpr ::std::size_t print_scatter_full_output_threshold_max_chars() noexcept +{ + return ::fast_io::details::decay::print_stack_buffer_max_size(); +} + +template <::std::integral char_type, typename outputstmtype> +inline constexpr ::std::size_t print_scatter_full_output_threshold() noexcept +{ + if constexpr (::fast_io::scatter_fallback_full_output_threshold_stream) + { + constexpr ::std::size_t threshold{ + scatter_fallback_full_output_threshold( + ::fast_io::io_reserve_type>)}; + constexpr ::std::size_t max_threshold{ + ::fast_io::details::decay::print_scatter_full_output_threshold_max_chars()}; + if constexpr (threshold < max_threshold) + { + return threshold; + } + else + { + return max_threshold; + } + } + else + { + return 0u; + } +} + +template +inline constexpr bool print_has_direct_write_bytes_operations = + ::fast_io::operations::decay::defines::has_write_all_bytes_overflow_define || + ::fast_io::operations::decay::defines::has_write_some_bytes_overflow_define || + (sizeof(typename outputstmtype::output_char_type) == 1 && + (::fast_io::operations::decay::defines::has_write_all_overflow_define || + ::fast_io::operations::decay::defines::has_write_some_overflow_define)); + +template +inline constexpr bool print_has_direct_scatter_write_bytes_operations = + ::fast_io::operations::decay::defines::has_scatter_write_all_bytes_overflow_define || + ::fast_io::operations::decay::defines::has_scatter_write_some_bytes_overflow_define || + (sizeof(typename outputstmtype::output_char_type) == 1 && + (::fast_io::operations::decay::defines::has_scatter_write_all_overflow_define || + ::fast_io::operations::decay::defines::has_scatter_write_some_overflow_define)); + +template +inline constexpr bool print_has_direct_write_operations = + ::fast_io::operations::decay::defines::has_write_all_overflow_define || + ::fast_io::operations::decay::defines::has_write_some_overflow_define || + (sizeof(typename outputstmtype::output_char_type) == 1 && + (::fast_io::operations::decay::defines::has_write_all_bytes_overflow_define || + ::fast_io::operations::decay::defines::has_write_some_bytes_overflow_define)); + +template +inline constexpr bool print_has_direct_scatter_write_operations = + ::fast_io::operations::decay::defines::has_scatter_write_all_overflow_define || + ::fast_io::operations::decay::defines::has_scatter_write_some_overflow_define || + (sizeof(typename outputstmtype::output_char_type) == 1 && + (::fast_io::operations::decay::defines::has_scatter_write_all_bytes_overflow_define || + ::fast_io::operations::decay::defines::has_scatter_write_some_bytes_overflow_define)); template inline constexpr char_type *print_small_scatter_copy_n(char_type const *first, ::std::size_t count, @@ -305,28 +404,39 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype } return; } - ::std::byte buffer[::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes]; - ::std::byte *curr{buffer}; - ::std::size_t remaining{::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes}; - for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + using char_type = typename outputstmtype::output_char_type; + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_scatter_full_output_threshold()}; + if constexpr (threshold_chars != 0 && + ::fast_io::details::decay::print_has_direct_write_bytes_operations && + ::fast_io::details::decay::print_has_direct_scatter_write_bytes_operations) { - ::std::size_t const len{iter->len}; - if (remaining < len) + char_type buffer[threshold_chars]; + auto curr{reinterpret_cast<::std::byte *>(buffer)}; + auto const first{curr}; + ::std::size_t remaining{threshold_chars * sizeof(char_type) - 1u}; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { - ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); - return; + ::std::size_t const len{iter->len}; + if (remaining < len) + { + ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); + return; + } + if (len != 0) + { + curr = ::fast_io::details::decay::print_small_scatter_copy_n( + static_cast<::std::byte const *>(iter->base), len, curr); + remaining -= len; + } } - if (len != 0) + if (curr != first) { - curr = ::fast_io::details::decay::print_small_scatter_copy_n(static_cast<::std::byte const *>(iter->base), - len, curr); - remaining -= len; + ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, curr); } + return; } - if (curr != buffer) - { - ::fast_io::operations::decay::write_all_bytes_decay(outstm, buffer, curr); - } + ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); } template @@ -349,12 +459,14 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( return; } constexpr ::std::size_t threshold_chars{ - ::fast_io::details::decay::print_small_scatter_coalesce_threshold_bytes / sizeof(char_type)}; - if constexpr (threshold_chars != 0) + ::fast_io::details::decay::print_scatter_full_output_threshold()}; + if constexpr (threshold_chars != 0 && + ::fast_io::details::decay::print_has_direct_write_operations && + ::fast_io::details::decay::print_has_direct_scatter_write_operations) { char_type buffer[threshold_chars]; char_type *curr{buffer}; - ::std::size_t remaining{threshold_chars}; + ::std::size_t remaining{threshold_chars - 1u}; for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { ::std::size_t const len{iter->len}; @@ -378,6 +490,226 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); } +template +inline constexpr void print_scatter_write_all_dispatch(outputstmtype outstm, scatter_type const *scatters, + ::std::size_t n) +{ + if constexpr (::std::same_as) + { + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters, n); + } + else + { + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters, n); + } +} + +template +inline constexpr void print_scatter_write_all_dispatch_with_line(outputstmtype outstm, scatter_type *scatters, + ::std::size_t n) +{ + if constexpr (needprintlf) + { + if constexpr (::std::same_as) + { + scatters[n - 1u] = ::fast_io::details::decay::line_scatter_common; + } + else + { + scatters[n - 1u] = ::fast_io::details::decay::line_scatter_common; + } + } + ::fast_io::details::decay::print_scatter_write_all_dispatch(outstm, scatters, n); +} + +template +inline constexpr void print_single_scatter_with_line(outputstmtype outstm, + ::fast_io::basic_io_scatter_t scatter_res) +{ + if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations) + { + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit<2u, ::fast_io::io_scatter_t>) + { + ::fast_io::io_scatter_t scatters[2]; + scatters[0] = {scatter_res.base, scatter_res.len * sizeof(char_type)}; + scatters[1] = {__builtin_addressof(char_literal_v), sizeof(char_type)}; + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters, 2u); + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> scatters(2u); + scatters.ptr[0] = {scatter_res.base, scatter_res.len * sizeof(char_type)}; + scatters.ptr[1] = {__builtin_addressof(char_literal_v), sizeof(char_type)}; + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters.ptr, 2u); + } + } + else + { + using scatter_type = ::fast_io::basic_io_scatter_t; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit<2u, scatter_type>) + { + scatter_type scatters[2]; + scatters[0] = scatter_res; + scatters[1] = {__builtin_addressof(char_literal_v), 1u}; + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters, 2u); + } + else + { + ::fast_io::details::local_operator_new_array_ptr scatters(2u); + scatters.ptr[0] = scatter_res; + scatters.ptr[1] = {__builtin_addressof(char_literal_v), 1u}; + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters.ptr, 2u); + } + } +} + +template +inline constexpr void print_reserve_scatters_bytes_with_scatter(outputstmtype outstm, ::fast_io::io_scatter_t *scatters, + T t) +{ + using value_type = ::std::remove_cvref_t; + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[reserve_buffer_size]; + ::fast_io::io_scatter_t *ptr{::fast_io::details::decay::prrsvsct_byte_common_impl(scatters, buffer, t)}; + if constexpr (line) + { + *ptr = ::fast_io::details::decay::line_scatter_common; + ++ptr; + } + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce( + outstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } + else + { + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + ::fast_io::io_scatter_t *ptr{::fast_io::details::decay::prrsvsct_byte_common_impl(scatters, buffer.ptr, t)}; + if constexpr (line) + { + *ptr = ::fast_io::details::decay::line_scatter_common; + ++ptr; + } + ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce( + outstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } +} + +template +inline constexpr void print_reserve_scatters_basic_with_scatter( + outputstmtype outstm, ::fast_io::basic_io_scatter_t *scatters, T t) +{ + using value_type = ::std::remove_cvref_t; + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[reserve_buffer_size]; + auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, buffer, t) + .scatters_pos_ptr}; + if constexpr (line) + { + *ptr = ::fast_io::details::decay::line_scatter_common; + ++ptr; + } + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce( + outstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } + else + { + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, buffer.ptr, + t) + .scatters_pos_ptr}; + if constexpr (line) + { + *ptr = ::fast_io::details::decay::line_scatter_common; + ++ptr; + } + ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce( + outstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } +} + +template +inline constexpr void print_control_single_reserve_scatters(outputstmtype outstm, T t) +{ + using value_type = ::std::remove_cvref_t; + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != SIZE_MAX); + constexpr ::std::size_t scattersnum{sz.scatters_size + static_cast<::std::size_t>(line)}; +#if __cpp_if_consteval >= 202106L + if !consteval +#else + if (!__builtin_is_constant_evaluated()) +#endif + { + if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< + outputstmtype>) + { + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + ::fast_io::io_scatter_t scatters[scattersnum]; + ::fast_io::details::decay::print_reserve_scatters_bytes_with_scatter( + outstm, scatters, t); + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> scatters(scattersnum); + ::fast_io::details::decay::print_reserve_scatters_bytes_with_scatter( + outstm, scatters.ptr, t); + } + return; + } + } + using scatter_type = ::fast_io::basic_io_scatter_t; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + scatter_type scatters[scattersnum]; + ::fast_io::details::decay::print_reserve_scatters_basic_with_scatter( + outstm, scatters, t); + } + else + { + ::fast_io::details::local_operator_new_array_ptr scatters(scattersnum); + ::fast_io::details::decay::print_reserve_scatters_basic_with_scatter( + outstm, scatters.ptr, t); + } +} + +template +inline constexpr void print_context_define_to_window(outputstmtype outstm, context_type &st, T t, char_type *buffer, + char_type *buffered) +{ + for (;;) + { + auto [resit, done] = st.print_context_define(t, buffer, buffered); + if constexpr (line) + { + if (done) + { + *resit = ::fast_io::char_literal_v; + ++resit; + } + ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); + if (done) + { + return; + } + } + else + { + ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); + if (done) + { + return; + } + } + } +} + template requires(::std::is_trivially_copyable_v && ::std::is_trivially_copyable_v) inline constexpr void print_control_single(output outstm, T t) @@ -446,26 +778,13 @@ inline constexpr void print_control_single(output outstm, T t) #endif basic_io_scatter_t scatter_res{print_scatter_define(::fast_io::io_reserve_type, t)}; - if constexpr (line) - { - if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< - output>) + if constexpr (line) { - ::fast_io::io_scatter_t scatters[2]{ - {scatter_res.base, scatter_res.len * sizeof(char_type)}, - {__builtin_addressof(char_literal_v), sizeof(char_type)}}; - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters, 2); + ::fast_io::details::decay::print_single_scatter_with_line(outstm, scatter_res); } else { - ::fast_io::basic_io_scatter_t scatters[2]{ - scatter_res, {__builtin_addressof(char_literal_v), 1}}; - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters, 2); - } - } - else - { - ::fast_io::operations::decay::write_all_decay(outstm, scatter_res.base, scatter_res.base + scatter_res.len); + ::fast_io::operations::decay::write_all_decay(outstm, scatter_res.base, scatter_res.base + scatter_res.len); } } else if constexpr (reserve_printable) @@ -516,37 +835,69 @@ inline constexpr void print_control_single(output outstm, T t) } else { - char_type buffer[size]; - if (!smaller) [[unlikely]] - { - bcurr = buffer; - } - bcurr = print_reserve_define(::fast_io::io_reserve_type, bcurr, t); - if constexpr (line) - { - *bcurr = lfch; - ++bcurr; - } if (smaller) [[likely]] { + bcurr = print_reserve_define(::fast_io::io_reserve_type, bcurr, t); + if constexpr (line) + { + *bcurr = lfch; + ++bcurr; + } obuffer_set_curr(outstm, bcurr); } else [[unlikely]] { - ::fast_io::operations::decay::write_all_decay(outstm, buffer, bcurr); + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[size]; + char_type *i{ + print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; + if constexpr (line) + { + *i = lfch; + ++i; + } + ::fast_io::operations::decay::write_all_decay(outstm, buffer, i); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(size); + char_type *i{ + print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; + if constexpr (line) + { + *i = lfch; + ++i; + } + ::fast_io::operations::decay::write_all_decay(outstm, newptr.ptr, i); + } } } } else { - char_type buffer[size]; - char_type *i{print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; - if constexpr (line) + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { - *i = lfch; - ++i; + char_type buffer[size]; + char_type *i{print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; + if constexpr (line) + { + *i = lfch; + ++i; + } + ::fast_io::operations::decay::write_all_decay(outstm, buffer, i); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(size); + char_type *i{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; + if constexpr (line) + { + *i = lfch; + ++i; + } + ::fast_io::operations::decay::write_all_decay(outstm, newptr.ptr, i); } - ::fast_io::operations::decay::write_all_decay(outstm, buffer, i); } } } @@ -695,51 +1046,14 @@ inline constexpr void print_control_single(output outstm, T t) } } } - else if constexpr (reserve_scatters_printable) - { - constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; - static_assert(!line || sz.scatters_size != SIZE_MAX); - constexpr ::std::size_t scattersnum{sz.scatters_size + static_cast<::std::size_t>(line)}; -#if __cpp_if_consteval >= 202106L - if !consteval -#else - if (!__builtin_is_constant_evaluated()) -#endif + else if constexpr (reserve_scatters_printable) { - if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< - output>) - { - ::fast_io::io_scatter_t scattersbuffer[scattersnum]; - char_type buffer[sz.reserve_size]; - ::fast_io::io_scatter_t *ptr{ - ::fast_io::details::decay::prrsvsct_byte_common_impl(scattersbuffer, buffer, t)}; - if constexpr (line) - { - *ptr = ::fast_io::details::decay::line_scatter_common; - ++ptr; - } - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce( - outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); - return; - } + ::fast_io::details::decay::print_control_single_reserve_scatters(outstm, t); } - ::fast_io::basic_io_scatter_t scattersbuffer[scattersnum]; - char_type buffer[sz.reserve_size]; - auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type>, - scattersbuffer, buffer, t) - .scatters_pos_ptr}; - if constexpr (line) + else if constexpr (::fast_io::transcode_imaginary_printable) { - *ptr = ::fast_io::details::decay::line_scatter_common; - ++ptr; + // todo? } - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce( - outstm, scattersbuffer, static_cast<::std::size_t>(ptr - scattersbuffer)); - } - else if constexpr (::fast_io::transcode_imaginary_printable) - { - // todo? - } else if constexpr (context_printable) { typename ::std::remove_cvref_t))>::type st; @@ -773,36 +1087,25 @@ inline constexpr void print_control_single(output outstm, T t) #if __has_cpp_attribute(unlikely) [[unlikely]] #endif - { - char_type buffer[reserved_size]; - char_type *buffered{buffer + reserved_size_no_line}; - for (;;) { - auto [resit, done] = st.print_context_define(t, buffer, buffered); - if constexpr (line) + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit< + reserved_size, char_type>) { - if (done) - { - *resit = lfch; - ++resit; - } - ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); - if (done) - { - return; - } + char_type buffer[reserved_size]; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window( + outstm, st, t, buffer, buffered); } else { - ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); - if (done) - { - return; - } + ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); + char_type *buffer{newptr.ptr}; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window( + outstm, st, t, buffer, buffered); } + return; } - return; - } } } else @@ -825,32 +1128,18 @@ inline constexpr void print_control_single(output outstm, T t) } else { - char_type buffer[reserved_size]; - char_type *buffered{buffer + reserved_size_no_line}; - for (;;) + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { - auto [resit, done] = st.print_context_define(t, buffer, buffered); - if constexpr (line) - { - if (done) - { - *resit = lfch; - ++resit; - } - ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); - if (done) - { - return; - } - } - else - { - ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); - if (done) - { - return; - } - } + char_type buffer[reserved_size]; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window(outstm, st, t, buffer, buffered); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); + char_type *buffer{newptr.ptr}; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window(outstm, st, t, buffer, buffered); } return; } @@ -986,7 +1275,14 @@ inline constexpr void print_context_capture_run(output outstm, char_type *buffer { ::fast_io::details::decay::context_capture_flush(outstm, buffer, curr); } - curr = print_reserve_define(::fast_io::io_reserve_type, curr, t); + if (sz <= static_cast<::std::size_t>(end - buffer)) + { + curr = print_reserve_define(::fast_io::io_reserve_type, curr, t); + } + else + { + ::fast_io::details::decay::print_control_single(outstm, t); + } } else if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { @@ -1534,6 +1830,127 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * return pscatters; } +template +inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... args) +{ + using scatter_type = ::std::conditional_t< + ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, + ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + scatter_type scatters[scatterscount]; + ::fast_io::details::decay::print_n_scatters(scatters, t, args...); + ::fast_io::details::decay::print_scatter_write_all_dispatch_with_line( + optstm, scatters, scatterscount); + } + else + { + ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); + ::fast_io::details::decay::print_n_scatters(scatters.ptr, t, args...); + ::fast_io::details::decay::print_scatter_write_all_dispatch_with_line( + optstm, scatters.ptr, scatterscount); + } +} + +template +inline constexpr void print_controls_scatters_reserve_with_scatter(outputstmtype optstm, scatter_type *scatters, T t, + Args... args) +{ + constexpr ::std::size_t buffer_size{mxsize == 0 ? 1u : mxsize}; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[buffer_size]; + auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, + buffer, t, + args...)}; + ::fast_io::details::decay::print_scatter_write_all_dispatch( + optstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } + else + { + ::fast_io::details::local_operator_new_array_ptr buffer(buffer_size); + auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, + buffer.ptr, t, + args...)}; + ::fast_io::details::decay::print_scatter_write_all_dispatch( + optstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + } +} + +template +inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, Args... args) +{ + using scatter_type = ::std::conditional_t< + ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, + ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + scatter_type scatters[scatterscount]; + ::fast_io::details::decay::print_controls_scatters_reserve_with_scatter(optstm, scatters, t, args...); + } + else + { + ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); + ::fast_io::details::decay::print_controls_scatters_reserve_with_scatter(optstm, scatters.ptr, t, + args...); + } +} + +template +inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outputstmtype optstm, scatter_type *scatters, + ::std::size_t totalsz, T t, Args... args) +{ + if constexpr (has_static_stack_size && + ::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + if (totalsz <= stack_buffer_size) + { + char_type buffer[stack_buffer_size]; + auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, + buffer, t, + args...)}; + ::fast_io::details::decay::print_scatter_write_all_dispatch( + optstm, scatters, static_cast<::std::size_t>(ptr - scatters)); + return; + } + } + ::fast_io::details::local_operator_new_array_ptr buffer(totalsz == 0 ? 1u : totalsz); + auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer.ptr, + t, args...)}; + ::fast_io::details::decay::print_scatter_write_all_dispatch( + optstm, scatters, static_cast<::std::size_t>(ptr - scatters)); +} + +template +inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype optstm, ::std::size_t totalsz, T t, + Args... args) +{ + using scatter_type = ::std::conditional_t< + ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, + ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + scatter_type scatters[scatterscount]; + ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< + needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters, totalsz, t, + args...); + } + else + { + ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); + ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< + needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters.ptr, totalsz, t, + args...); + } +} + template inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... args) { @@ -1574,9 +1991,19 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg : context_dynamic_size}; constexpr ::std::size_t buffer_size{reserve_context_dynamic_size < 32u ? 32u : reserve_context_dynamic_size}; - char_type buffer[buffer_size]; - ::fast_io::details::decay::print_context_capture_run( - optstm, buffer, buffer, buffer + buffer_size, t, args...); + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[buffer_size]; + ::fast_io::details::decay::print_context_capture_run( + optstm, buffer, buffer, buffer + buffer_size, t, args...); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(buffer_size); + char_type *buffer{newptr.ptr}; + ::fast_io::details::decay::print_context_capture_run( + optstm, buffer, buffer, buffer + buffer_size, t, args...); + } if constexpr (context_capture_res.position != n) { print_controls_impl(optstm, args...); @@ -1599,26 +2026,8 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg if constexpr (res.hasscatters && !res.hasreserve && !res.hasdynamicreserve) { constexpr ::std::size_t scatterscount{res.neededscatters + static_cast<::std::size_t>(needprintlf)}; - { - scatter_type scatters[scatterscount]; - ::fast_io::details::decay::print_n_scatters(scatters, t, args...); - if constexpr (needprintlf) - { - scatters[scatterscount - 1] = ::fast_io::details::decay::line_scatter_common< - char_type, ::std::conditional_t<::std::same_as, void, char_type>>; - } - if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< - outputstmtype>) - { - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, - scatterscount); - } - else - { - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, - scatterscount); - } - } + ::fast_io::details::decay::print_controls_scatters( + optstm, t, args...); if constexpr (res.position != n) { print_controls_impl(optstm, args...); @@ -1639,20 +2048,18 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg Args...>()}; constexpr ::std::size_t producer_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()}; + T, Args...>()}; constexpr ::std::size_t dynamic_stack_budget{ ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget()}; - /* - Cap only the dynamic reserve budget. mxsize belongs to the existing - reserve_printable storage and intentionally keeps its old stack behavior. - */ constexpr ::std::size_t stack_buffer_size{ ::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynamic_stack_budget)}; ::std::size_t dynsz{ ::fast_io::details::decay::ndynamic_print_reserve_size(t, args...)}; ::std::size_t totalsz{::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynsz)}; - if constexpr (has_static_stack_size && stack_buffer_size != 0) + if constexpr (has_static_stack_size && + ::fast_io::details::decay::print_stack_buffer_size_within_limit) { if (totalsz <= stack_buffer_size) { @@ -1708,38 +2115,41 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else { - char_type buffer[mxsize]; - char_type *ptred{ - ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; - if constexpr (needprintlf) + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { - *ptred = ::fast_io::char_literal_v; - ++ptred; + char_type buffer[mxsize]; + char_type *ptred{ + ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; + if constexpr (needprintlf) + { + *ptred = ::fast_io::char_literal_v; + ++ptred; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptred); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(mxsize); + char_type *buffer{newptr.ptr}; + char_type *ptred{ + ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; + if constexpr (needprintlf) + { + *ptred = ::fast_io::char_literal_v; + ++ptred; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptred); + } } - ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptred); } } - } else if constexpr (res.hasreserve && !res.hasdynamicreserve) { constexpr ::std::size_t scatterscount{res.neededscatters + static_cast<::std::size_t>(line && res.position == n)}; - scatter_type scatters[scatterscount]; - char_type buffer[mxsize]; - - auto ptr{::fast_io::details::decay::print_n_scatters_reserve( - scatters, buffer, t, args...)}; - ::std::size_t diff{static_cast<::std::size_t>(ptr - scatters)}; - - if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< - outputstmtype>) - { - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, diff); - } - else - { - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); - } + ::fast_io::details::decay::print_controls_scatters_reserve(optstm, t, args...); } else if constexpr (res.hasdynamicreserve) { @@ -1754,75 +2164,14 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg constexpr ::std::size_t dynamic_stack_budget{ ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget()}; - /* - Cap only the dynamic reserve budget. mxsize belongs to the existing - reserve/scatter storage and intentionally keeps its old stack behavior. - */ constexpr ::std::size_t stack_buffer_size{ ::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynamic_stack_budget)}; ::std::size_t dynsz{ ::fast_io::details::decay::ndynamic_print_reserve_size(t, args...)}; ::std::size_t totalsz{::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynsz)}; - scatter_type scatters[scatterscount]; - if constexpr (has_static_stack_size && stack_buffer_size != 0) - { - if (totalsz <= stack_buffer_size) - { - char_type buffer[stack_buffer_size]; - auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer, t, - args...)}; - ::std::size_t diff{static_cast<::std::size_t>(ptr - scatters)}; - if constexpr (::fast_io::operations::decay::defines:: - has_any_of_write_or_seek_pwrite_bytes_operations) - { - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, - diff); - } - else - { - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); - } - } - else - { - ::fast_io::details::local_operator_new_array_ptr newptr(totalsz); - char_type *buffer{newptr.ptr}; - auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer, t, - args...)}; - ::std::size_t diff{static_cast<::std::size_t>(ptr - scatters)}; - if constexpr (::fast_io::operations::decay::defines:: - has_any_of_write_or_seek_pwrite_bytes_operations) - { - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, - diff); - } - else - { - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); - } - } - } - else - { - ::fast_io::details::local_operator_new_array_ptr newptr(totalsz); - char_type *buffer{newptr.ptr}; - auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer, t, - args...)}; - ::std::size_t diff{static_cast<::std::size_t>(ptr - scatters)}; - if constexpr (::fast_io::operations::decay::defines:: - has_any_of_write_or_seek_pwrite_bytes_operations) - { - ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(optstm, scatters, - diff); - } - else - { - ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(optstm, scatters, diff); - } - } + ::fast_io::details::decay::print_controls_dynamic_scatters_reserve< + needprintlf, res.position, scatterscount, stack_buffer_size, has_static_stack_size, char_type>( + optstm, totalsz, t, args...); } if constexpr (res.position != n) { @@ -1853,39 +2202,23 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args using scatter_type = ::std::conditional_t< ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, io_scatter_t, basic_io_scatter_t>; - if constexpr (scatters_result.position != 0) - { - if constexpr (line) - { - static_assert(scatters_result.position != SIZE_MAX); - } - constexpr bool needprintlf{n == scatters_result.position && line}; - constexpr ::std::size_t scatterscount{scatters_result.position - scatters_result.null + static_cast<::std::size_t>(needprintlf)}; - scatter_type scatters[scatterscount]; - ::fast_io::details::decay::print_n_scatters(scatters, t, args...); - if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< - outputstmtype>) + if constexpr (scatters_result.position != 0) { - if constexpr (needprintlf) + if constexpr (line) { - scatters[scatterscount - 1] = ::fast_io::details::decay::line_scatter_common; + static_assert(scatters_result.position != SIZE_MAX); } - ::fast_io::operations::decay::scatter_write_all_bytes_decay(optstm, scatters, scatterscount); - } - else - { - if constexpr (needprintlf) + constexpr bool needprintlf{n == scatters_result.position && line}; + constexpr ::std::size_t scatterscount{scatters_result.position - scatters_result.null + + static_cast<::std::size_t>(needprintlf)}; + ::fast_io::details::decay::print_controls_scatters(optstm, t, args...); + if constexpr (scatters_result.position != n) { - scatters[scatterscount - 1] = ::fast_io::details::decay::line_scatter_common; + ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } - ::fast_io::operations::decay::scatter_write_all_decay(optstm, scatters, scatterscount); - } - if constexpr (scatters_result.position != n) - { - ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } - } else { constexpr auto rsvresult{ @@ -1913,30 +2246,53 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args ++bcurr; } obuffer_set_curr(optstm, bcurr); - } - else - { - char_type buffer[buffersize]; - if (!smaller) [[unlikely]] - { - bcurr = buffer; - } - bcurr = - ::fast_io::details::decay::print_n_reserve(bcurr, t, args...); - if constexpr (needprintlf) - { - *bcurr = ::fast_io::char_literal_v; - ++bcurr; } - if (smaller) [[likely]] - { - obuffer_set_curr(optstm, bcurr); - } - else [[unlikely]] + else { - ::fast_io::operations::decay::write_all_decay(optstm, buffer, bcurr); + if (smaller) [[likely]] + { + bcurr = + ::fast_io::details::decay::print_n_reserve(bcurr, t, + args...); + if constexpr (needprintlf) + { + *bcurr = ::fast_io::char_literal_v; + ++bcurr; + } + obuffer_set_curr(optstm, bcurr); + } + else [[unlikely]] + { + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + char_type buffer[buffersize]; + char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, + args...)}; + if constexpr (needprintlf) + { + *ptr = ::fast_io::char_literal_v; + ++ptr; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); + } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(buffersize); + char_type *buffer{newptr.ptr}; + char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, + args...)}; + if constexpr (needprintlf) + { + *ptr = ::fast_io::char_literal_v; + ++ptr; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); + } + } } - } if constexpr (rsvresult.position != n) { ::fast_io::details::decay::print_controls_buffer_impl( @@ -2119,6 +2475,32 @@ inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation && ::std::forward(t), ::std::forward(cont), ::std::make_index_sequence{}); } +template +struct print_semantic_value_prefix_continuation +{ + ::std::remove_reference_t *contptr; + ::std::remove_reference_t *valueptr; + + template + inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const + { + return (*contptr)(::std::forward(*valueptr), ::std::forward(tail_args)...); + } +}; + +template +struct print_semantic_lvalue_prefix_continuation +{ + ::std::remove_reference_t *contptr; + T *valueptr; + + template + inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const + { + return (*contptr)(*valueptr, ::std::forward(tail_args)...); + } +}; + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -2142,39 +2524,42 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t> && ::fast_io::details::print_pack< decltype(::fast_io::details::decay::print_semantic_node_ref(::std::forward(t)))>)) - { - return ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(t), [&](PackArgs &&...pack_args) -> decltype(auto) { - return ::fast_io::details::decay::print_semantic_pack_expand( - [&](ExpandedPackArgs &&...expanded_pack_args) -> decltype(auto) { - return ::fast_io::details::decay::print_semantic_pack_expand( - [&](TailArgs &&...tail_args) -> decltype(auto) { - return cont(::std::forward(expanded_pack_args)..., - ::std::forward(tail_args)...); - }, - ::std::forward(args)...); - }, - ::std::forward(pack_args)...); - }); - } - else if constexpr (already_forwarded) - { - return ::fast_io::details::decay::print_semantic_pack_expand( - [&](TailArgs &&...tail_args) -> decltype(auto) { - return cont(::std::forward(t), ::std::forward(tail_args)...); - }, - ::std::forward(args)...); - } - else - { - auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; - return ::fast_io::details::decay::print_semantic_pack_expand( - [&](TailArgs &&...tail_args) -> decltype(auto) { - return cont(forwarded, ::std::forward(tail_args)...); - }, - ::std::forward(args)...); - } -} + { + return ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(t), + [contptr = __builtin_addressof(cont), + ...argptrs = __builtin_addressof(args)]( + PackArgs &&...pack_args) -> decltype(auto) { + return ::fast_io::details::decay::print_semantic_pack_expand( + [contptr, argptrs...]( + ExpandedPackArgs &&...expanded_pack_args) -> decltype(auto) { + return ::fast_io::details::decay::print_semantic_pack_expand( + [contptr, ...expandedptrs = __builtin_addressof(expanded_pack_args)]< + typename... TailArgs>(TailArgs &&...tail_args) -> decltype(auto) { + return (*contptr)(::std::forward(*expandedptrs)..., + ::std::forward(tail_args)...); + }, + ::std::forward(*argptrs)...); + }, + ::std::forward(pack_args)...); + }); + } + else if constexpr (already_forwarded) + { + return ::fast_io::details::decay::print_semantic_pack_expand( + ::fast_io::details::decay::print_semantic_value_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(t)}, + ::std::forward(args)...); + } + else + { + auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; + return ::fast_io::details::decay::print_semantic_pack_expand( + ::fast_io::details::decay::print_semantic_lvalue_prefix_continuation< + continuation, decltype(forwarded)>{__builtin_addressof(cont), __builtin_addressof(forwarded)}, + ::std::forward(args)...); + } + } template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -2216,9 +2601,8 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont, else { return ::fast_io::details::decay::print_semantic_filter_nulls( - [&](TailArgs &&...tail_args) -> decltype(auto) { - return cont(::std::forward(t), ::std::forward(tail_args)...); - }, + ::fast_io::details::decay::print_semantic_value_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(t)}, ::std::forward(args)...); } } @@ -2530,21 +2914,43 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) return print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t)).len; } else if constexpr (::fast_io::reserve_scatters_printable) - { - constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; - static_assert(sz.scatters_size != 0); - ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; - char_type buffer[sz.reserve_size == 0 ? 1 : sz.reserve_size]; - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, - buffer, ::std::forward(t))}; - ::std::size_t len{}; - for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) { - len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + constexpr bool stack_buffer_ok{ + ::fast_io::details::decay::print_stack_buffer_size_within_limit && + ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; + if constexpr (stack_buffer_ok) + { + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[reserve_buffer_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + ::std::size_t len{}; + for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) + { + len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); + } + return len; + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( + sz.scatters_size); + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, + scatters.ptr, buffer.ptr, ::std::forward(t))}; + ::std::size_t len{}; + for (auto iter{scatters.ptr}; iter != result.scatters_pos_ptr; ++iter) + { + len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); + } + return len; + } } - return len; } -} template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -2557,6 +2963,42 @@ inline constexpr ::std::size_t print_semantic_width_placement_code(placement_typ return static_cast<::std::size_t>(placement); } +template <::std::integral char_type, typename width_traits, typename width_reference_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type print_semantic_width_fill_char(width_reference_type &&wr) noexcept +{ + if constexpr (width_traits::has_fill_char) + { + return static_cast(wr.ch); + } + else + { + return ::fast_io::char_literal_v; + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr auto print_semantic_width_placement(width_reference_type &&wr) noexcept +{ + if constexpr (width_traits::runtime_placement) + { + return wr.placement; + } + else + { + return width_traits::static_placement; + } +} + template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t); @@ -2612,6 +3054,22 @@ inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t) ::std::forward(forwarded)); } +template <::std::integral char_type> +struct print_semantic_precise_size_pack_continuation +{ + ::std::size_t *totalptr; + + template + inline constexpr void operator()(PackArgs &&...pack_args) const + { + ((*totalptr = ::fast_io::details::intrinsics::add_or_overflow_die( + *totalptr, + ::fast_io::operations::decay::print_semantic_precise_size_arg( + ::std::forward(pack_args)))), + ...); + } +}; + template <::std::integral char_type, typename T> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -2626,12 +3084,9 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) { ::std::size_t total{}; ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(node_ref), [&](PackArgs &&...pack_args) { - ((total = ::fast_io::details::intrinsics::add_or_overflow_die( - total, ::fast_io::operations::decay::print_semantic_precise_size_arg( - ::std::forward(pack_args)))), - ...); - }); + ::std::forward(node_ref), + ::fast_io::operations::decay::print_semantic_precise_size_pack_continuation{ + __builtin_addressof(total)}); return total; } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) @@ -2651,16 +3106,8 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) ::std::size_t const child_len{ ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; ::std::size_t const width{node_ref.width}; - auto const placement{[](width_reference_type &&wr) constexpr { - if constexpr (width_traits::runtime_placement) - { - return wr.placement; - } - else - { - return width_traits::static_placement; - } - }(node_ref)}; + auto const placement{ + ::fast_io::operations::decay::print_semantic_width_placement(node_ref)}; ::std::size_t const placement_code{ ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; if (width <= child_len || placement_code == 0u) @@ -2702,6 +3149,20 @@ inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T iter, ::std::forward(forwarded)); } +template <::std::integral char_type> +struct print_semantic_emit_unchecked_pack_continuation +{ + char_type **iterptr; + + template + inline constexpr void operator()(PackArgs &&...pack_args) const + { + ((*iterptr = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( + *iterptr, ::std::forward(pack_args))), + ...); + } +}; + template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, T &&t) { @@ -2741,20 +3202,41 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, return ::fast_io::details::non_overlapped_copy_n(scatter.base, scatter.len, iter); } else if constexpr (::fast_io::reserve_scatters_printable) - { - constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; - static_assert(sz.scatters_size != 0); - ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; - char_type buffer[sz.reserve_size == 0 ? 1 : sz.reserve_size]; - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, - buffer, ::std::forward(t))}; - for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) { - iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + constexpr bool stack_buffer_ok{ + ::fast_io::details::decay::print_stack_buffer_size_within_limit && + ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; + if constexpr (stack_buffer_ok) + { + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[reserve_buffer_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) + { + iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); + } + return iter; + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( + sz.scatters_size); + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, + scatters.ptr, buffer.ptr, ::std::forward(t))}; + for (auto scatter_iter{scatters.ptr}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) + { + iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); + } + return iter; + } } - return iter; } -} template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t) @@ -2764,11 +3246,9 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t if constexpr (::fast_io::details::print_pack) { ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(node_ref), [&](PackArgs &&...pack_args) { - ((iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( - iter, ::std::forward(pack_args))), - ...); - }); + ::std::forward(node_ref), + ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_continuation{ + __builtin_addressof(iter)}); return iter; } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) @@ -2785,26 +3265,10 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t ::std::size_t const len{ ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; ::std::size_t const width{node_ref.width}; - char_type const fillch{[](width_reference_type &&wr) constexpr { - if constexpr (width_traits::has_fill_char) - { - return static_cast(wr.ch); - } - else - { - return ::fast_io::char_literal_v; - } - }(node_ref)}; - auto const placement{[](width_reference_type &&wr) constexpr { - if constexpr (width_traits::runtime_placement) - { - return wr.placement; - } - else - { - return width_traits::static_placement; - } - }(node_ref)}; + char_type const fillch{ + ::fast_io::operations::decay::print_semantic_width_fill_char(node_ref)}; + auto const placement{ + ::fast_io::operations::decay::print_semantic_width_placement(node_ref)}; ::std::size_t const placement_code{ ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; if (width <= len || placement_code == 0u) @@ -3012,6 +3476,19 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } } +template +struct print_semantic_emit_node_pack_continuation +{ + outputstmtype optstm; + + template + inline constexpr void operator()(PackArgs &&...pack_args) const + { + ::fast_io::operations::decay::print_semantic_emit( + optstm, ::std::forward(pack_args)...); + } +}; + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3026,26 +3503,9 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) using width_child_type = ::fast_io::details::decay::print_semantic_forwarded_arg_t; ::std::size_t const width{width_ref.width}; - char_type const fillch{[](width_reference_type &&wr) constexpr { - if constexpr (width_traits::has_fill_char) - { - return static_cast(wr.ch); - } - else - { - return ::fast_io::char_literal_v; - } - }(width_ref)}; - auto const placement{[](width_reference_type &&wr) constexpr { - if constexpr (width_traits::runtime_placement) - { - return wr.placement; - } - else - { - return width_traits::static_placement; - } - }(width_ref)}; + char_type const fillch{ + ::fast_io::operations::decay::print_semantic_width_fill_char(width_ref)}; + auto const placement{::fast_io::operations::decay::print_semantic_width_placement(width_ref)}; ::std::size_t const placement_code{ ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; if constexpr (::fast_io::details::decay::print_semantic_static_precise_size) { ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(node_ref), [optstm](PackArgs &&...pack_args) { - ::fast_io::operations::decay::print_semantic_emit( - optstm, ::std::forward(pack_args)...); - }); + ::std::forward(node_ref), + ::fast_io::operations::decay::print_semantic_emit_node_pack_continuation{ + optstm}); } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) { @@ -3173,6 +3632,20 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype, continuation cont.template operator()(); } +template +struct print_semantic_emit_flat_prefix_continuation +{ + ::std::remove_reference_t *contptr; + ::std::remove_reference_t *valueptr; + + template + inline constexpr void operator()(Prefix &&...prefix) const + { + contptr->template operator()(::std::forward(*valueptr), + ::std::forward(prefix)...); + } +}; + template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -3196,13 +3669,52 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, contin { ::fast_io::operations::decay::print_semantic_emit_flat_impl( optstm, - [&](Prefix &&...prefix) constexpr { - cont.template operator()(::std::forward(t), ::std::forward(prefix)...); - }, + ::fast_io::operations::decay::print_semantic_emit_flat_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(t)}, ::std::forward(args)...); } } +template +struct print_semantic_emit_freestanding_continuation +{ + outputstmtype optstm; + + template + inline constexpr void operator()(Prefix &&...prefix) const + { + ::fast_io::operations::decay::print_freestanding_decay_no_pack( + optstm, ::std::forward(prefix)...); + } +}; + +template +struct print_semantic_emit_flat_continuation +{ + outputstmtype optstm; + + template + inline constexpr void operator()(FilteredArgs &&...filtered_args) const + { + ::fast_io::operations::decay::print_semantic_emit_flat_impl( + optstm, ::fast_io::operations::decay::print_semantic_emit_freestanding_continuation{optstm}, + ::std::forward(filtered_args)...); + } +}; + +template +struct print_semantic_filter_flat_continuation +{ + ::std::remove_reference_t *emitflatptr; + + template + inline constexpr void operator()(FlatArgs &&...flat_args) const + { + ::fast_io::details::decay::print_semantic_filter_nulls( + *emitflatptr, ::std::forward(flat_args)...); + } +}; + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3214,15 +3726,7 @@ template (FilteredArgs &&...filtered_args) constexpr { - ::fast_io::operations::decay::print_semantic_emit_flat_impl( - optstm, - [optstm](Prefix &&...prefix) constexpr { - ::fast_io::operations::decay::print_freestanding_decay_no_pack( - optstm, ::std::forward(prefix)...); - }, - ::std::forward(filtered_args)...); - }}; + print_semantic_emit_flat_continuation emit_flat{optstm}; if constexpr (!((::fast_io::details::decay::print_semantic_pack_argument_v || ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) || ...)) @@ -3232,10 +3736,8 @@ inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args) else { ::fast_io::details::decay::print_semantic_pack_expand( - [&](FlatArgs &&...flat_args) constexpr { - ::fast_io::details::decay::print_semantic_filter_nulls( - emit_flat, ::std::forward(flat_args)...); - }, + ::fast_io::operations::decay::print_semantic_filter_flat_continuation{ + __builtin_addressof(emit_flat)}, ::std::forward(args)...); } } diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index daaa9947b..8e61678cc 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -545,6 +545,13 @@ io_bytes_stream_ref_define(basic_posix_family_io_observer other return {other.fd}; } +template <::fast_io::posix_family family, ::std::integral char_type> +inline constexpr ::std::size_t scatter_fallback_full_output_threshold( + ::fast_io::io_reserve_type_t>) noexcept +{ + return static_cast<::std::size_t>(256u / sizeof(char_type)); +} + #if defined(__CYGWIN__) // https://github.com/cygwin/cygwin/blob/c43ec5f5951c7f4b882a0f8e619601a45ae70a91/newlib/libc/include/sys/_default_fcntl.h#L168 From b5faf9cfee59496b0b9ab95400dcabecede76c86 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 19:02:38 +0800 Subject: [PATCH 04/45] Fix fast_io C++20 semantic scatter expansion --- .../printimpl/print_freestanding_cxx20.h | 250 ++++++++++++++++-- .../include/fast_io_hosted/platforms/posix.h | 2 +- 2 files changed, 225 insertions(+), 27 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 6869a1c5f..895c26a2f 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -1807,10 +1807,10 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * } else if constexpr (n == 1 && needprintlf) { - *pit = ::fast_io::details::decay::line_scatter_common; - ++pit; + *pit.scatters_pos_ptr = ::fast_io::details::decay::line_scatter_common; + ++pit.scatters_pos_ptr; } - return pit; + return pit.scatters_pos_ptr; } } else if constexpr (::std::same_as) @@ -1903,6 +1903,11 @@ inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outputstmtype optstm, scatter_type *scatters, ::std::size_t totalsz, T t, Args... args) { @@ -1929,6 +1934,11 @@ inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outpu template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype optstm, ::std::size_t totalsz, T t, Args... args) { @@ -1951,6 +1961,67 @@ inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype opts } } +template +inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... args); + +template <::std::integral char_type> +inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_available() noexcept +{ + return false; +} + +template <::std::integral char_type, typename T, typename... Args> +inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_available() noexcept +{ + constexpr contiguous_scatter_result res{ + ::fast_io::details::decay::find_continuous_scatters_n()}; + return res.position != 0 && res.hasscatters && res.hasdynamicreserve; +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void print_controls_dynamic_scatters_reserve_fast_entry(outputstmtype optstm, T t, Args... args) +{ + using char_type = typename outputstmtype::output_char_type; + constexpr contiguous_scatter_result res{ + ::fast_io::details::decay::find_continuous_scatters_n()}; + static_assert(res.position != 0); + static_assert(res.hasscatters && res.hasdynamicreserve); + if constexpr (line) + { + static_assert(res.neededscatters != SIZE_MAX); + } + static_assert(SIZE_MAX != sizeof...(Args)); + constexpr ::std::size_t n{sizeof...(Args) + static_cast<::std::size_t>(1)}; + constexpr bool needprintlf{n == res.position && line}; + constexpr ::std::size_t mxsize{ + static_cast<::std::size_t>(res.neededspace + static_cast<::std::size_t>(needprintlf))}; + constexpr ::std::size_t scatterscount{res.neededscatters + + static_cast<::std::size_t>(line && res.position == n)}; + constexpr bool has_static_stack_size{ + ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size()}; + constexpr ::std::size_t producer_static_stack_size{ + ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()}; + constexpr ::std::size_t dynamic_stack_budget{ + ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget()}; + constexpr ::std::size_t stack_buffer_size{ + ::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynamic_stack_budget)}; + ::std::size_t dynsz{::fast_io::details::decay::ndynamic_print_reserve_size(t, args...)}; + ::std::size_t totalsz{::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynsz)}; + ::fast_io::details::decay::print_controls_dynamic_scatters_reserve< + needprintlf, res.position, scatterscount, stack_buffer_size, has_static_stack_size, char_type>(optstm, totalsz, + t, args...); + if constexpr (res.position != n) + { + print_controls_impl(optstm, args...); + } +} + template inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... args) { @@ -2482,6 +2553,11 @@ struct print_semantic_value_prefix_continuation ::std::remove_reference_t *valueptr; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { return (*contptr)(::std::forward(*valueptr), ::std::forward(tail_args)...); @@ -2495,6 +2571,11 @@ struct print_semantic_lvalue_prefix_continuation T *valueptr; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { return (*contptr)(*valueptr, ::std::forward(tail_args)...); @@ -2512,6 +2593,96 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont) return cont(); } +template +inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args); + +template +struct print_semantic_pack_expand_tail_continuation +{ + ::std::remove_reference_t *contptr; + ::fast_io::containers::tuple<::std::remove_reference_t *...> expandedptrs; + + template <::std::size_t... I, typename... TailArgs> +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + inline constexpr decltype(auto) operator_impl(::std::index_sequence, TailArgs &&...tail_args) const + { + return (*contptr)(::std::forward(*::fast_io::containers::get(expandedptrs))..., + ::std::forward(tail_args)...); + } + + template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const + { + return operator_impl(::std::make_index_sequence{}, + ::std::forward(tail_args)...); + } +}; + +template +struct print_semantic_pack_expand_middle_continuation +{ + ::std::remove_reference_t *contptr; + ::fast_io::containers::tuple<::std::remove_reference_t *...> const *argptrs; + + template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + inline constexpr decltype(auto) operator_impl(tail_continuation &&tail_cont, ::std::index_sequence) const + { + return ::fast_io::details::decay::print_semantic_pack_expand( + ::std::forward(tail_cont), + ::std::forward(*::fast_io::containers::get(*argptrs))...); + } + + template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + inline constexpr decltype(auto) operator()(ExpandedPackArgs &&...expanded_pack_args) const + { + return operator_impl( + ::fast_io::details::decay::print_semantic_pack_expand_tail_continuation< + already_forwarded, char_type, continuation, ExpandedPackArgs...>{ + contptr, {__builtin_addressof(expanded_pack_args)...}}, + ::std::make_index_sequence{}); + } +}; + +template +struct print_semantic_pack_expand_initial_continuation +{ + ::std::remove_reference_t *contptr; + ::fast_io::containers::tuple<::std::remove_reference_t *...> argptrs; + + template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + inline constexpr decltype(auto) operator()(PackArgs &&...pack_args) const + { + return ::fast_io::details::decay::print_semantic_pack_expand( + ::fast_io::details::decay::print_semantic_pack_expand_middle_continuation< + already_forwarded, char_type, continuation, Args...>{contptr, __builtin_addressof(argptrs)}, + ::std::forward(pack_args)...); + } +}; + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -2524,26 +2695,13 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t> && ::fast_io::details::print_pack< decltype(::fast_io::details::decay::print_semantic_node_ref(::std::forward(t)))>)) - { - return ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(t), - [contptr = __builtin_addressof(cont), - ...argptrs = __builtin_addressof(args)]( - PackArgs &&...pack_args) -> decltype(auto) { - return ::fast_io::details::decay::print_semantic_pack_expand( - [contptr, argptrs...]( - ExpandedPackArgs &&...expanded_pack_args) -> decltype(auto) { - return ::fast_io::details::decay::print_semantic_pack_expand( - [contptr, ...expandedptrs = __builtin_addressof(expanded_pack_args)]< - typename... TailArgs>(TailArgs &&...tail_args) -> decltype(auto) { - return (*contptr)(::std::forward(*expandedptrs)..., - ::std::forward(tail_args)...); - }, - ::std::forward(*argptrs)...); - }, - ::std::forward(pack_args)...); - }); - } + { + return ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(t), + ::fast_io::details::decay::print_semantic_pack_expand_initial_continuation< + already_forwarded, char_type, continuation, Args...>{__builtin_addressof(cont), + {__builtin_addressof(args)...}}); + } else if constexpr (already_forwarded) { return ::fast_io::details::decay::print_semantic_pack_expand( @@ -3639,6 +3797,11 @@ struct print_semantic_emit_flat_prefix_continuation ::std::remove_reference_t *valueptr; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr void operator()(Prefix &&...prefix) const { contptr->template operator()(::std::forward(*valueptr), @@ -3681,10 +3844,25 @@ struct print_semantic_emit_freestanding_continuation outputstmtype optstm; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr void operator()(Prefix &&...prefix) const { - ::fast_io::operations::decay::print_freestanding_decay_no_pack( - optstm, ::std::forward(prefix)...); + using char_type = typename outputstmtype::output_char_type; + if constexpr (::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry_available< + char_type, ::std::remove_cvref_t...>()) + { + ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry( + optstm, ::std::forward(prefix)...); + } + else + { + ::fast_io::operations::decay::print_freestanding_decay_no_pack( + optstm, ::std::forward(prefix)...); + } } }; @@ -3694,6 +3872,11 @@ struct print_semantic_emit_flat_continuation outputstmtype optstm; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr void operator()(FilteredArgs &&...filtered_args) const { ::fast_io::operations::decay::print_semantic_emit_flat_impl( @@ -3708,6 +3891,11 @@ struct print_semantic_filter_flat_continuation ::std::remove_reference_t *emitflatptr; template +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif inline constexpr void operator()(FlatArgs &&...flat_args) const { ::fast_io::details::decay::print_semantic_filter_nulls( @@ -3760,7 +3948,17 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A } else { - return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); + using char_type = typename outputstmtype::output_char_type; + if constexpr (::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry_available< + char_type, ::std::remove_cvref_t...>()) + { + return ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry(optstm, + args...); + } + else + { + return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); + } } } diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 8e61678cc..17a2bc98a 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -549,7 +549,7 @@ template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t scatter_fallback_full_output_threshold( ::fast_io::io_reserve_type_t>) noexcept { - return static_cast<::std::size_t>(256u / sizeof(char_type)); + return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); } #if defined(__CYGWIN__) From da0ee0dbfbee3dd680b07a18ac453f63fa2c0396 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 19:20:41 +0800 Subject: [PATCH 05/45] Optimize fast_io C++20 semantic width small output --- .../printimpl/print_freestanding_cxx20.h | 77 +++++++++++++++++-- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 895c26a2f..7a4ecc1c8 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -3565,14 +3565,77 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & { required = ::fast_io::details::intrinsics::add_or_overflow_die(required, static_cast<::std::size_t>(1u)); } - if (required <= static_cast<::std::size_t>(256u)) + constexpr ::std::size_t small_width_buffer_size{ + (::fast_io::details::decay::print_stack_buffer_max_size() < static_cast<::std::size_t>(256u)) + ? ::fast_io::details::decay::print_stack_buffer_max_size() + : static_cast<::std::size_t>(256u)}; + if constexpr (small_width_buffer_size != 0u) { - ::fast_io::basic_dynamic_output_buffer buffer; - auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; - ::fast_io::operations::decay::print_semantic_emit_width_direct( - buffer_ref, reference, width, fillch, placement_code, len); - ::fast_io::operations::decay::write_all_decay(optstm, buffer.begin_ptr, buffer.curr_ptr); - return; + if (required <= small_width_buffer_size) + { + char_type buffer[small_width_buffer_size]; + char_type *iter{buffer}; + if (width <= len || placement_code == 0u) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + } + else + { + ::std::size_t const padding{width - len}; + if (placement_code == 1u) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + } + else if (placement_code == 2u) + { + ::std::size_t const left_padding{padding >> 1u}; + ::std::size_t const right_padding{padding - left_padding}; + iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + iter = ::fast_io::details::my_fill_n(iter, right_padding, fillch); + } + else if (placement_code == 4u) + { + ::std::size_t const internal_len{ + ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; + if (internal_len == 0) + { + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, + reference); + } + else + { + char_type *const first{iter}; + char_type *const last{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference)}; + if (len < internal_len) + { + iter = last; + } + else + { + char_type *const shift_pos{first + internal_len}; + ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); + ::fast_io::details::my_fill_n(shift_pos, padding, fillch); + iter = first + width; + } + } + } + else + { + iter = ::fast_io::details::my_fill_n(iter, padding, fillch); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); + } + } + if constexpr (line) + { + *iter++ = ::fast_io::char_literal_v; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return; + } } if (width <= len || placement_code == 0u) { From d7acc7ff90b7843c5115f004d78e787b348923c2 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 19:54:30 +0800 Subject: [PATCH 06/45] Refactor print control logic in fast_io for improved clarity and performance - Simplified conditional structures in `print_freestanding_cxx20.h` to enhance readability and maintainability. - Streamlined buffer management and error handling in print functions, ensuring consistent behavior across different scenarios. - Updated comments and documentation to clarify the purpose of changes and improve user understanding of the print control mechanisms. --- .../printimpl/print_freestanding_cxx20.h | 382 +++++++++--------- 1 file changed, 189 insertions(+), 193 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 7a4ecc1c8..4359e0e3a 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -778,13 +778,13 @@ inline constexpr void print_control_single(output outstm, T t) #endif basic_io_scatter_t scatter_res{print_scatter_define(::fast_io::io_reserve_type, t)}; - if constexpr (line) - { - ::fast_io::details::decay::print_single_scatter_with_line(outstm, scatter_res); - } - else - { - ::fast_io::operations::decay::write_all_decay(outstm, scatter_res.base, scatter_res.base + scatter_res.len); + if constexpr (line) + { + ::fast_io::details::decay::print_single_scatter_with_line(outstm, scatter_res); + } + else + { + ::fast_io::operations::decay::write_all_decay(outstm, scatter_res.base, scatter_res.base + scatter_res.len); } } else if constexpr (reserve_printable) @@ -1046,14 +1046,14 @@ inline constexpr void print_control_single(output outstm, T t) } } } - else if constexpr (reserve_scatters_printable) - { - ::fast_io::details::decay::print_control_single_reserve_scatters(outstm, t); - } - else if constexpr (::fast_io::transcode_imaginary_printable) - { - // todo? - } + else if constexpr (reserve_scatters_printable) + { + ::fast_io::details::decay::print_control_single_reserve_scatters(outstm, t); + } + else if constexpr (::fast_io::transcode_imaginary_printable) + { + // todo? + } else if constexpr (context_printable) { typename ::std::remove_cvref_t))>::type st; @@ -1087,25 +1087,25 @@ inline constexpr void print_control_single(output outstm, T t) #if __has_cpp_attribute(unlikely) [[unlikely]] #endif + { + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit< + reserved_size, char_type>) { - if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit< - reserved_size, char_type>) - { - char_type buffer[reserved_size]; - char_type *buffered{buffer + reserved_size_no_line}; - ::fast_io::details::decay::print_context_define_to_window( - outstm, st, t, buffer, buffered); - } - else - { - ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); - char_type *buffer{newptr.ptr}; - char_type *buffered{buffer + reserved_size_no_line}; - ::fast_io::details::decay::print_context_define_to_window( - outstm, st, t, buffer, buffered); - } - return; + char_type buffer[reserved_size]; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window( + outstm, st, t, buffer, buffered); } + else + { + ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); + char_type *buffer{newptr.ptr}; + char_type *buffered{buffer + reserved_size_no_line}; + ::fast_io::details::decay::print_context_define_to_window( + outstm, st, t, buffer, buffered); + } + return; + } } } else @@ -1897,7 +1897,7 @@ inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); ::fast_io::details::decay::print_controls_scatters_reserve_with_scatter(optstm, scatters.ptr, t, - args...); + args...); } } @@ -1950,14 +1950,14 @@ inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype opts scatter_type scatters[scatterscount]; ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters, totalsz, t, - args...); + args...); } else { ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters.ptr, totalsz, t, - args...); + args...); } } @@ -2004,7 +2004,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve_fast_entry(outputs static_cast<::std::size_t>(line && res.position == n)}; constexpr bool has_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size()}; + Args...>()}; constexpr ::std::size_t producer_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()}; constexpr ::std::size_t dynamic_stack_budget{ @@ -2015,7 +2015,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve_fast_entry(outputs ::std::size_t totalsz{::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynsz)}; ::fast_io::details::decay::print_controls_dynamic_scatters_reserve< needprintlf, res.position, scatterscount, stack_buffer_size, has_static_stack_size, char_type>(optstm, totalsz, - t, args...); + t, args...); if constexpr (res.position != n) { print_controls_impl(optstm, args...); @@ -2026,7 +2026,7 @@ template , io_scatter_t, basic_io_scatter_t>; constexpr contiguous_scatter_result res{ @@ -2115,14 +2115,14 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg { constexpr bool has_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size()}; + char_type, T, + Args...>()}; constexpr ::std::size_t producer_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()}; + T, Args...>()}; constexpr ::std::size_t dynamic_stack_budget{ ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget()}; + char_type>()}; constexpr ::std::size_t stack_buffer_size{ ::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynamic_stack_budget)}; ::std::size_t dynsz{ @@ -2130,7 +2130,7 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg ::std::size_t totalsz{::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynsz)}; if constexpr (has_static_stack_size && ::fast_io::details::decay::print_stack_buffer_size_within_limit) + char_type>) { if (totalsz <= stack_buffer_size) { @@ -2187,7 +2187,7 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg else { if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) + char_type>) { char_type buffer[mxsize]; char_type *ptred{ @@ -2211,16 +2211,16 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg ++ptred; } ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptred); - } } } } + } else if constexpr (res.hasreserve && !res.hasdynamicreserve) { constexpr ::std::size_t scatterscount{res.neededscatters + static_cast<::std::size_t>(line && res.position == n)}; ::fast_io::details::decay::print_controls_scatters_reserve(optstm, t, args...); + mxsize, char_type>(optstm, t, args...); } else if constexpr (res.hasdynamicreserve) { @@ -2228,13 +2228,13 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg static_cast<::std::size_t>(line && res.position == n)}; constexpr bool has_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size()}; + Args...>()}; constexpr ::std::size_t producer_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()}; + Args...>()}; constexpr ::std::size_t dynamic_stack_budget{ ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget()}; + char_type>()}; constexpr ::std::size_t stack_buffer_size{ ::fast_io::details::intrinsics::add_or_overflow_die(mxsize, dynamic_stack_budget)}; ::std::size_t dynsz{ @@ -2270,26 +2270,26 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args constexpr ::std::size_t n{sizeof...(Args) + static_cast<::std::size_t>(1)}; constexpr auto scatters_result{ ::fast_io::details::decay::find_continuous_scatters_reserve_n()}; - using scatter_type = ::std::conditional_t< + using scatter_type [[maybe_unused]] = ::std::conditional_t< ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, io_scatter_t, basic_io_scatter_t>; - if constexpr (scatters_result.position != 0) + if constexpr (scatters_result.position != 0) + { + if constexpr (line) { - if constexpr (line) - { - static_assert(scatters_result.position != SIZE_MAX); - } - constexpr bool needprintlf{n == scatters_result.position && line}; - constexpr ::std::size_t scatterscount{scatters_result.position - scatters_result.null + - static_cast<::std::size_t>(needprintlf)}; - ::fast_io::details::decay::print_controls_scatters(optstm, t, args...); - if constexpr (scatters_result.position != n) - { - ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); - } + static_assert(scatters_result.position != SIZE_MAX); + }[[maybe_unused]] + constexpr bool needprintlf{n == scatters_result.position && line}; + constexpr ::std::size_t scatterscount{scatters_result.position - scatters_result.null + + static_cast<::std::size_t>(needprintlf)}; + ::fast_io::details::decay::print_controls_scatters(optstm, t, args...); + if constexpr (scatters_result.position != n) + { + ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } + } else { constexpr auto rsvresult{ @@ -2317,53 +2317,53 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args ++bcurr; } obuffer_set_curr(optstm, bcurr); + } + else + { + if (smaller) [[likely]] + { + bcurr = + ::fast_io::details::decay::print_n_reserve(bcurr, t, + args...); + if constexpr (needprintlf) + { + *bcurr = ::fast_io::char_literal_v; + ++bcurr; + } + obuffer_set_curr(optstm, bcurr); } - else + else [[unlikely]] { - if (smaller) [[likely]] + if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { - bcurr = - ::fast_io::details::decay::print_n_reserve(bcurr, t, - args...); + char_type buffer[buffersize]; + char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, + args...)}; if constexpr (needprintlf) { - *bcurr = ::fast_io::char_literal_v; - ++bcurr; + *ptr = ::fast_io::char_literal_v; + ++ptr; } - obuffer_set_curr(optstm, bcurr); + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); } - else [[unlikely]] + else { - if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) - { - char_type buffer[buffersize]; - char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, - args...)}; - if constexpr (needprintlf) - { - *ptr = ::fast_io::char_literal_v; - ++ptr; - } - ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); - } - else + ::fast_io::details::local_operator_new_array_ptr newptr(buffersize); + char_type *buffer{newptr.ptr}; + char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, + args...)}; + if constexpr (needprintlf) { - ::fast_io::details::local_operator_new_array_ptr newptr(buffersize); - char_type *buffer{newptr.ptr}; - char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, - args...)}; - if constexpr (needprintlf) - { - *ptr = ::fast_io::char_literal_v; - ++ptr; - } - ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); + *ptr = ::fast_io::char_literal_v; + ++ptr; } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); } } + } if constexpr (rsvresult.position != n) { ::fast_io::details::decay::print_controls_buffer_impl( @@ -2695,29 +2695,29 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t> && ::fast_io::details::print_pack< decltype(::fast_io::details::decay::print_semantic_node_ref(::std::forward(t)))>)) - { - return ::fast_io::details::decay::print_semantic_pack_apply( - ::std::forward(t), - ::fast_io::details::decay::print_semantic_pack_expand_initial_continuation< - already_forwarded, char_type, continuation, Args...>{__builtin_addressof(cont), - {__builtin_addressof(args)...}}); - } - else if constexpr (already_forwarded) - { - return ::fast_io::details::decay::print_semantic_pack_expand( - ::fast_io::details::decay::print_semantic_value_prefix_continuation{ - __builtin_addressof(cont), __builtin_addressof(t)}, - ::std::forward(args)...); - } - else - { - auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; - return ::fast_io::details::decay::print_semantic_pack_expand( - ::fast_io::details::decay::print_semantic_lvalue_prefix_continuation< - continuation, decltype(forwarded)>{__builtin_addressof(cont), __builtin_addressof(forwarded)}, - ::std::forward(args)...); - } - } + { + return ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(t), + ::fast_io::details::decay::print_semantic_pack_expand_initial_continuation< + already_forwarded, char_type, continuation, Args...>{__builtin_addressof(cont), + {__builtin_addressof(args)...}}); + } + else if constexpr (already_forwarded) + { + return ::fast_io::details::decay::print_semantic_pack_expand( + ::fast_io::details::decay::print_semantic_value_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(t)}, + ::std::forward(args)...); + } + else + { + auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; + return ::fast_io::details::decay::print_semantic_pack_expand( + ::fast_io::details::decay::print_semantic_lvalue_prefix_continuation< + continuation, decltype(forwarded)>{__builtin_addressof(cont), __builtin_addressof(forwarded)}, + ::std::forward(args)...); + } +} template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -2876,9 +2876,7 @@ template <::std::integral char_type, typename T1, typename T2> struct print_semantic_precise_size_ok_impl> : ::std::bool_constant< ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && - ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ::fast_io::details::decay::print_semantic_precise_size_ok>::value> {}; template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> @@ -2891,8 +2889,7 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_placement p ::std::integral ch_type> struct print_semantic_precise_size_ok_impl> : ::std::bool_constant< - ::std::same_as && - ::fast_io::details::decay::print_semantic_precise_size_ok< + ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> {}; @@ -2905,8 +2902,7 @@ struct print_semantic_precise_size_ok_impl struct print_semantic_precise_size_ok_impl> : ::std::bool_constant< - ::std::same_as && - ::fast_io::details::decay::print_semantic_precise_size_ok< + ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> {}; @@ -3072,43 +3068,43 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) return print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t)).len; } else if constexpr (::fast_io::reserve_scatters_printable) + { + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + constexpr bool stack_buffer_ok{ + ::fast_io::details::decay::print_stack_buffer_size_within_limit && + ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; + if constexpr (stack_buffer_ok) { - constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; - static_assert(sz.scatters_size != 0); - constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; - constexpr bool stack_buffer_ok{ - ::fast_io::details::decay::print_stack_buffer_size_within_limit && - ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; - if constexpr (stack_buffer_ok) + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[reserve_buffer_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + ::std::size_t len{}; + for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) { - ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; - char_type buffer[reserve_buffer_size]; - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, - buffer, ::std::forward(t))}; - ::std::size_t len{}; - for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) - { - len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); - } - return len; + len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); } - else + return len; + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( + sz.scatters_size); + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, + scatters.ptr, buffer.ptr, ::std::forward(t))}; + ::std::size_t len{}; + for (auto iter{scatters.ptr}; iter != result.scatters_pos_ptr; ++iter) { - ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( - sz.scatters_size); - ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, - scatters.ptr, buffer.ptr, ::std::forward(t))}; - ::std::size_t len{}; - for (auto iter{scatters.ptr}; iter != result.scatters_pos_ptr; ++iter) - { - len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); - } - return len; + len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); } + return len; } } +} template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -3360,41 +3356,41 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, return ::fast_io::details::non_overlapped_copy_n(scatter.base, scatter.len, iter); } else if constexpr (::fast_io::reserve_scatters_printable) + { + constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; + static_assert(sz.scatters_size != 0); + constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; + constexpr bool stack_buffer_ok{ + ::fast_io::details::decay::print_stack_buffer_size_within_limit && + ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; + if constexpr (stack_buffer_ok) { - constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; - static_assert(sz.scatters_size != 0); - constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; - constexpr bool stack_buffer_ok{ - ::fast_io::details::decay::print_stack_buffer_size_within_limit && - ::fast_io::details::decay::print_stack_buffer_size_within_limit>}; - if constexpr (stack_buffer_ok) + ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; + char_type buffer[reserve_buffer_size]; + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, + buffer, ::std::forward(t))}; + for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) { - ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; - char_type buffer[reserve_buffer_size]; - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, - buffer, ::std::forward(t))}; - for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) - { - iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); - } - return iter; + iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); } - else + return iter; + } + else + { + ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( + sz.scatters_size); + ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); + auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, + scatters.ptr, buffer.ptr, ::std::forward(t))}; + for (auto scatter_iter{scatters.ptr}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) { - ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( - sz.scatters_size); - ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); - auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, - scatters.ptr, buffer.ptr, ::std::forward(t))}; - for (auto scatter_iter{scatters.ptr}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) - { - iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); - } - return iter; + iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); } + return iter; } } +} template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t) @@ -3526,7 +3522,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & { iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, - reference); + reference); } else { @@ -3603,7 +3599,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & { iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, - reference); + reference); } else { @@ -3835,7 +3831,7 @@ inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) else if constexpr (::fast_io::details::decay::print_semantic_width_v) { ::fast_io::operations::decay::print_semantic_emit_width(optstm, - ::std::forward(t)); + ::std::forward(t)); } } @@ -3889,7 +3885,7 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, contin cont.template operator()(); ::fast_io::operations::decay::print_semantic_emit_node(optstm, ::std::forward(t)); ::fast_io::operations::decay::print_semantic_emit(optstm, - ::std::forward(args)...); + ::std::forward(args)...); } else { @@ -4079,7 +4075,7 @@ inline constexpr void print_freestanding(output &&outstm, Args &&...args) auto outref{::fast_io::operations::output_stream_ref(outstm)}; using char_type = typename decltype(outref)::output_char_type; if constexpr ((false || ... || - ::fast_io::details::decay::print_semantic_input_argument_v)) + ::fast_io::details::decay::print_semantic_input_argument_v)) { ::fast_io::operations::decay::print_semantic_emit( outref, From 79e3e2d2487f08e8b72b41ac1170dd770da6c570 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 7 Jul 2026 20:20:58 +0800 Subject: [PATCH 07/45] Add output stream reference definitions for lockable types in fast_io - Introduced new template functions `output_stream_ref_define` and `input_stream_ref_define` for `basic_general_io_lockable_ref` types, enhancing the handling of output and input streams. - Updated existing functions to support these new definitions, improving consistency and usability across the fast_io library. - Enhanced overall clarity and maintainability of the code by ensuring proper handling of lockable types in stream operations. --- .../include/fast_io_core_impl/io_lockable.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/third-parties/fast_io/include/fast_io_core_impl/io_lockable.h b/third-parties/fast_io/include/fast_io_core_impl/io_lockable.h index 3ac9418c6..c85499550 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/io_lockable.h +++ b/third-parties/fast_io/include/fast_io_core_impl/io_lockable.h @@ -118,6 +118,14 @@ output_stream_ref_define(::fast_io::basic_general_io_lockable_nonmovable + requires ::fast_io::operations::defines::has_output_or_io_stream_ref_define +inline constexpr basic_general_io_lockable_ref +output_stream_ref_define(::fast_io::basic_general_io_lockable_ref m) noexcept +{ + return m; +} + template requires ::fast_io::operations::defines::has_input_or_io_stream_ref_define inline constexpr basic_general_io_lockable_ref @@ -126,6 +134,14 @@ input_stream_ref_define(::fast_io::basic_general_io_lockable_nonmovable + requires ::fast_io::operations::defines::has_input_or_io_stream_ref_define +inline constexpr basic_general_io_lockable_ref +input_stream_ref_define(::fast_io::basic_general_io_lockable_ref m) noexcept +{ + return m; +} + template requires ::fast_io::operations::defines::has_io_stream_ref_define inline constexpr basic_general_io_lockable_ref io_stream_ref_define(::fast_io::basic_general_io_lockable_nonmovable &m) noexcept @@ -133,6 +149,13 @@ inline constexpr basic_general_io_lockable_ref io_stream_ref_define(:: return {__builtin_addressof(m)}; } +template + requires ::fast_io::operations::defines::has_io_stream_ref_define +inline constexpr basic_general_io_lockable_ref io_stream_ref_define(::fast_io::basic_general_io_lockable_ref m) noexcept +{ + return m; +} + template requires ::std::default_initializable struct basic_general_mutex_movable From a93f11247433cb4300956bb0ed5ee11d8d6d3628 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 12:22:33 +0800 Subject: [PATCH 08/45] Enhance fast_io stack buffer management and customization options - Introduced a new `custom.h` header for user-editable stack buffer size settings, allowing customization of the default maximum bytes for stack-based print operations. - Refactored existing stack buffer management functions to utilize a template-based approach for stack policies, improving flexibility and type safety. - Updated print-related functions to support the new stack policy mechanism, ensuring consistent behavior across different configurations. - Enhanced documentation to clarify the new features and their usage, aiding user understanding of stack buffer management in fast_io. --- .../fast_io_core_impl/concepts/operation.h | 15 +------ .../printimpl/print_freestanding_cxx20.h | 28 +++++++----- .../fast_io_freestanding_impl/stack/custom.h | 44 +++++++++++++++++++ .../fast_io_freestanding_impl/stack/impl.h | 36 +++++++++++++++ 4 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h create mode 100644 third-parties/fast_io/include/fast_io_freestanding_impl/stack/impl.h diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index dc1d0821c..35e65a29e 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -125,21 +125,10 @@ template <::std::size_t> struct reserve_static_stack_size_constant {}; -inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{131072u}; - -inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept -{ -#if defined(FAST_IO_PRINT_STACK_BUFFER_MAX_BYTES) - return static_cast<::std::size_t>(FAST_IO_PRINT_STACK_BUFFER_MAX_BYTES); -#else - return ::fast_io::details::print_stack_buffer_default_max_bytes; -#endif -} - -template <::std::integral char_type> +template <::std::integral char_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr ::std::size_t dynamic_reserve_default_static_stack_size() noexcept { - constexpr ::std::size_t bytes{::fast_io::details::print_stack_buffer_max_bytes()}; + constexpr ::std::size_t bytes{::fast_io::details::print_stack_buffer_max_bytes()}; if constexpr (bytes == 0) { return 0u; diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 4359e0e3a..1ae572581 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -167,15 +167,21 @@ concept minimum_buffer_output_stream_require_size_impl = inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{ ::fast_io::details::print_stack_buffer_default_max_bytes}; +using default_print_stack_policy = ::fast_io::details::default_print_stack_policy; + +template <::std::size_t requested_max_bytes> +using print_stack_policy = ::fast_io::details::print_stack_policy; + +template inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept { - return ::fast_io::details::print_stack_buffer_max_bytes(); + return ::fast_io::details::print_stack_buffer_max_bytes(); } -template +template inline constexpr ::std::size_t print_stack_buffer_max_element_count() noexcept { - constexpr ::std::size_t max_bytes{::fast_io::details::decay::print_stack_buffer_max_bytes()}; + constexpr ::std::size_t max_bytes{::fast_io::details::decay::print_stack_buffer_max_bytes()}; if constexpr (max_bytes < sizeof(element_type)) { return 0u; @@ -186,18 +192,18 @@ inline constexpr ::std::size_t print_stack_buffer_max_element_count() noexcept } } -template <::std::integral char_type> +template <::std::integral char_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr ::std::size_t print_stack_buffer_max_size() noexcept { - return ::fast_io::details::decay::print_stack_buffer_max_element_count(); + return ::fast_io::details::decay::print_stack_buffer_max_element_count(); } -template <::std::size_t requested_size, typename element_type> +template <::std::size_t requested_size, typename element_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr bool print_stack_buffer_size_within_limit = requested_size != 0 && - requested_size <= ::fast_io::details::decay::print_stack_buffer_max_element_count(); + requested_size <= ::fast_io::details::decay::print_stack_buffer_max_element_count(); -template <::std::size_t static_stack_size, ::std::integral char_type> +template <::std::size_t static_stack_size, ::std::integral char_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexcept { /* @@ -205,7 +211,7 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexc A print run can contain many dynamic producers, so directly merging all hints into one stack array can create a large frame and defeat the stack-safety purpose of this path. */ - constexpr ::std::size_t run_cap{::fast_io::details::decay::print_stack_buffer_max_size()}; + constexpr ::std::size_t run_cap{::fast_io::details::decay::print_stack_buffer_max_size()}; if constexpr (static_stack_size < run_cap) { return static_stack_size; @@ -216,7 +222,7 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexc } } -template +template inline constexpr ::std::size_t dynamic_print_reserve_static_stack_size() { using nocvreft = ::std::remove_cvref_t; @@ -225,7 +231,7 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_size() constexpr ::std::size_t static_stack_size{ print_reserve_static_stack_size(::fast_io::io_reserve_type)}; static_assert(!line || static_stack_size != SIZE_MAX); - return ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget() + + return ::fast_io::details::decay::dynamic_print_reserve_static_stack_budget() + static_cast<::std::size_t>(line); } else diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h new file mode 100644 index 000000000..74895789c --- /dev/null +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +namespace fast_io::custom +{ + +/* +This is the user-editable default stack-buffer budget for fast_io print paths. + +Users and downstream vendors may manually change `value` below to tune the +default maximum number of bytes that stack-based print materialization may use. +The shipped default is 128 KiB. A value of 0 disables the default stack budget +for paths that query this customization point. + +ODR note: +Do not implement this knob as a per-translation-unit macro. Macro-controlled +inline functions can make two TUs emit the same inline/template entity with +different compile-time constants, which is an ODR/IFNDR trap. + +This customization point is intentionally a template value consumed by +stack/impl.h to form: + + default_print_stack_policy = print_stack_policy + +The selected byte count is therefore encoded into the policy type and into the +template specializations that depend on it. When the value is changed for a +build, stack-budget-sensitive helper instantiations get a different type identity +and different mangled symbols instead of reusing the same specialization with a +different body. All TUs that include the same edited header still see the same +token sequence and the same name-lookup results, so the ordinary header-only ODR +requirements are preserved. + +Different libraries or binaries built with different edited values may therefore +coexist without requiring their print stack policy instantiations to be shared. +The tradeoff is possible code-size growth from separate template instantiations. +*/ +template +struct print_stack_buffer_default_max_bytes +{ + static inline constexpr ::std::size_t value{128u * 1024u}; +}; + +} // namespace fast_io::custom diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/impl.h b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/impl.h new file mode 100644 index 000000000..faf0bf4f0 --- /dev/null +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/impl.h @@ -0,0 +1,36 @@ +#pragma once + +#include "custom.h" + +namespace fast_io::details +{ + +inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{ + ::fast_io::custom::print_stack_buffer_default_max_bytes<>::value}; + +template <::std::size_t requested_max_bytes> +struct print_stack_policy +{ + static inline constexpr ::std::size_t max_bytes{requested_max_bytes}; +}; + +using default_print_stack_policy = ::fast_io::details::print_stack_policy< + ::fast_io::details::print_stack_buffer_default_max_bytes>; + +template +inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept +{ + return static_cast<::std::size_t>(stack_policy::max_bytes); +} + +} // namespace fast_io::details + +namespace fast_io +{ + +using default_print_stack_policy = ::fast_io::details::default_print_stack_policy; + +template <::std::size_t requested_max_bytes> +using print_stack_policy = ::fast_io::details::print_stack_policy; + +} // namespace fast_io From 1b3f2da6f9f3f0f8eb69029a6fa033fc9107a46c Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 12:31:17 +0800 Subject: [PATCH 09/45] Refactor fast_io header inclusions and remove unused files - Updated `fast_io_core.h` to include `traits.h` and `impl.h`, enhancing the organization of header dependencies. - Removed the `cond.h` and `width.h` files from the `fast_io_freestanding_impl` directory, streamlining the codebase by eliminating unused components. - Adjusted related includes in `fast_io_freestanding.h` to reflect the removal of obsolete files, improving overall clarity and maintainability of the fast_io library. --- third-parties/fast_io/include/fast_io_core.h | 3 +- .../manipulators}/cond.h | 2 + .../fast_io_core_impl/manipulators/forward.h | 31 ++ .../fast_io_core_impl/manipulators/impl.h | 8 + .../fast_io_core_impl/manipulators/pack.h | 33 +- .../fast_io_core_impl/manipulators/traits.h | 304 ++++++++++++++++++ .../manipulators}/width.h | 2 + .../printimpl/print_freestanding_cxx20.h | 277 ---------------- .../fast_io/include/fast_io_freestanding.h | 2 - 9 files changed, 351 insertions(+), 311 deletions(-) rename third-parties/fast_io/include/{fast_io_freestanding_impl => fast_io_core_impl/manipulators}/cond.h (99%) create mode 100644 third-parties/fast_io/include/fast_io_core_impl/manipulators/forward.h create mode 100644 third-parties/fast_io/include/fast_io_core_impl/manipulators/impl.h create mode 100644 third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h rename third-parties/fast_io/include/{fast_io_freestanding_impl => fast_io_core_impl/manipulators}/width.h (99%) diff --git a/third-parties/fast_io/include/fast_io_core.h b/third-parties/fast_io/include/fast_io_core.h index f529d304d..081d1f6de 100644 --- a/third-parties/fast_io/include/fast_io_core.h +++ b/third-parties/fast_io/include/fast_io_core.h @@ -67,7 +67,7 @@ #include "fast_io_dsal/impl/common.h" #include "fast_io_dsal/impl/span.h" #include "fast_io_dsal/impl/tuple.h" -#include "fast_io_core_impl/manipulators/pack.h" +#include "fast_io_core_impl/manipulators/traits.h" #include "fast_io_core_impl/operations/impl.h" // This should provide an option macro to disable any generation for table in freestanding environments. @@ -101,6 +101,7 @@ #include "fast_io_core_impl/concat/impl.h" #include "fast_io_core_impl/to.h" +#include "fast_io_core_impl/manipulators/impl.h" #include "fast_io_core_impl/http_header.h" #include "fast_io_core_impl/io_lockable.h" diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h similarity index 99% rename from third-parties/fast_io/include/fast_io_freestanding_impl/cond.h rename to third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h index 8c96901bd..2a2a2608f 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/cond.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h @@ -1,5 +1,7 @@ #pragma once +#include "forward.h" + namespace fast_io { diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/forward.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/forward.h new file mode 100644 index 000000000..bb753766a --- /dev/null +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/forward.h @@ -0,0 +1,31 @@ +#pragma once + +namespace fast_io +{ + +namespace manipulators +{ + +template +struct pack_t; + +template +struct condition; + +enum class scalar_placement : char8_t; + +template +struct width_t; + +template +struct width_ch_t; + +template +struct width_runtime_t; + +template +struct width_runtime_ch_t; + +} // namespace manipulators + +} // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/impl.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/impl.h new file mode 100644 index 000000000..bfebcbb06 --- /dev/null +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/impl.h @@ -0,0 +1,8 @@ +#pragma once + +#include "forward.h" +#include "traits.h" + +#include "pack.h" +#include "width.h" +#include "cond.h" diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h index a19269584..9bc41f927 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/pack.h @@ -1,5 +1,7 @@ #pragma once +#include "traits.h" + namespace fast_io { @@ -27,23 +29,6 @@ using pack_alias_type = namespace manipulators { -template -struct condition; - -enum class scalar_placement : char8_t; - -template -struct width_t; - -template -struct width_ch_t; - -template -struct width_runtime_t; - -template -struct width_runtime_ch_t; - struct pack_manip_tag_t {}; @@ -75,18 +60,4 @@ inline constexpr auto pack(Args &&...args) noexcept } // namespace manipulators -namespace details -{ - -template -inline constexpr bool is_print_pack_v = false; - -template -inline constexpr bool is_print_pack_v<::fast_io::manipulators::pack_t> = true; - -template -concept print_pack = is_print_pack_v<::std::remove_cvref_t>; - -} // namespace details - } // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h new file mode 100644 index 000000000..1a97d6914 --- /dev/null +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h @@ -0,0 +1,304 @@ +#pragma once + +#include "forward.h" + +namespace fast_io +{ + +namespace details +{ + +template +inline constexpr bool is_print_pack_v = false; + +template +inline constexpr bool is_print_pack_v<::fast_io::manipulators::pack_t> = true; + +template +concept print_pack = is_print_pack_v<::std::remove_cvref_t>; + +} // namespace details + +namespace details::decay +{ + +template +inline constexpr bool print_semantic_condition_v = false; + +template +inline constexpr bool + print_semantic_condition_v<::fast_io::manipulators::condition> = true; + +template +inline constexpr bool print_semantic_width_v = false; + +template <::fast_io::manipulators::scalar_placement placement, typename T> +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_t> = true; + +template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> +inline constexpr bool + print_semantic_width_v<::fast_io::manipulators::width_ch_t> = true; + +template +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_t> = true; + +template +inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_ch_t> = true; + +template +inline constexpr bool print_semantic_node_no_parameter_v = + ::fast_io::details::print_pack || + ::fast_io::details::decay::print_semantic_condition_v<::std::remove_cvref_t> || + ::fast_io::details::decay::print_semantic_width_v<::std::remove_cvref_t>; + +template +inline constexpr bool print_semantic_parameter_object_v = false; + +template +inline constexpr bool print_semantic_parameter_object_v<::fast_io::parameter> = true; + +template +inline constexpr bool print_semantic_parameter_v = false; + +template +inline constexpr bool print_semantic_parameter_v<::fast_io::parameter> = + ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t>; + +template +concept print_semantic_node = + ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t> || + ::fast_io::details::decay::print_semantic_parameter_v<::std::remove_cvref_t>; + +template +struct print_semantic_width_traits +{}; + +template <::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_width_traits<::fast_io::manipulators::width_t> +{ + inline static constexpr bool runtime_placement = false; + inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; + inline static constexpr bool has_fill_char = false; +}; + +template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> +struct print_semantic_width_traits<::fast_io::manipulators::width_ch_t> +{ + using fill_char_type = ch_type; + inline static constexpr bool runtime_placement = false; + inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; + inline static constexpr bool has_fill_char = true; +}; + +template +struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_t> +{ + inline static constexpr bool runtime_placement = true; + inline static constexpr bool has_fill_char = false; +}; + +template +struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_ch_t> +{ + using fill_char_type = ch_type; + inline static constexpr bool runtime_placement = true; + inline static constexpr bool has_fill_char = true; +}; + +template <::std::integral char_type, typename T> +struct print_freestanding_decay_param_okay_single; + +template <::std::integral char_type, typename T> +using print_semantic_forwarded_arg_t = + decltype(::fast_io::io_print_forward(::fast_io::io_print_alias(::std::declval()))); + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size_impl +{ + inline static constexpr bool available = ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>; + inline static constexpr ::std::size_t size = available ? 0u : SIZE_MAX; +}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size + : ::fast_io::details::decay::print_semantic_static_precise_size_impl> +{}; + +template <::std::integral char_type, typename T> + requires(!::fast_io::details::decay::print_semantic_parameter_object_v && + ::fast_io::static_precise_reserve_printable) +struct print_semantic_static_precise_size_impl +{ + inline static constexpr bool available = true; + inline static constexpr ::std::size_t size{ + print_reserve_static_precise_size(::fast_io::io_reserve_type)}; +}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_precise_size_impl> + : ::fast_io::details::decay::print_semantic_static_precise_size +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_static_precise_size_impl> +{ + inline static constexpr bool available = + (::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::available && + ...); + + static consteval ::std::size_t static_size() noexcept + { + if constexpr (available) + { + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t>::size)), + ...); + return total; + } + else + { + return SIZE_MAX; + } + } + + inline static constexpr ::std::size_t size{static_size()}; +}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_static_precise_size_impl> +{ + using first_size = ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + using second_size = ::fast_io::details::decay::print_semantic_static_precise_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + inline static constexpr bool available = + first_size::available && second_size::available && (first_size::size == second_size::size); + inline static constexpr ::std::size_t size = available ? first_size::size : SIZE_MAX; +}; + +template <::std::integral char_type, typename T> +inline constexpr bool print_semantic_precise_leaf_size_ok_v = + ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || + ::fast_io::static_precise_reserve_printable> || + ::fast_io::precise_reserve_printable> || + ::fast_io::scatter_printable> || + ::fast_io::reserve_scatters_printable>; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok + : ::fast_io::details::decay::print_semantic_precise_size_ok_impl> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + (::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && + ...)> +{}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ::fast_io::details::decay::print_semantic_precise_size_ok>::value> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, + ::std::integral ch_type> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_precise_size_ok_impl> + : ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, typename T, ::std::integral ch_type> +struct print_semantic_precise_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_params_okay : ::std::false_type +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_params_okay> + : print_semantic_params_okay> +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_params_okay> + : ::std::bool_constant< + (print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value && + ...)> +{}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_params_okay> + : ::std::bool_constant< + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_params_okay> + : print_freestanding_decay_param_okay_single> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, + ::std::integral ch_type> +struct print_semantic_params_okay> + : ::std::bool_constant< + ::std::same_as && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_params_okay> + : print_freestanding_decay_param_okay_single> +{}; + +template <::std::integral char_type, typename T, ::std::integral ch_type> +struct print_semantic_params_okay> + : ::std::bool_constant< + ::std::same_as && + print_freestanding_decay_param_okay_single< + char_type, print_semantic_forwarded_arg_t>::value> +{}; + +} // namespace details::decay + +} // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/width.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h similarity index 99% rename from third-parties/fast_io/include/fast_io_freestanding_impl/width.h rename to third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h index d14ece9d0..99267bda2 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/width.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h @@ -1,5 +1,7 @@ #pragma once +#include "forward.h" + namespace fast_io { diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 1ae572581..67dfda878 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -2388,89 +2388,6 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } } -template -inline constexpr bool print_semantic_condition_v = false; - -template -inline constexpr bool - print_semantic_condition_v<::fast_io::manipulators::condition> = true; - -template -inline constexpr bool print_semantic_width_v = false; - -template <::fast_io::manipulators::scalar_placement placement, typename T> -inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_t> = true; - -template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> -inline constexpr bool - print_semantic_width_v<::fast_io::manipulators::width_ch_t> = true; - -template -inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_t> = true; - -template -inline constexpr bool print_semantic_width_v<::fast_io::manipulators::width_runtime_ch_t> = true; - -template -inline constexpr bool print_semantic_node_no_parameter_v = - ::fast_io::details::print_pack || - ::fast_io::details::decay::print_semantic_condition_v<::std::remove_cvref_t> || - ::fast_io::details::decay::print_semantic_width_v<::std::remove_cvref_t>; - -template -inline constexpr bool print_semantic_parameter_object_v = false; - -template -inline constexpr bool print_semantic_parameter_object_v<::fast_io::parameter> = true; - -template -inline constexpr bool print_semantic_parameter_v = false; - -template -inline constexpr bool print_semantic_parameter_v<::fast_io::parameter> = - ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t>; - -template -concept print_semantic_node = - ::fast_io::details::decay::print_semantic_node_no_parameter_v<::std::remove_cvref_t> || - ::fast_io::details::decay::print_semantic_parameter_v<::std::remove_cvref_t>; - -template -struct print_semantic_width_traits -{}; - -template <::fast_io::manipulators::scalar_placement placement, typename T> -struct print_semantic_width_traits<::fast_io::manipulators::width_t> -{ - inline static constexpr bool runtime_placement = false; - inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; - inline static constexpr bool has_fill_char = false; -}; - -template <::fast_io::manipulators::scalar_placement placement, typename T, ::std::integral ch_type> -struct print_semantic_width_traits<::fast_io::manipulators::width_ch_t> -{ - using fill_char_type = ch_type; - inline static constexpr bool runtime_placement = false; - inline static constexpr ::fast_io::manipulators::scalar_placement static_placement = placement; - inline static constexpr bool has_fill_char = true; -}; - -template -struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_t> -{ - inline static constexpr bool runtime_placement = true; - inline static constexpr bool has_fill_char = false; -}; - -template -struct print_semantic_width_traits<::fast_io::manipulators::width_runtime_ch_t> -{ - using fill_char_type = ch_type; - inline static constexpr bool runtime_placement = true; - inline static constexpr bool has_fill_char = true; -}; - template inline constexpr decltype(auto) print_semantic_node_ref(T &&t) noexcept { @@ -2771,200 +2688,6 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont, } } -template <::std::integral char_type, typename T> -struct print_freestanding_decay_param_okay_single; - -template <::std::integral char_type, typename T> -using print_semantic_forwarded_arg_t = - decltype(::fast_io::io_print_forward(::fast_io::io_print_alias(::std::declval()))); - -template <::std::integral char_type, typename T> -struct print_semantic_static_precise_size_impl -{ - inline static constexpr bool available = ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>; - inline static constexpr ::std::size_t size = available ? 0u : SIZE_MAX; -}; - -template <::std::integral char_type, typename T> -struct print_semantic_static_precise_size - : ::fast_io::details::decay::print_semantic_static_precise_size_impl> -{}; - -template <::std::integral char_type, typename T> - requires(!::fast_io::details::decay::print_semantic_parameter_object_v && - ::fast_io::static_precise_reserve_printable) -struct print_semantic_static_precise_size_impl -{ - inline static constexpr bool available = true; - inline static constexpr ::std::size_t size{ - print_reserve_static_precise_size(::fast_io::io_reserve_type)}; -}; - -template <::std::integral char_type, typename T> -struct print_semantic_static_precise_size_impl> - : ::fast_io::details::decay::print_semantic_static_precise_size -{}; - -template <::std::integral char_type, typename... Args> -struct print_semantic_static_precise_size_impl> -{ - inline static constexpr bool available = - (::fast_io::details::decay::print_semantic_static_precise_size< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::available && - ...); - - static consteval ::std::size_t static_size() noexcept - { - if constexpr (available) - { - ::std::size_t total{}; - ((total = ::fast_io::details::intrinsics::add_or_overflow_die( - total, ::fast_io::details::decay::print_semantic_static_precise_size< - char_type, - ::fast_io::details::decay::print_semantic_forwarded_arg_t>::size)), - ...); - return total; - } - else - { - return SIZE_MAX; - } - } - - inline static constexpr ::std::size_t size{static_size()}; -}; - -template <::std::integral char_type, typename T1, typename T2> -struct print_semantic_static_precise_size_impl> -{ - using first_size = ::fast_io::details::decay::print_semantic_static_precise_size< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; - using second_size = ::fast_io::details::decay::print_semantic_static_precise_size< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; - inline static constexpr bool available = - first_size::available && second_size::available && (first_size::size == second_size::size); - inline static constexpr ::std::size_t size = available ? first_size::size : SIZE_MAX; -}; - -template <::std::integral char_type, typename T> -inline constexpr bool print_semantic_precise_leaf_size_ok_v = - ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || - ::fast_io::static_precise_reserve_printable> || - ::fast_io::precise_reserve_printable> || - ::fast_io::scatter_printable> || - ::fast_io::reserve_scatters_printable>; - -template <::std::integral char_type, typename T> -struct print_semantic_precise_size_ok_impl - : ::std::bool_constant< - ::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v> -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_precise_size_ok - : ::fast_io::details::decay::print_semantic_precise_size_ok_impl> -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_precise_size_ok_impl> - : ::fast_io::details::decay::print_semantic_precise_size_ok -{}; - -template <::std::integral char_type, typename... Args> -struct print_semantic_precise_size_ok_impl> - : ::std::bool_constant< - (::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && - ...)> -{}; - -template <::std::integral char_type, typename T1, typename T2> -struct print_semantic_precise_size_ok_impl> - : ::std::bool_constant< - ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ::fast_io::details::decay::print_semantic_precise_size_ok>::value> -{}; - -template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> -struct print_semantic_precise_size_ok_impl> - : ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> -{}; - -template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, - ::std::integral ch_type> -struct print_semantic_precise_size_ok_impl> - : ::std::bool_constant< - ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_precise_size_ok_impl> - : ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> -{}; - -template <::std::integral char_type, typename T, ::std::integral ch_type> -struct print_semantic_precise_size_ok_impl> - : ::std::bool_constant< - ::std::same_as && ::fast_io::details::decay::print_semantic_precise_size_ok< - char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_params_okay : ::std::false_type -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_params_okay> - : print_semantic_params_okay> -{}; - -template <::std::integral char_type, typename... Args> -struct print_semantic_params_okay> - : ::std::bool_constant< - (print_freestanding_decay_param_okay_single< - char_type, print_semantic_forwarded_arg_t>::value && - ...)> -{}; - -template <::std::integral char_type, typename T1, typename T2> -struct print_semantic_params_okay> - : ::std::bool_constant< - print_freestanding_decay_param_okay_single< - char_type, print_semantic_forwarded_arg_t>::value && - print_freestanding_decay_param_okay_single< - char_type, print_semantic_forwarded_arg_t>::value> -{}; - -template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> -struct print_semantic_params_okay> - : print_freestanding_decay_param_okay_single> -{}; - -template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, - ::std::integral ch_type> -struct print_semantic_params_okay> - : ::std::bool_constant< - ::std::same_as && - print_freestanding_decay_param_okay_single< - char_type, print_semantic_forwarded_arg_t>::value> -{}; - -template <::std::integral char_type, typename T> -struct print_semantic_params_okay> - : print_freestanding_decay_param_okay_single> -{}; - -template <::std::integral char_type, typename T, ::std::integral ch_type> -struct print_semantic_params_okay> - : ::std::bool_constant< - ::std::same_as && - print_freestanding_decay_param_okay_single< - char_type, print_semantic_forwarded_arg_t>::value> -{}; - template <::std::integral char_type, typename T> struct print_freestanding_decay_param_okay_single : ::std::bool_constant< diff --git a/third-parties/fast_io/include/fast_io_freestanding.h b/third-parties/fast_io/include/fast_io_freestanding.h index 3062be328..8c64ced1f 100644 --- a/third-parties/fast_io/include/fast_io_freestanding.h +++ b/third-parties/fast_io/include/fast_io_freestanding.h @@ -26,11 +26,9 @@ #include "fast_io_freestanding_impl/auto_indent.h" #include "fast_io_freestanding_impl/serializations/impl.h" #include "fast_io_freestanding_impl/space_reserve.h" -#include "fast_io_freestanding_impl/width.h" #if 0 #include "fast_io_freestanding_impl/scanners/impl.h" #endif -#include "fast_io_freestanding_impl/cond.h" #if defined(_GLIBCXX_BITSET) #include "fast_io_unit/bitset.h" From 576bc1f6f1aa0e82a28bf8b032fa474b5ed58970 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 12:37:03 +0800 Subject: [PATCH 10/45] Add conditional compilation guards in `cond.h` and `width.h` - Introduced `#if 0` guards in `cond.h` and `width.h` to temporarily disable sections of code, improving maintainability and allowing for easier future modifications. - This change enhances the organization of the codebase by clearly indicating which parts are currently inactive while preserving them for potential future use. --- .../include/fast_io_core_impl/manipulators/cond.h | 14 ++++++++------ .../include/fast_io_core_impl/manipulators/width.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h index 2a2a2608f..19169494d 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/cond.h @@ -104,10 +104,11 @@ inline constexpr auto cond(bool pred, T1 &&t1) noexcept } } -} // namespace manipulators - -namespace details -{ + } // namespace manipulators + +#if 0 + namespace details + { template concept cond_transferable_value = ::std::is_trivially_copyable_v<::fast_io::manipulators::condition> && @@ -807,5 +808,6 @@ inline constexpr void print_define(io_reserve_type_t(b, c.t2); } -} -} // namespace fast_io + } +#endif + } // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h index 99267bda2..6f0933ee3 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/width.h @@ -259,6 +259,7 @@ inline constexpr auto internal(T &&t, ::std::size_t n, char_type ch) noexcept } // namespace manipulators +#if 0 namespace details { @@ -689,4 +690,5 @@ print_reserve_define(io_reserve_type_t Date: Wed, 8 Jul 2026 12:44:17 +0800 Subject: [PATCH 11/45] Add new print semantic functions and improve coalescing logic in fast_io - Introduced several new template functions for precise total size calculation and unchecked emission in `print_freestanding_cxx20.h`, enhancing the flexibility of print operations. - Implemented a coalescing mechanism to optimize buffer usage during print operations, improving performance and reducing unnecessary allocations. - Updated existing print logic to utilize the new functions, ensuring consistent behavior and better resource management across the fast_io library. --- .../fast_io/include/fast_io_concept.h | 2 + .../printimpl/print_freestanding_cxx20.h | 164 +++++++++++++++++- 2 files changed, 161 insertions(+), 5 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_concept.h b/third-parties/fast_io/include/fast_io_concept.h index 8f27fc859..d5f2bad5b 100644 --- a/third-parties/fast_io/include/fast_io_concept.h +++ b/third-parties/fast_io/include/fast_io_concept.h @@ -16,6 +16,8 @@ #include "fast_io_dsal/impl/misc/push_macros.h" #include "fast_io_dsal/impl/misc/push_warnings.h" +#include "fast_io_freestanding_impl/stack/impl.h" + #include "fast_io_core_impl/freestanding/addressof.h" #include "fast_io_core_impl/concepts/impl.h" diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 67dfda878..043015260 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -3201,6 +3201,132 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } } +template +inline consteval ::std::size_t print_semantic_static_precise_total_size() noexcept +{ + if constexpr ((::fast_io::details::decay::print_semantic_static_precise_size::available && ...)) + { + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::details::decay::print_semantic_static_precise_size::size)), + ...); + if constexpr (line) + { + total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); + } + return total; + } + else + { + return SIZE_MAX; + } +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_semantic_precise_total_size(Args &&...args) +{ + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::operations::decay::print_semantic_precise_size_arg( + args))), + ...); + if constexpr (line) + { + total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); + } + return total; +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type *print_semantic_emit_unchecked_run(char_type *iter, Args &&...args) +{ + ((iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( + iter, ::std::forward(args))), + ...); + if constexpr (line) + { + *iter = ::fast_io::char_literal_v; + ++iter; + } + return iter; +} + +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, Args &&...args) +{ + if constexpr ((::fast_io::details::decay::print_semantic_precise_size_ok< + char_type, ::std::remove_cvref_t>::value && + ...)) + { + ::std::size_t const required{ + ::fast_io::operations::decay::print_semantic_precise_total_size( + args...)}; + if (required == 0) + { + return true; + } + if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) + { + char_type *const curr{obuffer_curr(optstm)}; + char_type *const end{obuffer_end(optstm)}; + if (static_cast<::std::size_t>(end - curr) >= required) + { + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + curr, ::std::forward(args)...)}; + obuffer_set_curr(optstm, iter); + return true; + } + } + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_scatter_full_output_threshold()}; + if constexpr (threshold_chars != 0) + { + constexpr ::std::size_t static_total{ + ::fast_io::operations::decay::print_semantic_static_precise_total_size...>()}; + if constexpr (static_total != SIZE_MAX && static_total <= threshold_chars) + { + constexpr ::std::size_t buffer_size{static_total == 0 ? 1u : static_total}; + char_type buffer[buffer_size]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } + else + { + if (required <= threshold_chars) + { + char_type buffer[threshold_chars]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } + } + } + } + return false; +} + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3667,9 +3793,14 @@ struct print_semantic_emit_flat_continuation #endif inline constexpr void operator()(FilteredArgs &&...filtered_args) const { - ::fast_io::operations::decay::print_semantic_emit_flat_impl( - optstm, ::fast_io::operations::decay::print_semantic_emit_freestanding_continuation{optstm}, - ::std::forward(filtered_args)...); + if (!::fast_io::operations::decay::print_semantic_try_precise_coalesce( + optstm, ::std::forward(filtered_args)...)) + { + ::fast_io::operations::decay::print_semantic_emit_flat_impl( + optstm, + ::fast_io::operations::decay::print_semantic_emit_freestanding_continuation{optstm}, + ::std::forward(filtered_args)...); + } } }; @@ -3729,7 +3860,30 @@ template #endif inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, Args... args) { - if constexpr ((::fast_io::details::decay::print_semantic_node || ...)) + if constexpr (::fast_io::operations::decay::defines::has_status_print_define) + { + return status_print_define(optstm, args...); + } + else if constexpr (sizeof...(Args) == 0) + { + if constexpr (line) + { + using char_type = typename outputstmtype::output_char_type; + return ::fast_io::operations::decay::char_put_decay(optstm, char_literal_v); + } + else + { + return; + } + } + else if constexpr (::fast_io::operations::decay::defines::has_output_or_io_stream_mutex_ref_define) + { + ::fast_io::operations::decay::stream_ref_decay_lock_guard lg{ + ::fast_io::operations::decay::output_stream_mutex_ref_decay(optstm)}; + return ::fast_io::operations::decay::print_freestanding_decay( + ::fast_io::operations::decay::output_stream_unlocked_ref_decay(optstm), args...); + } + else if constexpr ((::fast_io::details::decay::print_semantic_node || ...)) { using char_type = typename outputstmtype::output_char_type; return ::fast_io::operations::decay::print_semantic_emit(optstm, args...); @@ -3806,7 +3960,7 @@ inline constexpr void print_freestanding(output &&outstm, Args &&...args) if constexpr ((false || ... || ::fast_io::details::decay::print_semantic_input_argument_v)) { - ::fast_io::operations::decay::print_semantic_emit( + ::fast_io::operations::decay::print_freestanding_decay( outref, ::fast_io::details::decay::print_semantic_input_forward( ::std::forward(args))...); From 60f8cf22afd3dcea47c55581cfd4d3d2c7333108 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:00:36 +0800 Subject: [PATCH 12/45] Enhance print operations with full output coalescing and semantic checks - Added new template functions for full output coalescing thresholds in `print_freestanding_cxx20.h` and `posix.h`, optimizing buffer management during print operations. - Introduced semantic checks for precise concatenation in `concat_general.h`, improving the handling of print arguments and ensuring efficient memory usage. - Updated existing print logic to leverage the new coalescing and semantic functions, enhancing performance and consistency across the fast_io library. --- .../fast_io_core_impl/concat/concat_general.h | 50 ++++++++++++++++++- .../fast_io_core_impl/concepts/operation.h | 18 +++++++ .../printimpl/print_freestanding_cxx20.h | 27 +++++++++- .../include/fast_io_hosted/platforms/posix.h | 7 +++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h index 42231adeb..c80d19610 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h @@ -266,10 +266,41 @@ inline constexpr void basic_general_concat_decay_ref_impl_precise(T &str, Arg ar strlike_set_curr(io_strlike_type, str, ptr); } +template <::std::integral ch_type, typename... Args> +inline constexpr bool basic_general_concat_semantic_precise_ok = + (false || ... || ::fast_io::details::decay::print_semantic_node) && + (::fast_io::details::decay::print_semantic_precise_size_ok>::value && ...); + +template +inline constexpr void basic_general_concat_decay_ref_impl_semantic_precise(T &str, Args... args) +{ + ::std::size_t const precise_size{ + ::fast_io::operations::decay::print_semantic_precise_total_size(args...)}; + if constexpr (sso_buffer_strlike) + { + constexpr ::std::size_t local_cap{strlike_sso_size(io_strlike_type)}; + if (local_cap < precise_size) + { + strlike_reserve(io_strlike_type, str, precise_size); + } + } + else + { + strlike_reserve(io_strlike_type, str, precise_size); + } + auto first{strlike_begin(io_strlike_type, str)}; + auto ptr{::fast_io::operations::decay::print_semantic_emit_unchecked_run(first, args...)}; + strlike_set_curr(io_strlike_type, str, ptr); +} + template inline constexpr void basic_general_concat_decay_ref_impl(T &str, Args... args) { - if constexpr (((reserve_printable || scatter_printable || + if constexpr (basic_general_concat_semantic_precise_ok) + { + basic_general_concat_decay_ref_impl_semantic_precise(str, args...); + } + else if constexpr (((reserve_printable || scatter_printable || dynamic_reserve_printable) && ...)) { @@ -352,7 +383,22 @@ inline constexpr T basic_general_concat_phase1_decay_impl(Args... args) } else { - if constexpr (buffer_strlike) + if constexpr (basic_general_concat_semantic_precise_ok) + { + if constexpr (buffer_strlike) + { + T str; + basic_general_concat_decay_ref_impl_semantic_precise(str, args...); + return str; + } + else + { + basic_concat_buffer buffer; + basic_general_concat_decay_ref_impl_semantic_precise(buffer, args...); + return strlike_construct_define(io_strlike_type, buffer.buffer_begin, buffer.buffer_curr); + } + } + else if constexpr (buffer_strlike) { T str; basic_general_concat_decay_ref_impl(str, args...); diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index 35e65a29e..9062af6e8 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -222,6 +222,24 @@ concept scatter_fallback_full_output_threshold_stream = } -> ::std::same_as<::std::size_t>; }; +/// @brief full_output_coalesce_threshold_stream +/// @details Customizes the maximum full semantic output size, in char_type +/// units, for materializing a precise print run into contiguous +/// storage before writing it once. This is independent of scatter +/// writev fallback policy; streams may choose different thresholds. +/// Returning 0 disables this coalescing path for the stream. +/// @fn full_output_coalesce_threshold +/// @brief Returns the whole-output coalescing threshold, in char_type units. +template +concept full_output_coalesce_threshold_stream = + ::std::integral && requires { + typename ::fast_io::details::reserve_static_stack_size_constant< + full_output_coalesce_threshold(io_reserve_type>)>; + { + full_output_coalesce_threshold(io_reserve_type>) + } -> ::std::same_as<::std::size_t>; + }; + /// @brief printable_internal_shift /// @details Defines the behaviour when printed with ::fast_io::mnp::width<::fast_io::mnp::scalar_placement::internal> /// @fn print_define_internal_shift diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 043015260..d908be865 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -344,6 +344,31 @@ inline constexpr ::std::size_t print_scatter_full_output_threshold() noexcept } } +template <::std::integral char_type, typename outputstmtype> +inline constexpr ::std::size_t print_full_output_coalesce_threshold() noexcept +{ + if constexpr (::fast_io::full_output_coalesce_threshold_stream) + { + constexpr ::std::size_t threshold{ + full_output_coalesce_threshold( + ::fast_io::io_reserve_type>)}; + constexpr ::std::size_t max_threshold{ + ::fast_io::details::decay::print_scatter_full_output_threshold_max_chars()}; + if constexpr (threshold < max_threshold) + { + return threshold; + } + else + { + return max_threshold; + } + } + else + { + return ::fast_io::details::decay::print_scatter_full_output_threshold(); + } +} + template inline constexpr bool print_has_direct_write_bytes_operations = ::fast_io::operations::decay::defines::has_write_all_bytes_overflow_define || @@ -3294,7 +3319,7 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, } } constexpr ::std::size_t threshold_chars{ - ::fast_io::details::decay::print_scatter_full_output_threshold()}; + ::fast_io::details::decay::print_full_output_coalesce_threshold()}; if constexpr (threshold_chars != 0) { constexpr ::std::size_t static_total{ diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 17a2bc98a..23ee87bb4 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -552,6 +552,13 @@ inline constexpr ::std::size_t scatter_fallback_full_output_threshold( return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); } +template <::fast_io::posix_family family, ::std::integral char_type> +inline constexpr ::std::size_t full_output_coalesce_threshold( + ::fast_io::io_reserve_type_t>) noexcept +{ + return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); +} + #if defined(__CYGWIN__) // https://github.com/cygwin/cygwin/blob/c43ec5f5951c7f4b882a0f8e619601a45ae70a91/newlib/libc/include/sys/_default_fcntl.h#L168 From d878ddb6373cd889178be739d5fe66b005eaad59 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:00:59 +0800 Subject: [PATCH 13/45] Update stack buffer customization in fast_io to reflect environment-specific defaults - Enhanced the `custom.h` header to provide environment-specific default values for stack-based print operations, improving flexibility and performance. - Updated documentation to clarify the new default values based on target environments, ensuring users can effectively customize stack buffer sizes. - Refactored the `print_stack_buffer_default_max_bytes` structure to utilize conditional compilation for better adaptability across different platforms. --- .../fast_io_freestanding_impl/stack/custom.h | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h index 74895789c..dd92c67ec 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h @@ -8,10 +8,13 @@ namespace fast_io::custom /* This is the user-editable default stack-buffer budget for fast_io print paths. -Users and downstream vendors may manually change `value` below to tune the +Users and downstream vendors may manually replace `value` below to tune the default maximum number of bytes that stack-based print materialization may use. -The shipped default is 128 KiB. A value of 0 disables the default stack budget -for paths that query this customization point. +The shipped default is selected from the target environment: tiny freestanding +and kernel-like builds get 256 bytes, wasm and non-GNU Linux get 4 KiB, Darwin +and unknown hosted Unix get 16 KiB, Windows gets 32 KiB, and GNU/Linux gets +64 KiB. A value of 0 disables the default stack budget for paths that query +this customization point. ODR note: Do not implement this knob as a per-translation-unit macro. Macro-controlled @@ -38,7 +41,27 @@ The tradeoff is possible code-size growth from separate template instantiations. template struct print_stack_buffer_default_max_bytes { - static inline constexpr ::std::size_t value{128u * 1024u}; + static inline constexpr ::std::size_t value{ +#if defined(__KERNEL__) || defined(_KERNEL) || defined(_KERNEL_MODE) + 256u +#elif defined(__EMSCRIPTEN__) || defined(__wasm32__) || defined(__wasm64__) || defined(__wasm__) + 4u * 1024u +#elif !defined(__STDC_HOSTED__) || (__STDC_HOSTED__ == 0) + 256u +#elif defined(_WIN32) || defined(__CYGWIN__) + 32u * 1024u +#elif defined(__APPLE__) && defined(__MACH__) + 16u * 1024u +#elif defined(__linux__) && defined(__gnu_linux__) + 64u * 1024u +#elif defined(__linux__) + 4u * 1024u +#elif defined(__unix__) || defined(__unix) || defined(unix) + 16u * 1024u +#else + 16u * 1024u +#endif + }; }; } // namespace fast_io::custom From 81c1cc455208847f86154297b4350e0f25c60c43 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:02:09 +0800 Subject: [PATCH 14/45] Refactor stack buffer default value handling in fast_io - Updated the `print_stack_buffer_default_max_bytes` structure to introduce a clearer distinction between the default value and the user-configurable value, enhancing code readability. - Added documentation to guide users on customizing the default maximum bytes for stack-based print materialization, improving usability and flexibility in stack buffer management. --- .../include/fast_io_freestanding_impl/stack/custom.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h index dd92c67ec..9094f8923 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h @@ -24,7 +24,7 @@ different compile-time constants, which is an ODR/IFNDR trap. This customization point is intentionally a template value consumed by stack/impl.h to form: - default_print_stack_policy = print_stack_policy + default_print_stack_policy = print_stack_policy The selected byte count is therefore encoded into the policy type and into the template specializations that depend on it. When the value is changed for a @@ -41,7 +41,7 @@ The tradeoff is possible code-size growth from separate template instantiations. template struct print_stack_buffer_default_max_bytes { - static inline constexpr ::std::size_t value{ + static inline constexpr ::std::size_t default_value{ #if defined(__KERNEL__) || defined(_KERNEL) || defined(_KERNEL_MODE) 256u #elif defined(__EMSCRIPTEN__) || defined(__wasm32__) || defined(__wasm64__) || defined(__wasm__) @@ -62,6 +62,9 @@ struct print_stack_buffer_default_max_bytes 16u * 1024u #endif }; + + // Users and downstream vendors may manually replace `value` below to tune the default maximum number of bytes that stack-based print materialization may use. + static inline constexpr ::std::size_t value{default_value}; }; } // namespace fast_io::custom From f0ad2da727448c3dc34a5dc8b84f95d1c8c3159a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:19:20 +0800 Subject: [PATCH 15/45] Add new print semantic functions and enhance pack handling in fast_io - Introduced several new template functions for handling print semantics, including common factor checks and unchecked emission for pack elements in `print_freestanding_cxx20.h`. - Implemented a mechanism for comparing elements within packs, improving the flexibility and efficiency of print operations. - Enhanced existing functions to utilize the new semantic checks, ensuring consistent behavior and better resource management across the fast_io library. --- .../printimpl/print_freestanding_cxx20.h | 265 +++++++++++++++++- 1 file changed, 262 insertions(+), 3 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index d908be865..143781529 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -3049,6 +3049,11 @@ template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t); template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T &&t) { decltype(auto) forwarded{ @@ -3057,6 +3062,220 @@ inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T iter, ::std::forward(forwarded)); } +inline constexpr ::std::size_t print_semantic_common_factor_pack_max_size{8u}; + +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr bool print_semantic_common_factor_equal(::fast_io::io_null_t, ::fast_io::io_null_t) noexcept +{ + return true; +} + +template + requires(!::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> && + ::std::same_as<::std::remove_cvref_t, ::std::remove_cvref_t> && + requires(T const &first, U const &second) { + first.base; + second.base; + }) +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr bool print_semantic_common_factor_equal(T const &first, U const &second) noexcept +{ + if constexpr (requires { + first.len; + second.len; + }) + { + return first.base == second.base && first.len == second.len; + } + else + { + return first.base == second.base; + } +} + +template + requires(!::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> && + ::std::same_as<::std::remove_cvref_t, ::std::remove_cvref_t> && + requires(T const &first, U const &second) { + first.reference; + second.reference; + requires ::std::integral<::std::remove_cvref_t>; + }) +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr bool print_semantic_common_factor_equal(T const &first, U const &second) noexcept +{ + return first.reference == second.reference; +} + +template +concept print_semantic_common_factor_comparable = requires(T &&t, U &&u) { + { + ::fast_io::operations::decay::print_semantic_common_factor_equal( + ::std::forward(t), ::std::forward(u)) + } -> ::std::same_as; +}; + +template <::std::integral char_type, ::std::size_t index, typename Pack> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type *print_semantic_emit_unchecked_pack_element(char_type *iter, Pack &&pack) +{ + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( + iter, ::fast_io::containers::get(::std::forward(pack).storage)); +} + +template <::std::size_t index, typename Pack1, typename Pack2> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr bool print_semantic_pack_element_common_factor_equal(Pack1 &&pack1, Pack2 &&pack2) +{ + decltype(auto) first{::fast_io::containers::get(::std::forward(pack1).storage)}; + decltype(auto) second{::fast_io::containers::get(::std::forward(pack2).storage)}; + if constexpr (::fast_io::operations::decay::print_semantic_common_factor_comparable) + { + return ::fast_io::operations::decay::print_semantic_common_factor_equal( + ::std::forward(first), ::std::forward(second)); + } + else + { + return false; + } +} + +template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type *iter, Pack &&pack) +{ + if constexpr (first < last) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( + iter, ::std::forward(pack)); + return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( + iter, ::std::forward(pack)); + } + else + { + return iter; + } +} + +template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factored(char_type *iter, bool pred, + Pack1 &&pack1, Pack2 &&pack2) +{ + if constexpr (first < last) + { + if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( + ::std::forward(pack1), ::std::forward(pack2))) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( + iter, ::std::forward(pack1)); + return ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< + char_type, first + 1u, last>(iter, pred, ::std::forward(pack1), ::std::forward(pack2)); + } + if constexpr (first + 1u < last) + { + constexpr ::std::size_t suffix_index{last - 1u}; + if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( + ::std::forward(pack1), ::std::forward(pack2))) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< + char_type, first, suffix_index>(iter, pred, ::std::forward(pack1), + ::std::forward(pack2)); + return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( + iter, ::std::forward(pack1)); + } + } + if (pred) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( + iter, ::std::forward(pack1)); + } + return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( + iter, ::std::forward(pack2)); + } + else + { + return iter; + } +} + +template <::std::integral char_type> +struct print_semantic_condition_pack_factor_result +{ + char_type *iter; + bool done; +}; + +template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr print_semantic_condition_pack_factor_result +print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pred, Pack1 &&pack1, Pack2 &&pack2) +{ + if constexpr (first < last) + { + if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( + ::std::forward(pack1), ::std::forward(pack2))) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( + iter, ::std::forward(pack1)); + return {::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< + char_type, first + 1u, last>(iter, pred, ::std::forward(pack1), + ::std::forward(pack2)), + true}; + } + if constexpr (first + 1u < last) + { + constexpr ::std::size_t suffix_index{last - 1u}; + if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( + ::std::forward(pack1), ::std::forward(pack2))) + { + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< + char_type, first, suffix_index>(iter, pred, ::std::forward(pack1), + ::std::forward(pack2)); + iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( + iter, ::std::forward(pack1)); + return {iter, true}; + } + } + } + return {iter, false}; +} + template <::std::integral char_type> struct print_semantic_emit_unchecked_pack_continuation { @@ -3147,6 +3366,11 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } template <::std::integral char_type, typename T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t) { auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -3161,11 +3385,46 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) { - if (node_ref.pred) + using first_type = ::std::remove_cvref_t; + using second_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::print_pack && ::fast_io::details::print_pack) { - return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); + if constexpr (first_type::size == second_type::size && + first_type::size <= + ::fast_io::operations::decay::print_semantic_common_factor_pack_max_size) + { + auto const factor_result{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_try_factor< + char_type, 0u, first_type::size>(iter, node_ref.pred, node_ref.t1, node_ref.t2)}; + if (factor_result.done) + { + return factor_result.iter; + } + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, + node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); + } + else + { + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, + node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); + } + } + else + { + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); } - return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); } else if constexpr (::fast_io::details::decay::print_semantic_width_v) { From 1e2d62bb69693847a609e5db8633ea054860fc55 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:32:45 +0800 Subject: [PATCH 16/45] Enhance floating-point trait specialization in fast_io - Updated the conditional compilation for `iec559_traits<__float80>` to ensure proper handling based on the presence and values of long double characteristics. - Improved code clarity by refining the conditions under which the specialization is defined, enhancing compatibility across different platforms. --- third-parties/fast_io/include/fast_io_unit/floating/punning.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/punning.h b/third-parties/fast_io/include/fast_io_unit/floating/punning.h index ec03cbe0b..a168302be 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/punning.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/punning.h @@ -43,7 +43,9 @@ struct iec559_traits<__float16> }; #endif -#ifdef __SIZEOF_FLOAT80__ +#if defined(__SIZEOF_FLOAT80__) && \ + (!defined(__LDBL_MANT_DIG__) || !defined(__LDBL_MAX_EXP__) || !defined(__SIZEOF_LONG_DOUBLE__) || \ + __LDBL_MANT_DIG__ != 64 || __LDBL_MAX_EXP__ != 16384 || __SIZEOF_LONG_DOUBLE__ != __SIZEOF_FLOAT80__) template <> struct iec559_traits<__float80> { From 46007f97fc204a29afe9cee79cab4eca2eac07d0 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 13:44:45 +0800 Subject: [PATCH 17/45] Enhance scatter and reserve handling in print operations - Introduced new structures and functions to improve the handling of contiguous scatter and reserve runs in `print_freestanding_cxx20.h`. - Added detailed documentation for new template functions that scan parameter packs for leading contiguous scatter/reserve-friendly print runs. - Improved the logic for managing null outputs and dynamic reserves, ensuring more efficient and accurate print operations across the fast_io library. --- .../printimpl/print_freestanding_cxx20.h | 709 +++++++++++++++++- 1 file changed, 694 insertions(+), 15 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 143781529..7bfff8d75 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -6,10 +6,16 @@ namespace fast_io namespace details::decay { +/// @brief Reusable scatter descriptor for a single trailing newline. +/// @details The void specialization reports byte count for byte-scatter APIs; typed scatter descriptors report +/// character count for character-scatter APIs. +/// @tparam char_type the character type of the newline literal +/// @tparam T the scatter value type, or void for byte scatter output template <::std::integral char_type, typename T = char_type> inline constexpr basic_io_scatter_t line_scatter_common{__builtin_addressof(char_literal_v), ::std::same_as ? sizeof(char_type) : 1}; +/// @brief Describes the first contiguous print run that can be represented by scatters and reserve buffers. struct contiguous_scatter_result { ::std::size_t position{}; @@ -22,14 +28,23 @@ struct contiguous_scatter_result bool hasdynamicreserve{}; }; +/// @brief Scans a parameter pack for the leading contiguous scatter/reserve-friendly print run. +/// @details The result records how many arguments can be grouped, how many scatter descriptors and reserve +/// characters are needed, and whether the run contains null or reserve-like outputs. +/// @tparam char_type the character type used by the print run +/// @tparam Arg the current argument type +/// @tparam Args the remaining argument types +/// @return contiguous_scatter_result the compile-time description of the leading run template <::std::integral char_type, typename Arg, typename... Args> inline constexpr contiguous_scatter_result find_continuous_scatters_n() { contiguous_scatter_result ret{}; if constexpr (::fast_io::scatter_printable) { + // A scatter-printable argument contributes one existing output range to the contiguous run. if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first so the current argument can extend the leading run. ret = find_continuous_scatters_n(); } ++ret.position; @@ -39,12 +54,15 @@ inline constexpr contiguous_scatter_result find_continuous_scatters_n() } else if constexpr (::fast_io::reserve_printable) { + // A static reserve-printable argument contributes one materialized range and a known reserve size. if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first to preserve the aggregate run accounting. ret = find_continuous_scatters_n(); } else { + // A trailing reserve argument can be emitted directly without an extra scatter continuation. ret.lastisreserve = true; } constexpr ::std::size_t sz{print_reserve_size(::fast_io::io_reserve_type)}; @@ -57,12 +75,15 @@ inline constexpr contiguous_scatter_result find_continuous_scatters_n() } else if constexpr (::fast_io::dynamic_reserve_printable) { + // A dynamic reserve-printable argument is part of the run, but its reserve size is measured later. if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first so this dynamic reserve extends the same leading run. ret = find_continuous_scatters_n(); } else { + // A trailing dynamic reserve argument can be handled as the final materialized output. ret.lastisreserve = true; } ++ret.position; @@ -72,8 +93,10 @@ inline constexpr contiguous_scatter_result find_continuous_scatters_n() } else if constexpr (::fast_io::reserve_scatters_printable) { + // A reserve-scatters argument contributes its declared scatter count and reserve storage requirement. if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first to accumulate the complete leading run. ret = find_continuous_scatters_n(); } constexpr auto scatszres{print_reserve_scatters_size(::fast_io::io_reserve_type)}; @@ -88,8 +111,10 @@ inline constexpr contiguous_scatter_result find_continuous_scatters_n() } else if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output is counted so pack positions remain correct while no emitted characters are reserved. if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first before adding this null position to the run. ret = find_continuous_scatters_n(); } ++ret.position; @@ -98,10 +123,12 @@ inline constexpr contiguous_scatter_result find_continuous_scatters_n() } else if constexpr (::fast_io::printable) { + // A generic printable argument stops the scatter/reserve run because it needs the normal emit path. } return ret; } +/// @brief Describes a contiguous reserve-only or scatter-only prefix inside a print run. struct scatter_rsv_result { ::std::size_t position{}; @@ -109,100 +136,147 @@ struct scatter_rsv_result ::std::size_t null{}; }; +/// @brief Finds a contiguous reserve or scatter subrun used while building scatter descriptors. +/// @details The findscatter flag selects whether the scan accepts scatter-printable arguments or reserve-printable +/// arguments, while nulls are counted and skipped in either mode. +/// @tparam findscatter true to scan scatter-printable arguments, false to scan reserve-printable arguments +/// @tparam char_type the character type used by the print run +/// @tparam Arg the current argument type +/// @tparam Args the remaining argument types +/// @return scatter_rsv_result the accepted run length, reserve space, and null count template inline constexpr scatter_rsv_result find_continuous_scatters_reserve_n() { if constexpr (::fast_io::reserve_printable && !findscatter) { + // Reserve scan mode accepts static reserve-printable arguments and accumulates their reserve space. constexpr ::std::size_t sz{print_reserve_size(::fast_io::io_reserve_type)}; if constexpr (sizeof...(Args) == 0) { + // A single reserve argument forms a one-position run with its known reserve size. return {1, sz, 0}; } else { + // Additional arguments extend the reserve run while preserving accumulated nulls. auto res{find_continuous_scatters_reserve_n()}; return {res.position + 1, ::fast_io::details::intrinsics::add_or_overflow_die_chain(res.neededspace, sz), res.null}; } } else if constexpr (::fast_io::scatter_printable && findscatter) { + // Scatter scan mode accepts scatter-printable arguments without reserving extra character storage. if constexpr (sizeof...(Args) == 0) { + // A single scatter argument forms a one-position run with no reserve storage. return {1, 0, 0}; } else { + // Additional arguments extend the scatter run and keep the existing reserve accounting. auto res{find_continuous_scatters_reserve_n()}; return {res.position + 1, res.neededspace, res.null}; } } else if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output is accepted in both scan modes because it does not interrupt contiguous emission. if constexpr (sizeof...(Args) == 0) { + // A single null argument contributes one position and one null count. return {1, 0, 1}; } else { + // Additional arguments extend the run while the null count records this skipped output slot. auto res{find_continuous_scatters_reserve_n()}; return {res.position + 1, res.neededspace, ::fast_io::details::intrinsics::add_or_overflow_die_chain(res.null, static_cast<::std::size_t>(1))}; } } else { + // Any non-matching argument terminates the selected contiguous scatter or reserve subrun. return {0, 0, 0}; } } +/// @brief Checks whether a stream's minimum output buffer size is strictly larger than N. template inline constexpr bool minimum_buffer_output_stream_require_size_constant_impl = (N < obuffer_minimum_size_define(::fast_io::io_reserve_type)); +/// @brief Detects output streams that declare a minimum put area larger than N. template concept minimum_buffer_output_stream_require_size_impl = ::fast_io::operations::decay::defines::has_obuffer_minimum_size_operations && minimum_buffer_output_stream_require_size_constant_impl; +/// @brief Default maximum stack-buffer budget used by freestanding print helpers. inline constexpr ::std::size_t print_stack_buffer_default_max_bytes{ ::fast_io::details::print_stack_buffer_default_max_bytes}; using default_print_stack_policy = ::fast_io::details::default_print_stack_policy; +/// @brief Stack-buffer policy alias for freestanding print materialization. +/// @tparam requested_max_bytes requested maximum stack bytes for the policy template <::std::size_t requested_max_bytes> using print_stack_policy = ::fast_io::details::print_stack_policy; +/// @brief Returns the maximum stack-buffer budget in bytes for a print stack policy. +/// @tparam stack_policy the stack-buffer policy type +/// @return ::std::size_t the maximum number of stack bytes available to print helpers template inline constexpr ::std::size_t print_stack_buffer_max_bytes() noexcept { return ::fast_io::details::print_stack_buffer_max_bytes(); } +/// @brief Converts a byte stack-buffer budget into an element count for a concrete element type. +/// @tparam element_type the element stored in the stack buffer +/// @tparam stack_policy the stack-buffer policy type +/// @return ::std::size_t the maximum number of elements that fit in the policy budget template inline constexpr ::std::size_t print_stack_buffer_max_element_count() noexcept { constexpr ::std::size_t max_bytes{::fast_io::details::decay::print_stack_buffer_max_bytes()}; if constexpr (max_bytes < sizeof(element_type)) { + // The policy budget cannot hold even one element, so stack materialization is disabled. return 0u; } else { + // The policy budget is expressed in bytes and is rounded down to a whole element count. return max_bytes / sizeof(element_type); } } +/// @brief Returns the maximum character count for stack buffers in the print path. +/// @tparam char_type the character type stored in the buffer +/// @tparam stack_policy the stack-buffer policy type +/// @return ::std::size_t the maximum number of characters available on the stack template <::std::integral char_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr ::std::size_t print_stack_buffer_max_size() noexcept { return ::fast_io::details::decay::print_stack_buffer_max_element_count(); } +/// @brief Tests whether a requested fixed-size stack buffer is permitted by the print stack policy. +/// @tparam requested_size the requested element count +/// @tparam element_type the element stored in the stack buffer +/// @tparam stack_policy the stack-buffer policy type template <::std::size_t requested_size, typename element_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr bool print_stack_buffer_size_within_limit = requested_size != 0 && requested_size <= ::fast_io::details::decay::print_stack_buffer_max_element_count(); +/// @brief Clamps a dynamic reserve producer's static stack hint to the active print stack budget. +/// @details Dynamic producers may appear many times in one print run, so their local stack hints are bounded by the +/// print policy before a shared stack materialization path uses them. +/// @tparam static_stack_size the producer-declared static stack size +/// @tparam char_type the character type stored in the stack buffer +/// @tparam stack_policy the stack-buffer policy type +/// @return ::std::size_t the stack size allowed for this producer template <::std::size_t static_stack_size, ::std::integral char_type, typename stack_policy = ::fast_io::details::default_print_stack_policy> inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexcept { @@ -214,20 +288,29 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_budget() noexc constexpr ::std::size_t run_cap{::fast_io::details::decay::print_stack_buffer_max_size()}; if constexpr (static_stack_size < run_cap) { + // The producer's hint already fits inside the active stack-buffer policy. return static_stack_size; } else { + // The producer's hint is capped so one dynamic output cannot dominate the stack frame. return run_cap; } } +/// @brief Computes the bounded static stack size for one dynamic reserve-printable argument. +/// @tparam line true when a trailing newline must fit in the same materialization buffer +/// @tparam char_type the character type stored in the stack buffer +/// @tparam T the dynamic reserve-printable argument type +/// @tparam stack_policy the stack-buffer policy type +/// @return ::std::size_t the bounded stack size, or zero when no static hint is available template inline constexpr ::std::size_t dynamic_print_reserve_static_stack_size() { using nocvreft = ::std::remove_cvref_t; if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // Dynamic reserve producers with a static hint can use bounded stack materialization. constexpr ::std::size_t static_stack_size{ print_reserve_static_stack_size(::fast_io::io_reserve_type)}; static_assert(!line || static_stack_size != SIZE_MAX); @@ -236,16 +319,25 @@ inline constexpr ::std::size_t dynamic_print_reserve_static_stack_size() } else { + // Producers without a static hint use the non-static dynamic reserve path. return 0; } } +/// @brief Returns the static context buffer size for one context-printable argument. +/// @details Context-printable types may declare a fixed buffer size; otherwise this path uses the conservative +/// default used by the context printer. +/// @tparam line true when a trailing newline is included in the same context buffer +/// @tparam char_type the character type stored in the context buffer +/// @tparam T the context-printable argument type +/// @return ::std::size_t the static context buffer size in characters template inline constexpr ::std::size_t context_print_static_buffer_size() noexcept { using value_type = ::std::remove_cvref_t; if constexpr (::fast_io::context_printable_with_static_buffer_size) { + // A declared static context buffer size is validated before it drives stack allocation. constexpr ::std::size_t n{ print_context_static_buffer_size(::fast_io::io_reserve_type)}; static_assert(n != 0); @@ -255,20 +347,30 @@ inline constexpr ::std::size_t context_print_static_buffer_size() noexcept } else { + // Context producers without a declared size use the historical small default buffer. return 32u; } } +/// @brief Cached context buffer size for a context-printable argument. +/// @tparam line true when a trailing newline is included in the same context buffer +/// @tparam char_type the character type stored in the context buffer +/// @tparam T the context-printable argument type template inline constexpr ::std::size_t context_print_static_buffer_size_v{ ::fast_io::details::decay::context_print_static_buffer_size()}; +/// @brief Converts character scatter lengths into byte scatter lengths when char_type is wider than one byte. +/// @tparam sz sizeof(char_type), constrained to be non-zero +/// @param first the first scatter descriptor to update +/// @param last one past the final scatter descriptor to update template <::std::size_t sz> requires(sz != 0) inline constexpr void scatter_rsv_update_times(::fast_io::io_scatter_t *first, ::fast_io::io_scatter_t *last) noexcept { if constexpr (sz != 1) { + // Wide-character reserve scatters must report byte counts to byte-oriented scatter APIs. for (; first != last; ++first) { first->len *= sz; @@ -276,6 +378,8 @@ inline constexpr void scatter_rsv_update_times(::fast_io::io_scatter_t *first, : } } +/// @brief Result of building byte-oriented reserve-scatters output. +/// @tparam char_type the character type stored in the temporary reserve buffer template <::std::integral char_type> struct basic_reserve_scatters_define_byte_result { @@ -283,6 +387,15 @@ struct basic_reserve_scatters_define_byte_result char_type *reserve_pos_ptr; }; +/// @brief Builds reserve-scatters output for byte-oriented scatter APIs. +/// @details The typed scatter descriptors returned by the customization are reinterpreted as byte scatter +/// descriptors and length-adjusted for wide character types. +/// @tparam char_type the character type stored in the temporary reserve buffer +/// @tparam T the reserve-scatters printable argument type +/// @param pscatters the byte scatter descriptor storage +/// @param buffer the temporary reserve buffer +/// @param t the argument to materialize +/// @return basic_reserve_scatters_define_byte_result updated scatter and reserve-buffer cursors template <::std::integral char_type, typename T> inline ::fast_io::details::decay::basic_reserve_scatters_define_byte_result prrsvsct_byte_common_rsvsc_impl(io_scatter_t *pscatters, char_type *buffer, T t) @@ -302,28 +415,46 @@ prrsvsct_byte_common_rsvsc_impl(io_scatter_t *pscatters, char_type *buffer, T t) scatterioscattertypealiasptr ptr{reinterpret_cast(result.scatters_pos_ptr)}; if constexpr (sizeof(char_type) != 1) { + // Wide-character scatter lengths are converted from character counts to byte counts. scatter_rsv_update_times(pscatters, ptr); } return {ptr, result.reserve_pos_ptr}; } +/// @brief Builds byte-oriented reserve-scatters output and returns the final scatter cursor. +/// @tparam char_type the character type stored in the temporary reserve buffer +/// @tparam T the reserve-scatters printable argument type +/// @param pscatters the byte scatter descriptor storage +/// @param buffer the temporary reserve buffer +/// @param t the argument to materialize +/// @return io_scatter_t* one past the final scatter descriptor template <::std::integral char_type, typename T> inline auto prrsvsct_byte_common_impl(io_scatter_t *pscatters, char_type *buffer, T t) { return ::fast_io::details::decay::prrsvsct_byte_common_rsvsc_impl(pscatters, buffer, t).scatters_pos_ptr; } +/// @brief Returns the maximum scatter fallback coalescing threshold in characters. +/// @tparam char_type the output character type +/// @return ::std::size_t the stack-buffer character limit for scatter fallback coalescing template <::std::integral char_type> inline constexpr ::std::size_t print_scatter_full_output_threshold_max_chars() noexcept { return ::fast_io::details::decay::print_stack_buffer_max_size(); } +/// @brief Computes the scatter fallback coalescing threshold for an output stream. +/// @details A stream-provided threshold is capped by the active stack-buffer policy; streams without the customization +/// disable scatter fallback coalescing. +/// @tparam char_type the output character type +/// @tparam outputstmtype the decayed output stream reference type +/// @return ::std::size_t the threshold in characters, or zero when disabled template <::std::integral char_type, typename outputstmtype> inline constexpr ::std::size_t print_scatter_full_output_threshold() noexcept { if constexpr (::fast_io::scatter_fallback_full_output_threshold_stream) { + // Stream-specific scatter fallback thresholds are honored but never allowed to exceed the stack budget. constexpr ::std::size_t threshold{ scatter_fallback_full_output_threshold( ::fast_io::io_reserve_type>)}; @@ -331,24 +462,34 @@ inline constexpr ::std::size_t print_scatter_full_output_threshold() noexcept ::fast_io::details::decay::print_scatter_full_output_threshold_max_chars()}; if constexpr (threshold < max_threshold) { + // The stream threshold already fits within the stack-buffer policy. return threshold; } else { + // The stack-buffer policy caps oversized stream thresholds. return max_threshold; } } else { + // Streams without a scatter fallback threshold do not use stack coalescing for scatter fallback. return 0u; } } +/// @brief Computes the full-output coalescing threshold for an output stream. +/// @details Full-output coalescing has its own stream customization and falls back to the scatter fallback threshold +/// only when the full-output customization is absent. +/// @tparam char_type the output character type +/// @tparam outputstmtype the decayed output stream reference type +/// @return ::std::size_t the threshold in characters, or zero when both threshold paths are disabled template <::std::integral char_type, typename outputstmtype> inline constexpr ::std::size_t print_full_output_coalesce_threshold() noexcept { if constexpr (::fast_io::full_output_coalesce_threshold_stream) { + // A stream-specific full-output threshold is capped by the same stack-buffer policy as scatter fallback. constexpr ::std::size_t threshold{ full_output_coalesce_threshold( ::fast_io::io_reserve_type>)}; @@ -356,19 +497,24 @@ inline constexpr ::std::size_t print_full_output_coalesce_threshold() noexcept ::fast_io::details::decay::print_scatter_full_output_threshold_max_chars()}; if constexpr (threshold < max_threshold) { + // The stream threshold already fits within the stack-buffer policy. return threshold; } else { + // The stack-buffer policy caps oversized stream thresholds. return max_threshold; } } else { + // Without a full-output threshold, preserve the scatter fallback threshold behavior. return ::fast_io::details::decay::print_scatter_full_output_threshold(); } } +/// @brief Detects whether an output stream can write a contiguous byte range directly. +/// @tparam outputstmtype the decayed output stream reference type template inline constexpr bool print_has_direct_write_bytes_operations = ::fast_io::operations::decay::defines::has_write_all_bytes_overflow_define || @@ -377,6 +523,8 @@ inline constexpr bool print_has_direct_write_bytes_operations = (::fast_io::operations::decay::defines::has_write_all_overflow_define || ::fast_io::operations::decay::defines::has_write_some_overflow_define)); +/// @brief Detects whether an output stream can write byte scatter descriptors directly. +/// @tparam outputstmtype the decayed output stream reference type template inline constexpr bool print_has_direct_scatter_write_bytes_operations = ::fast_io::operations::decay::defines::has_scatter_write_all_bytes_overflow_define || @@ -385,6 +533,8 @@ inline constexpr bool print_has_direct_scatter_write_bytes_operations = (::fast_io::operations::decay::defines::has_scatter_write_all_overflow_define || ::fast_io::operations::decay::defines::has_scatter_write_some_overflow_define)); +/// @brief Detects whether an output stream can write a contiguous character range directly. +/// @tparam outputstmtype the decayed output stream reference type template inline constexpr bool print_has_direct_write_operations = ::fast_io::operations::decay::defines::has_write_all_overflow_define || @@ -393,6 +543,8 @@ inline constexpr bool print_has_direct_write_operations = (::fast_io::operations::decay::defines::has_write_all_bytes_overflow_define || ::fast_io::operations::decay::defines::has_write_some_bytes_overflow_define)); +/// @brief Detects whether an output stream can write character scatter descriptors directly. +/// @tparam outputstmtype the decayed output stream reference type template inline constexpr bool print_has_direct_scatter_write_operations = ::fast_io::operations::decay::defines::has_scatter_write_all_overflow_define || @@ -401,21 +553,38 @@ inline constexpr bool print_has_direct_scatter_write_operations = (::fast_io::operations::decay::defines::has_scatter_write_all_bytes_overflow_define || ::fast_io::operations::decay::defines::has_scatter_write_some_bytes_overflow_define)); +/// @brief Copies a small scatter payload into a contiguous coalescing buffer. +/// @details Very small ranges are copied with a simple loop to avoid helper overhead; larger ranges use the normal +/// non-overlapped copy routine. +/// @tparam char_type the element type being copied +/// @param first the first input element +/// @param count the number of elements to copy +/// @param result the first output element +/// @return char_type* one past the copied output template inline constexpr char_type *print_small_scatter_copy_n(char_type const *first, ::std::size_t count, char_type *result) noexcept { if (count <= 16u) { + // Tiny scatter ranges are copied inline to keep the coalescing hot path compact. for (::std::size_t i{}; i != count; ++i) { result[i] = first[i]; } return result + count; } + // Larger scatter ranges use the shared non-overlapped copy implementation. return ::fast_io::details::non_overlapped_copy_n(first, count, result); } +/// @brief Writes byte scatter descriptors, optionally coalescing small scatter runs first. +/// @details Empty and single-scatter runs are handled directly. Multi-scatter runs are copied into a bounded stack +/// buffer only when the stream supports both direct byte writes and direct byte scatter writes. +/// @tparam outputstmtype the decayed output stream reference type +/// @param outstm the output stream reference +/// @param scatters the first byte scatter descriptor +/// @param n the number of scatter descriptors template inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype outstm, ::fast_io::io_scatter_t const *scatters, @@ -423,13 +592,16 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype { if (n == 0) { + // An empty byte scatter run emits no output. return; } if (n == 1) { + // A single byte scatter avoids scatter-write overhead and writes the range directly when non-empty. ::std::size_t const len{scatters->len}; if (len != 0) { + // The single byte scatter contains payload bytes that can be written contiguously. auto first{static_cast<::std::byte const *>(scatters->base)}; ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, first + len); } @@ -442,6 +614,7 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype ::fast_io::details::decay::print_has_direct_write_bytes_operations && ::fast_io::details::decay::print_has_direct_scatter_write_bytes_operations) { + // Eligible multi-scatter byte runs first try bounded stack coalescing before using scatter write. char_type buffer[threshold_chars]; auto curr{reinterpret_cast<::std::byte *>(buffer)}; auto const first{curr}; @@ -451,11 +624,13 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype ::std::size_t const len{iter->len}; if (remaining < len) { + // The byte run exceeds the coalescing threshold, so preserve scatter output directly. ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); return; } if (len != 0) { + // Non-empty byte scatter payloads are appended to the contiguous coalescing buffer. curr = ::fast_io::details::decay::print_small_scatter_copy_n( static_cast<::std::byte const *>(iter->base), len, curr); remaining -= len; @@ -463,13 +638,21 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype } if (curr != first) { + // At least one byte payload was coalesced, so emit the compact contiguous byte range. ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, curr); } return; } + // Streams that cannot coalesce this path emit the original byte scatter descriptors directly. ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); } +/// @brief Writes character scatter descriptors, optionally coalescing small scatter runs first. +/// @details This mirrors the byte-scatter path but preserves character counts and uses character-range output APIs. +/// @tparam outputstmtype the decayed output stream reference type +/// @param outstm the output stream reference +/// @param scatters the first character scatter descriptor +/// @param n the number of scatter descriptors template inline constexpr void print_scatter_write_all_maybe_coalesce( outputstmtype outstm, ::fast_io::basic_io_scatter_t const *scatters, @@ -478,13 +661,16 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( using char_type = typename outputstmtype::output_char_type; if (n == 0) { + // An empty character scatter run emits no output. return; } if (n == 1) { + // A single character scatter avoids scatter-write overhead and writes the range directly when non-empty. auto [base, len] = *scatters; if (len != 0) { + // The single character scatter contains payload characters that can be written contiguously. ::fast_io::operations::decay::write_all_decay(outstm, base, base + len); } return; @@ -495,6 +681,7 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( ::fast_io::details::decay::print_has_direct_write_operations && ::fast_io::details::decay::print_has_direct_scatter_write_operations) { + // Eligible multi-scatter character runs first try bounded stack coalescing before scatter output. char_type buffer[threshold_chars]; char_type *curr{buffer}; ::std::size_t remaining{threshold_chars - 1u}; @@ -503,64 +690,96 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( ::std::size_t const len{iter->len}; if (remaining < len) { + // The character run exceeds the coalescing threshold, so preserve scatter output directly. ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); return; } if (len != 0) { + // Non-empty character scatter payloads are appended to the contiguous coalescing buffer. curr = ::fast_io::details::decay::print_small_scatter_copy_n(iter->base, len, curr); remaining -= len; } } if (curr != buffer) { + // At least one character payload was coalesced, so emit the compact contiguous character range. ::fast_io::operations::decay::write_all_decay(outstm, buffer, curr); } return; } + // Streams that cannot coalesce this path emit the original character scatter descriptors directly. ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); } +/// @brief Dispatches scatter output to the byte or character scatter writer. +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam scatter_type the scatter descriptor type +/// @param outstm the output stream reference +/// @param scatters the first scatter descriptor +/// @param n the number of scatter descriptors template inline constexpr void print_scatter_write_all_dispatch(outputstmtype outstm, scatter_type const *scatters, ::std::size_t n) { if constexpr (::std::same_as) { + // Byte scatter descriptors are routed to the byte-oriented coalescing writer. ::fast_io::details::decay::print_scatter_write_all_bytes_maybe_coalesce(outstm, scatters, n); } else { + // Character scatter descriptors are routed to the character-oriented coalescing writer. ::fast_io::details::decay::print_scatter_write_all_maybe_coalesce(outstm, scatters, n); } } +/// @brief Appends a newline scatter when requested and writes the full scatter run. +/// @tparam needprintlf true when the final scatter slot is reserved for a newline +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam char_type the output character type +/// @tparam scatter_type the scatter descriptor type +/// @param outstm the output stream reference +/// @param scatters the scatter descriptor storage +/// @param n the number of scatter descriptors, including the optional newline slot template inline constexpr void print_scatter_write_all_dispatch_with_line(outputstmtype outstm, scatter_type *scatters, ::std::size_t n) { if constexpr (needprintlf) { + // The caller reserved the final scatter slot for the trailing newline output. if constexpr (::std::same_as) { + // Byte scatter output stores the newline length in bytes. scatters[n - 1u] = ::fast_io::details::decay::line_scatter_common; } else { + // Character scatter output stores the newline length in characters. scatters[n - 1u] = ::fast_io::details::decay::line_scatter_common; } } ::fast_io::details::decay::print_scatter_write_all_dispatch(outstm, scatters, n); } +/// @brief Writes one character scatter followed by a newline. +/// @details Streams with byte-write support receive byte scatter descriptors; other streams receive character +/// scatter descriptors. Descriptor storage is placed on the stack when permitted by the stack policy. +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam char_type the output character type +/// @param outstm the output stream reference +/// @param scatter_res the scatter payload to write before the newline template inline constexpr void print_single_scatter_with_line(outputstmtype outstm, ::fast_io::basic_io_scatter_t scatter_res) { if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations) { + // Byte-capable streams receive byte scatter descriptors so wide characters have correct byte lengths. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit<2u, ::fast_io::io_scatter_t>) { + // Small byte scatter descriptor storage is kept on the stack. ::fast_io::io_scatter_t scatters[2]; scatters[0] = {scatter_res.base, scatter_res.len * sizeof(char_type)}; scatters[1] = {__builtin_addressof(char_literal_v), sizeof(char_type)}; @@ -568,6 +787,7 @@ inline constexpr void print_single_scatter_with_line(outputstmtype outstm, } else { + // Oversized byte scatter descriptor storage is allocated before writing the two-part output. ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> scatters(2u); scatters.ptr[0] = {scatter_res.base, scatter_res.len * sizeof(char_type)}; scatters.ptr[1] = {__builtin_addressof(char_literal_v), sizeof(char_type)}; @@ -576,9 +796,11 @@ inline constexpr void print_single_scatter_with_line(outputstmtype outstm, } else { + // Character-only streams receive character scatter descriptors directly. using scatter_type = ::fast_io::basic_io_scatter_t; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit<2u, scatter_type>) { + // Small character scatter descriptor storage is kept on the stack. scatter_type scatters[2]; scatters[0] = scatter_res; scatters[1] = {__builtin_addressof(char_literal_v), 1u}; @@ -586,6 +808,7 @@ inline constexpr void print_single_scatter_with_line(outputstmtype outstm, } else { + // Oversized character scatter descriptor storage is allocated before writing the two-part output. ::fast_io::details::local_operator_new_array_ptr scatters(2u); scatters.ptr[0] = scatter_res; scatters.ptr[1] = {__builtin_addressof(char_literal_v), 1u}; @@ -594,6 +817,14 @@ inline constexpr void print_single_scatter_with_line(outputstmtype outstm, } } +/// @brief Materializes reserve-scatters output into byte scatter descriptors and writes it. +/// @tparam line true when a trailing newline scatter is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam char_type the output character type +/// @tparam T the reserve-scatters printable type +/// @param outstm the output stream reference +/// @param scatters the byte scatter descriptor storage +/// @param t the argument to materialize template inline constexpr void print_reserve_scatters_bytes_with_scatter(outputstmtype outstm, ::fast_io::io_scatter_t *scatters, T t) @@ -603,10 +834,12 @@ inline constexpr void print_reserve_scatters_bytes_with_scatter(outputstmtype ou constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small reserve storage for byte scatter output is materialized on the stack. char_type buffer[reserve_buffer_size]; ::fast_io::io_scatter_t *ptr{::fast_io::details::decay::prrsvsct_byte_common_impl(scatters, buffer, t)}; if constexpr (line) { + // The line variant appends a byte-length newline scatter after the materialized scatters. *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } @@ -615,10 +848,12 @@ inline constexpr void print_reserve_scatters_bytes_with_scatter(outputstmtype ou } else { + // Large reserve storage for byte scatter output is allocated before materialization. ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); ::fast_io::io_scatter_t *ptr{::fast_io::details::decay::prrsvsct_byte_common_impl(scatters, buffer.ptr, t)}; if constexpr (line) { + // The line variant appends a byte-length newline scatter after the materialized scatters. *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } @@ -627,6 +862,14 @@ inline constexpr void print_reserve_scatters_bytes_with_scatter(outputstmtype ou } } +/// @brief Materializes reserve-scatters output into character scatter descriptors and writes it. +/// @tparam line true when a trailing newline scatter is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam char_type the output character type +/// @tparam T the reserve-scatters printable type +/// @param outstm the output stream reference +/// @param scatters the character scatter descriptor storage +/// @param t the argument to materialize template inline constexpr void print_reserve_scatters_basic_with_scatter( outputstmtype outstm, ::fast_io::basic_io_scatter_t *scatters, T t) @@ -636,11 +879,13 @@ inline constexpr void print_reserve_scatters_basic_with_scatter( constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small reserve storage for character scatter output is materialized on the stack. char_type buffer[reserve_buffer_size]; auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, buffer, t) .scatters_pos_ptr}; if constexpr (line) { + // The line variant appends a character-length newline scatter after the materialized scatters. *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } @@ -649,12 +894,14 @@ inline constexpr void print_reserve_scatters_basic_with_scatter( } else { + // Large reserve storage for character scatter output is allocated before materialization. ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); auto ptr{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, buffer.ptr, t) .scatters_pos_ptr}; if constexpr (line) { + // The line variant appends a character-length newline scatter after the materialized scatters. *ptr = ::fast_io::details::decay::line_scatter_common; ++ptr; } @@ -663,6 +910,15 @@ inline constexpr void print_reserve_scatters_basic_with_scatter( } } +/// @brief Emits one reserve-scatters printable argument through the best scatter descriptor representation. +/// @details Runtime paths prefer byte scatter descriptors when the stream supports byte writes; constant-evaluated +/// paths and character-only streams use typed character scatter descriptors. +/// @tparam line true when a trailing newline scatter is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam char_type the output character type +/// @tparam T the reserve-scatters printable type +/// @param outstm the output stream reference +/// @param t the argument to emit template inline constexpr void print_control_single_reserve_scatters(outputstmtype outstm, T t) { @@ -676,18 +932,21 @@ inline constexpr void print_control_single_reserve_scatters(outputstmtype outstm if (!__builtin_is_constant_evaluated()) #endif { + // Runtime byte-capable streams can use byte scatter descriptors for reserve-scatters output. if constexpr (::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations< outputstmtype>) { if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small byte scatter descriptor storage is kept on the stack. ::fast_io::io_scatter_t scatters[scattersnum]; ::fast_io::details::decay::print_reserve_scatters_bytes_with_scatter( outstm, scatters, t); } else { + // Large byte scatter descriptor storage is allocated before materializing the output. ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> scatters(scattersnum); ::fast_io::details::decay::print_reserve_scatters_bytes_with_scatter( outstm, scatters.ptr, t); @@ -698,18 +957,33 @@ inline constexpr void print_control_single_reserve_scatters(outputstmtype outstm using scatter_type = ::fast_io::basic_io_scatter_t; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Character scatter descriptor storage is kept on the stack when the policy allows it. scatter_type scatters[scattersnum]; ::fast_io::details::decay::print_reserve_scatters_basic_with_scatter( outstm, scatters, t); } else { + // Character scatter descriptor storage is allocated when the fixed run exceeds the stack policy. ::fast_io::details::local_operator_new_array_ptr scatters(scattersnum); ::fast_io::details::decay::print_reserve_scatters_basic_with_scatter( outstm, scatters.ptr, t); } } +/// @brief Drains a context-printable object through a caller-provided context buffer. +/// @details Each iteration asks the context producer for the next filled window and writes it to the stream; the line +/// variant appends the trailing newline only to the final completed window. +/// @tparam line true when a trailing newline is appended after the final context window +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam context_type the context state type +/// @tparam T the context-printable argument type +/// @tparam char_type the output character type +/// @param outstm the output stream reference +/// @param st the context object that produces output windows +/// @param t the argument being printed +/// @param buffer the first character in the context buffer +/// @param buffered one past the reusable buffered range passed to the producer template inline constexpr void print_context_define_to_window(outputstmtype outstm, context_type &st, T t, char_type *buffer, char_type *buffered) @@ -719,28 +993,41 @@ inline constexpr void print_context_define_to_window(outputstmtype outstm, conte auto [resit, done] = st.print_context_define(t, buffer, buffered); if constexpr (line) { + // The line variant may append the newline to the final context output window. if (done) { + // The producer finished, so the newline is appended before this final window is written. *resit = ::fast_io::char_literal_v; ++resit; } ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); if (done) { + // The final line-terminated context window has been written. return; } } else { + // The non-line variant writes each produced context window without adding characters. ::fast_io::operations::decay::write_all_decay(outstm, buffer, resit); if (done) { + // The final context window has been written. return; } } } } +/// @brief Emits one already-forwarded printable control argument to an output stream. +/// @details The dispatcher selects the most specialized single-argument path available: scatter, static reserve, +/// dynamic reserve, reserve-scatters, context printing, or the ordinary print_define customization. +/// @tparam line true when a trailing newline is appended after this argument +/// @tparam output the decayed output stream reference type +/// @tparam T the argument type +/// @param outstm the output stream reference +/// @param t the argument to emit template requires(::std::is_trivially_copyable_v && ::std::is_trivially_copyable_v) inline constexpr void print_control_single(output outstm, T t) @@ -751,6 +1038,7 @@ inline constexpr void print_control_single(output outstm, T t) constexpr auto lfch{char_literal_v}; if constexpr (scatter_printable) { + // Scatter-printable arguments already expose a contiguous output range. #if 0 basic_io_scatter_t scatter; if constexpr(::std::same_as>) @@ -811,15 +1099,18 @@ inline constexpr void print_control_single(output outstm, T t) if constexpr (line) { + // The line variant writes the scatter range and a trailing newline as one scatter-oriented output. ::fast_io::details::decay::print_single_scatter_with_line(outstm, scatter_res); } else { + // The non-line variant writes the scatter range directly. ::fast_io::operations::decay::write_all_decay(outstm, scatter_res.base, scatter_res.base + scatter_res.len); } } else if constexpr (reserve_printable) { + // Static reserve-printable arguments have a compile-time output size and can be materialized directly. constexpr ::std::size_t real_size{print_reserve_size(::fast_io::io_reserve_type)}; constexpr ::std::size_t size{real_size + static_cast<::std::size_t>(line)}; static_assert(real_size < PTRDIFF_MAX); @@ -845,20 +1136,24 @@ inline constexpr void print_control_single(output outstm, T t) if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations && !asan_activated) { + // Buffered streams first try to emit the static reserve output into the current put area. char_type *bcurr{obuffer_curr(outstm)}; char_type *bend{obuffer_end(outstm)}; ::std::ptrdiff_t const diff(bend - bcurr); bool smaller{static_cast<::std::ptrdiff_t>(size) < diff}; if constexpr (minimum_buffer_output_stream_require_size_impl) { + // Streams with a sufficient declared minimum buffer can flush once and then emit in place. if (!smaller) [[unlikely]] { + // The current put area is short, so prepare the minimum-size buffer before emission. obuffer_minimum_size_flush_prepare_define(outstm); bcurr = obuffer_curr(outstm); } bcurr = print_reserve_define(::fast_io::io_reserve_type, bcurr, t); if constexpr (line) { + // The line variant appends the newline in the same output-buffer commit. *bcurr = lfch; ++bcurr; } @@ -868,9 +1163,11 @@ inline constexpr void print_control_single(output outstm, T t) { if (smaller) [[likely]] { + // The current put area has enough room, so emit directly and commit the new cursor. bcurr = print_reserve_define(::fast_io::io_reserve_type, bcurr, t); if constexpr (line) { + // The line variant appends the newline in the same output-buffer commit. *bcurr = lfch; ++bcurr; } @@ -880,11 +1177,13 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // A small static reserve output is materialized on the stack and written once. char_type buffer[size]; char_type *i{ print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; if constexpr (line) { + // The line variant appends the newline before the stack buffer is written. *i = lfch; ++i; } @@ -892,11 +1191,13 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // A large static reserve output is materialized in heap storage and written once. ::fast_io::details::local_operator_new_array_ptr newptr(size); char_type *i{ print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *i = lfch; ++i; } @@ -909,10 +1210,12 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Without a usable output buffer, small static reserve output is materialized on the stack. char_type buffer[size]; char_type *i{print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; if constexpr (line) { + // The line variant appends the newline before the stack buffer is written. *i = lfch; ++i; } @@ -920,10 +1223,12 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a usable output buffer, large static reserve output is materialized in heap storage. ::fast_io::details::local_operator_new_array_ptr newptr(size); char_type *i{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *i = lfch; ++i; } @@ -934,23 +1239,28 @@ inline constexpr void print_control_single(output outstm, T t) } else if constexpr (dynamic_reserve_printable) { + // Dynamic reserve-printable arguments are measured at run time before choosing a materialization path. ::std::size_t size{print_reserve_size(::fast_io::io_reserve_type, t)}; constexpr ::std::size_t stack_buffer_size{ ::fast_io::details::decay::dynamic_print_reserve_static_stack_size()}; if constexpr (line) { + // The line variant reserves one additional character and validates pointer-difference bounds. constexpr ::std::size_t mx{::std::numeric_limits<::std::ptrdiff_t>::max() - 1}; if (size >= mx) { + // The measured run cannot be represented safely with a trailing newline. fast_terminate(); } ++size; } else { + // The non-line variant only needs to validate the measured dynamic output size. constexpr ::std::size_t mx{::std::numeric_limits<::std::ptrdiff_t>::max()}; if (mx < size) { + // The measured run cannot be represented safely by pointer differences. fast_terminate(); } } @@ -976,15 +1286,18 @@ inline constexpr void print_control_single(output outstm, T t) if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations && !asan_activated) { + // Buffered streams first try to emit the dynamic reserve output into the current put area. auto curr{obuffer_curr(outstm)}; auto ed{obuffer_end(outstm)}; ::std::ptrdiff_t diff(ed - curr); bool smaller{static_cast<::std::ptrdiff_t>(size) < diff}; if (smaller) { + // The current put area has enough room, so emit directly and commit the new cursor. auto it{print_reserve_define(::fast_io::io_reserve_type, curr, t)}; if constexpr (line) { + // The line variant appends the newline in the same output-buffer commit. *it = lfch; ++it; } @@ -997,13 +1310,16 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (stack_buffer_size != 0) { + // A static stack hint allows small dynamic output to avoid heap allocation. if (size <= stack_buffer_size) { + // The measured dynamic output fits in the bounded stack buffer. char_type stack_buffer[stack_buffer_size]; auto it{print_reserve_define(::fast_io::io_reserve_type, stack_buffer, t)}; if constexpr (line) { + // The line variant appends the newline before the stack buffer is written. *it = lfch; ++it; } @@ -1011,11 +1327,13 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // The measured dynamic output exceeds the stack hint and is materialized on the heap. ::fast_io::details::local_operator_new_array_ptr newptr(size); auto it{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *it = lfch; ++it; } @@ -1024,11 +1342,13 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a usable static stack hint, dynamic output is materialized on the heap. ::fast_io::details::local_operator_new_array_ptr newptr(size); auto it{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *it = lfch; ++it; } @@ -1040,12 +1360,15 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (stack_buffer_size != 0) { + // Without a usable output buffer, a static stack hint may still avoid heap allocation. if (size <= stack_buffer_size) { + // The measured dynamic output fits in the bounded stack buffer. char_type buffer[stack_buffer_size]; auto it{print_reserve_define(::fast_io::io_reserve_type, buffer, t)}; if constexpr (line) { + // The line variant appends the newline before the stack buffer is written. *it = lfch; ++it; } @@ -1053,10 +1376,12 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // The measured dynamic output exceeds the stack hint and is materialized on the heap. ::fast_io::details::local_operator_new_array_ptr newptr(size); auto it{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *it = lfch; ++it; } @@ -1065,10 +1390,12 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a stack hint, dynamic output is materialized on the heap and written once. ::fast_io::details::local_operator_new_array_ptr newptr(size); auto it{print_reserve_define(::fast_io::io_reserve_type, newptr.ptr, t)}; if constexpr (line) { + // The line variant appends the newline before the heap buffer is written. *it = lfch; ++it; } @@ -1079,14 +1406,17 @@ inline constexpr void print_control_single(output outstm, T t) } else if constexpr (reserve_scatters_printable) { + // Reserve-scatters printable arguments are emitted through the scatter descriptor materialization path. ::fast_io::details::decay::print_control_single_reserve_scatters(outstm, t); } else if constexpr (::fast_io::transcode_imaginary_printable) { + // Transcode-imaginary printables are recognized here, but this freestanding path has no active emitter yet. // todo? } else if constexpr (context_printable) { + // Context-printable arguments stream repeated context windows until their producer reports completion. typename ::std::remove_cvref_t))>::type st; constexpr ::std::size_t reserved_size{ ::fast_io::details::decay::context_print_static_buffer_size_v}; @@ -1094,6 +1424,7 @@ inline constexpr void print_control_single(output outstm, T t) static_cast<::std::ptrdiff_t>(reserved_size - static_cast<::std::size_t>(line))}; if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) { + // Buffered streams let the context producer fill the current put area directly when possible. for (;;) { auto bcurr{obuffer_curr(outstm)}; @@ -1105,12 +1436,14 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (minimum_buffer_output_stream_require_size_impl) { + // Streams with a sufficient minimum buffer can prepare a fresh window for context output. obuffer_minimum_size_flush_prepare_define(outstm); bcurr = obuffer_curr(outstm); bed = obuffer_end(outstm); } else { + // Streams without a sufficient minimum buffer flush before deciding on a fallback window. ::fast_io::operations::decay::output_stream_buffer_flush_decay(outstm); bcurr = obuffer_curr(outstm); bed = obuffer_end(outstm); @@ -1122,6 +1455,7 @@ inline constexpr void print_control_single(output outstm, T t) if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit< reserved_size, char_type>) { + // A too-small put area falls back to a stack context buffer when it fits the policy. char_type buffer[reserved_size]; char_type *buffered{buffer + reserved_size_no_line}; ::fast_io::details::decay::print_context_define_to_window( @@ -1129,6 +1463,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // A too-small put area falls back to heap context storage for large windows. ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); char_type *buffer{newptr.ptr}; char_type *buffered{buffer + reserved_size_no_line}; @@ -1141,6 +1476,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // A non-empty put area is handed directly to the context producer. auto [resit, done] = st.print_context_define(t, bcurr, bed); obuffer_set_curr(outstm, resit); if (done) @@ -1150,6 +1486,7 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (line) { + // The line variant writes the trailing newline after the final context window. ::fast_io::operations::decay::char_put_decay(outstm, lfch); } return; @@ -1161,12 +1498,14 @@ inline constexpr void print_control_single(output outstm, T t) { if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Unbuffered streams use a stack context buffer when the fixed window fits the policy. char_type buffer[reserved_size]; char_type *buffered{buffer + reserved_size_no_line}; ::fast_io::details::decay::print_context_define_to_window(outstm, st, t, buffer, buffered); } else { + // Unbuffered streams allocate context storage when the fixed window exceeds the stack policy. ::fast_io::details::local_operator_new_array_ptr newptr(reserved_size); char_type *buffer{newptr.ptr}; char_type *buffered{buffer + reserved_size_no_line}; @@ -1177,17 +1516,21 @@ inline constexpr void print_control_single(output outstm, T t) } else if constexpr (printable) { + // Generic printable arguments delegate to their print_define customization. print_define(::fast_io::io_reserve_type, outstm, t); if constexpr (line) { + // The line variant appends the newline after the customization has emitted its output. ::fast_io::operations::decay::char_put_decay(outstm, lfch); } } else if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output intentionally emits nothing. } else { + // Unrecognized argument types fail here with the printable concept diagnostic. constexpr bool no{printable}; static_assert(no, "type not printable"); } @@ -3048,6 +3391,14 @@ inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t) template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t); +/// @brief Forwards one semantic print argument and emits it into an already-sized buffer. +/// @details This wrapper preserves the semantic forwarding rules used by the checked emitters before entering the +/// unchecked node dispatcher. +/// @tparam char_type the character type of the destination buffer +/// @tparam T the argument type accepted by the semantic print layer +/// @param iter a pointer to the next output position in a buffer large enough for the argument +/// @param t the argument to print +/// @return char_type* a pointer one past the emitted argument template <::std::integral char_type, typename T> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3062,8 +3413,15 @@ inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T iter, ::std::forward(forwarded)); } +/// @brief Caps condition-pack common factoring to keep deep user packs from forcing excessive inlining. +/// @details Small packs benefit from shared-prefix or shared-suffix emission, while very large packs are left to the +/// original conditional output path to avoid code-size pressure. inline constexpr ::std::size_t print_semantic_common_factor_pack_max_size{8u}; +/// @brief Treats two null semantic print nodes as a trivially equal common factor. +/// @param the first null node +/// @param the second null node +/// @return bool true because both nodes emit no output #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -3074,6 +3432,14 @@ inline constexpr bool print_semantic_common_factor_equal(::fast_io::io_null_t, : return true; } +/// @brief Compares two scatter-like semantic leaves for byte-for-byte identical output. +/// @details A scatter leaf is a valid common factor only when both alternatives refer to the same base and, when the +/// extent is stored at run time, the same length. +/// @tparam T the first scatter-like node type +/// @tparam U the second scatter-like node type, constrained to the same decayed type as T +/// @param first the first candidate common factor +/// @param second the second candidate common factor +/// @return bool true when both leaves are safe to emit once before or after the conditional body template requires(!::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> && ::std::same_as<::std::remove_cvref_t, ::std::remove_cvref_t> && @@ -3093,14 +3459,24 @@ inline constexpr bool print_semantic_common_factor_equal(T const &first, U const second.len; }) { + // A dynamic scatter factor must match both the address and the emitted extent. return first.base == second.base && first.len == second.len; } else { + // A static-extent scatter factor carries its extent in the type, so matching the address is sufficient. return first.base == second.base; } } +/// @brief Compares two integral reference semantic leaves for identical output. +/// @details Value-like integral leaves can be factored when both condition alternatives carry the same reference +/// value and therefore emit the same characters. +/// @tparam T the first integral-reference node type +/// @tparam U the second integral-reference node type, constrained to the same decayed type as T +/// @param first the first candidate common factor +/// @param second the second candidate common factor +/// @return bool true when both leaves can be emitted on the shared path template requires(!::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> && ::std::same_as<::std::remove_cvref_t, ::std::remove_cvref_t> && @@ -3119,6 +3495,11 @@ inline constexpr bool print_semantic_common_factor_equal(T const &first, U const return first.reference == second.reference; } +/// @brief Detects whether two semantic leaves support conservative common-factor comparison. +/// @details Only leaf kinds with an equality predicate that proves identical output participate in condition-pack +/// factoring; every other kind falls back to the original conditional emission. +/// @tparam T the first leaf expression type +/// @tparam U the second leaf expression type template concept print_semantic_common_factor_comparable = requires(T &&t, U &&u) { { @@ -3127,6 +3508,13 @@ concept print_semantic_common_factor_comparable = requires(T &&t, U &&u) { } -> ::std::same_as; }; +/// @brief Emits one element of a semantic pack into an already-sized buffer. +/// @tparam char_type the character type of the destination buffer +/// @tparam index the pack element index to emit +/// @tparam Pack the semantic pack type +/// @param iter a pointer to the next output position +/// @param pack the pack whose indexed element is printed +/// @return char_type* a pointer one past the emitted element template <::std::integral char_type, ::std::size_t index, typename Pack> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3139,6 +3527,13 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_element(char_type iter, ::fast_io::containers::get(::std::forward(pack).storage)); } +/// @brief Tests whether the same indexed element of two packs is a safe shared output factor. +/// @tparam index the pack element index to compare +/// @tparam Pack1 the first semantic pack type +/// @tparam Pack2 the second semantic pack type +/// @param pack1 the pack from the true branch +/// @param pack2 the pack from the false branch +/// @return bool true when the indexed elements provably emit identical output template <::std::size_t index, typename Pack1, typename Pack2> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3152,15 +3547,25 @@ inline constexpr bool print_semantic_pack_element_common_factor_equal(Pack1 &&pa if constexpr (::fast_io::operations::decay::print_semantic_common_factor_comparable) { + // Comparable leaves may be shared when their equality predicate proves identical emitted bytes. return ::fast_io::operations::decay::print_semantic_common_factor_equal( ::std::forward(first), ::std::forward(second)); } else { + // Non-comparable leaves stay inside their original conditional branch to preserve output semantics. return false; } } +/// @brief Emits a contiguous half-open range of semantic pack elements. +/// @tparam char_type the character type of the destination buffer +/// @tparam first the first element index to emit +/// @tparam last one past the final element index to emit +/// @tparam Pack the semantic pack type +/// @param iter a pointer to the next output position +/// @param pack the pack providing the emitted elements +/// @return char_type* a pointer one past the emitted range template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3171,6 +3576,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type * { if constexpr (first < last) { + // A non-empty range emits the front element and recursively advances the output cursor. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( iter, ::std::forward(pack)); return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( @@ -3178,10 +3584,24 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type * } else { + // An empty range emits nothing and returns the current output cursor unchanged. return iter; } } +/// @brief Emits a condition-pack range after at least one shared edge factor has been found. +/// @details The routine recursively peels identical prefix and suffix elements so that only the irreducible middle +/// range remains guarded by the predicate. +/// @tparam char_type the character type of the destination buffer +/// @tparam first the first candidate element index +/// @tparam last one past the final candidate element index +/// @tparam Pack1 the semantic pack type for the true branch +/// @tparam Pack2 the semantic pack type for the false branch +/// @param iter a pointer to the next output position +/// @param pred the condition selecting the unfactored middle range +/// @param pack1 the pack emitted when pred is true +/// @param pack2 the pack emitted when pred is false +/// @return char_type* a pointer one past the emitted condition-pack range template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3196,6 +3616,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factore if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { + // A shared prefix element is emitted once before the remaining conditional output. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( iter, ::std::forward(pack1)); return ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< @@ -3207,6 +3628,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factore if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { + // A shared suffix element is delayed until the conditional middle range has been emitted. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< char_type, first, suffix_index>(iter, pred, ::std::forward(pack1), ::std::forward(pack2)); @@ -3217,18 +3639,23 @@ inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factore } if (pred) { + // The predicate selected the true branch, so emit the remaining true-pack range verbatim. return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( iter, ::std::forward(pack1)); } + // The predicate selected the false branch, so emit the remaining false-pack range verbatim. return ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_range( iter, ::std::forward(pack2)); } else { + // An exhausted factored range has no more output to append. return iter; } } +/// @brief Carries the result of a condition-pack common-factor attempt. +/// @tparam char_type the character type of the destination buffer template <::std::integral char_type> struct print_semantic_condition_pack_factor_result { @@ -3236,6 +3663,19 @@ struct print_semantic_condition_pack_factor_result bool done; }; +/// @brief Attempts to start conservative common factoring for a condition whose alternatives are equal-sized packs. +/// @details The attempt succeeds only when a shared prefix or suffix is found at the edge of the candidate range; +/// otherwise the caller must use the original conditional output path. +/// @tparam char_type the character type of the destination buffer +/// @tparam first the first candidate element index +/// @tparam last one past the final candidate element index +/// @tparam Pack1 the semantic pack type for the true branch +/// @tparam Pack2 the semantic pack type for the false branch +/// @param iter a pointer to the next output position +/// @param pred the condition selecting between the unfactored pack ranges +/// @param pack1 the pack emitted when pred is true +/// @param pack2 the pack emitted when pred is false +/// @return print_semantic_condition_pack_factor_result the updated cursor and success flag template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3250,6 +3690,7 @@ print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pr if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { + // A shared prefix starts the factored output path and proves the transformation is worthwhile. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_element( iter, ::std::forward(pack1)); return {::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< @@ -3263,6 +3704,7 @@ print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pr if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { + // A shared suffix starts the factored path by emitting the conditional middle first. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_factored< char_type, first, suffix_index>(iter, pred, ::std::forward(pack1), ::std::forward(pack2)); @@ -3273,9 +3715,12 @@ print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pr } } } + // No safe edge factor was found, so the caller must preserve the original conditional branch. return {iter, false}; } +/// @brief Applies unchecked semantic emission to every element supplied by a pack expansion. +/// @tparam char_type the character type of the destination buffer template <::std::integral char_type> struct print_semantic_emit_unchecked_pack_continuation { @@ -3290,16 +3735,26 @@ struct print_semantic_emit_unchecked_pack_continuation } }; +/// @brief Emits a semantic leaf into an already-sized buffer. +/// @details The dispatcher selects the most direct leaf protocol available: null output, reserve output, scatter +/// output, or reserve-scatters output. +/// @tparam char_type the character type of the destination buffer +/// @tparam T the semantic leaf type +/// @param iter a pointer to the next output position +/// @param t the leaf to emit +/// @return char_type* a pointer one past the emitted leaf template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, T &&t) { using value_type = ::std::remove_cvref_t; if constexpr (::std::same_as) { + // Null leaves intentionally emit no output and leave the cursor unchanged. return iter; } else if constexpr (::fast_io::static_precise_reserve_printable) { + // Static precise reserve leaves know their full extent and can write directly to the current cursor. return print_reserve_define(::fast_io::io_reserve_type, iter, ::std::forward(t)); } else if constexpr (::fast_io::precise_reserve_printable) @@ -3313,11 +3768,13 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } -> ::std::same_as; }) { + // Pointer-returning precise reserve leaves advance the cursor through their define operation. return print_reserve_precise_define(::fast_io::io_reserve_type, iter, precise_size, ::std::forward(t)); } else { + // Void-returning precise reserve leaves rely on the measured size to advance the cursor. print_reserve_precise_define(::fast_io::io_reserve_type, iter, precise_size, ::std::forward(t)); return iter + precise_size; @@ -3325,6 +3782,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } else if constexpr (::fast_io::scatter_printable) { + // Scatter leaves copy their single contiguous range into the unchecked output buffer. auto scatter{print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t))}; return ::fast_io::details::non_overlapped_copy_n(scatter.base, scatter.len, iter); } @@ -3339,6 +3797,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, ::fast_io::basic_io_scatter_t>}; if constexpr (stack_buffer_ok) { + // Small reserve-scatters leaves materialize temporary scatter and reserve storage on the stack. ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; char_type buffer[reserve_buffer_size]; auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, @@ -3351,6 +3810,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } else { + // Large reserve-scatters leaves allocate temporary scatter and reserve storage before copying output. ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( sz.scatters_size); ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); @@ -3365,6 +3825,15 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } } +/// @brief Emits any semantic print node into an already-sized buffer. +/// @details This unchecked dispatcher is used after precise sizing or coalescing has guaranteed enough contiguous +/// output space. It preserves semantic nodes such as packs, conditions, and width manipulators while +/// delegating leaves to the leaf protocol dispatcher. +/// @tparam char_type the character type of the destination buffer +/// @tparam T the semantic node or leaf type +/// @param iter a pointer to the next output position +/// @param t the node to emit +/// @return char_type* a pointer one past the emitted node template <::std::integral char_type, typename T> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3377,6 +3846,7 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t using node_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::print_pack) { + // Packs emit each element in storage order into the same unchecked output run. ::fast_io::details::decay::print_semantic_pack_apply( ::std::forward(node_ref), ::fast_io::operations::decay::print_semantic_emit_unchecked_pack_continuation{ @@ -3389,31 +3859,38 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t using second_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::print_pack && ::fast_io::details::print_pack) { + // Condition alternatives that are both packs may expose equal edge elements for shared emission. if constexpr (first_type::size == second_type::size && first_type::size <= ::fast_io::operations::decay::print_semantic_common_factor_pack_max_size) { + // Equal-sized small packs try conservative common factoring before falling back to the raw predicate. auto const factor_result{ ::fast_io::operations::decay::print_semantic_emit_unchecked_condition_pack_try_factor< char_type, 0u, first_type::size>(iter, node_ref.pred, node_ref.t1, node_ref.t2)}; if (factor_result.done) { + // A successful factor attempt has already emitted the complete condition-pack output. return factor_result.iter; } if (node_ref.pred) { + // The predicate selected the true pack after no safe shared factor was found. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); } + // The predicate selected the false pack after no safe shared factor was found. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); } else { if (node_ref.pred) { + // Large or unequal pack alternatives are emitted through the true branch without factoring. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); } + // Large or unequal pack alternatives are emitted through the false branch without factoring. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); } } @@ -3421,8 +3898,10 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t { if (node_ref.pred) { + // Non-pack condition alternatives emit the true branch selected by the predicate. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t1); } + // Non-pack condition alternatives emit the false branch selected by the predicate. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.t2); } } @@ -3440,16 +3919,19 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; if (width <= len || placement_code == 0u) { + // Width that is already satisfied, or disabled placement, emits the child without padding. return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); } ::std::size_t const padding{width - len}; if (placement_code == 1u) { + // Left placement emits the child first and appends all padding on the right. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); return ::fast_io::details::my_fill_n(iter, padding, fillch); } if (placement_code == 2u) { + // Center placement splits padding around the child, biasing the remainder to the right. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); @@ -3458,15 +3940,18 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } if (placement_code == 4u) { + // Internal placement inserts padding after the sign or prefix reported by the child. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.reference)}; if (internal_len != 0) { + // A non-zero internal shift allows padding to be inserted inside the already-emitted child bytes. char_type *const first{iter}; char_type *const last{ ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference)}; if (len < internal_len) { + // An invalid internal span leaves the child output unchanged instead of moving bytes past it. return last; } char_type *const shift_pos{first + internal_len}; @@ -3475,37 +3960,55 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t return first + width; } } + // Right placement, or internal placement without a shift point, emits padding before the child. iter = ::fast_io::details::my_fill_n(iter, padding, fillch); return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); } else { + // Leaf nodes use the fastest printable protocol supported by their decayed type. return ::fast_io::operations::decay::print_semantic_emit_unchecked_leaf( iter, ::std::forward(node_ref)); } } +/// @brief Computes the total static precise size for a semantic print run. +/// @details The value is available only when every argument has a compile-time precise semantic size. +/// @tparam line true when a trailing newline is part of the run +/// @tparam char_type the character type of the output run +/// @tparam Args the argument types in the run +/// @return ::std::size_t the static total size, or SIZE_MAX when any argument is not statically sized template inline consteval ::std::size_t print_semantic_static_precise_total_size() noexcept { if constexpr ((::fast_io::details::decay::print_semantic_static_precise_size::available && ...)) { + // A fully static run can sum all compile-time sizes and feed fixed-size stack coalescing. ::std::size_t total{}; ((total = ::fast_io::details::intrinsics::add_or_overflow_die( total, ::fast_io::details::decay::print_semantic_static_precise_size::size)), ...); if constexpr (line) { + // The line variant reserves one additional character for the trailing newline. total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); } return total; } else { + // A non-static argument prevents fixed-size coalescing at compile time. return SIZE_MAX; } } +/// @brief Computes the run-time precise size for a semantic print run. +/// @details This is used by checked coalescing paths before allocating or reserving contiguous output space. +/// @tparam line true when a trailing newline is part of the run +/// @tparam char_type the character type of the output run +/// @tparam Args the argument types in the run +/// @param args the arguments whose semantic sizes are measured +/// @return ::std::size_t the total number of characters needed by the run template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3521,11 +4024,19 @@ inline constexpr ::std::size_t print_semantic_precise_total_size(Args &&...args) ...); if constexpr (line) { + // The line variant includes the final newline in the measured run. total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); } return total; } +/// @brief Emits a precise semantic print run into an already-sized contiguous buffer. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the destination buffer +/// @tparam Args the argument types in the run +/// @param iter a pointer to the next output position +/// @param args the arguments to emit in order +/// @return char_type* a pointer one past the emitted run template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3539,12 +4050,23 @@ inline constexpr char_type *print_semantic_emit_unchecked_run(char_type *iter, A ...); if constexpr (line) { + // The line variant appends a newline after the last emitted argument. *iter = ::fast_io::char_literal_v; ++iter; } return iter; } +/// @brief Attempts to coalesce a precise semantic print run into one contiguous output operation. +/// @details The function first uses existing output-buffer space when available, then falls back to a bounded stack +/// buffer according to the full-output coalescing threshold. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the run +/// @param optstm the output stream reference +/// @param args the arguments to emit +/// @return bool true when the whole run was emitted by this coalescing path template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3557,19 +4079,23 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, char_type, ::std::remove_cvref_t>::value && ...)) { + // A fully precise run can be measured before choosing a coalesced output strategy. ::std::size_t const required{ ::fast_io::operations::decay::print_semantic_precise_total_size( args...)}; if (required == 0) { + // An empty precise run completes without touching the output stream. return true; } if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) { + // Buffered streams can coalesce directly into their current put area when enough space remains. char_type *const curr{obuffer_curr(optstm)}; char_type *const end{obuffer_end(optstm)}; if (static_cast<::std::size_t>(end - curr) >= required) { + // The existing output buffer has enough room, so emit in place and advance the stream cursor once. char_type *const iter{ ::fast_io::operations::decay::print_semantic_emit_unchecked_run( curr, ::std::forward(args)...)}; @@ -3581,11 +4107,13 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, ::fast_io::details::decay::print_full_output_coalesce_threshold()}; if constexpr (threshold_chars != 0) { + // A non-zero full-output threshold permits bounded stack-buffer coalescing. constexpr ::std::size_t static_total{ ::fast_io::operations::decay::print_semantic_static_precise_total_size...>()}; if constexpr (static_total != SIZE_MAX && static_total <= threshold_chars) { + // A compile-time bounded run uses an exactly-sized stack buffer and one write operation. constexpr ::std::size_t buffer_size{static_total == 0 ? 1u : static_total}; char_type buffer[buffer_size]; char_type *const iter{ @@ -3598,6 +4126,7 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, { if (required <= threshold_chars) { + // A run-time bounded run uses the threshold-sized stack buffer and one write operation. char_type buffer[threshold_chars]; char_type *const iter{ ::fast_io::operations::decay::print_semantic_emit_unchecked_run( @@ -3608,9 +4137,23 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, } } } + // At least one requirement for precise coalescing failed, so the caller must use the normal emit path. return false; } +/// @brief Emits a width-formatted semantic child using the most direct available output path. +/// @details The function first tries the stream put area, then a small stack buffer, and finally a streaming fallback +/// that writes padding and child output in sequence. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the child reference type +/// @param optstm the output stream reference +/// @param reference the child node or leaf to format +/// @param width the requested minimum field width +/// @param fillch the padding character +/// @param placement_code the normalized width placement code +/// @param len the precise child length template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3623,18 +4166,22 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & { if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) { + // Buffered streams first try to build the entire width-formatted field in the existing put area. ::std::size_t required{(width <= len || placement_code == 0u) ? len : width}; if constexpr (line) { + // The line variant needs one additional output slot for the trailing newline. required = ::fast_io::details::intrinsics::add_or_overflow_die(required, static_cast<::std::size_t>(1u)); } char_type *const curr{obuffer_curr(optstm)}; char_type *const end{obuffer_end(optstm)}; if (static_cast<::std::size_t>(end - curr) >= required) { + // The stream put area can hold the complete field, so emit directly and commit once. char_type *iter{curr}; if (width <= len || placement_code == 0u) { + // Direct-buffer width is already satisfied, so only the child output is emitted. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } else @@ -3642,11 +4189,13 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & ::std::size_t const padding{width - len}; if (placement_code == 1u) { + // Direct-buffer left placement appends all padding after the child output. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); iter = ::fast_io::details::my_fill_n(iter, padding, fillch); } else if (placement_code == 2u) { + // Direct-buffer center placement emits left padding, child output, then right padding. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); @@ -3655,25 +4204,30 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else if (placement_code == 4u) { + // Direct-buffer internal placement inserts padding after the child's sign or prefix. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; if (internal_len == 0) { + // Without an internal shift point, internal placement falls back to right placement. iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } else { + // A valid internal shift point lets the already-emitted suffix move right to make room. char_type *const first{iter}; char_type *const last{ ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference)}; if (len < internal_len) { + // An invalid internal span preserves the child output and avoids padding insertion. iter = last; } else { + // The suffix is shifted right and the internal padding is filled in place. char_type *const shift_pos{first + internal_len}; ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); ::fast_io::details::my_fill_n(shift_pos, padding, fillch); @@ -3683,12 +4237,14 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Direct-buffer right placement emits all padding before the child output. iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } } if constexpr (line) { + // The line variant terminates the completed field with a newline. *iter++ = ::fast_io::char_literal_v; } obuffer_set_curr(optstm, iter); @@ -3698,6 +4254,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & ::std::size_t required{(width <= len || placement_code == 0u) ? len : width}; if constexpr (line) { + // The fallback sizing path also accounts for the optional trailing newline. required = ::fast_io::details::intrinsics::add_or_overflow_die(required, static_cast<::std::size_t>(1u)); } constexpr ::std::size_t small_width_buffer_size{ @@ -3708,10 +4265,12 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & { if (required <= small_width_buffer_size) { + // Small width-formatted fields use a bounded stack buffer and one stream write. char_type buffer[small_width_buffer_size]; char_type *iter{buffer}; if (width <= len || placement_code == 0u) { + // Stack-buffer width is already satisfied, so only the child output is emitted. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } else @@ -3719,11 +4278,13 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & ::std::size_t const padding{width - len}; if (placement_code == 1u) { + // Stack-buffer left placement appends all padding after the child output. iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); iter = ::fast_io::details::my_fill_n(iter, padding, fillch); } else if (placement_code == 2u) { + // Stack-buffer center placement emits left padding, child output, then right padding. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); @@ -3732,25 +4293,30 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else if (placement_code == 4u) { + // Stack-buffer internal placement inserts padding after the child's sign or prefix. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; if (internal_len == 0) { + // Without an internal shift point, internal placement falls back to right placement. iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } else { + // A valid internal shift point lets the already-emitted suffix move right to make room. char_type *const first{iter}; char_type *const last{ ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference)}; if (len < internal_len) { + // An invalid internal span preserves the child output and avoids padding insertion. iter = last; } else { + // The suffix is shifted right and the internal padding is filled in place. char_type *const shift_pos{first + internal_len}; ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); ::fast_io::details::my_fill_n(shift_pos, padding, fillch); @@ -3760,12 +4326,14 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Stack-buffer right placement emits all padding before the child output. iter = ::fast_io::details::my_fill_n(iter, padding, fillch); iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, reference); } } if constexpr (line) { + // The line variant terminates the stack-buffered field with a newline. *iter++ = ::fast_io::char_literal_v; } ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); @@ -3774,6 +4342,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } if (width <= len || placement_code == 0u) { + // Streaming fallback width is already satisfied, so emit the child directly to the stream. ::fast_io::operations::decay::print_semantic_emit(optstm, reference); } else @@ -3781,11 +4350,13 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & ::std::size_t const padding{width - len}; if (placement_code == 1u) { + // Streaming fallback left placement writes the child first and then the trailing padding. ::fast_io::operations::decay::print_semantic_emit(optstm, reference); ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); } else if (placement_code == 2u) { + // Streaming fallback center placement writes leading padding, child output, and trailing padding. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; ::fast_io::operations::decay::print_semantic_write_fill(optstm, left_padding, fillch); @@ -3794,15 +4365,18 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else if (placement_code == 4u) { + // Streaming fallback internal placement may need a temporary child buffer for byte insertion. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(reference)}; if (internal_len == 0) { + // Without an internal shift point, internal placement falls back to right placement. ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::print_semantic_emit(optstm, reference); } else { + // The child is buffered so padding can be inserted between its internal prefix and suffix. ::fast_io::basic_dynamic_output_buffer buffer; auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; ::fast_io::operations::decay::print_semantic_emit(buffer_ref, reference); @@ -3810,10 +4384,12 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & char_type const *const last{buffer.curr_ptr}; if (len < internal_len) { + // An invalid internal span preserves the buffered child output without inserting padding. ::fast_io::operations::decay::write_all_decay(optstm, first, last); } else { + // The stream receives the internal prefix, padding, and remaining child suffix in order. ::fast_io::operations::decay::write_all_decay(optstm, first, first + internal_len); ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::write_all_decay(optstm, first + internal_len, last); @@ -3822,16 +4398,22 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Streaming fallback right placement writes leading padding before the child output. ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::print_semantic_emit(optstm, reference); } } if constexpr (line) { + // The line variant appends the newline after the streaming fallback field. ::fast_io::operations::decay::char_put_decay(optstm, ::fast_io::char_literal_v); } } +/// @brief Emits every element of a semantic pack through the checked stream emitter. +/// @tparam line true when the last emitted element should carry the caller's line policy +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type template struct print_semantic_emit_node_pack_continuation { @@ -3845,6 +4427,15 @@ struct print_semantic_emit_node_pack_continuation } }; +/// @brief Emits a semantic width node to an output stream. +/// @details Precise-size children use the direct width emitter; non-precise children are first materialized into a +/// dynamic buffer so width placement can be applied accurately. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the width semantic node type +/// @param optstm the output stream reference +/// @param t the width node to emit template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3867,6 +4458,7 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) if constexpr (::fast_io::details::decay::print_semantic_static_precise_size::available) { + // Static precise children can pass a compile-time length directly into the width emitter. constexpr ::std::size_t len{ ::fast_io::details::decay::print_semantic_static_precise_size::size}; ::fast_io::operations::decay::print_semantic_emit_width_direct( @@ -3875,12 +4467,14 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) } else if constexpr (::fast_io::details::decay::print_semantic_precise_size_ok::value) { + // Run-time precise children are measured once and then emitted through the direct width path. ::std::size_t const len{ ::fast_io::operations::decay::print_semantic_precise_size_arg(width_ref.reference)}; ::fast_io::operations::decay::print_semantic_emit_width_direct( optstm, width_ref.reference, width, fillch, placement_code, len); return; } + // Non-precise children are buffered so the final child length is known before padding is written. ::fast_io::basic_dynamic_output_buffer buffer; auto buffer_ref{::fast_io::operations::output_stream_ref(buffer)}; ::fast_io::operations::decay::print_semantic_emit(buffer_ref, width_ref.reference); @@ -3889,6 +4483,7 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) ::std::size_t const len{static_cast<::std::size_t>(last - first)}; if (width <= len || placement_code == 0u) { + // Buffered fallback width is already satisfied, so the child bytes are forwarded unchanged. ::fast_io::operations::decay::write_all_decay(optstm, first, last); } else @@ -3896,11 +4491,13 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) ::std::size_t const padding{width - len}; if (placement_code == 1u) { + // Buffered fallback left placement writes the child bytes followed by trailing padding. ::fast_io::operations::decay::write_all_decay(optstm, first, last); ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); } else if (placement_code == 2u) { + // Buffered fallback center placement writes leading padding, child bytes, and trailing padding. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; ::fast_io::operations::decay::print_semantic_write_fill(optstm, left_padding, fillch); @@ -3909,19 +4506,23 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) } else if (placement_code == 4u) { + // Buffered fallback internal placement uses the child's reported prefix length for insertion. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(width_ref.reference)}; if (internal_len == 0) { + // Without an internal shift point, internal placement falls back to right placement. ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::write_all_decay(optstm, first, last); } else if (len < internal_len) { + // An invalid internal span preserves the buffered child output without inserting padding. ::fast_io::operations::decay::write_all_decay(optstm, first, last); } else { + // The stream receives the buffered internal prefix, padding, and suffix in order. ::fast_io::operations::decay::write_all_decay(optstm, first, first + internal_len); ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::write_all_decay(optstm, first + internal_len, last); @@ -3929,16 +4530,27 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) } else { + // Buffered fallback right placement writes leading padding before the child bytes. ::fast_io::operations::decay::print_semantic_write_fill(optstm, padding, fillch); ::fast_io::operations::decay::write_all_decay(optstm, first, last); } } if constexpr (line) { + // The line variant appends a newline after the buffered fallback field. ::fast_io::operations::decay::char_put_decay(optstm, ::fast_io::char_literal_v); } } +/// @brief Emits one semantic node to an output stream. +/// @details The checked node dispatcher handles packs, conditions, and width nodes before returning control to the +/// flattened semantic emitter. +/// @tparam line true when this node is responsible for the caller's trailing newline +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the semantic node type +/// @param optstm the output stream reference +/// @param t the semantic node to emit template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3951,6 +4563,7 @@ inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) using node_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::print_pack) { + // Pack nodes are expanded and each element is emitted through the checked stream path. ::fast_io::details::decay::print_semantic_pack_apply( ::std::forward(node_ref), ::fast_io::operations::decay::print_semantic_emit_node_pack_continuation{ @@ -3960,20 +4573,28 @@ inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) { if (node_ref.pred) { + // Condition nodes emit the true alternative selected by the predicate. ::fast_io::operations::decay::print_semantic_emit(optstm, node_ref.t1); } else { + // Condition nodes emit the false alternative selected by the predicate. ::fast_io::operations::decay::print_semantic_emit(optstm, node_ref.t2); } } else if constexpr (::fast_io::details::decay::print_semantic_width_v) { + // Width nodes delegate to the width-specific emitter so padding placement remains centralized. ::fast_io::operations::decay::print_semantic_emit_width(optstm, ::std::forward(t)); } } +/// @brief Invokes the accumulated flattened semantic continuation with the final line policy. +/// @tparam line true when the flattened run appends a trailing newline +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam continuation the accumulated prefix continuation type template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -3988,6 +4609,9 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype, continuation cont.template operator()(); } +/// @brief Accumulates non-node arguments while flattening a semantic print run. +/// @tparam continuation the downstream flattened continuation type +/// @tparam T the argument type captured as part of the prefix template struct print_semantic_emit_flat_prefix_continuation { @@ -4007,6 +4631,15 @@ struct print_semantic_emit_flat_prefix_continuation } }; +/// @brief Flattens a semantic print run until the next semantic node is reached. +/// @details Plain arguments are accumulated into the prefix continuation; once a node is found, the prefix is emitted, +/// the node is handled, and flattening resumes for the remaining arguments. +/// @tparam line true when the final flattened run appends a trailing newline +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam continuation the accumulated prefix continuation type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types template #if __has_cpp_attribute(__gnu__::__always_inline__) @@ -4021,6 +4654,7 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, contin { if constexpr (::fast_io::details::decay::print_semantic_node) { + // A semantic node terminates the current flat prefix before node-specific output is emitted. cont.template operator()(); ::fast_io::operations::decay::print_semantic_emit_node(optstm, ::std::forward(t)); ::fast_io::operations::decay::print_semantic_emit(optstm, @@ -4028,6 +4662,7 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, contin } else { + // A plain argument is captured into the prefix so later coalescing can treat it as one flat run. ::fast_io::operations::decay::print_semantic_emit_flat_impl( optstm, ::fast_io::operations::decay::print_semantic_emit_flat_prefix_continuation{ @@ -4036,6 +4671,8 @@ inline constexpr void print_semantic_emit_flat_impl(outputstmtype optstm, contin } } +/// @brief Emits the flattened prefix through the ordinary freestanding printing machinery. +/// @tparam outputstmtype the decayed output stream reference type template struct print_semantic_emit_freestanding_continuation { @@ -4053,17 +4690,24 @@ struct print_semantic_emit_freestanding_continuation if constexpr (::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry_available< char_type, ::std::remove_cvref_t...>()) { + // The fast dynamic reserve-scatters entry handles the prefix when every argument supports it. ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry( optstm, ::std::forward(prefix)...); } else { + // The generic no-pack path emits prefixes that do not match the reserve-scatters fast entry. ::fast_io::operations::decay::print_freestanding_decay_no_pack( optstm, ::std::forward(prefix)...); } } }; +/// @brief Entry continuation for flattened semantic emission. +/// @details It first tries precise coalescing for the whole filtered run, then falls back to semantic flattening. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type template struct print_semantic_emit_flat_continuation { @@ -4080,6 +4724,7 @@ struct print_semantic_emit_flat_continuation if (!::fast_io::operations::decay::print_semantic_try_precise_coalesce( optstm, ::std::forward(filtered_args)...)) { + // Failed precise coalescing keeps semantics by flattening nodes around ordinary freestanding output. ::fast_io::operations::decay::print_semantic_emit_flat_impl( optstm, ::fast_io::operations::decay::print_semantic_emit_freestanding_continuation{optstm}, @@ -4088,6 +4733,8 @@ struct print_semantic_emit_flat_continuation } }; +/// @brief Filters null semantic outputs before the flattened emitter is invoked. +/// @tparam emit_flat_type the flattened emitter continuation type template struct print_semantic_filter_flat_continuation { @@ -4106,12 +4753,17 @@ struct print_semantic_filter_flat_continuation } }; +/// @brief Emits a semantic-aware freestanding print run. +/// @details The entry expands semantic packs and nulls when needed, then routes the resulting flat run through +/// coalescing and semantic node emission. +/// @tparam line true when a trailing newline is appended +/// @tparam already_forwarded true when inputs have already passed semantic input forwarding +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the print run +/// @param optstm the output stream reference +/// @param args the arguments to emit template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -4122,10 +4774,12 @@ inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args) ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) || ...)) { + // A run without semantic pack arguments or nulls can enter the flattened emitter directly. emit_flat(::std::forward(args)...); } else { + // Pack arguments and nulls are expanded or removed before the flattened emitter sees the run. ::fast_io::details::decay::print_semantic_pack_expand( ::fast_io::operations::decay::print_semantic_filter_flat_continuation{ __builtin_addressof(emit_flat)}, @@ -4133,12 +4787,16 @@ inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args) } } +/// @brief Freestanding print dispatcher for already-decayed stream references. +/// @details This function selects status printing, empty-line handling, mutex unlocking, semantic emission, or the +/// regular non-semantic freestanding paths. +/// @tparam line true when a trailing newline is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the print run +/// @param optstm the output stream reference +/// @param args the arguments to emit +/// @return decltype(auto) the return value of the selected output customization, when any template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -4146,22 +4804,26 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A { if constexpr (::fast_io::operations::decay::defines::has_status_print_define) { + // Status-print streams delegate the whole freestanding run to their status customization. return status_print_define(optstm, args...); } else if constexpr (sizeof...(Args) == 0) { if constexpr (line) { + // An empty line-print run emits exactly one newline character. using char_type = typename outputstmtype::output_char_type; return ::fast_io::operations::decay::char_put_decay(optstm, char_literal_v); } else { + // An empty non-line print run emits nothing. return; } } else if constexpr (::fast_io::operations::decay::defines::has_output_or_io_stream_mutex_ref_define) { + // Mutex-wrapped streams lock once and then print through the unlocked stream reference. ::fast_io::operations::decay::stream_ref_decay_lock_guard lg{ ::fast_io::operations::decay::output_stream_mutex_ref_decay(optstm)}; return ::fast_io::operations::decay::print_freestanding_decay( @@ -4169,6 +4831,7 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A } else if constexpr ((::fast_io::details::decay::print_semantic_node || ...)) { + // Semantic nodes require the semantic-aware emitter before falling back to ordinary leaf output. using char_type = typename outputstmtype::output_char_type; return ::fast_io::operations::decay::print_semantic_emit(optstm, args...); } @@ -4178,16 +4841,27 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A if constexpr (::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry_available< char_type, ::std::remove_cvref_t...>()) { + // Non-semantic arguments use the dynamic reserve-scatters fast entry when it is available. return ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry(optstm, args...); } else { + // Non-semantic arguments without the fast entry use the general no-pack freestanding path. return ::fast_io::operations::decay::print_freestanding_decay_no_pack(optstm, args...); } } } +/// @brief Cold wrapper around the freestanding print dispatcher. +/// @details This keeps unlikely front-end call sites out of the hot dispatcher body on compilers that support cold +/// placement or branch hints. +/// @tparam line true when a trailing newline is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the print run +/// @param optstm the output stream reference +/// @param args the arguments to emit +/// @return decltype(auto) the return value of the selected output customization, when any template #if __has_cpp_attribute(__gnu__::__cold__) [[__gnu__::__cold__]] @@ -4231,12 +4905,15 @@ concept print_freestanding_okay = ::fast_io::operations::decay::defines::print_f } // namespace defines +/// @brief Public freestanding print entry point. +/// @details The wrapper obtains the stream reference, forwards semantic input arguments when present, and then +/// delegates to the decayed freestanding dispatcher. +/// @tparam line true when a trailing newline is appended +/// @tparam output the output stream object type +/// @tparam Args the argument types in the print run +/// @param outstm the output stream object +/// @param args the arguments to emit template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr void print_freestanding(output &&outstm, Args &&...args) { auto outref{::fast_io::operations::output_stream_ref(outstm)}; @@ -4244,6 +4921,7 @@ inline constexpr void print_freestanding(output &&outstm, Args &&...args) if constexpr ((false || ... || ::fast_io::details::decay::print_semantic_input_argument_v)) { + // Semantic input arguments are forwarded into their semantic representation before decayed emission. ::fast_io::operations::decay::print_freestanding_decay( outref, ::fast_io::details::decay::print_semantic_input_forward( @@ -4251,6 +4929,7 @@ inline constexpr void print_freestanding(output &&outstm, Args &&...args) } else { + // Ordinary arguments use the regular alias and print-forward path before decayed emission. ::fast_io::operations::decay::print_freestanding_decay( outref, io_print_forward(io_print_alias(args))...); } From 031106f8a3b67387e29e26cd7d6ea680ea7cb91a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 14:21:22 +0800 Subject: [PATCH 18/45] Enhance force-inlining rationale in print operations - Added detailed comments explaining the rationale for force-inlining various functions in `print_freestanding_cxx20.h`, improving code clarity and maintainability. - The updates focus on optimizing performance by ensuring that critical functions are inlined, reducing call overhead and enhancing execution efficiency during print operations. --- .../printimpl/print_freestanding_cxx20.h | 247 ++---------------- 1 file changed, 22 insertions(+), 225 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 7bfff8d75..5b4430f06 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -1744,6 +1744,8 @@ inline constexpr char_type* printrsvcontiguousimpl(char_type* iter,Arg arg,Args. #endif template +// Force-inline rationale: this is the small top-level control recursion for a known line flag; inlining lets the +// newline case and the final single-control tail fold into the selected output path instead of leaving a call chain. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -1775,6 +1777,8 @@ inline constexpr void print_controls_line(output outstm, T t, Args... args) } template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> +// Force-inline rationale: reserve-only emission is a pre-sized cursor advance; inlining exposes the fixed recursion +// depth so null arguments disappear and the cursor updates become straight-line stores. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -1815,6 +1819,8 @@ inline constexpr char_type *print_n_reserve(char_type *ptr, T t, Args... args) } template <::std::size_t n, ::std::integral char_type, typename scattertype, typename T, typename... Args> +// Force-inline rationale: scatter descriptor construction is pure template dispatch; inlining keeps the descriptor +// count, byte/character scatter conversion, and null elision visible to the final scatter write. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -1871,6 +1877,8 @@ inline constexpr void print_n_scatters(basic_io_scatter_t *pscatter } template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> +// Force-inline rationale: dynamic reserve sizing feeds a single allocation decision; inlining folds non-dynamic +// arguments to zero and keeps overflow-checked additions adjacent to the caller's allocation branch. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -1910,6 +1918,8 @@ inline constexpr ::std::size_t ndynamic_print_reserve_size(T t, Args... args) } template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> +// Force-inline rationale: static stack-budget aggregation decides whether the dynamic reserve path can stay on the +// stack; inlining lets the all-static/heap-fallback branch collapse before code generation. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -1973,6 +1983,8 @@ inline constexpr bool ndynamic_print_reserve_has_static_stack_size() template +// Force-inline rationale: the forward declaration carries the same attribute as the definitions so compilers that +// bind force-inline on the first declaration do not outline the reserve/scatter cursor state machine. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2004,6 +2016,8 @@ inline constexpr bool print_next_is_reserve() noexcept template +// Force-inline rationale: this continuation coalesces adjacent reserve fragments into one scatter entry; inlining +// keeps the base pointer, current pointer, and next-argument classification in one foldable cursor pipeline. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2071,6 +2085,8 @@ inline constexpr auto print_n_scatters_reserve_cont(basic_io_scatter_t +// Force-inline rationale: the mixed reserve/scatter materializer is the boundary where compile-time argument classes +// become descriptor writes; inlining avoids outlining the recursive state that determines the final scatter count. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2277,6 +2293,8 @@ inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, template +// Force-inline rationale: the dynamic reserve writer has a hot stack-buffer fast path; inlining lets the caller's +// known stack budget and total-size branch select stack storage or heap storage without an extra outlined frame. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2308,6 +2326,8 @@ inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outpu template +// Force-inline rationale: this wrapper chooses stack versus heap scatter storage from template constants; inlining +// keeps that storage decision adjacent to the dynamic reserve writer and prevents a redundant allocator-shaped call. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2353,6 +2373,8 @@ inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_availab } template +// Force-inline rationale: the fast entry is reached only after compile-time contiguous-scatter analysis succeeds; +// inlining preserves those constants so the selected reserve/scatter path is emitted directly at the semantic boundary. #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -2791,11 +2813,6 @@ inline constexpr bool print_semantic_input_argument_v = ::fast_io::details::decay::print_semantic_input_forward_t>; template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr decltype(auto) print_semantic_input_forward(T &&t) noexcept { if constexpr (::fast_io::details::decay::print_semantic_node_no_parameter_v< @@ -2811,11 +2828,6 @@ inline constexpr decltype(auto) print_semantic_input_forward(T &&t) noexcept } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuation &&cont, ::std::index_sequence) { @@ -2824,11 +2836,6 @@ inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuati } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation &&cont) { using pack_type = @@ -2844,11 +2851,6 @@ struct print_semantic_value_prefix_continuation ::std::remove_reference_t *valueptr; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { return (*contptr)(::std::forward(*valueptr), ::std::forward(tail_args)...); @@ -2862,11 +2864,6 @@ struct print_semantic_lvalue_prefix_continuation T *valueptr; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { return (*contptr)(*valueptr, ::std::forward(tail_args)...); @@ -2874,11 +2871,6 @@ struct print_semantic_lvalue_prefix_continuation }; template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont) { return cont(); @@ -2894,11 +2886,6 @@ struct print_semantic_pack_expand_tail_continuation ::fast_io::containers::tuple<::std::remove_reference_t *...> expandedptrs; template <::std::size_t... I, typename... TailArgs> -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator_impl(::std::index_sequence, TailArgs &&...tail_args) const { return (*contptr)(::std::forward(*::fast_io::containers::get(expandedptrs))..., @@ -2906,11 +2893,6 @@ struct print_semantic_pack_expand_tail_continuation } template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { return operator_impl(::std::make_index_sequence{}, @@ -2925,11 +2907,6 @@ struct print_semantic_pack_expand_middle_continuation ::fast_io::containers::tuple<::std::remove_reference_t *...> const *argptrs; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator_impl(tail_continuation &&tail_cont, ::std::index_sequence) const { return ::fast_io::details::decay::print_semantic_pack_expand( @@ -2938,11 +2915,6 @@ struct print_semantic_pack_expand_middle_continuation } template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator()(ExpandedPackArgs &&...expanded_pack_args) const { return operator_impl( @@ -2960,11 +2932,6 @@ struct print_semantic_pack_expand_initial_continuation ::fast_io::containers::tuple<::std::remove_reference_t *...> argptrs; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr decltype(auto) operator()(PackArgs &&...pack_args) const { return ::fast_io::details::decay::print_semantic_pack_expand( @@ -2975,11 +2942,6 @@ struct print_semantic_pack_expand_initial_continuation }; template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args) { if constexpr (::fast_io::details::print_pack || @@ -3011,11 +2973,6 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -3032,11 +2989,6 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont) } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -3115,11 +3067,6 @@ template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr void print_semantic_write_fill(outputstmtype optstm, ::std::size_t n, char_type ch) { if (n == 0) @@ -3140,11 +3087,6 @@ template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t); template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) { using value_type = ::std::remove_cvref_t; @@ -3204,22 +3146,12 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_width_placement_code(placement_type placement) noexcept { return static_cast<::std::size_t>(placement); } template <::std::integral char_type, typename width_traits, typename width_reference_type> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type print_semantic_width_fill_char(width_reference_type &&wr) noexcept { if constexpr (width_traits::has_fill_char) @@ -3233,11 +3165,6 @@ inline constexpr char_type print_semantic_width_fill_char(width_reference_type & } template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr auto print_semantic_width_placement(width_reference_type &&wr) noexcept { if constexpr (width_traits::runtime_placement) @@ -3254,11 +3181,6 @@ template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t); template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_internal_shift(T &&t) { auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -3292,11 +3214,6 @@ inline constexpr ::std::size_t print_semantic_internal_shift(T &&t) } template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t) { decltype(auto) forwarded{ @@ -3322,11 +3239,6 @@ struct print_semantic_precise_size_pack_continuation }; template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_precise_size(T &&t) { auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -3375,11 +3287,6 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) } template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t) { decltype(auto) forwarded{ @@ -3400,11 +3307,6 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t /// @param t the argument to print /// @return char_type* a pointer one past the emitted argument template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked_arg(char_type *iter, T &&t) { decltype(auto) forwarded{ @@ -3422,11 +3324,6 @@ inline constexpr ::std::size_t print_semantic_common_factor_pack_max_size{8u}; /// @param the first null node /// @param the second null node /// @return bool true because both nodes emit no output -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr bool print_semantic_common_factor_equal(::fast_io::io_null_t, ::fast_io::io_null_t) noexcept { return true; @@ -3447,11 +3344,6 @@ template first.base; second.base; }) -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr bool print_semantic_common_factor_equal(T const &first, U const &second) noexcept { if constexpr (requires { @@ -3485,11 +3377,6 @@ template second.reference; requires ::std::integral<::std::remove_cvref_t>; }) -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr bool print_semantic_common_factor_equal(T const &first, U const &second) noexcept { return first.reference == second.reference; @@ -3516,11 +3403,6 @@ concept print_semantic_common_factor_comparable = requires(T &&t, U &&u) { /// @param pack the pack whose indexed element is printed /// @return char_type* a pointer one past the emitted element template <::std::integral char_type, ::std::size_t index, typename Pack> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked_pack_element(char_type *iter, Pack &&pack) { return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( @@ -3535,11 +3417,6 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_element(char_type /// @param pack2 the pack from the false branch /// @return bool true when the indexed elements provably emit identical output template <::std::size_t index, typename Pack1, typename Pack2> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr bool print_semantic_pack_element_common_factor_equal(Pack1 &&pack1, Pack2 &&pack2) { decltype(auto) first{::fast_io::containers::get(::std::forward(pack1).storage)}; @@ -3567,11 +3444,6 @@ inline constexpr bool print_semantic_pack_element_common_factor_equal(Pack1 &&pa /// @param pack the pack providing the emitted elements /// @return char_type* a pointer one past the emitted range template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type *iter, Pack &&pack) { if constexpr (first < last) @@ -3603,11 +3475,6 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type * /// @param pack2 the pack emitted when pred is false /// @return char_type* a pointer one past the emitted condition-pack range template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factored(char_type *iter, bool pred, Pack1 &&pack1, Pack2 &&pack2) { @@ -3677,11 +3544,6 @@ struct print_semantic_condition_pack_factor_result /// @param pack2 the pack emitted when pred is false /// @return print_semantic_condition_pack_factor_result the updated cursor and success flag template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr print_semantic_condition_pack_factor_result print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pred, Pack1 &&pack1, Pack2 &&pack2) { @@ -3835,11 +3697,6 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, /// @param t the node to emit /// @return char_type* a pointer one past the emitted node template <::std::integral char_type, typename T> -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t) { auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -4010,11 +3867,6 @@ inline consteval ::std::size_t print_semantic_static_precise_total_size() noexce /// @param args the arguments whose semantic sizes are measured /// @return ::std::size_t the total number of characters needed by the run template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr ::std::size_t print_semantic_precise_total_size(Args &&...args) { ::std::size_t total{}; @@ -4038,11 +3890,6 @@ inline constexpr ::std::size_t print_semantic_precise_total_size(Args &&...args) /// @param args the arguments to emit in order /// @return char_type* a pointer one past the emitted run template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr char_type *print_semantic_emit_unchecked_run(char_type *iter, Args &&...args) { ((iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg( @@ -4068,11 +3915,6 @@ inline constexpr char_type *print_semantic_emit_unchecked_run(char_type *iter, A /// @param args the arguments to emit /// @return bool true when the whole run was emitted by this coalescing path template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, Args &&...args) { if constexpr ((::fast_io::details::decay::print_semantic_precise_size_ok< @@ -4155,11 +3997,6 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, /// @param placement_code the normalized width placement code /// @param len the precise child length template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T &&reference, ::std::size_t width, char_type fillch, ::std::size_t placement_code, ::std::size_t len) @@ -4437,11 +4274,6 @@ struct print_semantic_emit_node_pack_continuation /// @param optstm the output stream reference /// @param t the width node to emit template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) { auto &&width_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -4552,11 +4384,6 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) /// @param optstm the output stream reference /// @param t the semantic node to emit template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) { auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; @@ -4596,11 +4423,6 @@ inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) /// @tparam outputstmtype the decayed output stream reference type /// @tparam continuation the accumulated prefix continuation type template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -4619,11 +4441,6 @@ struct print_semantic_emit_flat_prefix_continuation ::std::remove_reference_t *valueptr; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr void operator()(Prefix &&...prefix) const { contptr->template operator()(::std::forward(*valueptr), @@ -4642,11 +4459,6 @@ struct print_semantic_emit_flat_prefix_continuation /// @tparam Args the remaining argument types template -#if __has_cpp_attribute(__gnu__::__always_inline__) -[[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) -[[msvc::forceinline]] -#endif #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] #endif @@ -4679,11 +4491,6 @@ struct print_semantic_emit_freestanding_continuation outputstmtype optstm; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr void operator()(Prefix &&...prefix) const { using char_type = typename outputstmtype::output_char_type; @@ -4714,11 +4521,6 @@ struct print_semantic_emit_flat_continuation outputstmtype optstm; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr void operator()(FilteredArgs &&...filtered_args) const { if (!::fast_io::operations::decay::print_semantic_try_precise_coalesce( @@ -4741,11 +4543,6 @@ struct print_semantic_filter_flat_continuation ::std::remove_reference_t *emitflatptr; template -#if __has_cpp_attribute(__gnu__::__always_inline__) - [[__gnu__::__always_inline__]] -#elif __has_cpp_attribute(msvc::forceinline) - [[msvc::forceinline]] -#endif inline constexpr void operator()(FlatArgs &&...flat_args) const { ::fast_io::details::decay::print_semantic_filter_nulls( From d4472e96c14c649666b465229f33b20bfaee650a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 14:43:07 +0800 Subject: [PATCH 19/45] Add detailed comments to enhance clarity in print control logic - Improved documentation within `print_freestanding_cxx20.h` by adding comments that clarify the decision-making process for buffer management during print operations. - The updates focus on explaining the conditions under which different output strategies are employed, such as direct output, stack materialization, and dynamic storage, enhancing code readability and maintainability. --- .../printimpl/print_freestanding_cxx20.h | 753 +++++++++++++++++- 1 file changed, 750 insertions(+), 3 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 5b4430f06..f42272e75 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -1161,6 +1161,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a minimum-buffer guarantee, choose between direct put-area output and fallback storage. if (smaller) [[likely]] { // The current put area has enough room, so emit directly and commit the new cursor. @@ -1175,6 +1176,7 @@ inline constexpr void print_control_single(output outstm, T t) } else [[unlikely]] { + // The current put area is too small, so materialize the output outside the stream buffer. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { // A small static reserve output is materialized on the stack and written once. @@ -1208,6 +1210,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a usable output buffer, choose stack or heap materialization for the static reserve output. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { // Without a usable output buffer, small static reserve output is materialized on the stack. @@ -1308,6 +1311,7 @@ inline constexpr void print_control_single(output outstm, T t) [[unlikely]] #endif { + // The current put area is too small, so dynamic output falls back to temporary storage. if constexpr (stack_buffer_size != 0) { // A static stack hint allows small dynamic output to avoid heap allocation. @@ -1358,6 +1362,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Without a usable output buffer, dynamic output chooses between static stack hint and heap storage. if constexpr (stack_buffer_size != 0) { // Without a usable output buffer, a static stack hint may still avoid heap allocation. @@ -1434,6 +1439,7 @@ inline constexpr void print_control_single(output outstm, T t) [[unlikely]] #endif { + // An exhausted put area must be refreshed or replaced by an explicit context window. if constexpr (minimum_buffer_output_stream_require_size_impl) { // Streams with a sufficient minimum buffer can prepare a fresh window for context output. @@ -1452,6 +1458,7 @@ inline constexpr void print_control_single(output outstm, T t) [[unlikely]] #endif { + // Even after flushing, the put area is too small for one context producer window. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit< reserved_size, char_type>) { @@ -1484,6 +1491,7 @@ inline constexpr void print_control_single(output outstm, T t) [[likely]] #endif { + // The context producer finished this argument, so only optional line termination remains. if constexpr (line) { // The line variant writes the trailing newline after the final context window. @@ -1496,6 +1504,7 @@ inline constexpr void print_control_single(output outstm, T t) } else { + // Unbuffered context output uses an explicit context window sized by the static context requirement. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { // Unbuffered streams use a stack context buffer when the fixed window fits the policy. @@ -1536,6 +1545,9 @@ inline constexpr void print_control_single(output outstm, T t) } } +/// @brief Describes a contiguous run that can share a context-capture buffer. +/// @details The scan tracks context producers, dynamic reserve producers with bounded stack hints, and static +/// reserve bursts so the caller can size one reusable capture buffer for the run. struct context_capture_run_result { ::std::size_t position{}; @@ -1547,15 +1559,24 @@ struct context_capture_run_result bool has_dynamic{}; }; +/// @brief Finds the leading run that benefits from context-capture buffering. +/// @details Static reserve outputs are grouped into burst sizes, while context and bounded dynamic reserve outputs +/// contribute reusable window sizes. Null outputs keep position accounting without increasing storage. +/// @tparam char_type the character type used by the print run +/// @tparam Arg the current argument type +/// @tparam Args the remaining argument types +/// @return context_capture_run_result the capture-buffer requirements for the leading run template <::std::integral char_type, typename Arg, typename... Args> inline constexpr context_capture_run_result find_context_capture_run_n() { using nocvreft = ::std::remove_cvref_t; if constexpr (::fast_io::reserve_printable) { + // Static reserve output can be accumulated into the leading contiguous reserve burst. context_capture_run_result ret{}; if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first so this reserve output extends the front of the run. ret = ::fast_io::details::decay::find_context_capture_run_n(); } constexpr ::std::size_t sz{print_reserve_size(::fast_io::io_reserve_type)}; @@ -1565,15 +1586,18 @@ inline constexpr context_capture_run_result find_context_capture_run_n() ::fast_io::details::intrinsics::add_or_overflow_die_chain(ret.leading_static_reserve_burst_size, sz); if (ret.max_static_reserve_burst_size < ret.leading_static_reserve_burst_size) { + // The current static reserve burst is the largest contiguous reserve output seen so far. ret.max_static_reserve_burst_size = ret.leading_static_reserve_burst_size; } return ret; } else if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // Bounded dynamic reserve output participates through its reusable maximum stack window. context_capture_run_result ret{}; if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first before this dynamic producer resets the static burst. ret = ::fast_io::details::decay::find_context_capture_run_n(); } constexpr ::std::size_t static_stack_size{ @@ -1585,15 +1609,18 @@ inline constexpr context_capture_run_result find_context_capture_run_n() ret.has_dynamic = true; if (ret.dynamic_buffer_size < dynamic_buffer_size) { + // The capture buffer must fit the largest bounded dynamic reserve window in the run. ret.dynamic_buffer_size = dynamic_buffer_size; } return ret; } else if constexpr (::fast_io::context_printable_with_static_buffer_size) { + // Context-printable output anchors the capture run and requires its declared context window. context_capture_run_result ret{}; if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first before this context producer resets the static burst. ret = ::fast_io::details::decay::find_context_capture_run_n(); } constexpr ::std::size_t context_buffer_size{ @@ -1603,15 +1630,18 @@ inline constexpr context_capture_run_result find_context_capture_run_n() ret.has_context = true; if (ret.context_buffer_size < context_buffer_size) { + // The capture buffer must fit the largest static context window in the run. ret.context_buffer_size = context_buffer_size; } return ret; } else if constexpr (::std::same_as) { + // Null output keeps the argument position in the run while requiring no capture storage. context_capture_run_result ret{}; if constexpr (sizeof...(Args) != 0) { + // Remaining arguments are scanned first so the null can extend the positional prefix. ret = ::fast_io::details::decay::find_context_capture_run_n(); } ++ret.position; @@ -1619,20 +1649,44 @@ inline constexpr context_capture_run_result find_context_capture_run_n() } else { + // Any other output protocol terminates the context-capture prefix. return {}; } } +/// @brief Writes pending context-capture buffer contents and resets the cursor. +/// @tparam output the decayed output stream reference type +/// @tparam char_type the character type stored in the capture buffer +/// @param outstm the output stream reference +/// @param buffer the first character in the capture buffer +/// @param curr the current output cursor, reset to buffer after flushing template inline constexpr void context_capture_flush(output outstm, char_type *buffer, char_type *&curr) { if (curr != buffer) { + // Pending captured output is emitted as one contiguous range before the buffer is reused. ::fast_io::operations::decay::write_all_decay(outstm, buffer, curr); curr = buffer; } } +/// @brief Emits a captured context/static-reserve run through a reusable buffer. +/// @details Static reserve and bounded dynamic reserve values are appended to the capture buffer when they fit; +/// larger values fall back to their ordinary single-control output path. Context producers stream windows +/// through the same buffer and the final recursive step appends the optional newline. +/// @tparam needprintlf true when the final captured output should append a newline +/// @tparam n the number of arguments remaining in the captured run +/// @tparam output the decayed output stream reference type +/// @tparam char_type the character type stored in the capture buffer +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param outstm the output stream reference +/// @param buffer the first character in the capture buffer +/// @param curr the current output cursor +/// @param end one past the capture buffer +/// @param t the current argument to emit +/// @param args the remaining arguments to emit template inline constexpr void print_context_capture_run(output outstm, char_type *buffer, char_type *curr, char_type *end, @@ -1640,67 +1694,83 @@ inline constexpr void print_context_capture_run(output outstm, char_type *buffer { if constexpr (n != 0) { + // A non-empty captured run consumes the current argument and then continues recursively. using value_type = ::std::remove_cvref_t; if constexpr (::fast_io::reserve_printable) { + // Static reserve output is appended to the capture buffer when the fixed size fits. constexpr ::std::size_t sz{print_reserve_size(::fast_io::io_reserve_type)}; static_assert(sz < PTRDIFF_MAX); if (static_cast<::std::size_t>(end - curr) < sz) { + // The current buffer tail is too small, so existing captured output is flushed first. ::fast_io::details::decay::context_capture_flush(outstm, buffer, curr); } if (sz <= static_cast<::std::size_t>(end - buffer)) { + // The static reserve output fits in the reusable capture buffer. curr = print_reserve_define(::fast_io::io_reserve_type, curr, t); } else { + // The static reserve output is larger than the capture buffer and must use its normal path. ::fast_io::details::decay::print_control_single(outstm, t); } } else if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // Bounded dynamic reserve output is measured before deciding whether it fits the capture buffer. ::std::size_t const sz{print_reserve_size(::fast_io::io_reserve_type, t)}; if (static_cast<::std::size_t>(end - curr) < sz) { + // The current buffer tail is too small, so existing captured output is flushed first. ::fast_io::details::decay::context_capture_flush(outstm, buffer, curr); } if (sz <= static_cast<::std::size_t>(end - buffer)) { + // The measured dynamic reserve output fits in the reusable capture buffer. curr = print_reserve_define(::fast_io::io_reserve_type, curr, t); } else { + // The measured dynamic reserve output is too large and must use its normal path. ::fast_io::details::decay::print_control_single(outstm, t); } } else if constexpr (::fast_io::context_printable_with_static_buffer_size) { + // Context-printable output streams repeated producer windows through the capture buffer. typename ::std::remove_cvref_t))>::type st; for (;;) { if (curr == end) { + // A full capture buffer is emitted before asking the context producer for another window. ::fast_io::details::decay::context_capture_flush(outstm, buffer, curr); } auto [resit, done] = st.print_context_define(t, curr, end); curr = resit; if (done) { + // The context producer has completed the current argument. break; } } } else if constexpr (::std::same_as) { + // Null output intentionally contributes no captured characters. } if constexpr (n == 1) { + // The final captured argument is responsible for the optional newline and final flush. if constexpr (needprintlf) { + // The line variant appends a trailing newline into the capture buffer before flushing. if (curr == end) { + // A full buffer is flushed to make room for the newline character. ::fast_io::details::decay::context_capture_flush(outstm, buffer, curr); } *curr = ::fast_io::char_literal_v; @@ -1710,6 +1780,7 @@ inline constexpr void print_context_capture_run(output outstm, char_type *buffer } else { + // More captured arguments remain, so continue the run with the updated buffer cursor. ::fast_io::details::decay::print_context_capture_run(outstm, buffer, curr, end, args...); } @@ -1743,6 +1814,16 @@ inline constexpr char_type* printrsvcontiguousimpl(char_type* iter,Arg arg,Args. } #endif +/// @brief Emits a control sequence while preserving the caller's final line policy. +/// @details The final argument receives the line flag when requested; earlier arguments are emitted without a +/// newline so the whole sequence behaves like one logical print run. +/// @tparam ln true when the final argument should append a newline +/// @tparam output the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param outstm the output stream reference +/// @param t the current argument to emit +/// @param args the remaining arguments to emit template // Force-inline rationale: this is the small top-level control recursion for a known line flag; inlining lets the // newline case and the final single-control tail fold into the selected output path instead of leaving a call chain. @@ -1755,20 +1836,24 @@ inline constexpr void print_controls_line(output outstm, T t, Args... args) { if constexpr (sizeof...(Args) == 0) { + // The final control argument receives the caller's line policy. print_control_single(outstm, t); } else { #if (defined(__OPTIMIZE__) || defined(__OPTIMIZE_SIZE__)) && 0 + // Disabled experimental multi-control path would emit the run through a specialized recursion. print_controls_line_multi_impl(outstm, t, args...); #else if constexpr (ln) { + // Line mode emits the head without a newline and recurses so only the tail receives the newline. print_control_single(outstm, t); print_controls_line(outstm, args...); } else { + // Non-line mode emits every control argument without adding line termination. print_control_single(outstm, t); (print_control(outstm, args), ...); } @@ -1776,6 +1861,17 @@ inline constexpr void print_controls_line(output outstm, T t, Args... args) } } +/// @brief Materializes N reserve-printable arguments into an existing contiguous buffer. +/// @details Null arguments are skipped, while reserve-printable arguments advance the cursor through +/// print_reserve_define. The returned cursor marks one past the materialized output. +/// @tparam n the number of argument positions to consume +/// @tparam char_type the buffer character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param ptr the current output cursor +/// @param t the current argument +/// @param args the remaining arguments +/// @return char_type* one past the materialized reserve output template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> // Force-inline rationale: reserve-only emission is a pre-sized cursor advance; inlining exposes the fixed recursion // depth so null arguments disappear and the cursor updates become straight-line stores. @@ -1788,36 +1884,55 @@ inline constexpr char_type *print_n_reserve(char_type *ptr, T t, Args... args) { if constexpr (n == 0) { + // No argument positions are requested, so the output cursor is unchanged. return ptr; } else { + // At least one argument position remains in this contiguous reserve run. if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output consumes a position but emits no characters into the reserve buffer. if constexpr (sizeof...(Args) == 0 || n < 2) { + // The null argument is the final consumed position. return ptr; } else { + // Additional positions remain, so continue with the same output cursor. return print_n_reserve(ptr, args...); } } else { + // Reserve-printable output materializes directly into the current contiguous buffer. ptr = print_reserve_define(::fast_io::io_reserve_type>, ptr, t); if constexpr (sizeof...(Args) == 0 || n < 2) { + // The current reserve output completes the requested run. return ptr; } else { + // More reserve positions remain, so continue from the advanced cursor. return print_n_reserve(ptr, args...); } } } } +/// @brief Builds N scatter descriptors from scatter-printable arguments. +/// @details Null arguments are skipped, byte scatter descriptors convert character counts to byte counts, and typed +/// scatter descriptors preserve character counts for character-oriented output. +/// @tparam n the number of argument positions to consume +/// @tparam char_type the print character type +/// @tparam scattertype void for byte scatters, or char_type for character scatters +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param pscatters the current scatter descriptor cursor +/// @param t the current argument +/// @param args the remaining arguments template <::std::size_t n, ::std::integral char_type, typename scattertype, typename T, typename... Args> // Force-inline rationale: scatter descriptor construction is pure template dispatch; inlining keeps the descriptor // count, byte/character scatter conversion, and null elision visible to the final scatter write. @@ -1838,44 +1953,62 @@ inline constexpr void print_n_scatters(basic_io_scatter_t *pscatter { if constexpr (n != 0) { + // A non-empty scatter run consumes the current argument position. if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output contributes no scatter descriptor but may be followed by more output positions. if constexpr (1 < n) { + // Continue building descriptors for the remaining argument positions. return print_n_scatters(pscatters, args...); } } else if constexpr (::std::same_as<::std::remove_cvref_t, basic_io_scatter_t>) { + // Existing scatter descriptors can be copied or byte-length-adjusted without calling customization. if constexpr (::std::same_as) { + // Byte scatter output stores the payload extent in bytes. *pscatters = io_scatter_t{t.base, t.len * sizeof(char_type)}; } else { + // Character scatter output preserves the existing typed descriptor. *pscatters = t; } } else { + // Scatter-printable output is queried for its single contiguous output range. basic_io_scatter_t sct{print_scatter_define(::fast_io::io_reserve_type, t)}; if constexpr (::std::same_as) { + // Byte scatter output stores the produced scatter extent in bytes. *pscatters = io_scatter_t{sct.base, sct.len * sizeof(char_type)}; } else { + // Character scatter output stores the produced scatter extent in characters. *pscatters = sct; } } if constexpr (1 < n) { + // More consumed positions remain, so advance the descriptor cursor and continue. ++pscatters; return print_n_scatters(pscatters, args...); } } } +/// @brief Computes the combined run-time reserve size for dynamic reserve arguments in a prefix. +/// @tparam n the number of argument positions to inspect +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param t the current argument +/// @param args the remaining arguments +/// @return ::std::size_t the sum of dynamic reserve sizes in the inspected prefix template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> // Force-inline rationale: dynamic reserve sizing feeds a single allocation decision; inlining folds non-dynamic // arguments to zero and keeps overflow-checked additions adjacent to the caller's allocation branch. @@ -1889,34 +2022,47 @@ inline constexpr ::std::size_t ndynamic_print_reserve_size(T t, Args... args) using nocvreft = ::std::remove_cvref_t; if constexpr (n == 0) { + // An empty prefix has no dynamic reserve size contribution. return 0; } else if constexpr (n == 1) { + // The final inspected position either contributes its measured dynamic size or zero. if constexpr (::fast_io::dynamic_reserve_printable) { + // Dynamic reserve output contributes its run-time measured size. return print_reserve_size(::fast_io::io_reserve_type, t); } else { + // Non-dynamic output contributes no run-time reserve size. return 0; } } else { + // Multiple positions are accumulated with overflow checking. if constexpr (::fast_io::dynamic_reserve_printable) { + // The current dynamic reserve size is added to the remaining prefix size. return ::fast_io::details::intrinsics::add_or_overflow_die( print_reserve_size(::fast_io::io_reserve_type, t), ::fast_io::details::decay::ndynamic_print_reserve_size(args...)); } else { + // Non-dynamic output is skipped while the remaining prefix is measured. return ::fast_io::details::decay::ndynamic_print_reserve_size(args...); } } } +/// @brief Computes the combined static stack hints for dynamic reserve arguments in a prefix. +/// @tparam n the number of argument positions to inspect +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @return ::std::size_t the sum of declared static stack hints in the inspected prefix template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> // Force-inline rationale: static stack-budget aggregation decides whether the dynamic reserve path can stay on the // stack; inlining lets the all-static/heap-fallback branch collapse before code generation. @@ -1930,57 +2076,86 @@ inline constexpr ::std::size_t ndynamic_print_reserve_static_stack_size() using nocvreft = ::std::remove_cvref_t; if constexpr (n == 0) { + // An empty prefix needs no dynamic reserve stack budget. return 0; } else if constexpr (n == 1) { + // The final inspected position either contributes its static stack hint or zero. if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // A bounded dynamic reserve producer contributes its declared static stack hint. return print_reserve_static_stack_size(::fast_io::io_reserve_type); } else { + // Producers without a static hint contribute no stack budget. return 0; } } else { + // Multiple positions are accumulated with overflow checking. if constexpr (::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // The current static stack hint is added to the remaining prefix budget. return ::fast_io::details::intrinsics::add_or_overflow_die( print_reserve_static_stack_size(::fast_io::io_reserve_type), ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size()); } else { + // Output without a static dynamic-reserve hint is skipped while the remaining prefix is inspected. return ::fast_io::details::decay::ndynamic_print_reserve_static_stack_size(); } } } +/// @brief Tests whether every dynamic reserve argument in a prefix has a static stack hint. +/// @tparam n the number of argument positions to inspect +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @return bool true when stack-buffer materialization can be bounded statically template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> inline constexpr bool ndynamic_print_reserve_has_static_stack_size() { using nocvreft = ::std::remove_cvref_t; if constexpr (n == 0) { + // An empty prefix trivially satisfies the static-stack-hint requirement. return true; } else if constexpr (::fast_io::dynamic_reserve_printable && !::fast_io::dynamic_reserve_with_possible_static_stack_size) { + // A dynamic reserve producer without a static hint forces heap materialization. return false; } else if constexpr (n == 1) { + // The final inspected position did not violate the static-stack-hint requirement. return true; } else { + // Continue checking the remaining prefix positions. return ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size(); } } +/// @brief Builds mixed reserve/scatter descriptors for a prefix into caller-provided storage. +/// @tparam needprintlf true when the final descriptor should include a trailing newline +/// @tparam n the number of argument positions to consume +/// @tparam char_type the print character type +/// @tparam scattertype void for byte scatters, or char_type for character scatters +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param pscatters the current scatter descriptor cursor +/// @param ptr the current reserve-buffer cursor +/// @param t the current argument +/// @param args the remaining arguments +/// @return one past the final scatter descriptor written template // Force-inline rationale: the forward declaration carries the same attribute as the definitions so compilers that @@ -1993,6 +2168,11 @@ template *pscatters, char_type *ptr, T t, Args... args); +/// @brief Tests whether the next argument in a pack is reserve-like. +/// @tparam char_type the print character type +/// @tparam T the next argument type +/// @tparam Args ignored remaining argument types +/// @return bool true when the next argument can continue a reserve coalescing run template <::std::integral char_type, typename T, typename... Args> inline constexpr bool print_next_is_reserve() noexcept { @@ -2000,20 +2180,40 @@ inline constexpr bool print_next_is_reserve() noexcept if constexpr (::fast_io::reserve_printable || ::fast_io::dynamic_reserve_printable) { + // Static and dynamic reserve-printable arguments can extend the current reserve scatter. return true; } else { + // Non-reserve output terminates the current reserve scatter. return false; } } +/// @brief Empty-pack overload for reserve-lookahead checks. +/// @tparam char_type the print character type +/// @return bool false because no next argument exists template <::std::integral char_type> inline constexpr bool print_next_is_reserve() noexcept { return false; } +/// @brief Continues coalescing adjacent reserve outputs into the current scatter descriptor. +/// @details The continuation keeps one reserve-buffer base pointer while reserve-like arguments remain adjacent. +/// When the run ends, it commits the coalesced range as either a byte or character scatter descriptor. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam n the number of argument positions remaining +/// @tparam char_type the print character type +/// @tparam scattertype void for byte scatters, or char_type for character scatters +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param pscatters the current scatter descriptor cursor +/// @param base the first character in the coalesced reserve range +/// @param ptr the current reserve-buffer cursor +/// @param t the current argument +/// @param args the remaining arguments +/// @return one past the final scatter descriptor written template // Force-inline rationale: this continuation coalesces adjacent reserve fragments into one scatter entry; inlining @@ -2028,34 +2228,42 @@ inline constexpr auto print_n_scatters_reserve_cont(basic_io_scatter_t; if constexpr (reserve_printable || dynamic_reserve_printable) { + // Reserve-like output extends the active contiguous reserve buffer range. ptr = print_reserve_define(::fast_io::io_reserve_type, ptr, t); if constexpr (::fast_io::details::decay::print_next_is_reserve()) { + // The next output is also reserve-like, so keep coalescing into the same scatter descriptor. return ::fast_io::details::decay::print_n_scatters_reserve_cont( pscatters, base, ptr, args...); } else { + // The reserve run ends here, so commit the buffered range as one scatter descriptor. if constexpr (n == 1 && needprintlf) { + // The line variant appends its newline inside the final coalesced reserve range. *ptr = char_literal_v; ++ptr; } ::std::size_t const sz{static_cast<::std::size_t>(ptr - base)}; if constexpr (::std::same_as) { + // Byte scatter output records the coalesced reserve range length in bytes. *pscatters = io_scatter_t{base, sz * sizeof(char_type)}; } else { + // Character scatter output records the coalesced reserve range length in characters. *pscatters = basic_io_scatter_t{base, sz}; } ++pscatters; if constexpr (1 < n) { + // More output remains after the reserve run, so resume the mixed materializer. return ::fast_io::details::decay::print_n_scatters_reserve( pscatters, ptr, args...); } @@ -2063,26 +2271,45 @@ inline constexpr auto print_n_scatters_reserve_cont(basic_io_scatter_t(ptr - base)}; if constexpr (::std::same_as) { + // Byte scatter output commits the completed reserve run in bytes. *pscatters = io_scatter_t{base, sz * sizeof(char_type)}; } else { + // Character scatter output commits the completed reserve run in characters. *pscatters = basic_io_scatter_t{base, sz}; } ++pscatters; if constexpr (1 < n) { + // The terminating argument still belongs to the overall run and is processed next. return ::fast_io::details::decay::print_n_scatters_reserve( pscatters, ptr, t, args...); } } } + // No more output positions remain, so return the current scatter cursor. return pscatters; } +/// @brief Materializes a mixed scatter/reserve prefix into scatter descriptors and reserve storage. +/// @details Reserve-like outputs are coalesced into contiguous reserve scatters, existing scatter outputs are +/// forwarded as descriptors, reserve-scatters outputs append their own descriptors, and nulls are skipped. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam n the number of argument positions to consume +/// @tparam char_type the print character type +/// @tparam scattertype void for byte scatters, or char_type for character scatters +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param pscatters the current scatter descriptor cursor +/// @param ptr the current reserve-buffer cursor +/// @param t the current argument +/// @param args the remaining arguments +/// @return one past the final scatter descriptor written template // Force-inline rationale: the mixed reserve/scatter materializer is the boundary where compile-time argument classes @@ -2097,36 +2324,44 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * { if constexpr (n != 0) { + // A non-empty mixed run consumes the current argument position. using nocvreft = ::std::remove_cvref_t; if constexpr (::fast_io::reserve_printable || ::fast_io::dynamic_reserve_printable) { + // Reserve-like output is materialized into reserve storage before becoming a scatter descriptor. auto ptred{print_reserve_define(::fast_io::io_reserve_type, ptr, t)}; if constexpr (sizeof...(Args) != 0 && ::fast_io::details::decay::print_next_is_reserve()) { + // Adjacent reserve-like output is coalesced into the same scatter descriptor. return ::fast_io::details::decay::print_n_scatters_reserve_cont( pscatters, ptr, ptred, args...); } else { + // This reserve output stands alone and is committed as one scatter descriptor. if constexpr (n == 1 && needprintlf) { + // The line variant appends its newline to the final reserve range before committing it. *ptred = char_literal_v; ++ptred; } ::std::size_t const sz{static_cast<::std::size_t>(ptred - ptr)}; if constexpr (::std::same_as) { + // Byte scatter output records the reserve range length in bytes. *pscatters = io_scatter_t{ptr, sz * sizeof(char_type)}; } else { + // Character scatter output records the reserve range length in characters. *pscatters = basic_io_scatter_t{ptr, sz}; } ++pscatters; if constexpr (1 < n) { + // Remaining positions continue after the committed reserve range. return ::fast_io::details::decay::print_n_scatters_reserve( pscatters, ptred, args...); } @@ -2134,53 +2369,66 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * } else if constexpr (::fast_io::scatter_printable) { + // Scatter-printable output contributes a descriptor without consuming reserve storage. if constexpr (::std::same_as>) { + // Existing scatter descriptor arguments are copied directly when their representation matches. if constexpr (::std::same_as) { + // Byte scatter output converts an existing character length to bytes. *pscatters = io_scatter_t{t.base, t.len * sizeof(char_type)}; } else { + // Character scatter output preserves the existing typed descriptor. *pscatters = t; } } else { + // General scatter-printable output is queried for its exposed output range. basic_io_scatter_t sct{print_scatter_define(::fast_io::io_reserve_type, t)}; if constexpr (::std::same_as) { + // Byte scatter output stores the exposed range length in bytes. *pscatters = io_scatter_t{sct.base, sct.len * sizeof(char_type)}; } else { + // Character scatter output stores the exposed range length in characters. *pscatters = sct; } } ++pscatters; if constexpr (n == 1 && needprintlf) { + // The line variant appends a separate newline scatter after the final scatter output. *pscatters = ::fast_io::details::decay::line_scatter_common; ++pscatters; } if constexpr (1 < n) { + // Remaining positions continue with the same reserve-buffer cursor. return ::fast_io::details::decay::print_n_scatters_reserve(pscatters, ptr, args...); } } else if constexpr (::fast_io::reserve_scatters_printable) { + // Reserve-scatters output supplies one or more descriptors and advances reserve storage itself. if constexpr (::std::same_as) { + // Byte scatter output uses the byte-oriented reserve-scatters adapter. auto pit{::fast_io::details::decay::prrsvsct_byte_common_rsvsc_impl(pscatters, ptr, t)}; if constexpr (1 < n) { + // Remaining positions continue from the adapter's updated cursors. return ::fast_io::details::decay::print_n_scatters_reserve( pit.scatters_pos_ptr, pit.reserve_pos_ptr, args...); } else if constexpr (n == 1 && needprintlf) { + // The line variant appends a newline scatter after the final byte reserve-scatters output. *pit.scatters_pos_ptr = ::fast_io::details::decay::line_scatter_common; ++pit.scatters_pos_ptr; } @@ -2188,15 +2436,18 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * } else { + // Character scatter output uses the typed reserve-scatters customization directly. auto pit{ print_reserve_scatters_define(::fast_io::io_reserve_type, pscatters, ptr, t)}; if constexpr (1 < n) { + // Remaining positions continue from the customization's updated cursors. return ::fast_io::details::decay::print_n_scatters_reserve( pit.scatters_pos_ptr, pit.reserve_pos_ptr, args...); } else if constexpr (n == 1 && needprintlf) { + // The line variant appends a newline scatter after the final typed reserve-scatters output. *pit.scatters_pos_ptr = ::fast_io::details::decay::line_scatter_common; ++pit.scatters_pos_ptr; } @@ -2205,21 +2456,36 @@ inline constexpr auto print_n_scatters_reserve(basic_io_scatter_t * } else if constexpr (::std::same_as) { + // Null output consumes a position while emitting no scatter descriptor. if constexpr (n == 1 && needprintlf) { + // A final null still carries the caller's requested trailing newline. *pscatters = ::fast_io::details::decay::line_scatter_common; ++pscatters; } if constexpr (1 < n) { + // Continue with the next argument while preserving the current cursors. return ::fast_io::details::decay::print_n_scatters_reserve(pscatters, ptr, args...); } } } + // No more positions remain, so return the current scatter cursor. return pscatters; } +/// @brief Writes a scatter-only prefix using stack or heap descriptor storage. +/// @tparam needprintlf true when the final descriptor should be a trailing newline +/// @tparam position the number of argument positions included in the scatter prefix +/// @tparam scatterscount the number of scatter descriptors to write +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... args) @@ -2229,6 +2495,7 @@ inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small scatter descriptor runs are built on the stack and written once. scatter_type scatters[scatterscount]; ::fast_io::details::decay::print_n_scatters(scatters, t, args...); ::fast_io::details::decay::print_scatter_write_all_dispatch_with_line( @@ -2236,6 +2503,7 @@ inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... } else { + // Large scatter descriptor runs allocate descriptor storage before one scatter write. ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); ::fast_io::details::decay::print_n_scatters(scatters.ptr, t, args...); ::fast_io::details::decay::print_scatter_write_all_dispatch_with_line( @@ -2243,6 +2511,19 @@ inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... } } +/// @brief Materializes a mixed reserve/scatter prefix with caller-provided scatter descriptor storage. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam position the number of argument positions included in the prefix +/// @tparam mxsize the static reserve-buffer character requirement +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam scatter_type the scatter descriptor representation +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param scatters the caller-provided scatter descriptor storage +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_scatters_reserve_with_scatter(outputstmtype optstm, scatter_type *scatters, T t, @@ -2251,6 +2532,7 @@ inline constexpr void print_controls_scatters_reserve_with_scatter(outputstmtype constexpr ::std::size_t buffer_size{mxsize == 0 ? 1u : mxsize}; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small reserve storage is materialized on the stack before scatter output is written. char_type buffer[buffer_size]; auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer, t, @@ -2260,6 +2542,7 @@ inline constexpr void print_controls_scatters_reserve_with_scatter(outputstmtype } else { + // Large reserve storage is allocated before scatter descriptors are materialized and written. ::fast_io::details::local_operator_new_array_ptr buffer(buffer_size); auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer.ptr, t, @@ -2269,6 +2552,18 @@ inline constexpr void print_controls_scatters_reserve_with_scatter(outputstmtype } } +/// @brief Writes a mixed static reserve/scatter prefix using stack or heap descriptor storage. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam position the number of argument positions included in the prefix +/// @tparam scatterscount the number of scatter descriptors available +/// @tparam mxsize the static reserve-buffer character requirement +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, Args... args) @@ -2278,12 +2573,14 @@ inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small scatter descriptor storage is kept on the stack for the mixed reserve/scatter run. scatter_type scatters[scatterscount]; ::fast_io::details::decay::print_controls_scatters_reserve_with_scatter(optstm, scatters, t, args...); } else { + // Large scatter descriptor storage is allocated before the mixed run is materialized. ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); ::fast_io::details::decay::print_controls_scatters_reserve_with_scatter(optstm, scatters.ptr, t, @@ -2291,6 +2588,23 @@ inline constexpr void print_controls_scatters_reserve(outputstmtype optstm, T t, } } +/// @brief Materializes a mixed dynamic reserve/scatter prefix with caller-provided descriptor storage. +/// @details When every dynamic producer has a static stack hint and the measured total fits the bounded stack buffer, +/// reserve storage stays on the stack; otherwise the measured total drives one heap allocation. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam position the number of argument positions included in the prefix +/// @tparam stack_buffer_size the bounded stack reserve-buffer size +/// @tparam has_static_stack_size true when all dynamic producers have static stack hints +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam scatter_type the scatter descriptor representation +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param scatters the caller-provided scatter descriptor storage +/// @param totalsz the measured reserve-buffer character requirement +/// @param t the current argument +/// @param args the remaining arguments template // Force-inline rationale: the dynamic reserve writer has a hot stack-buffer fast path; inlining lets the caller's @@ -2306,8 +2620,10 @@ inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outpu if constexpr (has_static_stack_size && ::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // A statically bounded dynamic run may use stack reserve storage when the measured total fits. if (totalsz <= stack_buffer_size) { + // The measured dynamic reserve output fits in the bounded stack buffer and writes once. char_type buffer[stack_buffer_size]; auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer, t, @@ -2317,6 +2633,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outpu return; } } + // The measured reserve output needs heap storage before descriptors can be written. ::fast_io::details::local_operator_new_array_ptr buffer(totalsz == 0 ? 1u : totalsz); auto ptr{::fast_io::details::decay::print_n_scatters_reserve(scatters, buffer.ptr, t, args...)}; @@ -2324,6 +2641,20 @@ inline constexpr void print_controls_dynamic_scatters_reserve_with_scatter(outpu optstm, scatters, static_cast<::std::size_t>(ptr - scatters)); } +/// @brief Writes a mixed dynamic reserve/scatter prefix using stack or heap descriptor storage. +/// @tparam needprintlf true when the final consumed output should append a newline +/// @tparam position the number of argument positions included in the prefix +/// @tparam scatterscount the number of scatter descriptors available +/// @tparam stack_buffer_size the bounded stack reserve-buffer size +/// @tparam has_static_stack_size true when all dynamic producers have static stack hints +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param totalsz the measured reserve-buffer character requirement +/// @param t the current argument +/// @param args the remaining arguments template // Force-inline rationale: this wrapper chooses stack versus heap scatter storage from template constants; inlining @@ -2341,6 +2672,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype opts ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small scatter descriptor storage is kept on the stack for the dynamic mixed run. scatter_type scatters[scatterscount]; ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters, totalsz, t, @@ -2348,6 +2680,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype opts } else { + // Large scatter descriptor storage is allocated before the dynamic mixed run is materialized. ::fast_io::details::local_operator_new_array_ptr scatters(scatterscount); ::fast_io::details::decay::print_controls_dynamic_scatters_reserve_with_scatter< needprintlf, position, stack_buffer_size, has_static_stack_size, char_type>(optstm, scatters.ptr, totalsz, t, @@ -2355,15 +2688,32 @@ inline constexpr void print_controls_dynamic_scatters_reserve(outputstmtype opts } } +/// @brief Primary non-buffered control dispatcher for a freestanding print run. +/// @tparam line true when the final emitted output should append a newline +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam skippings number of already-consumed head arguments to skip +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... args); +/// @brief Empty-pack overload for the dynamic reserve/scatter fast-entry test. +/// @tparam char_type the print character type +/// @return bool false because no argument prefix exists template <::std::integral char_type> inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_available() noexcept { return false; } +/// @brief Tests whether a print run can enter the dynamic reserve/scatter fast path. +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @return bool true when the leading run contains both scatter output and dynamic reserve output template <::std::integral char_type, typename T, typename... Args> inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_available() noexcept { @@ -2372,6 +2722,14 @@ inline constexpr bool print_controls_dynamic_scatters_reserve_fast_entry_availab return res.position != 0 && res.hasscatters && res.hasdynamicreserve; } +/// @brief Emits the leading dynamic reserve/scatter run through the specialized fast path. +/// @tparam line true when the final emitted output should append a newline +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template // Force-inline rationale: the fast entry is reached only after compile-time contiguous-scatter analysis succeeds; // inlining preserves those constants so the selected reserve/scatter path is emitted directly at the semantic boundary. @@ -2389,6 +2747,7 @@ inline constexpr void print_controls_dynamic_scatters_reserve_fast_entry(outputs static_assert(res.hasscatters && res.hasdynamicreserve); if constexpr (line) { + // A line-terminated fast path must have a finite scatter count for the optional newline descriptor. static_assert(res.neededscatters != SIZE_MAX); } static_assert(SIZE_MAX != sizeof...(Args)); @@ -2414,10 +2773,22 @@ inline constexpr void print_controls_dynamic_scatters_reserve_fast_entry(outputs t, args...); if constexpr (res.position != n) { + // Remaining arguments after the fast prefix continue through the general control dispatcher. print_controls_impl(optstm, args...); } } +/// @brief Emits a freestanding control run without relying on the stream put area. +/// @details The dispatcher coalesces context-capable runs, scatter-only prefixes, reserve-only prefixes, and mixed +/// reserve/scatter prefixes before recursively emitting any remaining arguments. +/// @tparam line true when the final emitted output should append a newline +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam skippings number of already-consumed head arguments to skip +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... args) { @@ -2431,14 +2802,17 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg ::fast_io::details::decay::find_context_capture_run_n()}; if constexpr (skippings != 0) { + // Already-emitted prefix positions are skipped before dispatching the remaining arguments. print_controls_impl(optstm, args...); } else if constexpr (sizeof...(Args) == 0) { + // A single remaining argument uses the specialized one-control emitter with the caller's line policy. print_control_single(optstm, t); } else if constexpr (context_capture_res.has_context) { + // Runs containing context-printable output use a reusable capture buffer to reduce small writes. static_assert(SIZE_MAX != sizeof...(Args)); constexpr ::std::size_t n{sizeof...(Args) + static_cast<::std::size_t>(1)}; constexpr bool needprintlf{n == context_capture_res.position && line}; @@ -2460,12 +2834,14 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg : reserve_context_dynamic_size}; if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small context-capture buffers stay on the stack while the captured run is emitted. char_type buffer[buffer_size]; ::fast_io::details::decay::print_context_capture_run( optstm, buffer, buffer, buffer + buffer_size, t, args...); } else { + // Large context-capture buffers are allocated before the captured run is emitted. ::fast_io::details::local_operator_new_array_ptr newptr(buffer_size); char_type *buffer{newptr.ptr}; ::fast_io::details::decay::print_context_capture_run( @@ -2473,18 +2849,22 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } if constexpr (context_capture_res.position != n) { + // Arguments after the context-captured prefix continue through the general dispatcher. print_controls_impl(optstm, args...); } } else if constexpr (res.position == 0) { + // The current argument cannot join a contiguous optimized prefix, so emit it alone. print_control_single(optstm, t); print_controls_impl(optstm, args...); } else { + // A contiguous scatter/reserve-friendly prefix was found and can be emitted as one optimized run. if constexpr (line) { + // A line-terminated optimized prefix must have a finite scatter count when a newline descriptor is needed. static_assert(res.neededscatters != SIZE_MAX); } static_assert(SIZE_MAX != sizeof...(Args)); @@ -2492,23 +2872,28 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg constexpr bool needprintlf{n == res.position && line}; if constexpr (res.hasscatters && !res.hasreserve && !res.hasdynamicreserve) { + // Scatter-only prefixes are emitted directly as scatter descriptors. constexpr ::std::size_t scatterscount{res.neededscatters + static_cast<::std::size_t>(needprintlf)}; ::fast_io::details::decay::print_controls_scatters( optstm, t, args...); if constexpr (res.position != n) { + // Arguments after the scatter-only prefix continue through the general dispatcher. print_controls_impl(optstm, args...); } } else { + // Prefixes with reserve output need temporary character storage before they can be written. constexpr ::std::size_t mxsize{ static_cast<::std::size_t>(res.neededspace + static_cast<::std::size_t>(needprintlf))}; if constexpr (!res.hasscatters) { + // Reserve-only prefixes can be materialized as one contiguous output range. static_assert(!needprintlf || res.neededspace != SIZE_MAX); if constexpr (res.hasdynamicreserve) { + // Dynamic reserve-only prefixes are measured before choosing stack or heap storage. constexpr bool has_static_stack_size{ ::fast_io::details::decay::ndynamic_print_reserve_has_static_stack_size) { + // A statically bounded dynamic reserve prefix may stay on the stack. if (totalsz <= stack_buffer_size) { + // The measured dynamic reserve prefix fits in the bounded stack buffer. char_type buffer[stack_buffer_size]; char_type *ptred{ ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the contiguous stack buffer. *ptred = ::fast_io::char_literal_v; ++ptred; } @@ -2543,6 +2931,7 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else { + // The measured dynamic reserve prefix exceeds the stack budget and uses heap storage. ::fast_io::details::local_operator_new_array_ptr newptr(totalsz); char_type *buffer{newptr.ptr}; char_type *ptred{ @@ -2550,6 +2939,7 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the contiguous heap buffer. *ptred = ::fast_io::char_literal_v; ++ptred; } @@ -2558,12 +2948,14 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else { + // Dynamic reserve prefixes without bounded stack eligibility use heap storage. ::fast_io::details::local_operator_new_array_ptr newptr(totalsz); char_type *buffer{newptr.ptr}; char_type *ptred{ ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the contiguous heap buffer. *ptred = ::fast_io::char_literal_v; ++ptred; } @@ -2572,24 +2964,30 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else if constexpr (res.hasreserve) { + // Static reserve-only prefixes have a compile-time storage requirement. if constexpr (res.neededspace == 0) { + // A zero-sized reserve prefix can only emit the optional trailing newline. if constexpr (needprintlf) { + // The line variant emits the trailing newline as a single character output. ::fast_io::operations::decay::char_put_decay(optstm, ::fast_io::char_literal_v); } } else { + // Non-empty static reserve prefixes are materialized before a single contiguous write. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small static reserve prefixes are materialized on the stack. char_type buffer[mxsize]; char_type *ptred{ ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the stack buffer. *ptred = ::fast_io::char_literal_v; ++ptred; } @@ -2597,12 +2995,14 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else { + // Large static reserve prefixes are materialized in heap storage. ::fast_io::details::local_operator_new_array_ptr newptr(mxsize); char_type *buffer{newptr.ptr}; char_type *ptred{ ::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the heap buffer. *ptred = ::fast_io::char_literal_v; ++ptred; } @@ -2613,6 +3013,7 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } else if constexpr (res.hasreserve && !res.hasdynamicreserve) { + // Mixed scatter/static-reserve prefixes are emitted through prebuilt scatter descriptors. constexpr ::std::size_t scatterscount{res.neededscatters + static_cast<::std::size_t>(line && res.position == n)}; ::fast_io::details::decay::print_controls_scatters_reserve(line && res.position == n)}; constexpr bool has_static_stack_size{ @@ -2642,25 +3044,40 @@ inline constexpr void print_controls_impl(outputstmtype optstm, T t, Args... arg } if constexpr (res.position != n) { + // Arguments after the optimized prefix continue through the general dispatcher. print_controls_impl(optstm, args...); } } } } +/// @brief Emits a freestanding control run while taking advantage of an output stream put area. +/// @details Scatter prefixes bypass the put area through scatter writes; reserve prefixes first try the current +/// output buffer and only fall back to temporary storage when the put area is too small. +/// @tparam line true when the final emitted output should append a newline +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam skippings number of already-consumed head arguments to skip +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments template inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args... args) { if constexpr (skippings != 0) { + // Already-emitted prefix positions are skipped before the buffered dispatcher resumes. ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } else if constexpr (sizeof...(Args) == 0) { + // A single remaining argument uses the one-control emitter with the caller's line policy. print_control_single(optstm, t); } else { + // Multiple arguments may expose scatter or reserve prefixes that can be grouped before recursion. using char_type = typename outputstmtype::output_char_type; static_assert(SIZE_MAX != sizeof...(Args)); constexpr ::std::size_t n{sizeof...(Args) + static_cast<::std::size_t>(1)}; @@ -2671,10 +3088,13 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args io_scatter_t, basic_io_scatter_t>; if constexpr (scatters_result.position != 0) { + // A leading scatter-friendly prefix is emitted through scatter descriptors. if constexpr (line) { + // A line-terminated scatter prefix must have a finite position for the optional newline descriptor. static_assert(scatters_result.position != SIZE_MAX); - }[[maybe_unused]] + } + constexpr bool needprintlf{n == scatters_result.position && line}; constexpr ::std::size_t scatterscount{scatters_result.position - scatters_result.null + static_cast<::std::size_t>(needprintlf)}; @@ -2682,16 +3102,19 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args scatterscount, char_type>(optstm, t, args...); if constexpr (scatters_result.position != n) { + // Arguments after the scatter prefix continue through the buffered dispatcher. ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } } else { + // Without a scatter prefix, try to group a reserve-only prefix into the output buffer. constexpr auto rsvresult{ ::fast_io::details::decay::find_continuous_scatters_reserve_n()}; if constexpr (1 < rsvresult.position) { + // Reserve prefixes with more than one position can amortize buffer checks and writes. constexpr bool needprintlf{n == rsvresult.position && line}; constexpr ::std::size_t buffersize{rsvresult.neededspace + static_cast<::std::size_t>(needprintlf)}; char_type *bcurr{obuffer_curr(optstm)}; @@ -2700,8 +3123,10 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args bool smaller{static_cast<::std::ptrdiff_t>(buffersize) < diff}; if constexpr (minimum_buffer_output_stream_require_size_impl) { + // Streams with a sufficient minimum put area can prepare the buffer once before writing in place. if (!smaller) [[unlikely]] { + // The current put area is short, so prepare a minimum-size buffer for the reserve prefix. obuffer_minimum_size_flush_prepare_define(optstm); bcurr = obuffer_curr(optstm); } @@ -2709,6 +3134,7 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args ::fast_io::details::decay::print_n_reserve(bcurr, t, args...); if constexpr (needprintlf) { + // The line variant appends the newline in the same output-buffer commit. *bcurr = ::fast_io::char_literal_v; ++bcurr; } @@ -2716,13 +3142,16 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } else { + // Streams without a minimum-size guarantee use the current put area only when it already fits. if (smaller) [[likely]] { + // The current put area can hold the entire reserve prefix. bcurr = ::fast_io::details::decay::print_n_reserve(bcurr, t, args...); if constexpr (needprintlf) { + // The line variant appends the newline in the same output-buffer commit. *bcurr = ::fast_io::char_literal_v; ++bcurr; } @@ -2730,15 +3159,18 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } else [[unlikely]] { + // The current put area is too small, so materialize the reserve prefix out of line. if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { + // Small reserve prefixes fall back to stack materialization and one write. char_type buffer[buffersize]; char_type *ptr{::fast_io::details::decay::print_n_reserve(buffer, t, args...)}; if constexpr (needprintlf) { + // The line variant appends the newline to the stack buffer before writing. *ptr = ::fast_io::char_literal_v; ++ptr; } @@ -2746,6 +3178,7 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } else { + // Large reserve prefixes fall back to heap materialization and one write. ::fast_io::details::local_operator_new_array_ptr newptr(buffersize); char_type *buffer{newptr.ptr}; char_type *ptr{::fast_io::details::decay::print_n_reserve; ++ptr; } @@ -2762,15 +3196,18 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } if constexpr (rsvresult.position != n) { + // Arguments after the reserve prefix continue through the buffered dispatcher. ::fast_io::details::decay::print_controls_buffer_impl( optstm, args...); } } else { + // No useful prefix was found, so emit the current argument and continue with the tail. ::fast_io::details::decay::print_control_single(optstm, t); if constexpr (sizeof...(args) != 0) { + // The tail still owns the caller's final line policy. ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } } @@ -2778,19 +3215,27 @@ inline constexpr void print_controls_buffer_impl(outputstmtype optstm, T t, Args } } +/// @brief Returns the semantic node stored inside a parameter object, or the value itself. +/// @tparam T the semantic candidate type +/// @param t the semantic candidate object +/// @return decltype(auto) the unwrapped semantic node reference or the original object template inline constexpr decltype(auto) print_semantic_node_ref(T &&t) noexcept { if constexpr (::fast_io::details::decay::print_semantic_parameter_object_v<::std::remove_cvref_t>) { + // Parameter objects forward semantic operations to their wrapped reference member. return ::fast_io::details::decay::print_semantic_node_ref(::std::forward(t).reference); } else { + // Plain semantic nodes and leaves are already the reference used by the printer. return ::std::forward(t); } } +/// @brief Detects semantic arguments that expand into print packs. +/// @tparam T the argument type to inspect template inline constexpr bool print_semantic_pack_argument_v = ::fast_io::details::print_pack || @@ -2798,13 +3243,22 @@ inline constexpr bool print_semantic_pack_argument_v = ::fast_io::details::print_pack< decltype(::fast_io::details::decay::print_semantic_node_ref(::std::declval()))>); +/// @brief Alias result type produced by io_print_alias for one semantic input argument. +/// @tparam char_type the print character type +/// @tparam T the input argument type template <::std::integral char_type, typename T> using print_semantic_input_alias_t = decltype(::fast_io::io_print_alias(::std::declval())); +/// @brief Forwarded semantic input type after aliasing and character-aware print forwarding. +/// @tparam char_type the print character type +/// @tparam T the input argument type template <::std::integral char_type, typename T> using print_semantic_input_forward_t = decltype(::fast_io::io_print_forward(::fast_io::io_print_alias(::std::declval()))); +/// @brief Detects inputs that become semantic nodes after aliasing and print forwarding. +/// @tparam char_type the print character type +/// @tparam T the input argument type template <::std::integral char_type, typename T> inline constexpr bool print_semantic_input_argument_v = ::fast_io::details::decay::print_semantic_node_no_parameter_v< @@ -2812,6 +3266,11 @@ inline constexpr bool print_semantic_input_argument_v = ::fast_io::details::decay::print_semantic_node< ::fast_io::details::decay::print_semantic_input_forward_t>; +/// @brief Applies the semantic input forwarding protocol to one argument. +/// @tparam char_type the print character type +/// @tparam T the input argument type +/// @param t the input argument +/// @return decltype(auto) the semantic node or forwarded print argument template <::std::integral char_type, typename T> inline constexpr decltype(auto) print_semantic_input_forward(T &&t) noexcept { @@ -2819,14 +3278,23 @@ inline constexpr decltype(auto) print_semantic_input_forward(T &&t) noexcept ::std::remove_cvref_t< decltype(::fast_io::io_print_alias(::std::forward(t)))>>) { + // Alias results that are already parameterless semantic nodes must not be forwarded again. return ::fast_io::io_print_alias(::std::forward(t)); } else { + // Ordinary alias results use the character-aware print forwarding path. return ::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t))); } } +/// @brief Invokes a continuation with every element from a semantic pack. +/// @tparam T the pack-like semantic node type +/// @tparam continuation the continuation receiving expanded elements +/// @tparam I the pack indices to expand +/// @param t the pack-like semantic node +/// @param cont the continuation to invoke +/// @return decltype(auto) the continuation result template inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuation &&cont, ::std::index_sequence) @@ -2835,6 +3303,12 @@ inline constexpr decltype(auto) print_semantic_pack_apply_impl(T &&t, continuati return cont(::fast_io::containers::get(::std::forward(pack_ref).storage)...); } +/// @brief Expands a semantic pack using its declared pack size. +/// @tparam T the pack-like semantic node type +/// @tparam continuation the continuation receiving expanded elements +/// @param t the pack-like semantic node +/// @param cont the continuation to invoke +/// @return decltype(auto) the continuation result template inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation &&cont) { @@ -2844,12 +3318,19 @@ inline constexpr decltype(auto) print_semantic_pack_apply(T &&t, continuation && ::std::forward(t), ::std::forward(cont), ::std::make_index_sequence{}); } +/// @brief Prefix continuation that prepends a stored value reference to later semantic arguments. +/// @tparam continuation the downstream continuation type +/// @tparam T the stored value type template struct print_semantic_value_prefix_continuation { ::std::remove_reference_t *contptr; ::std::remove_reference_t *valueptr; + /// @brief Invokes the downstream continuation with the stored value before the tail arguments. + /// @tparam TailArgs the tail argument types + /// @param tail_args the tail arguments + /// @return decltype(auto) the downstream continuation result template inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { @@ -2857,12 +3338,19 @@ struct print_semantic_value_prefix_continuation } }; +/// @brief Prefix continuation that prepends a stored lvalue to later semantic arguments. +/// @tparam continuation the downstream continuation type +/// @tparam T the stored lvalue type template struct print_semantic_lvalue_prefix_continuation { ::std::remove_reference_t *contptr; T *valueptr; + /// @brief Invokes the downstream continuation with the stored lvalue before the tail arguments. + /// @tparam TailArgs the tail argument types + /// @param tail_args the tail arguments + /// @return decltype(auto) the downstream continuation result template inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { @@ -2870,21 +3358,48 @@ struct print_semantic_lvalue_prefix_continuation } }; +/// @brief Completes semantic pack expansion when no input arguments remain. +/// @tparam already_forwarded true when non-pack inputs were already print-forwarded +/// @tparam char_type the print character type +/// @tparam continuation the continuation receiving the expanded run +/// @param cont the continuation to invoke +/// @return decltype(auto) the continuation result template inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont) { return cont(); } +/// @brief Expands semantic pack arguments before invoking a continuation. +/// @tparam already_forwarded true when non-pack inputs were already print-forwarded +/// @tparam char_type the print character type +/// @tparam continuation the continuation receiving the expanded run +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param cont the continuation to invoke +/// @param t the current argument +/// @param args the remaining arguments +/// @return decltype(auto) the continuation result template inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args); +/// @brief Reattaches already-expanded pack elements in front of the remaining tail. +/// @tparam already_forwarded true when non-pack inputs were already print-forwarded +/// @tparam char_type the print character type +/// @tparam continuation the final continuation type +/// @tparam ExpandedPackArgs the expanded pack element types template struct print_semantic_pack_expand_tail_continuation { ::std::remove_reference_t *contptr; ::fast_io::containers::tuple<::std::remove_reference_t *...> expandedptrs; + /// @brief Invokes the final continuation with expanded pack elements followed by tail arguments. + /// @tparam I the expanded pack indices + /// @tparam TailArgs the tail argument types + /// @param the index sequence selecting expanded pack element pointers + /// @param tail_args the tail arguments + /// @return decltype(auto) the final continuation result template <::std::size_t... I, typename... TailArgs> inline constexpr decltype(auto) operator_impl(::std::index_sequence, TailArgs &&...tail_args) const { @@ -2892,6 +3407,10 @@ struct print_semantic_pack_expand_tail_continuation ::std::forward(tail_args)...); } + /// @brief Builds the index sequence used to reattach expanded pack elements. + /// @tparam TailArgs the tail argument types + /// @param tail_args the tail arguments + /// @return decltype(auto) the final continuation result template inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const { @@ -2900,12 +3419,23 @@ struct print_semantic_pack_expand_tail_continuation } }; +/// @brief Continues pack expansion after one pack has been expanded in the middle of a run. +/// @tparam already_forwarded true when non-pack inputs were already print-forwarded +/// @tparam char_type the print character type +/// @tparam continuation the final continuation type +/// @tparam Args the original tail argument types template struct print_semantic_pack_expand_middle_continuation { ::std::remove_reference_t *contptr; ::fast_io::containers::tuple<::std::remove_reference_t *...> const *argptrs; + /// @brief Restarts pack expansion for the saved tail arguments. + /// @tparam tail_continuation the continuation carrying the already-expanded pack prefix + /// @tparam I the saved tail indices + /// @param tail_cont the continuation receiving the expanded tail + /// @param the index sequence selecting saved tail pointers + /// @return decltype(auto) the final continuation result template inline constexpr decltype(auto) operator_impl(tail_continuation &&tail_cont, ::std::index_sequence) const { @@ -2914,6 +3444,10 @@ struct print_semantic_pack_expand_middle_continuation ::std::forward(*::fast_io::containers::get(*argptrs))...); } + /// @brief Captures the expanded pack elements and resumes expansion for the saved tail. + /// @tparam ExpandedPackArgs the expanded pack element types + /// @param expanded_pack_args the expanded pack elements + /// @return decltype(auto) the final continuation result template inline constexpr decltype(auto) operator()(ExpandedPackArgs &&...expanded_pack_args) const { @@ -2925,12 +3459,21 @@ struct print_semantic_pack_expand_middle_continuation } }; +/// @brief Entry continuation used when the current argument expands into a semantic pack. +/// @tparam already_forwarded true when non-pack inputs were already print-forwarded +/// @tparam char_type the print character type +/// @tparam continuation the final continuation type +/// @tparam Args the saved tail argument types template struct print_semantic_pack_expand_initial_continuation { ::std::remove_reference_t *contptr; ::fast_io::containers::tuple<::std::remove_reference_t *...> argptrs; + /// @brief Receives the current pack's elements and resumes expansion with the saved tail. + /// @tparam PackArgs the expanded current-pack element types + /// @param pack_args the expanded current-pack elements + /// @return decltype(auto) the final continuation result template inline constexpr decltype(auto) operator()(PackArgs &&...pack_args) const { @@ -2941,6 +3484,18 @@ struct print_semantic_pack_expand_initial_continuation } }; +/// @brief Expands semantic packs and forwards ordinary semantic inputs before invoking a continuation. +/// @details Pack arguments are replaced by their stored elements. Ordinary arguments either keep their already +/// forwarded form or are aliased and print-forwarded into a stable local value. +/// @tparam already_forwarded true when ordinary inputs have already passed semantic forwarding +/// @tparam char_type the print character type +/// @tparam continuation the continuation receiving the expanded run +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param cont the continuation to invoke +/// @param t the current argument +/// @param args the remaining arguments +/// @return decltype(auto) the continuation result template inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, T &&t, Args &&...args) { @@ -2949,6 +3504,7 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, ::fast_io::details::print_pack< decltype(::fast_io::details::decay::print_semantic_node_ref(::std::forward(t)))>)) { + // Pack arguments are expanded in place and then rejoined with the saved tail arguments. return ::fast_io::details::decay::print_semantic_pack_apply( ::std::forward(t), ::fast_io::details::decay::print_semantic_pack_expand_initial_continuation< @@ -2957,6 +3513,7 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, } else if constexpr (already_forwarded) { + // Already-forwarded ordinary arguments are captured by reference and prepended to the expanded tail. return ::fast_io::details::decay::print_semantic_pack_expand( ::fast_io::details::decay::print_semantic_value_prefix_continuation{ __builtin_addressof(cont), __builtin_addressof(t)}, @@ -2964,6 +3521,7 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, } else { + // Ordinary inputs are aliased and print-forwarded once, then kept alive through the prefix continuation. auto forwarded{::fast_io::io_print_forward(::fast_io::io_print_alias(::std::forward(t)))}; return ::fast_io::details::decay::print_semantic_pack_expand( ::fast_io::details::decay::print_semantic_lvalue_prefix_continuation< @@ -2972,6 +3530,14 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, } } +/// @brief Completes null filtering for a semantic argument run. +/// @details If the original run contained only nulls, one null is preserved so the downstream printer sees a valid +/// no-output argument; otherwise all null arguments are removed. +/// @tparam had_null true when at least one null argument was seen +/// @tparam has_value true when at least one non-null argument was preserved +/// @tparam continuation the continuation receiving the filtered run +/// @param cont the continuation to invoke +/// @return decltype(auto) the continuation result template #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] @@ -2980,14 +3546,26 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont) { if constexpr (!has_value && had_null) { + // A run made only of nulls preserves one null to represent intentional empty output. return cont(::fast_io::io_null); } else { + // A run with real values drops all nulls before invoking the continuation. return cont(); } } +/// @brief Removes null arguments from a semantic run before flattened emission. +/// @tparam had_null true when at least one null argument was seen before T +/// @tparam has_value true when at least one non-null argument was preserved before T +/// @tparam continuation the continuation receiving the filtered run +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param cont the continuation to invoke +/// @param t the current argument +/// @param args the remaining arguments +/// @return decltype(auto) the continuation result template #if __has_cpp_attribute(__gnu__::__flatten__) [[__gnu__::__flatten__]] @@ -2996,11 +3574,13 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont, { if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) { + // Null output is removed from mixed semantic runs but remembered in case the whole run is null. return ::fast_io::details::decay::print_semantic_filter_nulls( ::std::forward(cont), ::std::forward(args)...); } else { + // Non-null output is preserved by prepending it to the filtered tail continuation. return ::fast_io::details::decay::print_semantic_filter_nulls( ::fast_io::details::decay::print_semantic_value_prefix_continuation{ __builtin_addressof(cont), __builtin_addressof(t)}, @@ -3008,6 +3588,9 @@ inline constexpr decltype(auto) print_semantic_filter_nulls(continuation &&cont, } } +/// @brief Checks whether one decayed print argument is accepted by the freestanding dispatcher. +/// @tparam char_type the output character type +/// @tparam T the decayed argument type template <::std::integral char_type, typename T> struct print_freestanding_decay_param_okay_single : ::std::bool_constant< @@ -3027,27 +3610,41 @@ namespace operations namespace decay { +/// @brief Freestanding print dispatcher for runs that do not require semantic pack expansion. +/// @details The dispatcher handles status-print customizations, empty line output, mutex-wrapped streams, buffered +/// streams, and the generic control emitter. +/// @tparam line true when a trailing newline is appended +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the print run +/// @param optstm the output stream reference +/// @param args the arguments to emit +/// @return decltype(auto) the selected output path's return value, when any template inline constexpr decltype(auto) print_freestanding_decay_no_pack(outputstmtype optstm, Args... args) { if constexpr (::fast_io::operations::decay::defines::has_status_print_define) { + // Status-print streams handle the complete run through their stream-specific customization. return status_print_define(optstm, args...); } else if constexpr (sizeof...(Args) == 0) { + // Empty runs either emit only the requested newline or produce no output. if constexpr (line) { + // The line variant of an empty run emits exactly one newline character. using char_type = typename outputstmtype::output_char_type; return ::fast_io::operations::decay::char_put_decay(optstm, char_literal_v); } else { + // The non-line variant of an empty run emits nothing. return; } } else if constexpr (::fast_io::operations::decay::defines::has_output_or_io_stream_mutex_ref_define) { + // Mutex-wrapped streams lock once and delegate output to the unlocked stream reference. ::fast_io::operations::decay::stream_ref_decay_lock_guard lg{ ::fast_io::operations::decay::output_stream_mutex_ref_decay(optstm)}; return ::fast_io::operations::decay::print_freestanding_decay_no_pack( @@ -3055,59 +3652,94 @@ inline constexpr decltype(auto) print_freestanding_decay_no_pack(outputstmtype o } else if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) { + // Buffered streams use the put-area-aware control dispatcher. return ::fast_io::details::decay::print_controls_buffer_impl(optstm, args...); } else { + // Streams without a put area use the generic control dispatcher. return ::fast_io::details::decay::print_controls_impl(optstm, args...); } } +/// @brief Forward declaration for semantic-aware freestanding emission. +/// @tparam line true when a trailing newline is appended +/// @tparam already_forwarded true when inputs have already passed semantic forwarding +/// @tparam char_type the output character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the print run +/// @param optstm the output stream reference +/// @param args the arguments to emit template inline constexpr void print_semantic_emit(outputstmtype optstm, Args &&...args); +/// @brief Writes a repeated fill character to a stream in fixed-size chunks. +/// @tparam char_type the output character type +/// @tparam outputstmtype the decayed output stream reference type +/// @param optstm the output stream reference +/// @param n the number of fill characters to emit +/// @param ch the fill character template <::std::integral char_type, typename outputstmtype> inline constexpr void print_semantic_write_fill(outputstmtype optstm, ::std::size_t n, char_type ch) { if (n == 0) { + // Empty padding emits no output. return; } char_type buffer[64]; ::fast_io::details::my_fill_n(buffer, static_cast<::std::size_t>(64u), ch); while (static_cast<::std::size_t>(64u) < n) { + // Full fill chunks are written until only a tail chunk remains. ::fast_io::operations::decay::write_all_decay(optstm, buffer, buffer + 64); n -= static_cast<::std::size_t>(64u); } ::fast_io::operations::decay::write_all_decay(optstm, buffer, buffer + n); } +/// @brief Forward declaration for measuring a semantic argument after input forwarding. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to measure +/// @return ::std::size_t the precise emitted character count template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t); +/// @brief Computes the precise emitted size for a semantic leaf. +/// @details Leaves use their most direct sizing protocol; reserve-scatters leaves materialize descriptors only to +/// sum the generated scatter lengths. +/// @tparam char_type the output character type +/// @tparam T the semantic leaf type +/// @param t the leaf to measure +/// @return ::std::size_t the precise emitted character count template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) { using value_type = ::std::remove_cvref_t; if constexpr (::std::same_as) { + // Null leaves emit no characters. return 0; } else if constexpr (::fast_io::static_precise_reserve_printable) { + // Static precise reserve leaves expose their character count at compile time. return print_reserve_static_precise_size(::fast_io::io_reserve_type); } else if constexpr (::fast_io::precise_reserve_printable) { + // Run-time precise reserve leaves report their exact character count before emission. return print_reserve_precise_size(::fast_io::io_reserve_type, ::std::forward(t)); } else if constexpr (::fast_io::scatter_printable) { + // Scatter leaves already expose the single contiguous output range length. return print_scatter_define(::fast_io::io_reserve_type, ::std::forward(t)).len; } else if constexpr (::fast_io::reserve_scatters_printable) { + // Reserve-scatters leaves must build their scatters before the total length can be summed. constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; static_assert(sz.scatters_size != 0); constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; @@ -3117,6 +3749,7 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) ::fast_io::basic_io_scatter_t>}; if constexpr (stack_buffer_ok) { + // Small reserve-scatters sizing uses stack descriptor and reserve storage. ::fast_io::basic_io_scatter_t scatters[sz.scatters_size]; char_type buffer[reserve_buffer_size]; auto result{print_reserve_scatters_define(::fast_io::io_reserve_type, scatters, @@ -3124,12 +3757,14 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) ::std::size_t len{}; for (auto iter{scatters}; iter != result.scatters_pos_ptr; ++iter) { + // Each produced scatter contributes its character length to the precise total. len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); } return len; } else { + // Large reserve-scatters sizing allocates descriptor and reserve storage before summing lengths. ::fast_io::details::local_operator_new_array_ptr<::fast_io::basic_io_scatter_t> scatters( sz.scatters_size); ::fast_io::details::local_operator_new_array_ptr buffer(reserve_buffer_size); @@ -3138,6 +3773,7 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) ::std::size_t len{}; for (auto iter{scatters.ptr}; iter != result.scatters_pos_ptr; ++iter) { + // Each produced scatter contributes its character length to the precise total. len = ::fast_io::details::intrinsics::add_or_overflow_die(len, iter->len); } return len; @@ -3145,41 +3781,70 @@ inline constexpr ::std::size_t print_semantic_precise_size_leaf(T &&t) } } +/// @brief Normalizes a width placement value into its integral dispatch code. +/// @tparam placement_type the placement enumeration or integral-like type +/// @param placement the placement value +/// @return ::std::size_t the normalized placement code template inline constexpr ::std::size_t print_semantic_width_placement_code(placement_type placement) noexcept { return static_cast<::std::size_t>(placement); } +/// @brief Returns the fill character for a semantic width node. +/// @tparam char_type the output character type +/// @tparam width_traits the compile-time width trait bundle +/// @tparam width_reference_type the width node reference type +/// @param wr the width node reference +/// @return char_type the selected fill character template <::std::integral char_type, typename width_traits, typename width_reference_type> inline constexpr char_type print_semantic_width_fill_char(width_reference_type &&wr) noexcept { if constexpr (width_traits::has_fill_char) { + // Width nodes with an explicit fill character cast it to the active output character type. return static_cast(wr.ch); } else { + // Width nodes without an explicit fill character use a space. return ::fast_io::char_literal_v; } } +/// @brief Returns the width placement selected by static traits or by the node at run time. +/// @tparam width_traits the compile-time width trait bundle +/// @tparam width_reference_type the width node reference type +/// @param wr the width node reference +/// @return auto the selected placement value template inline constexpr auto print_semantic_width_placement(width_reference_type &&wr) noexcept { if constexpr (width_traits::runtime_placement) { + // Runtime-placement width nodes carry the placement value in the node itself. return wr.placement; } else { + // Static-placement width nodes use the placement encoded in their traits. return width_traits::static_placement; } } +/// @brief Forward declaration for measuring a semantic argument's internal padding shift. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to inspect +/// @return ::std::size_t the internal shift position, or zero when unavailable template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t); +/// @brief Computes the internal padding insertion point for a semantic node or leaf. +/// @tparam char_type the output character type +/// @tparam T the semantic node or leaf type +/// @param t the semantic node or leaf to inspect +/// @return ::std::size_t the internal shift position, or zero when unavailable template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_internal_shift(T &&t) { @@ -3187,32 +3852,44 @@ inline constexpr ::std::size_t print_semantic_internal_shift(T &&t) using node_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::decay::print_semantic_condition_v) { + // Conditions measure the internal shift of the branch that will actually be emitted. if (node_ref.pred) { + // The true branch is selected for internal padding placement. return ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.t1); } + // The false branch is selected for internal padding placement. return ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.t2); } else if constexpr (::fast_io::details::print_pack || ::fast_io::details::decay::print_semantic_width_v) { + // Packs and nested width nodes do not expose one stable leaf-level internal shift. return 0; } else { + // Leaf nodes may provide a print_define_internal_shift customization. using value_type = ::std::remove_cvref_t; if constexpr (::fast_io::printable_internal_shift) { + // The leaf customization reports the number of characters before internal padding. return print_define_internal_shift(::fast_io::io_reserve_type, ::std::forward(node_ref)); } else { + // Leaves without an internal-shift customization fall back to ordinary right placement. return 0; } } } +/// @brief Applies semantic input forwarding before measuring internal padding shift. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to inspect +/// @return ::std::size_t the internal shift position, or zero when unavailable template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t) { @@ -3222,11 +3899,16 @@ inline constexpr ::std::size_t print_semantic_internal_shift_arg(T &&t) ::std::forward(forwarded)); } +/// @brief Continuation that accumulates precise sizes for expanded semantic pack elements. +/// @tparam char_type the output character type template <::std::integral char_type> struct print_semantic_precise_size_pack_continuation { ::std::size_t *totalptr; + /// @brief Adds every expanded pack element size to the running precise total. + /// @tparam PackArgs the expanded pack element types + /// @param pack_args the expanded pack elements template inline constexpr void operator()(PackArgs &&...pack_args) const { @@ -3238,6 +3920,11 @@ struct print_semantic_precise_size_pack_continuation } }; +/// @brief Computes the precise emitted size for a semantic node or leaf. +/// @tparam char_type the output character type +/// @tparam T the semantic node or leaf type +/// @param t the semantic node or leaf to measure +/// @return ::std::size_t the precise emitted character count template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_precise_size(T &&t) { @@ -3245,6 +3932,7 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) using node_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::print_pack) { + // Packs sum the precise size of every stored element. ::std::size_t total{}; ::fast_io::details::decay::print_semantic_pack_apply( ::std::forward(node_ref), @@ -3254,17 +3942,21 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) { + // Conditions measure only the branch that will be emitted. if (node_ref.pred) { + // The predicate selected the true branch for size calculation. return ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.t1); } else { + // The predicate selected the false branch for size calculation. return ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.t2); } } else if constexpr (::fast_io::details::decay::print_semantic_width_v) { + // Width nodes measure the child and then account for the requested field width. using width_traits = ::fast_io::details::decay::print_semantic_width_traits; ::std::size_t const child_len{ ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; @@ -3275,17 +3967,25 @@ inline constexpr ::std::size_t print_semantic_precise_size(T &&t) ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; if (width <= child_len || placement_code == 0u) { + // Disabled placement or an already-wide child emits only the child size. return child_len; } + // Active placement pads the child up to the requested field width. return width; } else { + // Leaf nodes use their leaf-specific precise-size protocol. return ::fast_io::operations::decay::print_semantic_precise_size_leaf( ::std::forward(node_ref)); } } +/// @brief Applies semantic input forwarding before precise-size measurement. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to measure +/// @return ::std::size_t the precise emitted character count template <::std::integral char_type, typename T> inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t) { @@ -3295,6 +3995,12 @@ inline constexpr ::std::size_t print_semantic_precise_size_arg(T &&t) ::std::forward(forwarded)); } +/// @brief Forward declaration for unchecked semantic emission into an already-sized buffer. +/// @tparam char_type the destination buffer character type +/// @tparam T the semantic node or leaf type +/// @param iter the current output cursor +/// @param t the semantic node or leaf to emit +/// @return char_type* one past the emitted output template <::std::integral char_type, typename T> inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t); @@ -3422,7 +4128,7 @@ inline constexpr bool print_semantic_pack_element_common_factor_equal(Pack1 &&pa decltype(auto) first{::fast_io::containers::get(::std::forward(pack1).storage)}; decltype(auto) second{::fast_io::containers::get(::std::forward(pack2).storage)}; if constexpr (::fast_io::operations::decay::print_semantic_common_factor_comparable) + decltype(second)>) { // Comparable leaves may be shared when their equality predicate proves identical emitted bytes. return ::fast_io::operations::decay::print_semantic_common_factor_equal( @@ -3476,10 +4182,11 @@ inline constexpr char_type *print_semantic_emit_unchecked_pack_range(char_type * /// @return char_type* a pointer one past the emitted condition-pack range template <::std::integral char_type, ::std::size_t first, ::std::size_t last, typename Pack1, typename Pack2> inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factored(char_type *iter, bool pred, - Pack1 &&pack1, Pack2 &&pack2) + Pack1 &&pack1, Pack2 &&pack2) { if constexpr (first < last) { + // A non-empty candidate range first tries to peel a shared prefix element. if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { @@ -3491,6 +4198,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_condition_pack_factore } if constexpr (first + 1u < last) { + // A range with at least two elements can also peel a shared suffix element. constexpr ::std::size_t suffix_index{last - 1u}; if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) @@ -3549,6 +4257,7 @@ print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pr { if constexpr (first < last) { + // A non-empty candidate range can start factoring from either edge. if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) { @@ -3562,6 +4271,7 @@ print_semantic_emit_unchecked_condition_pack_try_factor(char_type *iter, bool pr } if constexpr (first + 1u < last) { + // A range with at least two elements can try a suffix factor after prefix factoring fails. constexpr ::std::size_t suffix_index{last - 1u}; if (::fast_io::operations::decay::print_semantic_pack_element_common_factor_equal( ::std::forward(pack1), ::std::forward(pack2))) @@ -3588,6 +4298,9 @@ struct print_semantic_emit_unchecked_pack_continuation { char_type **iterptr; + /// @brief Emits every expanded pack element and updates the shared output cursor. + /// @tparam PackArgs the expanded pack element types + /// @param pack_args the expanded pack elements template inline constexpr void operator()(PackArgs &&...pack_args) const { @@ -3621,6 +4334,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } else if constexpr (::fast_io::precise_reserve_printable) { + // Precise reserve leaves compute their exact size before choosing the define protocol. ::std::size_t const precise_size{ print_reserve_precise_size(::fast_io::io_reserve_type, t)}; if constexpr (requires { @@ -3650,6 +4364,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, } else if constexpr (::fast_io::reserve_scatters_printable) { + // Reserve-scatters leaves materialize temporary descriptors before copying each segment. constexpr auto sz{print_reserve_scatters_size(::fast_io::io_reserve_type)}; static_assert(sz.scatters_size != 0); constexpr ::std::size_t reserve_buffer_size{sz.reserve_size == 0 ? 1u : sz.reserve_size}; @@ -3666,6 +4381,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, buffer, ::std::forward(t))}; for (auto scatter_iter{scatters}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) { + // Each produced scatter segment is copied into the unchecked contiguous output buffer. iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); } return iter; @@ -3680,6 +4396,7 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, scatters.ptr, buffer.ptr, ::std::forward(t))}; for (auto scatter_iter{scatters.ptr}; scatter_iter != result.scatters_pos_ptr; ++scatter_iter) { + // Each produced scatter segment is copied into the unchecked contiguous output buffer. iter = ::fast_io::details::non_overlapped_copy_n(scatter_iter->base, scatter_iter->len, iter); } return iter; @@ -3712,6 +4429,7 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) { + // Condition nodes choose one alternative, with optional common factoring for small pack alternatives. using first_type = ::std::remove_cvref_t; using second_type = ::std::remove_cvref_t; if constexpr (::fast_io::details::print_pack && ::fast_io::details::print_pack) @@ -3741,6 +4459,7 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else { + // Large or unequal pack alternatives skip factoring and use the predicate directly. if (node_ref.pred) { // Large or unequal pack alternatives are emitted through the true branch without factoring. @@ -3753,6 +4472,7 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else { + // Non-pack alternatives are emitted by the predicate without common-factor analysis. if (node_ref.pred) { // Non-pack condition alternatives emit the true branch selected by the predicate. @@ -3764,6 +4484,7 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else if constexpr (::fast_io::details::decay::print_semantic_width_v) { + // Width nodes need child length, fill, and placement before writing padded output. using width_traits = ::fast_io::details::decay::print_semantic_width_traits; ::std::size_t const len{ ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; @@ -3966,6 +4687,7 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, } else { + // Non-static precise runs can still coalesce when the measured size fits the stack threshold. if (required <= threshold_chars) { // A run-time bounded run uses the threshold-sized stack buffer and one write operation. @@ -4023,6 +4745,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Direct-buffer padding is required before the formatted field can be committed. ::std::size_t const padding{width - len}; if (placement_code == 1u) { @@ -4100,6 +4823,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & : static_cast<::std::size_t>(256u)}; if constexpr (small_width_buffer_size != 0u) { + // A non-zero small-width buffer enables a stack-buffer fallback for compact formatted fields. if (required <= small_width_buffer_size) { // Small width-formatted fields use a bounded stack buffer and one stream write. @@ -4112,6 +4836,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Stack-buffer padding is required before the formatted field can be written once. ::std::size_t const padding{width - len}; if (placement_code == 1u) { @@ -4184,6 +4909,7 @@ inline constexpr void print_semantic_emit_width_direct(outputstmtype optstm, T & } else { + // Streaming fallback padding is written directly around the child output. ::std::size_t const padding{width - len}; if (placement_code == 1u) { @@ -4256,6 +4982,9 @@ struct print_semantic_emit_node_pack_continuation { outputstmtype optstm; + /// @brief Emits all expanded pack elements through the checked semantic emitter. + /// @tparam PackArgs the expanded pack element types + /// @param pack_args the expanded pack elements template inline constexpr void operator()(PackArgs &&...pack_args) const { @@ -4320,6 +5049,7 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) } else { + // Buffered fallback padding is written around the materialized child bytes. ::std::size_t const padding{width - len}; if (placement_code == 1u) { @@ -4398,6 +5128,7 @@ inline constexpr void print_semantic_emit_node(outputstmtype optstm, T &&t) } else if constexpr (::fast_io::details::decay::print_semantic_condition_v) { + // Checked condition nodes dispatch exactly the branch selected by the predicate. if (node_ref.pred) { // Condition nodes emit the true alternative selected by the predicate. @@ -4440,6 +5171,10 @@ struct print_semantic_emit_flat_prefix_continuation ::std::remove_reference_t *contptr; ::std::remove_reference_t *valueptr; + /// @brief Prepends the captured plain argument to the flattened prefix. + /// @tparam prefix_line true when the prefix should append a newline + /// @tparam Prefix the already-accumulated prefix argument types + /// @param prefix the already-accumulated prefix arguments template inline constexpr void operator()(Prefix &&...prefix) const { @@ -4490,6 +5225,10 @@ struct print_semantic_emit_freestanding_continuation { outputstmtype optstm; + /// @brief Emits the accumulated flattened prefix through the best freestanding path. + /// @tparam prefix_line true when this prefix should append a newline + /// @tparam Prefix the flattened prefix argument types + /// @param prefix the flattened prefix arguments template inline constexpr void operator()(Prefix &&...prefix) const { @@ -4520,6 +5259,9 @@ struct print_semantic_emit_flat_continuation { outputstmtype optstm; + /// @brief Emits a filtered semantic run, preferring precise coalescing before flattening. + /// @tparam FilteredArgs the filtered argument types + /// @param filtered_args the filtered arguments template inline constexpr void operator()(FilteredArgs &&...filtered_args) const { @@ -4542,6 +5284,9 @@ struct print_semantic_filter_flat_continuation { ::std::remove_reference_t *emitflatptr; + /// @brief Removes null outputs from a flattened semantic run before emission. + /// @tparam FlatArgs the flattened argument types + /// @param flat_args the flattened arguments template inline constexpr void operator()(FlatArgs &&...flat_args) const { @@ -4606,6 +5351,7 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A } else if constexpr (sizeof...(Args) == 0) { + // Empty decayed runs either emit only the requested newline or produce no output. if constexpr (line) { // An empty line-print run emits exactly one newline character. @@ -4634,6 +5380,7 @@ inline constexpr decltype(auto) print_freestanding_decay(outputstmtype optstm, A } else { + // Non-semantic non-empty runs choose between the fast reserve/scatter entry and the generic path. using char_type = typename outputstmtype::output_char_type; if constexpr (::fast_io::details::decay::print_controls_dynamic_scatters_reserve_fast_entry_available< char_type, ::std::remove_cvref_t...>()) From 3051a7ae05db5ebdff6319888e007e24bf0c2164 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 14:47:01 +0800 Subject: [PATCH 20/45] Enhance template function support for scatter types in range views - Added new template specializations for `sized_range_view_t` and `range_view_t` to support various scatter types, improving flexibility in handling input iterators. - Introduced constraints to ensure proper type handling, enhancing the robustness of the range view implementations in the fast_io library. --- .../include/fast_io_core_impl/range_view.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/third-parties/fast_io/include/fast_io_core_impl/range_view.h b/third-parties/fast_io/include/fast_io_core_impl/range_view.h index 20dce14a0..261b73b43 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/range_view.h +++ b/third-parties/fast_io/include/fast_io_core_impl/range_view.h @@ -12,9 +12,18 @@ struct sized_range_view_t iterator begin; ::std::size_t size; }; + template <::std::integral char_type, ::std::input_iterator I> sized_range_view_t(basic_io_scatter_t, I, ::std::size_t) -> sized_range_view_t; +template + requires requires(scatter_type scatter) { + typename ::std::remove_cvref_t::value_type; + static_cast::value_type>>(scatter); + } +sized_range_view_t(scatter_type, I, ::std::size_t) + -> sized_range_view_t::value_type, I>; + template <::std::integral ch_type, ::std::input_iterator It> struct range_view_t { @@ -24,9 +33,17 @@ struct range_view_t iterator begin; iterator end; }; + template <::std::integral char_type, ::std::input_iterator I> range_view_t(basic_io_scatter_t, I, I) -> range_view_t; +template + requires requires(scatter_type scatter) { + typename ::std::remove_cvref_t::value_type; + static_cast::value_type>>(scatter); + } +range_view_t(scatter_type, I, I) -> range_view_t::value_type, I>; + template <::std::integral char_type, ::std::input_iterator It> inline constexpr ::std::size_t print_reserve_size(io_reserve_type_t>, sized_range_view_t t) From 89b0a3f2f25ba3af1dba49b2a32a504f0aece1de Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 15:00:54 +0800 Subject: [PATCH 21/45] Refactor character integral handling in fast_io - Updated the handling of character code units to clearly separate them from numeric integrals, ensuring that default printing does not mistakenly format characters as numbers. - Renamed the `cintegral` function to `char_as_integer` for better clarity in its purpose. - Added comments to clarify the rationale behind the separation of character code units from numeric types, enhancing code readability and maintainability. --- .../fast_io/include/fast_io_core_impl/integers/impl.h | 5 ++++- .../fast_io/include/fast_io_core_impl/integers/pointer.h | 2 ++ third-parties/fast_io/include/fast_io_core_impl/utils.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index 007e452de..2ee9a32f1 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -1062,6 +1062,9 @@ inline constexpr auto uhexupperfull(scalar_type t) noexcept static_cast<::fast_io::details::my_make_unsigned_t<::std::remove_cvref_t>>(t)); } +// Character code units are deliberately separated from the default numeric +// print path even though they satisfy ::std::integral. Use chvw for a character +// view and cintegral when the numeric value of a character code unit is intended. template requires(::fast_io::details::scalar_integrals) inline constexpr auto dec(scalar_type t) noexcept @@ -1072,7 +1075,7 @@ inline constexpr auto dec(scalar_type t) noexcept template requires(::fast_io::details::character_integral) -inline constexpr auto cintegral(scalar_type t) noexcept +inline constexpr auto char_as_integer(scalar_type t) noexcept { return dec(t); } diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h index 69bedcabd..a56083053 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h @@ -47,6 +47,8 @@ struct static_scatter_t template <::std::integral T> inline constexpr chvw_t chvw(T ch) noexcept { + // Explicit single-character view. This marker avoids the ambiguous path where + // character code units are both ::std::integral values and printable text. return {ch}; } diff --git a/third-parties/fast_io/include/fast_io_core_impl/utils.h b/third-parties/fast_io/include/fast_io_core_impl/utils.h index 4a4a9a0ff..0e6e13bbc 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/utils.h +++ b/third-parties/fast_io/include/fast_io_core_impl/utils.h @@ -141,6 +141,9 @@ concept my_integral = #endif ; +// Character code-unit types are a special case: they satisfy ::std::integral, +// but fast_io also treats them as characters. Keep them split from ordinary +// numeric integrals so default printing does not accidentally format 'x' as 120. template concept character_integral = ::std::same_as<::std::remove_cvref_t, char> || ::std::same_as<::std::remove_cvref_t, wchar_t> || From 5921313b9bc60d80a115b16aedf7e09e138829a7 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 15:44:18 +0800 Subject: [PATCH 22/45] Enhance floating-point precision handling in fast_io - Introduced new floating precision options, including `significant_preserve_tailing_zero` and `fractional_preserve_tailing_zero`, to improve control over floating-point formatting. - Updated existing functions to utilize the new precision options, enhancing the flexibility and accuracy of floating-point output. - Refactored related logic to ensure proper handling of precision modes, improving code clarity and maintainability. --- .../include/fast_io_core_impl/integers/impl.h | 106 ++++++++- .../fast_io_freestanding_impl/stack/custom.h | 34 +-- .../include/fast_io_unit/floating/decfloat.h | 175 +++++++++++++- .../include/fast_io_unit/floating/hexfloat.h | 214 ++++++++++++++++-- .../include/fast_io_unit/floating/impl.h | 23 +- .../include/fast_io_unit/floating/roundtrip.h | 131 +++++++++-- 6 files changed, 603 insertions(+), 80 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index 2ee9a32f1..d9f466668 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -64,7 +64,9 @@ enum class floating_nan_payload_scan : ::std::uint_fast8_t enum class floating_precision : ::std::uint_least8_t { significant, - fractional + fractional, + significant_preserve_tailing_zero, + fractional_preserve_tailing_zero }; enum class floating_rounding : ::std::uint_least8_t @@ -373,6 +375,21 @@ inline constexpr ::fast_io::manipulators::scalar_flags floating_precision_roundi ::fast_io::details::set_floating_rounding_flag( ::fast_io::details::set_floating_precision_flag(flags, precision), rounding)}; +template <::fast_io::manipulators::floating_precision precision> +inline constexpr bool floating_precision_is_significant{ + precision == ::fast_io::manipulators::floating_precision::significant || + precision == ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero}; + +template <::fast_io::manipulators::floating_precision precision> +inline constexpr bool floating_precision_is_fractional{ + precision == ::fast_io::manipulators::floating_precision::fractional || + precision == ::fast_io::manipulators::floating_precision::fractional_preserve_tailing_zero}; + +template <::fast_io::manipulators::floating_precision precision> +inline constexpr bool floating_precision_preserves_trailing_zero{ + precision == ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero || + precision == ::fast_io::manipulators::floating_precision::fractional_preserve_tailing_zero}; + inline constexpr ::fast_io::manipulators::scalar_flags set_json_float_flag(::fast_io::manipulators::scalar_flags flags, bool json_float) noexcept { @@ -760,7 +777,7 @@ inline constexpr auto scalar_flags_int_cache(scalar_type t) noexcept } template -concept scalar_integrals = ::fast_io::details::my_integral || +concept scalar_integrals = ::fast_io::details::non_character_integral || ::std::same_as<::std::nullptr_t, ::std::remove_cvref_t> || ::std::same_as<::std::byte, ::std::remove_cvref_t> || ::std::same_as> || @@ -1064,7 +1081,7 @@ inline constexpr auto uhexupperfull(scalar_type t) noexcept // Character code units are deliberately separated from the default numeric // print path even though they satisfy ::std::integral. Use chvw for a character -// view and cintegral when the numeric value of a character code unit is intended. +// view and char_as_integer when the numeric value of a character code unit is intended. template requires(::fast_io::details::scalar_integrals) inline constexpr auto dec(scalar_type t) noexcept @@ -1077,7 +1094,8 @@ template requires(::fast_io::details::character_integral) inline constexpr auto char_as_integer(scalar_type t) noexcept { - return dec(t); + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<10, false, shbase, full>>(t); } template @@ -1160,24 +1178,56 @@ inline constexpr auto hexfloat0x(scalar_type t) noexcept static_cast(t)}; } -template +template requires(::fast_io::details::my_floating_point) inline constexpr auto hexfloat(scalar_type t, ::std::size_t n) noexcept { using float_alias_type = ::fast_io::details::float_alias_type; - return scalar_manip_precision_t<::fast_io::details::hexafloat_mani_flags_cache, float_alias_type>{ + return scalar_manip_precision_t< + ::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::details::hexafloat_mani_flags_cache, precision_mode, rounding_policy>, + float_alias_type>{ static_cast(t), n}; } -template +template + requires(::fast_io::details::my_floating_point) +inline constexpr auto hexfloat(scalar_type t, ::std::size_t n) noexcept +{ + return hexfloat(t, n); +} + +template requires(::fast_io::details::my_floating_point) inline constexpr auto hexfloat0x(scalar_type t, ::std::size_t n) noexcept { using float_alias_type = ::fast_io::details::float_alias_type; - return scalar_manip_precision_t<::fast_io::details::hexafloat_mani_flags_cache, float_alias_type>{ + return scalar_manip_precision_t< + ::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::details::hexafloat_mani_flags_cache, precision_mode, rounding_policy>, + float_alias_type>{ static_cast(t), n}; } +template + requires(::fast_io::details::my_floating_point) +inline constexpr auto hexfloat0x(scalar_type t, ::std::size_t n) noexcept +{ + return hexfloat0x(t, n); +} + template requires(::fast_io::details::my_floating_point) inline constexpr auto comma_hexfloat(scalar_type t) noexcept @@ -1196,24 +1246,56 @@ inline constexpr auto comma_hexfloat0x(scalar_type t) noexcept static_cast(t)}; } -template +template requires(::fast_io::details::my_floating_point) inline constexpr auto comma_hexfloat(scalar_type t, ::std::size_t n) noexcept { using float_alias_type = ::fast_io::details::float_alias_type; - return scalar_manip_precision_t<::fast_io::details::hexafloat_mani_flags_cache, float_alias_type>{ + return scalar_manip_precision_t< + ::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::details::hexafloat_mani_flags_cache, precision_mode, rounding_policy>, + float_alias_type>{ static_cast(t), n}; } -template +template + requires(::fast_io::details::my_floating_point) +inline constexpr auto comma_hexfloat(scalar_type t, ::std::size_t n) noexcept +{ + return comma_hexfloat(t, n); +} + +template requires(::fast_io::details::my_floating_point) inline constexpr auto comma_hexfloat0x(scalar_type t, ::std::size_t n) noexcept { using float_alias_type = ::fast_io::details::float_alias_type; - return scalar_manip_precision_t<::fast_io::details::hexafloat_mani_flags_cache, float_alias_type>{ + return scalar_manip_precision_t< + ::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::details::hexafloat_mani_flags_cache, precision_mode, rounding_policy>, + float_alias_type>{ static_cast(t), n}; } +template + requires(::fast_io::details::my_floating_point) +inline constexpr auto comma_hexfloat0x(scalar_type t, ::std::size_t n) noexcept +{ + return comma_hexfloat0x(t, n); +} + template requires(::fast_io::details::my_floating_point) inline constexpr auto decimal(scalar_type t) noexcept diff --git a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h index 9094f8923..a35eae86f 100644 --- a/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h +++ b/third-parties/fast_io/include/fast_io_freestanding_impl/stack/custom.h @@ -38,33 +38,37 @@ Different libraries or binaries built with different edited values may therefore coexist without requiring their print stack policy instantiations to be shared. The tradeoff is possible code-size growth from separate template instantiations. */ -template -struct print_stack_buffer_default_max_bytes + +namespace details { - static inline constexpr ::std::size_t default_value{ +inline constexpr ::std::size_t default_stack_buffer_value{ #if defined(__KERNEL__) || defined(_KERNEL) || defined(_KERNEL_MODE) - 256u + 256u #elif defined(__EMSCRIPTEN__) || defined(__wasm32__) || defined(__wasm64__) || defined(__wasm__) - 4u * 1024u + 4u * 1024u #elif !defined(__STDC_HOSTED__) || (__STDC_HOSTED__ == 0) - 256u + 256u #elif defined(_WIN32) || defined(__CYGWIN__) - 32u * 1024u + 32u * 1024u #elif defined(__APPLE__) && defined(__MACH__) - 16u * 1024u + 16u * 1024u #elif defined(__linux__) && defined(__gnu_linux__) - 64u * 1024u + 64u * 1024u #elif defined(__linux__) - 4u * 1024u + 4u * 1024u #elif defined(__unix__) || defined(__unix) || defined(unix) - 16u * 1024u + 16u * 1024u #else - 16u * 1024u + 16u * 1024u #endif - }; +}; +} // namespace details - // Users and downstream vendors may manually replace `value` below to tune the default maximum number of bytes that stack-based print materialization may use. - static inline constexpr ::std::size_t value{default_value}; +// Users and downstream vendors may manually replace `value` below to tune the default maximum number of bytes that stack-based print materialization may use. +template <::std::size_t custom_value = details::default_stack_buffer_value> +struct print_stack_buffer_default_max_bytes +{ + static inline constexpr ::std::size_t value{custom_value}; }; } // namespace fast_io::custom diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 4303f33bc..b568bba0f 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -949,7 +949,7 @@ scan_decfloat_assign_precision(T &value, bool negative, scan_decfloat_significan { auto adjusted_exponent{::fast_io::details::scan_decfloat_adjusted_exponent(state, exponent)}; auto significand{state.significand}; - if constexpr (precision_mode == ::fast_io::manipulators::floating_precision::significant) + if constexpr (::fast_io::details::floating_precision_is_significant) { if (!precision) { @@ -1442,6 +1442,179 @@ scan_contiguous_define(io_reserve_type_t + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr auto +scan_context_type(io_reserve_type_t>) noexcept +{ + return io_type_t<::fast_io::details::scan_floating_context>{}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_result +scan_context_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, char_type const *begin, + char_type const *end, + ::fast_io::manipulators::scalar_manip_t value) noexcept +{ + if (!state.size) + { + begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); + if (begin == end) + { + return {end, ::fast_io::parse_code::partial}; + } + } + auto const old_size{state.size}; + auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; + if (append_result.code != ::fast_io::parse_code::partial) + { + return append_result; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + value.reference = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state, old_size, begin, end, parse_result.iter), + ::fast_io::parse_code::ok}; + } + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::end_of_file || + parse_result.code == ::fast_io::parse_code::partial) + { + return {end, ::fast_io::parse_code::partial}; + } + return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), + parse_result.code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_code +scan_context_eof_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, + ::fast_io::manipulators::scalar_manip_t value) noexcept +{ + if (!state.size) + { + return ::fast_io::parse_code::end_of_file; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + value.reference = parsed_value; + } + return parse_result.code; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr auto +scan_context_type(io_reserve_type_t>) noexcept +{ + return io_type_t<::fast_io::details::scan_floating_context>{}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_result +scan_context_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, char_type const *begin, + char_type const *end, + ::fast_io::manipulators::scalar_manip_precision_t value) noexcept +{ + if (!state.size) + { + begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); + if (begin == end) + { + return {end, ::fast_io::parse_code::partial}; + } + } + auto const old_size{state.size}; + auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; + if (append_result.code != ::fast_io::parse_code::partial) + { + return append_result; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value, value.precision)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + value.reference = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state, old_size, begin, end, parse_result.iter), + ::fast_io::parse_code::ok}; + } + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::end_of_file || + parse_result.code == ::fast_io::parse_code::partial) + { + return {end, ::fast_io::parse_code::partial}; + } + return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), + parse_result.code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_code +scan_context_eof_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, + ::fast_io::manipulators::scalar_manip_precision_t value) noexcept +{ + if (!state.size) + { + return ::fast_io::parse_code::end_of_file; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value, value.precision)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + value.reference = parsed_value; + } + return parse_result.code; +} + template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, details::my_floating_point T> requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat && !details::scan_decfloat_supported_floating_point) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index 818360514..c57e31d49 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -1026,6 +1026,8 @@ template <::fast_io::manipulators::floating_rounding rounding> template inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *iter, flt f, ::std::size_t precision) noexcept @@ -1037,23 +1039,23 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i case ::fast_io::manipulators::floating_rounding::toward_plus_infinity: return print_rsvhexfloat_precision_define_impl< showbase, showbase_uppercase, showpos, uppercase, uppercase_e, comma, - ::fast_io::manipulators::floating_rounding::toward_plus_infinity, nan_show_sign, nan_show_type>( - iter, f, precision); + ::fast_io::manipulators::floating_rounding::toward_plus_infinity, precision_mode, nan_show_sign, + nan_show_type>(iter, f, precision); case ::fast_io::manipulators::floating_rounding::toward_minus_infinity: return print_rsvhexfloat_precision_define_impl< showbase, showbase_uppercase, showpos, uppercase, uppercase_e, comma, - ::fast_io::manipulators::floating_rounding::toward_minus_infinity, nan_show_sign, nan_show_type>( - iter, f, precision); + ::fast_io::manipulators::floating_rounding::toward_minus_infinity, precision_mode, nan_show_sign, + nan_show_type>(iter, f, precision); case ::fast_io::manipulators::floating_rounding::toward_zero: return print_rsvhexfloat_precision_define_impl< showbase, showbase_uppercase, showpos, uppercase, uppercase_e, comma, - ::fast_io::manipulators::floating_rounding::toward_zero, nan_show_sign, nan_show_type>( - iter, f, precision); + ::fast_io::manipulators::floating_rounding::toward_zero, precision_mode, nan_show_sign, + nan_show_type>(iter, f, precision); default: return print_rsvhexfloat_precision_define_impl< showbase, showbase_uppercase, showpos, uppercase, uppercase_e, comma, - ::fast_io::manipulators::floating_rounding::nearest_to_even, nan_show_sign, nan_show_type>( - iter, f, precision); + ::fast_io::manipulators::floating_rounding::nearest_to_even, precision_mode, nan_show_sign, + nan_show_type>(iter, f, precision); } } else @@ -1074,9 +1076,18 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i { return prsv_fp_nan_impl(iter, mantissa, sign); } - if (!precision) + constexpr bool fractional_precision{ + ::fast_io::details::floating_precision_is_fractional}; + constexpr bool preserve_trailing_zero{ + ::fast_io::details::floating_precision_preserves_trailing_zero}; + ::std::size_t total_precision{precision}; + if constexpr (fractional_precision) { - precision = 1u; + ++total_precision; + } + else if (!total_precision) + { + total_precision = 1u; } iter = print_rsv_fp_sign_impl(iter, sign); if constexpr (showbase) @@ -1086,11 +1097,12 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i if (!mantissa && !exponent) { iter = print_rsvhexfloat_digit_impl(iter, 0u); - if (1u < precision) + auto const digits_to_print{preserve_trailing_zero ? total_precision : 1u}; + if (1u < digits_to_print) { *iter = char_literal_v<(comma ? u8',' : u8'.'), char_type>; ++iter; - iter = ::fast_io::details::my_fill_n(iter, precision - 1u, char_literal_v); + iter = ::fast_io::details::my_fill_n(iter, digits_to_print - 1u, char_literal_v); } *iter = char_literal_v < uppercase_e ? u8'P' : u8'p', char_type > ; ++iter; @@ -1116,16 +1128,16 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i (aligned_mantissa >> shift) & static_cast(0xFu)); }; char8_t digits[total_hex_digits + 1u]{}; - auto const retained_digits{precision < total_hex_digits ? precision : total_hex_digits}; + auto const retained_digits{total_precision < total_hex_digits ? total_precision : total_hex_digits}; for (::std::size_t index{}; index != retained_digits; ++index) { digits[index] = static_cast(hex_digit_at(index)); } - if (precision < total_hex_digits) + if (total_precision < total_hex_digits) { - auto const next_digit{hex_digit_at(precision)}; + auto const next_digit{hex_digit_at(total_precision)}; bool tail_nonzero{}; - for (auto index{precision + 1u}; index != total_hex_digits; ++index) + for (auto index{total_precision + 1u}; index != total_hex_digits; ++index) { if (hex_digit_at(index)) { @@ -1134,9 +1146,9 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i } } if (::fast_io::details::print_rsvhexfloat_round_up( - sign, digits[precision - 1u], next_digit, tail_nonzero)) + sign, digits[total_precision - 1u], next_digit, tail_nonzero)) { - for (auto index{precision}; index; --index) + for (auto index{total_precision}; index; --index) { auto &digit{digits[index - 1u]}; if (digit != 0xFu) @@ -1157,18 +1169,30 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i } } } + auto digits_to_print{retained_digits}; + if constexpr (preserve_trailing_zero) + { + digits_to_print = total_precision; + } + else + { + for (; 1u < digits_to_print && !digits[digits_to_print - 1u]; --digits_to_print) + { + } + } iter = print_rsvhexfloat_digit_impl(iter, digits[0]); - if (1u < precision) + if (1u < digits_to_print) { *iter = char_literal_v<(comma ? u8',' : u8'.'), char_type>; ++iter; - for (::std::size_t index{1u}; index != retained_digits; ++index) + auto const digit_limit{digits_to_print < retained_digits ? digits_to_print : retained_digits}; + for (::std::size_t index{1u}; index != digit_limit; ++index) { iter = print_rsvhexfloat_digit_impl(iter, digits[index]); } - if (retained_digits < precision) + if (digit_limit < digits_to_print) { - iter = ::fast_io::details::my_fill_n(iter, precision - retained_digits, + iter = ::fast_io::details::my_fill_n(iter, digits_to_print - digit_limit, char_literal_v); } } @@ -1183,6 +1207,67 @@ inline constexpr ::std::size_t print_rsvhexfloat_size_cache{ print_integer_reserved_size_cache<16, showbase, true, false, mantissa_type> + 6 + print_integer_reserved_size_cache<10, true, true, false, ::std::int_least32_t>}; +template <::std::integral char_type> +struct scan_floating_context +{ + static inline constexpr ::std::size_t capacity{8192u}; + ::fast_io::freestanding::array buffer; + ::std::size_t size{}; +}; + +template <::std::integral char_type, bool noskipws> +inline constexpr char_type const *scan_floating_context_skip_space(char_type const *first, + char_type const *last) noexcept +{ + if constexpr (noskipws) + { + return first; + } + else + { + return ::fast_io::details::find_space_common_impl(first, last); + } +} + +template <::std::integral char_type> +[[nodiscard]] inline constexpr char_type const * +scan_floating_context_map_iter(scan_floating_context const &state, ::std::size_t old_size, + char_type const *chunk_begin, char_type const *chunk_end, + char_type const *parsed_iter) noexcept +{ + auto const offset{static_cast<::std::size_t>(parsed_iter - state.buffer.data())}; + if (offset <= old_size) + { + return chunk_begin; + } + auto const consumed{offset - old_size}; + auto const chunk_size{static_cast<::std::size_t>(chunk_end - chunk_begin)}; + if (chunk_size < consumed) + { + return chunk_end; + } + return chunk_begin + consumed; +} + +template <::std::integral char_type> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_floating_context_append(scan_floating_context &state, char_type const *chunk_begin, + char_type const *chunk_end) noexcept +{ + auto const chunk_size{static_cast<::std::size_t>(chunk_end - chunk_begin)}; + if (!chunk_size) + { + return {chunk_end, ::fast_io::parse_code::partial}; + } + if (scan_floating_context::capacity - state.size < chunk_size) + { + return {chunk_begin, ::fast_io::parse_code::overflow}; + } + ::fast_io::freestanding::non_overlapped_copy_n(chunk_begin, chunk_size, state.buffer.data() + state.size); + state.size += chunk_size; + return {chunk_end, ::fast_io::parse_code::partial}; +} + } // namespace details namespace manipulators @@ -1238,4 +1323,89 @@ scan_contiguous_define(io_reserve_type_t(begin, end, value.reference); } +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T> + requires(flags.floating == ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr auto +scan_context_type(io_reserve_type_t>) noexcept +{ + return io_type_t<::fast_io::details::scan_floating_context>{}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T> + requires(flags.floating == ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_result +scan_context_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, char_type const *begin, + char_type const *end, + ::fast_io::manipulators::scalar_manip_t value) noexcept +{ + if (!state.size) + { + begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); + if (begin == end) + { + return {end, ::fast_io::parse_code::partial}; + } + } + auto const old_size{state.size}; + auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; + if (append_result.code != ::fast_io::parse_code::partial) + { + return append_result; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + value.reference = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state, old_size, begin, end, parse_result.iter), + ::fast_io::parse_code::ok}; + } + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::end_of_file || + parse_result.code == ::fast_io::parse_code::partial) + { + return {end, ::fast_io::parse_code::partial}; + } + return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), + parse_result.code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T> + requires(flags.floating == ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::fast_io::parse_code +scan_context_eof_define(io_reserve_type_t>, + ::fast_io::details::scan_floating_context &state, + ::fast_io::manipulators::scalar_manip_t value) noexcept +{ + if (!state.size) + { + return ::fast_io::parse_code::end_of_file; + } + T parsed_value{}; + auto const *buffer_begin{state.buffer.data()}; + auto const *buffer_end{buffer_begin + state.size}; + auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + value.reference = parsed_value; + } + return parse_result.code; +} + } // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_unit/floating/impl.h b/third-parties/fast_io/include/fast_io_unit/floating/impl.h index 1c0f1f634..9ad44ec64 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/impl.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/impl.h @@ -348,8 +348,9 @@ inline constexpr ::std::size_t print_reserve_size(io_reserve_type_t>, manipulators::scalar_manip_precision_t f) noexcept { - static_assert(flags.precision == manipulators::floating_precision::significant, - "fast_io hexfloat precision supports only total/significant hexadecimal digit precision"); + static_assert(::fast_io::details::floating_precision_is_significant || + ::fast_io::details::floating_precision_is_fractional, + "fast_io hexfloat precision supports significant and fractional hexadecimal digit precision"); using trait = ::fast_io::details::iec559_traits; ::std::size_t base_size{}; if constexpr (::std::same_as<::std::remove_cvref_t, long double> @@ -389,7 +390,8 @@ print_reserve_size(io_reserve_type_t; } return ::fast_io::details::intrinsics::add_or_overflow_die( - ::fast_io::details::intrinsics::add_or_overflow_die(base_size, f.precision), 8u); + ::fast_io::details::intrinsics::add_or_overflow_die(base_size, f.precision), + ::fast_io::details::floating_precision_is_fractional ? 9u : 8u); } template <::std::integral char_type, manipulators::scalar_flags flags, details::my_floating_point flt> @@ -398,8 +400,9 @@ inline constexpr char_type *print_reserve_define( io_reserve_type_t>, char_type *iter, manipulators::scalar_manip_precision_t f) noexcept { - static_assert(flags.precision == manipulators::floating_precision::significant, - "fast_io hexfloat precision supports only total/significant hexadecimal digit precision"); + static_assert(::fast_io::details::floating_precision_is_significant || + ::fast_io::details::floating_precision_is_fractional, + "fast_io hexfloat precision supports significant and fractional hexadecimal digit precision"); if constexpr (::std::same_as<::std::remove_cvref_t, long double> #if defined(__SIZEOF_FLOAT128__) || defined(__FLOAT128__) || ::std::same_as<::std::remove_cvref_t, __float128> @@ -412,7 +415,7 @@ inline constexpr char_type *print_reserve_define( { return details::print_rsvhexfloat_precision_define_impl< flags.showbase, flags.uppercase_showbase, flags.showpos, flags.uppercase, flags.uppercase_e, - flags.comma, flags.rounding, flags.nan_show_sign, flags.nan_show_type>( + flags.comma, flags.rounding, flags.precision, flags.nan_show_sign, flags.nan_show_type>( iter, static_cast<__float80>(f.reference), f.precision); } else @@ -422,21 +425,21 @@ inline constexpr char_type *print_reserve_define( { return details::print_rsvhexfloat_precision_define_impl< flags.showbase, flags.uppercase_showbase, flags.showpos, flags.uppercase, flags.uppercase_e, - flags.comma, flags.rounding, flags.nan_show_sign, flags.nan_show_type>( + flags.comma, flags.rounding, flags.precision, flags.nan_show_sign, flags.nan_show_type>( iter, static_cast<__float128>(f.reference), f.precision); } else #endif return details::print_rsvhexfloat_precision_define_impl< flags.showbase, flags.uppercase_showbase, flags.showpos, flags.uppercase, flags.uppercase_e, - flags.comma, flags.rounding, flags.nan_show_sign, flags.nan_show_type>( + flags.comma, flags.rounding, flags.precision, flags.nan_show_sign, flags.nan_show_type>( iter, static_cast(f.reference), f.precision); } else { return details::print_rsvhexfloat_precision_define_impl< flags.showbase, flags.uppercase_showbase, flags.showpos, flags.uppercase, flags.uppercase_e, flags.comma, - flags.rounding, flags.nan_show_sign, flags.nan_show_type>(iter, f.reference, f.precision); + flags.rounding, flags.precision, flags.nan_show_sign, flags.nan_show_type>(iter, f.reference, f.precision); } } @@ -452,7 +455,7 @@ print_reserve_size(io_reserve_type_t; constexpr auto reserve_floating{ - (flags.precision == manipulators::floating_precision::fractional && + (::fast_io::details::floating_precision_is_fractional && flags.floating == manipulators::floating_format::decimal) ? manipulators::floating_format::fixed : flags.floating}; diff --git a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h index ec52a6652..8d8e4f975 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h @@ -1749,7 +1749,7 @@ template <::fast_io::manipulators::floating_rounding rounding> } } -template +template inline constexpr void print_rsv_fp_round_to_significant( typename iec559_traits::mantissa_type &m10, ::std::int_least32_t &e10, ::std::size_t precision, bool negative) noexcept @@ -1787,6 +1787,10 @@ inline constexpr void print_rsv_fp_round_to_significant( } return; } + if constexpr (!preserve_trailing_zero) + { + return; + } auto const carrier_precision{precision < ::fast_io::details::iec559_traits::m10digits ? precision : ::fast_io::details::iec559_traits::m10digits}; @@ -1845,6 +1849,20 @@ inline constexpr void print_rsv_fp_round_to_fractional( e10 = target_e10; } +template +inline constexpr void print_rsv_fp_trim_trailing_zero(mantissa_type &m10, ::std::int_least32_t &e10) noexcept +{ + if (!m10) + { + return; + } + for (; m10 % 10u == 0u;) + { + m10 /= 10u; + ++e10; + } +} + template inline constexpr char_type *print_rsv_fp_append_point_zeros(char_type *iter, ::std::size_t precision) noexcept { @@ -1938,7 +1956,7 @@ inline constexpr char_type *print_rsv_fp_fixed_precision_impl(char_type *iter, return iter; } -template +template inline constexpr char_type *print_rsv_fp_scientific_precision_impl( char_type *iter, typename iec559_traits::mantissa_type m10, ::std::int_least32_t e10, ::std::size_t precision) noexcept @@ -1950,12 +1968,31 @@ inline constexpr char_type *print_rsv_fp_scientific_precision_impl( *iter = *itp1; if (precision) { - *itp1 = char_literal_v<(comma ? u8',' : u8'.'), char_type>; auto const available{static_cast<::std::size_t>(olength - 1)}; - iter = itp1 + olength; - if (available < precision) + if constexpr (preserve_trailing_zero) { - iter = ::fast_io::details::fill_zeros_impl(iter, precision - available); + *itp1 = char_literal_v<(comma ? u8',' : u8'.'), char_type>; + iter = itp1 + olength; + if (available < precision) + { + iter = ::fast_io::details::fill_zeros_impl(iter, precision - available); + } + } + else + { + auto used{available < precision ? available : precision}; + for (; used && itp1[used] == char_literal_v; --used) + { + } + if (used) + { + *itp1 = char_literal_v<(comma ? u8',' : u8'.'), char_type>; + iter = itp1 + used + 1u; + } + else + { + ++iter; + } } } else @@ -1973,22 +2010,37 @@ inline constexpr char_type *print_rsv_fp_precision_decision_impl( char_type *iter, typename iec559_traits::mantissa_type m10, ::std::int_least32_t e10, ::std::size_t precision, bool negative) noexcept { + constexpr bool uses_significant_precision{ + ::fast_io::details::floating_precision_is_significant}; + constexpr bool uses_fractional_precision{ + ::fast_io::details::floating_precision_is_fractional}; + constexpr bool preserve_trailing_zero{ + ::fast_io::details::floating_precision_preserves_trailing_zero}; if constexpr (mt == ::fast_io::manipulators::floating_format::scientific) { auto significant_precision{precision + 1u}; - if constexpr (precision_mode == ::fast_io::manipulators::floating_precision::significant) + if constexpr (uses_significant_precision) { significant_precision = precision ? precision : 1u; precision = significant_precision - 1u; } - ::fast_io::details::print_rsv_fp_round_to_significant( + ::fast_io::details::print_rsv_fp_round_to_significant( m10, e10, significant_precision, negative); - return ::fast_io::details::print_rsv_fp_scientific_precision_impl( + if constexpr (!preserve_trailing_zero) + { + ::fast_io::details::print_rsv_fp_trim_trailing_zero(m10, e10); + } + return ::fast_io::details::print_rsv_fp_scientific_precision_impl( iter, m10, e10, precision); } - else if constexpr (precision_mode == ::fast_io::manipulators::floating_precision::fractional) + else if constexpr (uses_fractional_precision) { ::fast_io::details::print_rsv_fp_round_to_fractional(m10, e10, precision, negative); + if constexpr (!preserve_trailing_zero) + { + ::fast_io::details::print_rsv_fp_trim_trailing_zero(m10, e10); + } if constexpr (mt == ::fast_io::manipulators::floating_format::general) { return ::fast_io::details::print_rsv_fp_decision_impl( - iter, m10, e10, precision); + if constexpr (preserve_trailing_zero) + { + return ::fast_io::details::print_rsv_fp_fixed_precision_impl( + iter, m10, e10, precision); + } + else + { + return ::fast_io::details::print_rsv_fp_fixed_decision_impl( + iter, m10, e10); + } } } else { - ::fast_io::details::print_rsv_fp_round_to_significant(m10, e10, precision, negative); + ::fast_io::details::print_rsv_fp_round_to_significant( + m10, e10, precision, negative); + if constexpr (!preserve_trailing_zero) + { + ::fast_io::details::print_rsv_fp_trim_trailing_zero(m10, e10); + } if constexpr (mt == ::fast_io::manipulators::floating_format::fixed) { return ::fast_io::details::print_rsv_fp_fixed_decision_impl(iter, m10, e10); @@ -2156,26 +2221,52 @@ inline constexpr char_type *print_rsvflt_precision_define_impl(char_type *iter, { *iter = char_literal_v; ++iter; - if (precision) + if constexpr (::fast_io::details::floating_precision_preserves_trailing_zero) { - *iter = char_literal_v<(comma ? u8',' : u8'.'), char_type>; - ++iter; - iter = ::fast_io::details::fill_zeros_impl(iter, precision); + if constexpr (::fast_io::details::floating_precision_is_significant) + { + precision = precision ? precision - 1u : 0u; + } + if (precision) + { + *iter = char_literal_v<(comma ? u8',' : u8'.'), char_type>; + ++iter; + iter = ::fast_io::details::fill_zeros_impl(iter, precision); + } } return print_rsv_fp_e_impl(iter, 0); } - else if constexpr (precision_mode == ::fast_io::manipulators::floating_precision::fractional) + else if constexpr (::fast_io::details::floating_precision_is_fractional) + { + *iter = char_literal_v; + ++iter; + if constexpr (json_float) + { + if (!precision || !::fast_io::details::floating_precision_preserves_trailing_zero) + { + return ::fast_io::details::print_rsv_fp_append_json_float_zero(iter); + } + } + if constexpr (::fast_io::details::floating_precision_preserves_trailing_zero) + { + return ::fast_io::details::print_rsv_fp_append_point_zeros(iter, precision); + } + return iter; + } + else if constexpr (precision_mode == + ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero) { *iter = char_literal_v; ++iter; if constexpr (json_float) { - if (!precision) + if (precision <= 1u) { return ::fast_io::details::print_rsv_fp_append_json_float_zero(iter); } } - return ::fast_io::details::print_rsv_fp_append_point_zeros(iter, precision); + return ::fast_io::details::print_rsv_fp_append_point_zeros( + iter, precision ? precision - 1u : 0u); } else { From f52345e89d3928fe9a92d18d4ea2894f06e0a0bb Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 18:11:06 +0800 Subject: [PATCH 23/45] Enhance parsing and handling of floating-point and hexadecimal formats in fast_io - Improved the parsing logic for decimal and hexadecimal floating-point numbers, adding support for special cases and overflow handling. - Introduced new utility functions for checking digit validity and handling non-zero digits in hexadecimal formats. - Refactored existing functions to enhance clarity and maintainability, ensuring robust handling of edge cases in floating-point parsing. - Updated the floating-point context management to better accommodate special values and exponent handling, improving overall parsing accuracy. --- .../integers/sto/sto_contiguous.h | 53 +- .../include/fast_io_unit/floating/decfloat.h | 811 ++++++++++++++---- .../include/fast_io_unit/floating/hexfloat.h | 627 ++++++++++++-- .../include/fast_io_unit/floating/punning.h | 8 +- 4 files changed, 1261 insertions(+), 238 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h index b0753a1f4..0347d7e93 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h @@ -1431,10 +1431,21 @@ sc_int_ctx_prefix_phase(::std::uint_least8_t &sz, char_type const *first, char_t } if constexpr (base == 8 && !oct_c2y) { + if (sz != 0) + { + sz = 0; + return {first, ongoing_parse_code}; + } if (*first != char_literal_v) [[unlikely]] { return {first, parse_code::invalid}; } + ++first; + if (first == last) + { + sz = 1; + return {first, parse_code::partial}; + } } else { @@ -1720,31 +1731,34 @@ inline constexpr parse_result scan_context_define_parse_impl( case scan_integral_context_phase::zero: case scan_integral_context_phase::zero_skip: { - auto phase_ret = sc_int_ctx_zero_phase(st.integer_phase, first, last); - if (phase_ret.code != ongoing_parse_code) + if constexpr (!(shbase && base != 10)) { - if constexpr (skipzero) + auto phase_ret = sc_int_ctx_zero_phase(st.integer_phase, first, last); + if (phase_ret.code != ongoing_parse_code) { - if (phase_ret.code == parse_code::ok) - { - t = {}; - } - else if (phase_ret.code == parse_code::invalid && phase == scan_integral_context_phase::zero_skip) + if constexpr (skipzero) { - t = {}; - phase_ret.code = parse_code::ok; + if (phase_ret.code == parse_code::ok) + { + t = {}; + } + else if (phase_ret.code == parse_code::invalid && phase == scan_integral_context_phase::zero_skip) + { + t = {}; + phase_ret.code = parse_code::ok; + } } - } - else - { - if (phase_ret.code == parse_code::ok) + else { - t = {}; + if (phase_ret.code == parse_code::ok) + { + t = {}; + } } + return phase_ret; } - return phase_ret; + first = phase_ret.iter; } - first = phase_ret.iter; [[fallthrough]]; } case scan_integral_context_phase::digit: @@ -1767,6 +1781,11 @@ inline constexpr parse_result scan_context_define_parse_impl( return phase_ret; } } + case scan_integral_context_phase::overflow: + { + first = skip_digits(first, last); + return {first, (first == last) ? parse_code::partial : parse_code::overflow}; + } default: { return sc_int_ctx_skip_digits_phase(first, last); diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index b568bba0f..4dbc26995 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -71,10 +71,32 @@ template inline constexpr bool scan_decfloat_compute_supported{ ::fast_io::details::scan_decfloat_compute_supported_impl::value}; +template > +struct scan_decfloat_native_wide_supported_impl +{ + inline static constexpr bool value{}; +}; + +template +struct scan_decfloat_native_wide_supported_impl +{ + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + inline static constexpr bool value{!::fast_io::details::scan_decfloat_compute_supported && + (sizeof(typename trait::mantissa_type) > sizeof(::std::uint_least64_t) || + trait::e10max > ::fast_io::details::iec559_traits::e10max || + ::fast_io::details::fp_floating_point_is_float80)}; +}; + +template +inline constexpr bool scan_decfloat_native_wide_supported{ + ::fast_io::details::scan_decfloat_native_wide_supported_impl::value}; + template concept scan_decfloat_supported_floating_point = ::fast_io::details::my_floating_point<::std::remove_cvref_t> && - ::fast_io::details::scan_decfloat_compute_supported; + (::fast_io::details::scan_decfloat_compute_supported || + ::fast_io::details::scan_decfloat_native_wide_supported); template > struct scan_decfloat_binary32_like_impl @@ -249,7 +271,22 @@ struct scan_decfloat_fast_result bool handled{}; }; -inline constexpr ::std::uint_least64_t scan_decfloat_significand_digit_limit{19u}; +inline constexpr ::std::uint_least64_t scan_decfloat_uint64_significand_digit_limit{19u}; + +template +inline constexpr ::std::uint_least64_t scan_decfloat_significand_digit_limit{ + []() constexpr noexcept { + using trait = ::fast_io::details::iec559_traits<::std::remove_cvref_t>; + constexpr ::std::uint_least64_t digits{static_cast<::std::uint_least64_t>(trait::m10digits) + 2u}; + if constexpr (digits < ::fast_io::details::scan_decfloat_uint64_significand_digit_limit) + { + return digits; + } + else + { + return ::fast_io::details::scan_decfloat_uint64_significand_digit_limit; + } + }()}; inline constexpr ::std::int_least32_t scan_decfloat_dragonbox_min_power10{-342}; inline constexpr ::std::int_least32_t scan_decfloat_dragonbox_max_power10{326}; inline constexpr ::std::uint_least64_t scan_decfloat_pow10_0_to_8_table[]{ @@ -646,6 +683,66 @@ scan_decfloat_adjusted_exponent(scan_decfloat_significand_state const &state, static_cast<::std::int_least64_t>(state.significant_digits - state.stored_digits)); } +template +[[nodiscard]] inline constexpr ::fast_io::parse_code +scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t significand, + ::std::int_least64_t adjusted_exponent) noexcept +{ + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + if (!significand) + { + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::ok; + } + constexpr auto e10max{static_cast<::std::int_least64_t>(trait::e10max)}; + constexpr auto underflow_guard{ + static_cast<::std::int_least64_t>(trait::e10max + trait::m10digits + 8u)}; + if (adjusted_exponent > e10max) + { + ::fast_io::details::fp_assign_infinity(value, negative); + return ::fast_io::parse_code::overflow; + } + if (adjusted_exponent < -underflow_guard) + { + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::overflow; + } + no_cvref_t result{static_cast(significand)}; + auto const &table{::fast_io::details::scan_decfloat_power10_table}; + constexpr ::std::int_least64_t chunk{19}; + if (adjusted_exponent < 0) + { + auto exponent{-adjusted_exponent}; + for (; exponent >= chunk; exponent -= chunk) + { + result /= table.index_unchecked(static_cast<::std::size_t>(chunk)); + } + if (exponent) + { + result /= table.index_unchecked(static_cast<::std::size_t>(exponent)); + } + } + else + { + for (; adjusted_exponent >= chunk; adjusted_exponent -= chunk) + { + result *= table.index_unchecked(static_cast<::std::size_t>(chunk)); + } + if (adjusted_exponent) + { + result *= table.index_unchecked(static_cast<::std::size_t>(adjusted_exponent)); + } + } + value = negative ? -result : result; + if (result == static_cast(0.0)) + { + return ::fast_io::parse_code::overflow; + } + return ::fast_io::parse_code::ok; +} + template <::fast_io::manipulators::floating_rounding rounding> [[nodiscard]] inline constexpr bool scan_decfloat_decimal_round_up(bool negative, ::std::uint_least64_t rounded_down, @@ -675,9 +772,11 @@ scan_decfloat_decimal_round_up(bool negative, ::std::uint_least64_t rounded_down } } +template inline constexpr void scan_decfloat_append_digit(scan_decfloat_significand_state &state, char8_t digit, bool after_decimal) noexcept { + constexpr auto digit_limit{::fast_io::details::scan_decfloat_significand_digit_limit}; state.has_digit = true; if (after_decimal) { @@ -692,7 +791,7 @@ inline constexpr void scan_decfloat_append_digit(scan_decfloat_significand_state state.has_nonzero_digit = true; } ++state.significant_digits; - if (state.stored_digits != ::fast_io::details::scan_decfloat_significand_digit_limit) + if (state.stored_digits != digit_limit) { state.significand = state.significand * 10u + static_cast<::std::uint_least64_t>(digit); ++state.stored_digits; @@ -710,6 +809,12 @@ scan_decfloat_ascii8_is_digits(::std::uint_least64_t val) noexcept 0x8080808080808080u) == 0); } +[[nodiscard]] inline constexpr bool +scan_decfloat_ascii8_has_nonzero_digit(::std::uint_least64_t val) noexcept +{ + return (val ^ 0x3030303030303030u) != 0; +} + [[nodiscard]] inline constexpr ::std::uint_least32_t scan_decfloat_ascii8_parse(::std::uint_least64_t val) noexcept { @@ -722,10 +827,12 @@ scan_decfloat_ascii8_parse(::std::uint_least64_t val) noexcept return static_cast<::std::uint_least32_t>(val); } +template inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significand_state &state, bool after_decimal, ::std::uint_least32_t digits) noexcept { + constexpr auto digit_limit{::fast_io::details::scan_decfloat_significand_digit_limit}; state.has_digit = true; if (after_decimal) { @@ -740,7 +847,7 @@ inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significan state.has_nonzero_digit = true; } state.significant_digits += 8u; - if (state.stored_digits == ::fast_io::details::scan_decfloat_significand_digit_limit) + if (state.stored_digits == digit_limit) { if (digits != 0) { @@ -748,7 +855,7 @@ inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significan } return; } - auto const available{::fast_io::details::scan_decfloat_significand_digit_limit - state.stored_digits}; + auto const available{digit_limit - state.stored_digits}; if (8u <= available) { state.significand = state.significand * 100000000u + static_cast<::std::uint_least64_t>(digits); @@ -761,7 +868,7 @@ inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significan state.significand = state.significand * ::fast_io::details::scan_decfloat_pow10_0_to_8_table[available] + static_cast<::std::uint_least64_t>(stored_part); - state.stored_digits = ::fast_io::details::scan_decfloat_significand_digit_limit; + state.stored_digits = digit_limit; if (truncated_part != 0) { state.truncated_nonzero = true; @@ -778,10 +885,10 @@ inline constexpr char_type const * scan_decfloat_digits(char_type const *first, char_type const *last, bool after_decimal, scan_decfloat_significand_state &state) noexcept { + constexpr auto digit_limit{::fast_io::details::scan_decfloat_significand_digit_limit}; char8_t digit{}; if constexpr (sizeof(char_type) == sizeof(char8_t) && !::fast_io::details::is_ebcdic && - ::std::numeric_limits<::std::uint_least64_t>::digits == 64u && - ::fast_io::details::scan_decfloat_binary64_like) + ::std::numeric_limits<::std::uint_least64_t>::digits == 64u) { #ifdef __cpp_if_consteval if !consteval @@ -795,7 +902,7 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d { return first; } - ::fast_io::details::scan_decfloat_append_digit(state, digit, after_decimal); + ::fast_io::details::scan_decfloat_append_digit(state, digit, after_decimal); } if (static_cast<::std::size_t>(last - first) >= 2u * sizeof(::std::uint_least64_t)) { @@ -811,8 +918,25 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d { break; } - ::fast_io::details::scan_decfloat_append_eight_digits( - state, after_decimal, ::fast_io::details::scan_decfloat_ascii8_parse(val)); + if (state.has_nonzero_digit && + state.stored_digits == digit_limit) + { + if (after_decimal) + { + state.fractional_digits += 8u; + } + state.significant_digits += 8u; + if (!state.truncated_nonzero && + ::fast_io::details::scan_decfloat_ascii8_has_nonzero_digit(val)) + { + state.truncated_nonzero = true; + } + } + else + { + ::fast_io::details::scan_decfloat_append_eight_digits( + state, after_decimal, ::fast_io::details::scan_decfloat_ascii8_parse(val)); + } first += sizeof(::std::uint_least64_t); } } @@ -820,7 +944,7 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d } for (; first != last && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) { - ::fast_io::details::scan_decfloat_append_digit(state, digit, after_decimal); + ::fast_io::details::scan_decfloat_append_digit(state, digit, after_decimal); } return first; } @@ -863,28 +987,39 @@ template ) { - if (::fast_io::details::scan_decfloat_compute_adjusted( - adjusted_exponent, state.significand, negative, adjusted)) + ::fast_io::details::scan_decfloat_adjusted_mantissa adjusted; + if (!state.truncated_nonzero) { - return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); + if (::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, state.significand, negative, adjusted)) + { + return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); + } + } + else + { + ::fast_io::details::scan_decfloat_adjusted_mantissa adjusted_next; + if (::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, state.significand, negative, adjusted) && + ::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, state.significand + 1u, negative, adjusted_next) && + adjusted.mantissa == adjusted_next.mantissa && adjusted.power2 == adjusted_next.power2) + { + return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); + } } } + if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) + { + return ::fast_io::details::scan_decfloat_assign_native_wide( + value, negative, state.significand, adjusted_exponent); + } else { - ::fast_io::details::scan_decfloat_adjusted_mantissa adjusted_next; - if (::fast_io::details::scan_decfloat_compute_adjusted( - adjusted_exponent, state.significand, negative, adjusted) && - ::fast_io::details::scan_decfloat_compute_adjusted( - adjusted_exponent, state.significand + 1u, negative, adjusted_next) && - adjusted.mantissa == adjusted_next.mantissa && adjusted.power2 == adjusted_next.power2) - { - return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); - } + return ::fast_io::parse_code::partial; } - return ::fast_io::parse_code::partial; } template ( - adjusted_exponent, significand, negative, adjusted)) + if constexpr (::fast_io::details::scan_decfloat_compute_supported) + { + ::fast_io::details::scan_decfloat_adjusted_mantissa adjusted; + if (::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, significand, negative, adjusted)) + { + return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, significand, adjusted); + } + } + if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) + { + return ::fast_io::details::scan_decfloat_assign_native_wide( + value, negative, significand, adjusted_exponent); + } + else { - return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, significand, adjusted); + return ::fast_io::parse_code::partial; } - return ::fast_io::parse_code::partial; } template ( - adjusted_exponent, significand, negative, adjusted)) + if constexpr (::fast_io::details::scan_decfloat_compute_supported) + { + ::fast_io::details::scan_decfloat_adjusted_mantissa adjusted; + if (::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, significand, negative, adjusted)) + { + return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, significand, adjusted); + } + } + if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) + { + return ::fast_io::details::scan_decfloat_assign_native_wide( + value, negative, significand, adjusted_exponent); + } + else { - return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, significand, adjusted); + return ::fast_io::parse_code::partial; } - return ::fast_io::parse_code::partial; } template @@ -1137,13 +1294,14 @@ scan_decfloat_contiguous_short_define_impl(char_type const *begin, char_type con else #endif { + constexpr auto digit_limit{::fast_io::details::scan_decfloat_significand_digit_limit}; ::std::uint_least64_t significand{}; ::std::uint_least64_t digit_count{}; ::std::uint_least64_t fractional_digits{}; char8_t digit{}; for (; first != end && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) { - if (digit_count == ::fast_io::details::scan_decfloat_significand_digit_limit) + if (digit_count == digit_limit) { return {}; } @@ -1156,7 +1314,7 @@ scan_decfloat_contiguous_short_define_impl(char_type const *begin, char_type con { ++first; auto const *fraction_begin{first}; - for (; digit_count + 8u <= ::fast_io::details::scan_decfloat_significand_digit_limit && + for (; digit_count + 8u <= digit_limit && static_cast<::std::size_t>(end - first) >= sizeof(::std::uint_least64_t);) { ::std::uint_least64_t val; @@ -1177,7 +1335,7 @@ scan_decfloat_contiguous_short_define_impl(char_type const *begin, char_type con } for (; first != end && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) { - if (digit_count == ::fast_io::details::scan_decfloat_significand_digit_limit) + if (digit_count == digit_limit) { return {}; } @@ -1409,6 +1567,450 @@ scan_decfloat_contiguous_define(char_type const *begin, char_type const *end, T begin, end, value, precision); } +enum class scan_decfloat_context_phase : ::std::uint_least8_t +{ + start, + after_sign, + integer, + fraction, + exponent_start, + exponent_after_sign, + exponent_digits, + special +}; + +template <::std::integral char_type> +struct scan_decfloat_context +{ + ::fast_io::details::scan_decfloat_significand_state significand_state; + ::fast_io::details::scan_floating_context special_buffer; + ::std::uint_least64_t exponent{}; + ::fast_io::details::scan_decfloat_context_phase phase{}; + char_type sign_char{}; + bool negative{}; + bool has_sign{}; + bool has_decimal_point{}; + bool has_exponent_marker{}; + bool exponent_negative{}; + bool exponent_has_digit{}; + bool exponent_overflow{}; + bool special_sign_prefixed{}; +}; + +template <::std::integral char_type> +inline constexpr void scan_decfloat_context_append_exponent_digit( + ::fast_io::details::scan_decfloat_context &state, char8_t digit) noexcept +{ + state.exponent_has_digit = true; + constexpr auto exponent_limit{ + static_cast<::std::uint_least64_t>((::std::numeric_limits<::std::int_least64_t>::max)())}; + auto const value{static_cast<::std::uint_least64_t>(digit)}; + if (state.exponent > (exponent_limit - value) / 10u) + { + state.exponent = exponent_limit; + state.exponent_overflow = true; + } + else if (!state.exponent_overflow) + { + state.exponent = state.exponent * 10u + value; + } +} + +template <::std::integral char_type> +[[nodiscard]] inline constexpr ::std::int_least64_t +scan_decfloat_context_exponent(::fast_io::details::scan_decfloat_context const &state) noexcept +{ + return state.exponent_negative ? -static_cast<::std::int_least64_t>(state.exponent) + : static_cast<::std::int_least64_t>(state.exponent); +} + +template +[[nodiscard]] inline constexpr ::fast_io::parse_code +scan_decfloat_context_assign(::fast_io::details::scan_decfloat_context const &state, T &value, + ::std::size_t precision = 0) noexcept +{ + if (!state.significand_state.has_digit) + { + return ::fast_io::parse_code::invalid; + } + if constexpr (flags.floating == ::fast_io::manipulators::floating_format::scientific) + { + if (!state.has_exponent_marker || !state.exponent_has_digit) + { + return ::fast_io::parse_code::invalid; + } + } + if (!state.significand_state.has_nonzero_digit) + { + value = state.negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::ok; + } + auto const exponent{state.exponent_has_digit + ? ::fast_io::details::scan_decfloat_context_exponent(state) + : ::std::int_least64_t{}}; + if constexpr (use_precision) + { + return ::fast_io::details::scan_decfloat_assign_precision( + value, state.negative, state.significand_state, exponent, precision); + } + else + { + if constexpr (flags.rounding == ::fast_io::manipulators::floating_rounding::current_environment) + { + return ::fast_io::details::scan_decfloat_assign_current_environment( + value, state.negative, state.significand_state, exponent); + } + else + { + return ::fast_io::details::scan_decfloat_assign( + value, state.negative, state.significand_state, exponent); + } + } +} + +template <::std::integral char_type> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_decfloat_context_append_special_prefix(::fast_io::details::scan_decfloat_context &state, + char_type const *chunk_begin) noexcept +{ + if (state.has_sign && !state.special_sign_prefixed) + { + if (state.special_buffer.size == state.special_buffer.capacity) + { + return {chunk_begin, ::fast_io::parse_code::overflow}; + } + state.special_buffer.buffer.index_unchecked(state.special_buffer.size) = state.sign_char; + ++state.special_buffer.size; + state.special_sign_prefixed = true; + } + return {chunk_begin, ::fast_io::parse_code::partial}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + scan_decfloat_supported_floating_point T, bool use_precision> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_decfloat_context_special_define(::fast_io::details::scan_decfloat_context &state, + char_type const *begin, char_type const *end, T &value, + ::std::size_t precision = 0) noexcept +{ + auto prefix_result{::fast_io::details::scan_decfloat_context_append_special_prefix(state, begin)}; + if (prefix_result.code != ::fast_io::parse_code::partial) + { + return prefix_result; + } + auto const old_size{state.special_buffer.size}; + auto const append_result{::fast_io::details::scan_floating_context_append(state.special_buffer, begin, end)}; + if (append_result.code != ::fast_io::parse_code::partial) + { + return append_result; + } + T parsed_value{}; + auto const *buffer_begin{state.special_buffer.buffer.data()}; + auto const *buffer_end{buffer_begin + state.special_buffer.size}; + ::fast_io::parse_result parse_result; + if constexpr (use_precision) + { + parse_result = ::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value, precision); + } + else + { + parse_result = ::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value); + } + if (parse_result.code == ::fast_io::parse_code::ok) + { + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (::fast_io::details::scan_hexfloat_special_parse_may_extend( + parse_result.iter, buffer_end)) + { + return {end, ::fast_io::parse_code::partial}; + } + value = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, end, parse_result.iter), + ::fast_io::parse_code::ok}; + } + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::end_of_file || + parse_result.code == ::fast_io::parse_code::partial) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::invalid) + { + if (buffer_begin != buffer_end && + !::fast_io::char_category::is_c_space(*(buffer_end - 1))) + { + return {end, ::fast_io::parse_code::partial}; + } + } + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, end, parse_result.iter), + parse_result.code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + scan_decfloat_supported_floating_point T, bool use_precision> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_decfloat_context_numeric_define(::fast_io::details::scan_decfloat_context &state, + char_type const *first, char_type const *end, T &value, + ::std::size_t precision) noexcept; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + scan_decfloat_supported_floating_point T, bool use_precision> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_decfloat_context_finish_or_exponent(::fast_io::details::scan_decfloat_context &state, + char_type const *first, char_type const *end, T &value, + ::std::size_t precision = 0) noexcept +{ + constexpr auto lower_e{::fast_io::char_literal_v}; + constexpr auto upper_e{::fast_io::char_literal_v}; + if constexpr (flags.floating != ::fast_io::manipulators::floating_format::fixed) + { + if (first != end && (*first == lower_e || *first == upper_e)) + { + state.has_exponent_marker = true; + state.phase = ::fast_io::details::scan_decfloat_context_phase::exponent_start; + ++first; + if (first == end) + { + return {first, ::fast_io::parse_code::partial}; + } + return ::fast_io::details::scan_decfloat_context_numeric_define( + state, first, end, value, precision); + } + } + auto code{::fast_io::details::scan_decfloat_context_assign(state, value, precision)}; + return {first, code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + scan_decfloat_supported_floating_point T, bool use_precision> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_decfloat_context_numeric_define(::fast_io::details::scan_decfloat_context &state, + char_type const *first, char_type const *end, T &value, + ::std::size_t precision) noexcept +{ + constexpr auto plus{::fast_io::char_literal_v}; + constexpr auto minus{::fast_io::char_literal_v}; + constexpr auto dot{::fast_io::char_literal_v}; + for (;;) + { + switch (state.phase) + { + case ::fast_io::details::scan_decfloat_context_phase::start: + if (!state.significand_state.has_digit && !state.has_sign) + { + first = ::fast_io::details::scan_floating_context_skip_space(first, end); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + } + if (*first == minus) + { + state.negative = true; + state.has_sign = true; + state.sign_char = *first; + ++first; + state.phase = ::fast_io::details::scan_decfloat_context_phase::after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + if constexpr (flags.allow_leading_plus) + { + if (*first == plus) + { + state.has_sign = true; + state.sign_char = *first; + ++first; + state.phase = ::fast_io::details::scan_decfloat_context_phase::after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + } + state.phase = ::fast_io::details::scan_decfloat_context_phase::integer; + break; + case ::fast_io::details::scan_decfloat_context_phase::after_sign: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + state.phase = ::fast_io::details::scan_decfloat_context_phase::integer; + break; + case ::fast_io::details::scan_decfloat_context_phase::integer: + { + char8_t digit{}; + if (first != end && *first == dot) + { + state.has_decimal_point = true; + state.phase = ::fast_io::details::scan_decfloat_context_phase::fraction; + ++first; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + auto const *before_digits{first}; + first = ::fast_io::details::scan_decfloat_digits( + first, end, false, state.significand_state); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (first == before_digits && !state.significand_state.has_digit && + !::fast_io::details::scan_decfloat_decimal_digit(*first, digit)) + { + state.phase = ::fast_io::details::scan_decfloat_context_phase::special; + return ::fast_io::details::scan_decfloat_context_special_define( + state, first, end, value, precision); + } + if (first != end && *first == dot) + { + state.has_decimal_point = true; + state.phase = ::fast_io::details::scan_decfloat_context_phase::fraction; + ++first; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + return ::fast_io::details::scan_decfloat_context_finish_or_exponent( + state, first, end, value, precision); + } + case ::fast_io::details::scan_decfloat_context_phase::fraction: + first = ::fast_io::details::scan_decfloat_digits( + first, end, true, state.significand_state); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (!state.significand_state.has_digit) + { + return {first, ::fast_io::parse_code::invalid}; + } + return ::fast_io::details::scan_decfloat_context_finish_or_exponent( + state, first, end, value, precision); + case ::fast_io::details::scan_decfloat_context_phase::exponent_start: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (*first == minus) + { + state.exponent_negative = true; + ++first; + state.phase = ::fast_io::details::scan_decfloat_context_phase::exponent_after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + if (*first == plus) + { + ++first; + state.phase = ::fast_io::details::scan_decfloat_context_phase::exponent_after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + state.phase = ::fast_io::details::scan_decfloat_context_phase::exponent_digits; + break; + case ::fast_io::details::scan_decfloat_context_phase::exponent_after_sign: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + state.phase = ::fast_io::details::scan_decfloat_context_phase::exponent_digits; + break; + case ::fast_io::details::scan_decfloat_context_phase::exponent_digits: + { + char8_t digit{}; + for (; first != end && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) + { + ::fast_io::details::scan_decfloat_context_append_exponent_digit(state, digit); + } + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (!state.exponent_has_digit) + { + if constexpr (flags.floating == ::fast_io::manipulators::floating_format::scientific) + { + return {first, ::fast_io::parse_code::invalid}; + } + } + auto code{::fast_io::details::scan_decfloat_context_assign( + state, value, precision)}; + return {first, code}; + } + case ::fast_io::details::scan_decfloat_context_phase::special: + return ::fast_io::details::scan_decfloat_context_special_define( + state, first, end, value, precision); + } + } +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + scan_decfloat_supported_floating_point T, bool use_precision> +[[nodiscard]] inline constexpr ::fast_io::parse_code +scan_decfloat_context_eof(::fast_io::details::scan_decfloat_context &state, T &value, + ::std::size_t precision = 0) noexcept +{ + if (state.phase == ::fast_io::details::scan_decfloat_context_phase::special) + { + if (!state.special_buffer.size) + { + return ::fast_io::parse_code::end_of_file; + } + T parsed_value{}; + auto const *buffer_begin{state.special_buffer.buffer.data()}; + auto const *buffer_end{buffer_begin + state.special_buffer.size}; + ::fast_io::parse_result parse_result; + if constexpr (use_precision) + { + parse_result = ::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value, precision); + } + else + { + parse_result = ::fast_io::details::scan_decfloat_contiguous_define( + buffer_begin, buffer_end, parsed_value); + } + if (parse_result.code == ::fast_io::parse_code::ok) + { + value = parsed_value; + } + return parse_result.code; + } + if (state.phase == ::fast_io::details::scan_decfloat_context_phase::start && !state.significand_state.has_digit && + !state.has_sign) + { + return ::fast_io::parse_code::end_of_file; + } + return ::fast_io::details::scan_decfloat_context_assign(state, value, precision); +} + } // namespace details template @@ -1448,7 +2050,7 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags inline constexpr auto scan_context_type(io_reserve_type_t>) noexcept { - return io_type_t<::fast_io::details::scan_floating_context>{}; + return io_type_t<::fast_io::details::scan_decfloat_context>{}; } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1456,51 +2058,12 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) inline constexpr ::fast_io::parse_result scan_context_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, char_type const *begin, + ::fast_io::details::scan_decfloat_context &state, char_type const *begin, char_type const *end, ::fast_io::manipulators::scalar_manip_t value) noexcept { - if (!state.size) - { - begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); - if (begin == end) - { - return {end, ::fast_io::parse_code::partial}; - } - } - auto const old_size{state.size}; - auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; - if (append_result.code != ::fast_io::parse_code::partial) - { - return append_result; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( - buffer_begin, buffer_end, parsed_value)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - value.reference = parsed_value; - return {::fast_io::details::scan_floating_context_map_iter( - state, old_size, begin, end, parse_result.iter), - ::fast_io::parse_code::ok}; - } - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - if (parse_result.code == ::fast_io::parse_code::end_of_file || - parse_result.code == ::fast_io::parse_code::partial) - { - return {end, ::fast_io::parse_code::partial}; - } - return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), - parse_result.code}; + return ::fast_io::details::scan_decfloat_context_numeric_define( + state, begin, end, value.reference, 0); } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1508,23 +2071,10 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) inline constexpr ::fast_io::parse_code scan_context_eof_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, + ::fast_io::details::scan_decfloat_context &state, ::fast_io::manipulators::scalar_manip_t value) noexcept { - if (!state.size) - { - return ::fast_io::parse_code::end_of_file; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( - buffer_begin, buffer_end, parsed_value)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - value.reference = parsed_value; - } - return parse_result.code; + return ::fast_io::details::scan_decfloat_context_eof(state, value.reference); } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1534,7 +2084,7 @@ inline constexpr auto scan_context_type(io_reserve_type_t>) noexcept { - return io_type_t<::fast_io::details::scan_floating_context>{}; + return io_type_t<::fast_io::details::scan_decfloat_context>{}; } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1543,51 +2093,12 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags inline constexpr ::fast_io::parse_result scan_context_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, char_type const *begin, + ::fast_io::details::scan_decfloat_context &state, char_type const *begin, char_type const *end, ::fast_io::manipulators::scalar_manip_precision_t value) noexcept { - if (!state.size) - { - begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); - if (begin == end) - { - return {end, ::fast_io::parse_code::partial}; - } - } - auto const old_size{state.size}; - auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; - if (append_result.code != ::fast_io::parse_code::partial) - { - return append_result; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( - buffer_begin, buffer_end, parsed_value, value.precision)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - value.reference = parsed_value; - return {::fast_io::details::scan_floating_context_map_iter( - state, old_size, begin, end, parse_result.iter), - ::fast_io::parse_code::ok}; - } - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - if (parse_result.code == ::fast_io::parse_code::end_of_file || - parse_result.code == ::fast_io::parse_code::partial) - { - return {end, ::fast_io::parse_code::partial}; - } - return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), - parse_result.code}; + return ::fast_io::details::scan_decfloat_context_numeric_define( + state, begin, end, value.reference, value.precision); } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1596,23 +2107,11 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags inline constexpr ::fast_io::parse_code scan_context_eof_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, + ::fast_io::details::scan_decfloat_context &state, ::fast_io::manipulators::scalar_manip_precision_t value) noexcept { - if (!state.size) - { - return ::fast_io::parse_code::end_of_file; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_decfloat_contiguous_define( - buffer_begin, buffer_end, parsed_value, value.precision)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - value.reference = parsed_value; - } - return parse_result.code; + return ::fast_io::details::scan_decfloat_context_eof( + state, value.reference, value.precision); } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, details::my_floating_point T> diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index c57e31d49..014ef68b7 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -91,6 +91,15 @@ template return ch == ::fast_io::char_literal_v || ch == ::fast_io::char_literal_v; } +template <::std::integral char_type> +[[nodiscard]] inline constexpr bool scan_hexfloat_caseless_equal_ascii(char_type ch, char8_t lower) noexcept +{ + auto const lower_ch{::fast_io::char_literal_add(lower)}; + auto const upper_ch{::fast_io::char_literal_add( + static_cast(lower - static_cast(u8'a' - u8'A')))}; + return ch == lower_ch || ch == upper_ch; +} + template <::std::integral char_type> struct scan_hexfloat_special_result { @@ -212,6 +221,48 @@ scan_hexfloat_special_value(char_type const *first, char_type const *end, bool n return {}; } +template <::fast_io::manipulators::floating_nan_payload_scan nan_payload_scan, ::std::integral char_type> +[[nodiscard]] inline constexpr bool +scan_hexfloat_special_parse_may_extend(char_type const *first, char_type const *last) noexcept +{ + if (first == last) + { + return true; + } + if (::fast_io::details::scan_hexfloat_caseless_equal(*first)) + { + constexpr char8_t infinity_suffix[]{u8"inity"}; + auto scan{first}; + for (::std::size_t index{}; scan != last && index != 5u; ++scan, ++index) + { + if (!::fast_io::details::scan_hexfloat_caseless_equal_ascii(*scan, infinity_suffix[index])) + { + return false; + } + } + return scan == last; + } + if constexpr (nan_payload_scan != ::fast_io::manipulators::floating_nan_payload_scan::none) + { + if (*first == ::fast_io::char_literal_v) + { + for (auto scan{first + 1}; scan != last; ++scan) + { + if (*scan == ::fast_io::char_literal_v) + { + return false; + } + if (!::fast_io::details::scan_hexfloat_nan_sequence_char(*scan)) + { + return false; + } + } + return true; + } + } + return false; +} + template struct scan_hexfloat_significand_state { @@ -306,9 +357,7 @@ inline constexpr void scan_hexfloat_ascii8_pack(::std::uint_least64_t val, ::std #elif __has_cpp_attribute(msvc::forceinline) [[msvc::forceinline]] #endif -[[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_nibbles(::std::uint_least64_t val, - ::std::uint_least64_t &nibbles, - ::std::uint_least64_t &packed) noexcept +[[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_is_xdigits(::std::uint_least64_t val) noexcept { constexpr ::std::uint_least64_t first_bound1{0x3939393939393939}; constexpr ::std::uint_least64_t first_bound2{0x1919191919191919}; @@ -318,14 +367,69 @@ inline constexpr void scan_hexfloat_ascii8_pack(::std::uint_least64_t val, ::std ~(((val + 0x3f3f3f3f3f3f3f3f) | (val - 0x4040404040404040)) & ((val + 0x1f1f1f1f1f1f1f1f) | (val - 0x6060606060606060)))) & 0x8080808080808080}; - if (invalid) + return !invalid; +} + +[[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_has_nonzero_digit(::std::uint_least64_t val) noexcept +{ + return (val ^ 0x3030303030303030) != 0; +} + +[[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_nibbles_only(::std::uint_least64_t val, + ::std::uint_least64_t &nibbles) noexcept +{ + if (!::fast_io::details::scan_hexfloat_ascii8_is_xdigits(val)) + { + return false; + } + val -= 0x3030303030303030; + nibbles = (val & 0x0f0f0f0f0f0f0f0f) + ((val & 0x1010101010101010) >> 4) * 9u; + return true; +} + +[[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_nibbles(::std::uint_least64_t val, + ::std::uint_least64_t &nibbles, + ::std::uint_least64_t &packed) noexcept +{ + if (!::fast_io::details::scan_hexfloat_ascii8_nibbles_only(val, nibbles)) { return false; } - ::fast_io::details::scan_hexfloat_ascii8_pack(val, nibbles, packed); + constexpr ::std::uint_least64_t mask{0x000000FF000000FF}; + constexpr ::std::uint_least64_t pow_base_2{ + ::fast_io::details::compile_pow_n<::std::uint_least64_t, 16, 2>}; + constexpr ::std::uint_least64_t pow_base_4{ + ::fast_io::details::compile_pow_n<::std::uint_least64_t, 16, 4>}; + constexpr ::std::uint_least64_t pow_base_6{ + ::fast_io::details::compile_pow_n<::std::uint_least64_t, 16, 6>}; + constexpr ::std::uint_least64_t mul1{pow_base_2 + (pow_base_6 << 32u)}; + constexpr ::std::uint_least64_t mul2{1u + (pow_base_4 << 32u)}; + auto folded{(nibbles * 16u) + (nibbles >> 8u)}; + packed = (((folded & mask) * mul1) + (((folded >> 16u) & mask) * mul2)) >> 32u; return true; } +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void scan_hexfloat_skip_ascii8_after_storage_limit(state_type &state, bool after_decimal, + bool has_nonzero_digit) noexcept +{ + state.has_digit = true; + if (after_decimal) + { + state.fractional_hex_digits += 8u; + } + state.significant_hex_digits += 8u; + if (!state.truncated_nonzero && has_nonzero_digit) + { + state.truncated_nonzero = true; + } +} + template <::std::size_t stored_hex_digits_limit, typename state_type> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -411,6 +515,17 @@ inline constexpr char_type const *scan_hexfloat_significand_run(char_type const val = ::fast_io::little_endian(val); } ::std::uint_least64_t nibbles{}; + if (state.has_nonzero_digit && state.stored_hex_digits == stored_hex_digits_limit) + { + if (!::fast_io::details::scan_hexfloat_ascii8_is_xdigits(val)) + { + break; + } + ::fast_io::details::scan_hexfloat_skip_ascii8_after_storage_limit(state, after_decimal, + ::fast_io::details::scan_hexfloat_ascii8_has_nonzero_digit(val)); + first += sizeof(::std::uint_least64_t); + continue; + } ::std::uint_least64_t packed{}; if (!::fast_io::details::scan_hexfloat_ascii8_nibbles(val, nibbles, packed)) { @@ -1268,6 +1383,438 @@ scan_floating_context_append(scan_floating_context &state, char_type return {chunk_end, ::fast_io::parse_code::partial}; } +enum class scan_hexfloat_context_phase : ::std::uint_least8_t +{ + start, + after_sign, + prefix_zero, + prefix_x, + integer, + fraction, + exponent_start, + exponent_after_sign, + exponent_digits, + special +}; + +template <::std::integral char_type, typename storage_type> +struct scan_hexfloat_context +{ + ::fast_io::details::scan_hexfloat_significand_state significand_state; + ::fast_io::details::scan_floating_context special_buffer; + ::std::uint_least64_t exponent{}; + ::fast_io::details::scan_hexfloat_context_phase phase{}; + char_type sign_char{}; + bool negative{}; + bool has_sign{}; + bool special_sign_prefixed{}; + bool has_exponent_marker{}; + bool exponent_negative{}; + bool exponent_has_digit{}; + bool exponent_overflow{}; +}; + +template <::std::integral char_type, typename storage_type> +inline constexpr void scan_hexfloat_context_append_exponent_digit( + ::fast_io::details::scan_hexfloat_context &state, char8_t digit) noexcept +{ + state.exponent_has_digit = true; + constexpr auto exponent_limit{ + static_cast<::std::uint_least64_t>((::std::numeric_limits<::std::int_least64_t>::max)())}; + auto const value{static_cast<::std::uint_least64_t>(digit)}; + if (state.exponent > (exponent_limit - value) / 10u) + { + state.exponent = exponent_limit; + state.exponent_overflow = true; + } + else if (!state.exponent_overflow) + { + state.exponent = state.exponent * 10u + value; + } +} + +template <::std::integral char_type, typename storage_type> +[[nodiscard]] inline constexpr ::std::int_least64_t +scan_hexfloat_context_exponent( + ::fast_io::details::scan_hexfloat_context const &state) noexcept +{ + return state.exponent_negative ? -static_cast<::std::int_least64_t>(state.exponent) + : static_cast<::std::int_least64_t>(state.exponent); +} + +template +[[nodiscard]] inline constexpr ::fast_io::parse_code +scan_hexfloat_context_assign( + ::fast_io::details::scan_hexfloat_context const &state, T &value) noexcept +{ + if (!state.significand_state.has_digit || !state.has_exponent_marker || !state.exponent_has_digit) + { + return ::fast_io::parse_code::invalid; + } + if (!state.significand_state.has_nonzero_digit) + { + value = state.negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::ok; + } + constexpr auto int64_max{(::std::numeric_limits<::std::int_least64_t>::max)()}; + constexpr auto int64_min{(::std::numeric_limits<::std::int_least64_t>::min)()}; + auto const fractional_exponent{state.significand_state.fractional_hex_digits > int64_max / 4 + ? int64_max + : state.significand_state.fractional_hex_digits * 4}; + auto const exponent{::fast_io::details::scan_hexfloat_context_exponent(state)}; + auto const binary_exponent{exponent < int64_min + fractional_exponent ? int64_min + : exponent - fractional_exponent}; + return ::fast_io::details::scan_hexfloat_assign_ieee_result( + value, state.negative, state.significand_state.stored, state.significand_state.stored_hex_digits, + state.significand_state.significant_hex_digits, state.significand_state.truncated_nonzero, + binary_exponent); +} + +template <::std::integral char_type, typename storage_type> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_hexfloat_context_append_special_prefix( + ::fast_io::details::scan_hexfloat_context &state, + char_type const *chunk_begin) noexcept +{ + if (state.has_sign && !state.special_sign_prefixed) + { + if (state.special_buffer.size == state.special_buffer.capacity) + { + return {chunk_begin, ::fast_io::parse_code::overflow}; + } + state.special_buffer.buffer.index_unchecked(state.special_buffer.size) = state.sign_char; + ++state.special_buffer.size; + state.special_sign_prefixed = true; + } + return {chunk_begin, ::fast_io::parse_code::partial}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T, typename storage_type> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_hexfloat_context_special_define( + ::fast_io::details::scan_hexfloat_context &state, + char_type const *begin, char_type const *end, T &value) noexcept +{ + auto prefix_result{::fast_io::details::scan_hexfloat_context_append_special_prefix(state, begin)}; + if (prefix_result.code != ::fast_io::parse_code::partial) + { + return prefix_result; + } + auto const old_size{state.special_buffer.size}; + auto const append_result{::fast_io::details::scan_floating_context_append(state.special_buffer, begin, end)}; + if (append_result.code != ::fast_io::parse_code::partial) + { + return append_result; + } + T parsed_value{}; + auto const *buffer_begin{state.special_buffer.buffer.data()}; + auto const *buffer_end{buffer_begin + state.special_buffer.size}; + auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (::fast_io::details::scan_hexfloat_special_parse_may_extend( + parse_result.iter, buffer_end)) + { + return {end, ::fast_io::parse_code::partial}; + } + value = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, end, parse_result.iter), + ::fast_io::parse_code::ok}; + } + if (parse_result.iter == buffer_end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::end_of_file || + parse_result.code == ::fast_io::parse_code::partial) + { + return {end, ::fast_io::parse_code::partial}; + } + if (parse_result.code == ::fast_io::parse_code::invalid) + { + if (buffer_begin != buffer_end && + !::fast_io::char_category::is_c_space(*(buffer_end - 1))) + { + return {end, ::fast_io::parse_code::partial}; + } + } + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, end, parse_result.iter), + parse_result.code}; +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T, typename storage_type> +[[nodiscard]] inline constexpr ::fast_io::parse_result +scan_hexfloat_context_numeric_define( + ::fast_io::details::scan_hexfloat_context &state, + char_type const *first, char_type const *end, T &value) noexcept +{ + using trait = ::fast_io::details::iec559_traits; + constexpr ::std::size_t precision_bits{trait::mbits + 1u}; + constexpr ::std::size_t stored_hex_digits_limit{(precision_bits + 7u) / 4u}; + constexpr auto plus{::fast_io::char_literal_v}; + constexpr auto minus{::fast_io::char_literal_v}; + constexpr auto zero{::fast_io::char_literal_v}; + constexpr auto lower_x{::fast_io::char_literal_v}; + constexpr auto upper_x{::fast_io::char_literal_v}; + constexpr auto dot{::fast_io::char_literal_v}; + constexpr auto lower_p{::fast_io::char_literal_v}; + constexpr auto upper_p{::fast_io::char_literal_v}; + for (;;) + { + switch (state.phase) + { + case ::fast_io::details::scan_hexfloat_context_phase::start: + if (!state.significand_state.has_digit && !state.has_sign) + { + first = ::fast_io::details::scan_floating_context_skip_space(first, end); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + } + if (*first == minus) + { + state.negative = true; + state.has_sign = true; + state.sign_char = *first; + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + if constexpr (flags.allow_leading_plus) + { + if (*first == plus) + { + state.has_sign = true; + state.sign_char = *first; + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + } + state.phase = ::fast_io::details::scan_hexfloat_context_phase::after_sign; + break; + case ::fast_io::details::scan_hexfloat_context_phase::after_sign: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if constexpr (flags.showbase) + { + if (*first == zero) + { + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::prefix_x; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + state.phase = ::fast_io::details::scan_hexfloat_context_phase::special; + return ::fast_io::details::scan_hexfloat_context_special_define( + state, first, end, value); + } + else + { + state.phase = ::fast_io::details::scan_hexfloat_context_phase::integer; + break; + } + case ::fast_io::details::scan_hexfloat_context_phase::prefix_zero: + state.phase = ::fast_io::details::scan_hexfloat_context_phase::prefix_x; + break; + case ::fast_io::details::scan_hexfloat_context_phase::prefix_x: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (*first != lower_x && *first != upper_x) + { + return {first, ::fast_io::parse_code::invalid}; + } + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::integer; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + case ::fast_io::details::scan_hexfloat_context_phase::integer: + { + if (first != end && *first == dot) + { + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::fraction; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + auto const *before_digits{first}; + first = ::fast_io::details::scan_hexfloat_significand_run( + first, end, false, state.significand_state); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (first == before_digits && !state.significand_state.has_digit) + { + state.phase = ::fast_io::details::scan_hexfloat_context_phase::special; + return ::fast_io::details::scan_hexfloat_context_special_define( + state, first, end, value); + } + if (first != end && *first == dot) + { + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::fraction; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + if (first != end && (*first == lower_p || *first == upper_p)) + { + state.has_exponent_marker = true; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_start; + ++first; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + return {first, ::fast_io::parse_code::invalid}; + } + case ::fast_io::details::scan_hexfloat_context_phase::fraction: + first = ::fast_io::details::scan_hexfloat_significand_run( + first, end, true, state.significand_state); + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (!state.significand_state.has_digit) + { + return {first, ::fast_io::parse_code::invalid}; + } + if (first != end && (*first == lower_p || *first == upper_p)) + { + state.has_exponent_marker = true; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_start; + ++first; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + return {first, ::fast_io::parse_code::invalid}; + case ::fast_io::details::scan_hexfloat_context_phase::exponent_start: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + if (*first == minus) + { + state.exponent_negative = true; + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + if (*first == plus) + { + ++first; + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_after_sign; + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + break; + } + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_digits; + break; + case ::fast_io::details::scan_hexfloat_context_phase::exponent_after_sign: + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + state.phase = ::fast_io::details::scan_hexfloat_context_phase::exponent_digits; + break; + case ::fast_io::details::scan_hexfloat_context_phase::exponent_digits: + { + char8_t digit{}; + for (; first != end && ::fast_io::details::scan_hexfloat_decimal_digit(*first, digit); ++first) + { + ::fast_io::details::scan_hexfloat_context_append_exponent_digit(state, digit); + } + if (first == end) + { + return {end, ::fast_io::parse_code::partial}; + } + auto code{::fast_io::details::scan_hexfloat_context_assign(state, value)}; + return {first, code}; + } + case ::fast_io::details::scan_hexfloat_context_phase::special: + return ::fast_io::details::scan_hexfloat_context_special_define( + state, first, end, value); + } + } +} + +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + ::fast_io::details::my_floating_point T, typename storage_type> +[[nodiscard]] inline constexpr ::fast_io::parse_code +scan_hexfloat_context_eof( + ::fast_io::details::scan_hexfloat_context &state, T &value) noexcept +{ + if (state.phase == ::fast_io::details::scan_hexfloat_context_phase::special) + { + if (!state.special_buffer.size) + { + return ::fast_io::parse_code::end_of_file; + } + T parsed_value{}; + auto const *buffer_begin{state.special_buffer.buffer.data()}; + auto const *buffer_end{buffer_begin + state.special_buffer.size}; + auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( + buffer_begin, buffer_end, parsed_value)}; + if (parse_result.code == ::fast_io::parse_code::ok) + { + value = parsed_value; + } + return parse_result.code; + } + if (state.phase == ::fast_io::details::scan_hexfloat_context_phase::start && !state.significand_state.has_digit && + !state.has_sign) + { + return ::fast_io::parse_code::end_of_file; + } + return ::fast_io::details::scan_hexfloat_context_assign(state, value); +} + } // namespace details namespace manipulators @@ -1329,7 +1876,10 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags inline constexpr auto scan_context_type(io_reserve_type_t>) noexcept { - return io_type_t<::fast_io::details::scan_floating_context>{}; + using trait = ::fast_io::details::iec559_traits; + constexpr ::std::size_t precision_bits{trait::mbits + 1u}; + using storage_type = ::fast_io::details::scan_hexfloat_storage_t; + return io_type_t<::fast_io::details::scan_hexfloat_context>{}; } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1337,51 +1887,14 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags requires(flags.floating == ::fast_io::manipulators::floating_format::hexfloat) inline constexpr ::fast_io::parse_result scan_context_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, char_type const *begin, + typename ::std::remove_cvref_t>))>::type &state, + char_type const *begin, char_type const *end, ::fast_io::manipulators::scalar_manip_t value) noexcept { - if (!state.size) - { - begin = ::fast_io::details::scan_floating_context_skip_space(begin, end); - if (begin == end) - { - return {end, ::fast_io::parse_code::partial}; - } - } - auto const old_size{state.size}; - auto const append_result{::fast_io::details::scan_floating_context_append(state, begin, end)}; - if (append_result.code != ::fast_io::parse_code::partial) - { - return append_result; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( - buffer_begin, buffer_end, parsed_value)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - value.reference = parsed_value; - return {::fast_io::details::scan_floating_context_map_iter( - state, old_size, begin, end, parse_result.iter), - ::fast_io::parse_code::ok}; - } - if (parse_result.iter == buffer_end) - { - return {end, ::fast_io::parse_code::partial}; - } - if (parse_result.code == ::fast_io::parse_code::end_of_file || - parse_result.code == ::fast_io::parse_code::partial) - { - return {end, ::fast_io::parse_code::partial}; - } - return {::fast_io::details::scan_floating_context_map_iter(state, old_size, begin, end, parse_result.iter), - parse_result.code}; + return ::fast_io::details::scan_hexfloat_context_numeric_define( + state, begin, end, value.reference); } template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, @@ -1389,23 +1902,11 @@ template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags requires(flags.floating == ::fast_io::manipulators::floating_format::hexfloat) inline constexpr ::fast_io::parse_code scan_context_eof_define(io_reserve_type_t>, - ::fast_io::details::scan_floating_context &state, + typename ::std::remove_cvref_t>))>::type &state, ::fast_io::manipulators::scalar_manip_t value) noexcept { - if (!state.size) - { - return ::fast_io::parse_code::end_of_file; - } - T parsed_value{}; - auto const *buffer_begin{state.buffer.data()}; - auto const *buffer_end{buffer_begin + state.size}; - auto parse_result{::fast_io::details::scan_hexfloat_contiguous_define_impl( - buffer_begin, buffer_end, parsed_value)}; - if (parse_result.code == ::fast_io::parse_code::ok) - { - value.reference = parsed_value; - } - return parse_result.code; + return ::fast_io::details::scan_hexfloat_context_eof(state, value.reference); } } // namespace fast_io diff --git a/third-parties/fast_io/include/fast_io_unit/floating/punning.h b/third-parties/fast_io/include/fast_io_unit/floating/punning.h index a168302be..7d3f72425 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/punning.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/punning.h @@ -501,12 +501,16 @@ inline constexpr bool fp_nan_is_signaling(mantissa_type mantissa) noexcept return mantissa != 0 && (mantissa & fp_quiet_nan_mantissa_mask()) == 0; } -#ifdef __SIZEOF_FLOAT80__ +#if defined(__SIZEOF_FLOAT80__) || \ + (defined(__LDBL_MANT_DIG__) && defined(__LDBL_MAX_EXP__) && __LDBL_MANT_DIG__ == 64 && \ + __LDBL_MAX_EXP__ == 16384) template inline constexpr bool fp_floating_point_is_float80{ +#ifdef __SIZEOF_FLOAT80__ ::std::same_as<::std::remove_cv_t, __float80> || +#endif (::std::same_as<::std::remove_cv_t, long double> && - sizeof(long double) == sizeof(__float80) && ::std::numeric_limits::digits == 64 && + ::std::numeric_limits::digits == 64 && ::std::numeric_limits::max_exponent == 16384)}; template From 241a015847a964c166efff59c0d1b372bcc38b30 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 18:48:12 +0800 Subject: [PATCH 24/45] Enhance decfloat and hexfloat parsing in fast_io - Introduced new structures and functions to improve the handling of exact digits and bigint operations in decimal floating-point parsing. - Refactored hexfloat parsing logic to streamline the handling of hexadecimal digits and improve efficiency. - Enhanced state management for both decfloat and hexfloat to better track significant digits and truncation conditions, ensuring more accurate parsing results. - Updated existing functions to utilize new capabilities, improving overall performance and maintainability of floating-point and hexadecimal parsing. --- .../include/fast_io_unit/floating/decfloat.h | 764 +++++++++++++++++- .../include/fast_io_unit/floating/hexfloat.h | 190 ++++- 2 files changed, 897 insertions(+), 57 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 4dbc26995..0afe59593 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -252,15 +252,20 @@ inline constexpr bool scan_decfloat_clinger_environment_independent{ #endif }; +inline constexpr ::std::size_t scan_decfloat_exact_digit_capacity{768u}; + struct scan_decfloat_significand_state { ::std::uint_least64_t significand{}; ::std::uint_least64_t significant_digits{}; ::std::uint_least64_t stored_digits{}; ::std::uint_least64_t fractional_digits{}; + ::std::uint_least16_t exact_stored_digits{}; bool has_digit{}; bool has_nonzero_digit{}; bool truncated_nonzero{}; + bool exact_truncated_nonzero{}; + char8_t exact_digits[scan_decfloat_exact_digit_capacity]{}; }; template <::std::integral char_type> @@ -292,6 +297,43 @@ inline constexpr ::std::int_least32_t scan_decfloat_dragonbox_max_power10{326}; inline constexpr ::std::uint_least64_t scan_decfloat_pow10_0_to_8_table[]{ 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u}; +inline constexpr void scan_decfloat_append_exact_digit(scan_decfloat_significand_state &state, + char8_t digit) noexcept +{ + if (state.exact_stored_digits != ::fast_io::details::scan_decfloat_exact_digit_capacity) + { + state.exact_digits[state.exact_stored_digits] = digit; + ++state.exact_stored_digits; + } + else if (digit != 0) + { + state.exact_truncated_nonzero = true; + } +} + +inline constexpr void scan_decfloat_append_exact_eight_digits(scan_decfloat_significand_state &state, + ::std::uint_least32_t digits) noexcept +{ + if (state.exact_stored_digits == ::fast_io::details::scan_decfloat_exact_digit_capacity) + { + if (digits != 0) + { + state.exact_truncated_nonzero = true; + } + return; + } + for (auto divisor{10000000u}; divisor != 0u; divisor /= 10u) + { + auto const digit{static_cast(digits / divisor)}; + digits -= static_cast<::std::uint_least32_t>(digit) * divisor; + ::fast_io::details::scan_decfloat_append_exact_digit(state, digit); + if (divisor == 1u) + { + break; + } + } +} + struct scan_decfloat_adjusted_mantissa { ::std::uint_least64_t mantissa{}; @@ -743,6 +785,668 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return ::fast_io::parse_code::ok; } +#ifdef __SIZEOF_INT128__ + inline constexpr ::std::size_t scan_decfloat_bigint_limb_capacity{288u}; + + struct scan_decfloat_bigint + { + ::std::uint_least64_t limb[scan_decfloat_bigint_limb_capacity]{}; + ::std::size_t size{}; + }; + + inline constexpr void scan_decfloat_bigint_normalize(scan_decfloat_bigint &value) noexcept + { + for (; value.size && value.limb[value.size - 1u] == 0u; --value.size) + { + } + } + + inline constexpr void scan_decfloat_bigint_set_u64(scan_decfloat_bigint &value, + ::std::uint_least64_t word) noexcept + { + value.limb[0] = word; + value.size = word == 0u ? 0u : 1u; + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_mul_small(scan_decfloat_bigint &value, + ::std::uint_least32_t multiplier) noexcept + { + __uint128_t carry{}; + for (::std::size_t index{}; index != value.size; ++index) + { + auto const product{static_cast<__uint128_t>(value.limb[index]) * multiplier + carry}; + value.limb[index] = static_cast<::std::uint_least64_t>(product); + carry = product >> 64u; + } + if (carry) + { + if (value.size == ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + value.limb[value.size] = static_cast<::std::uint_least64_t>(carry); + ++value.size; + } + return true; + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_add_small(scan_decfloat_bigint &value, + ::std::uint_least32_t addend) noexcept + { + if (!value.size) + { + ::fast_io::details::scan_decfloat_bigint_set_u64(value, addend); + return true; + } + __uint128_t sum{static_cast<__uint128_t>(value.limb[0]) + addend}; + value.limb[0] = static_cast<::std::uint_least64_t>(sum); + auto carry{sum >> 64u}; + for (::std::size_t index{1u}; carry && index != value.size; ++index) + { + sum = static_cast<__uint128_t>(value.limb[index]) + carry; + value.limb[index] = static_cast<::std::uint_least64_t>(sum); + carry = sum >> 64u; + } + if (carry) + { + if (value.size == ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + value.limb[value.size] = static_cast<::std::uint_least64_t>(carry); + ++value.size; + } + return true; + } + + inline constexpr bool scan_decfloat_bigint_from_digits(scan_decfloat_bigint &value, + scan_decfloat_significand_state const &state) noexcept + { + value = {}; + for (::std::size_t index{}; index != state.exact_stored_digits; ++index) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_small(value, 10u) || + !::fast_io::details::scan_decfloat_bigint_add_small( + value, static_cast<::std::uint_least32_t>(state.exact_digits[index]))) + { + return false; + } + } + return true; + } + + [[nodiscard]] inline constexpr ::std::size_t + scan_decfloat_bigint_bit_width(scan_decfloat_bigint const &value) noexcept + { + if (!value.size) + { + return 0u; + } + return (value.size - 1u) * 64u + + static_cast<::std::size_t>(::std::bit_width(value.limb[value.size - 1u])); + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_get_bit(scan_decfloat_bigint const &value, + ::std::size_t bit) noexcept + { + auto const limb_index{bit / 64u}; + if (limb_index >= value.size) + { + return false; + } + return ((value.limb[limb_index] >> (bit % 64u)) & 1u) != 0u; + } + + [[nodiscard]] inline constexpr int scan_decfloat_bigint_compare(scan_decfloat_bigint const &left, + scan_decfloat_bigint const &right) noexcept + { + if (left.size != right.size) + { + return left.size < right.size ? -1 : 1; + } + for (auto index{left.size}; index != 0u;) + { + --index; + if (left.limb[index] != right.limb[index]) + { + return left.limb[index] < right.limb[index] ? -1 : 1; + } + } + return 0; + } + + inline constexpr void scan_decfloat_bigint_sub_assign(scan_decfloat_bigint &left, + scan_decfloat_bigint const &right) noexcept + { + ::std::uint_least64_t borrow{}; + for (::std::size_t index{}; index != left.size; ++index) + { + auto const subtrahend{index < right.size ? right.limb[index] : ::std::uint_least64_t{}}; + auto const old{left.limb[index]}; + auto const with_borrow{static_cast<::std::uint_least64_t>(subtrahend + borrow)}; + left.limb[index] = static_cast<::std::uint_least64_t>(old - with_borrow); + borrow = old < with_borrow || (borrow && with_borrow == 0u); + } + ::fast_io::details::scan_decfloat_bigint_normalize(left); + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_shl1_add_bit(scan_decfloat_bigint &value, + bool bit) noexcept + { + ::std::uint_least64_t carry{static_cast<::std::uint_least64_t>(bit)}; + for (::std::size_t index{}; index != value.size; ++index) + { + auto const next_carry{value.limb[index] >> 63u}; + value.limb[index] = static_cast<::std::uint_least64_t>((value.limb[index] << 1u) | carry); + carry = next_carry; + } + if (carry) + { + if (value.size == ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + value.limb[value.size] = carry; + ++value.size; + } + else if (bit && !value.size) + { + value.limb[0] = 1u; + value.size = 1u; + } + return true; + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_shift_left(scan_decfloat_bigint &out, + scan_decfloat_bigint const &in, + ::std::size_t shift) noexcept + { + out = {}; + if (!in.size) + { + return true; + } + auto const limb_shift{shift / 64u}; + auto const bit_shift{shift % 64u}; + if (in.size + limb_shift + (bit_shift != 0u) > + ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + for (::std::size_t index{}; index != limb_shift; ++index) + { + out.limb[index] = 0u; + } + ::std::uint_least64_t carry{}; + for (::std::size_t index{}; index != in.size; ++index) + { + auto const word{in.limb[index]}; + out.limb[index + limb_shift] = static_cast<::std::uint_least64_t>((word << bit_shift) | carry); + carry = bit_shift == 0u ? 0u : static_cast<::std::uint_least64_t>(word >> (64u - bit_shift)); + } + out.size = in.size + limb_shift; + if (carry) + { + out.limb[out.size] = carry; + ++out.size; + } + ::fast_io::details::scan_decfloat_bigint_normalize(out); + return true; + } + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_pow5(scan_decfloat_bigint &value, + ::std::uint_least64_t exponent) noexcept + { + ::fast_io::details::scan_decfloat_bigint_set_u64(value, 1u); + for (; exponent; --exponent) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_small(value, 5u)) + { + return false; + } + } + return true; + } + + [[nodiscard]] inline constexpr ::std::int_least64_t + scan_decfloat_bigint_floor_log2_ratio(scan_decfloat_bigint const &numerator, + scan_decfloat_bigint const &denominator) noexcept + { + auto const numerator_bits{::fast_io::details::scan_decfloat_bigint_bit_width(numerator)}; + auto const denominator_bits{::fast_io::details::scan_decfloat_bigint_bit_width(denominator)}; + auto exponent{static_cast<::std::int_least64_t>(numerator_bits) - + static_cast<::std::int_least64_t>(denominator_bits)}; + ::fast_io::details::scan_decfloat_bigint shifted; + if (exponent >= 0) + { + (void)::fast_io::details::scan_decfloat_bigint_shift_left( + shifted, denominator, static_cast<::std::size_t>(exponent)); + if (::fast_io::details::scan_decfloat_bigint_compare(numerator, shifted) < 0) + { + --exponent; + } + } + else + { + (void)::fast_io::details::scan_decfloat_bigint_shift_left( + shifted, numerator, static_cast<::std::size_t>(-exponent)); + if (::fast_io::details::scan_decfloat_bigint_compare(shifted, denominator) < 0) + { + --exponent; + } + } + return exponent; + } + + struct scan_decfloat_bigint_div_result + { + __uint128_t quotient{}; + int twice_remainder_compare{}; + bool remainder_nonzero{}; + bool quotient_overflow{}; + }; + + [[nodiscard]] inline constexpr scan_decfloat_bigint_div_result + scan_decfloat_bigint_div_shifted_to_u128(scan_decfloat_bigint const &numerator, + scan_decfloat_bigint const &denominator, + ::std::int_least64_t binary_shift) noexcept + { + ::fast_io::details::scan_decfloat_bigint divisor; + if (binary_shift < 0) + { + if (!::fast_io::details::scan_decfloat_bigint_shift_left( + divisor, denominator, static_cast<::std::size_t>(-binary_shift))) + { + return {.quotient_overflow = true}; + } + } + else + { + divisor = denominator; + } + auto const dividend_bits{ + ::fast_io::details::scan_decfloat_bigint_bit_width(numerator) + + (binary_shift > 0 ? static_cast<::std::size_t>(binary_shift) : 0u)}; + ::fast_io::details::scan_decfloat_bigint remainder; + __uint128_t quotient{}; + bool quotient_overflow{}; + for (auto bit_index{dividend_bits}; bit_index != 0u;) + { + --bit_index; + bool bit{}; + if (binary_shift > 0) + { + auto const shift{static_cast<::std::size_t>(binary_shift)}; + bit = bit_index >= shift && + ::fast_io::details::scan_decfloat_bigint_get_bit(numerator, bit_index - shift); + } + else + { + bit = ::fast_io::details::scan_decfloat_bigint_get_bit(numerator, bit_index); + } + if (!::fast_io::details::scan_decfloat_bigint_shl1_add_bit(remainder, bit)) + { + return {.quotient_overflow = true}; + } + if (::fast_io::details::scan_decfloat_bigint_compare(remainder, divisor) >= 0) + { + ::fast_io::details::scan_decfloat_bigint_sub_assign(remainder, divisor); + if (bit_index < 128u) + { + quotient |= static_cast<__uint128_t>(1u) << bit_index; + } + else + { + quotient_overflow = true; + } + } + } + ::fast_io::details::scan_decfloat_bigint twice_remainder{remainder}; + (void)::fast_io::details::scan_decfloat_bigint_shl1_add_bit(twice_remainder, false); + return {.quotient = quotient, + .twice_remainder_compare = + ::fast_io::details::scan_decfloat_bigint_compare(twice_remainder, divisor), + .remainder_nonzero = remainder.size != 0u, + .quotient_overflow = quotient_overflow}; + } + + template <::fast_io::manipulators::floating_rounding rounding> + [[nodiscard]] inline constexpr bool scan_decfloat_big_round_up(bool negative, __uint128_t quotient, + int twice_remainder_compare, + bool remainder_nonzero, + bool tail_nonzero) noexcept + { + if constexpr (::fast_io::details::floating_rounding_is_nearest) + { + if (twice_remainder_compare < 0) + { + return false; + } + if (twice_remainder_compare > 0 || tail_nonzero) + { + return true; + } + if (!remainder_nonzero) + { + return false; + } + return ::fast_io::details::floating_rounding_nearest_tie_round_up( + negative, static_cast<::std::uint_least64_t>(quotient) << 1u); + } + else + { + return (remainder_nonzero || tail_nonzero) && + ::fast_io::details::floating_rounding_directed_round_up(negative); + } + } + + template + inline constexpr void scan_decfloat_assign_min_subnormal(T &value, bool negative) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + using mantissa_type = typename trait::mantissa_type; + constexpr auto mbits{trait::mbits}; + constexpr auto ebits{trait::ebits}; + if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + ::fast_io::details::fp_assign_float80_bits(value, 1u, 0u, negative); + } + else + { + mantissa_type bits{1u}; + if (negative) + { + bits |= static_cast(static_cast(1u) << (mbits + ebits)); + } + ::fast_io::details::fp_assign_bits(value, bits); + } + } + + template + inline constexpr void scan_decfloat_assign_max_finite(T &value, bool negative) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + using mantissa_type = typename trait::mantissa_type; + constexpr auto mbits{trait::mbits}; + constexpr auto ebits{trait::ebits}; + if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + ::fast_io::details::fp_assign_float80_bits(value, static_cast<::std::uint_least64_t>(~::std::uint_least64_t{}), + (static_cast<::std::uint_least32_t>(1u) << ebits) - 2u, + negative); + } + else + { + constexpr mantissa_type exponent{(static_cast(1u) << ebits) - 2u}; + constexpr mantissa_type fraction{(static_cast(1u) << mbits) - 1u}; + mantissa_type bits{static_cast((exponent << mbits) | fraction)}; + if (negative) + { + bits |= static_cast(static_cast(1u) << (mbits + ebits)); + } + ::fast_io::details::fp_assign_bits(value, bits); + } + } + + template <::fast_io::manipulators::floating_rounding rounding, typename T> + inline constexpr ::fast_io::parse_code scan_decfloat_assign_overflow_value(T &value, bool negative) noexcept + { + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::toward_zero || + (rounding == ::fast_io::manipulators::floating_rounding::toward_plus_infinity && negative) || + (rounding == ::fast_io::manipulators::floating_rounding::toward_minus_infinity && !negative)) + { + ::fast_io::details::scan_decfloat_assign_max_finite(value, negative); + } + else + { + ::fast_io::details::fp_assign_infinity(value, negative); + } + return ::fast_io::parse_code::overflow; + } + + template + [[nodiscard]] inline constexpr ::fast_io::parse_code + scan_decfloat_assign_big(T &value, bool negative, scan_decfloat_significand_state const &state, + ::std::int_least64_t exponent) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + using mantissa_type = typename trait::mantissa_type; + constexpr ::std::size_t mbits{trait::mbits}; + constexpr ::std::size_t ebits{trait::ebits}; + constexpr ::std::size_t precision_bits{mbits + 1u}; + constexpr auto bias{ + static_cast<::std::int_least64_t>((static_cast<::std::uint_least32_t>(1u) << ebits) >> 1u) - 1}; + constexpr auto min_exponent{1 - bias}; + constexpr auto max_exponent{bias}; + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::current_environment) + { + switch (::fast_io::details::current_floating_rounding()) + { + case ::fast_io::manipulators::floating_rounding::toward_plus_infinity: + return ::fast_io::details::scan_decfloat_assign_big< + T, ::fast_io::manipulators::floating_rounding::toward_plus_infinity>( + value, negative, state, exponent); + case ::fast_io::manipulators::floating_rounding::toward_minus_infinity: + return ::fast_io::details::scan_decfloat_assign_big< + T, ::fast_io::manipulators::floating_rounding::toward_minus_infinity>( + value, negative, state, exponent); + case ::fast_io::manipulators::floating_rounding::toward_zero: + return ::fast_io::details::scan_decfloat_assign_big< + T, ::fast_io::manipulators::floating_rounding::toward_zero>( + value, negative, state, exponent); + default: + return ::fast_io::details::scan_decfloat_assign_big< + T, ::fast_io::manipulators::floating_rounding::nearest_to_even>( + value, negative, state, exponent); + } + } + else + { + if (!state.exact_stored_digits) + { + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::ok; + } + auto decimal_exponent{::fast_io::details::scan_decfloat_saturating_add( + exponent, -static_cast<::std::int_least64_t>(state.fractional_digits))}; + decimal_exponent = ::fast_io::details::scan_decfloat_saturating_add( + decimal_exponent, + static_cast<::std::int_least64_t>(state.significant_digits - state.exact_stored_digits)); + auto const decimal_top_exponent{::fast_io::details::scan_decfloat_saturating_add( + decimal_exponent, static_cast<::std::int_least64_t>(state.exact_stored_digits - 1u))}; + constexpr auto high_guard{static_cast<::std::int_least64_t>(trait::e10max + 2u)}; + constexpr auto low_guard{static_cast<::std::int_least64_t>(trait::e10max + trait::m10digits + 16u)}; + if (decimal_top_exponent > high_guard) + { + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); + } + if (decimal_top_exponent < -low_guard) + { + if constexpr (!::fast_io::details::floating_rounding_is_nearest) + { + if (state.has_nonzero_digit && + ::fast_io::details::floating_rounding_directed_round_up(negative)) + { + ::fast_io::details::scan_decfloat_assign_min_subnormal(value, negative); + return ::fast_io::parse_code::overflow; + } + } + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::overflow; + } + + ::fast_io::details::scan_decfloat_bigint numerator; + if (!::fast_io::details::scan_decfloat_bigint_from_digits(numerator, state)) + { + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); + } + ::fast_io::details::scan_decfloat_bigint denominator; + ::fast_io::details::scan_decfloat_bigint_set_u64(denominator, 1u); + ::std::int_least64_t binary_exponent_adjust{}; + if (decimal_exponent >= 0) + { + for (::std::int_least64_t counter{}; counter != decimal_exponent; ++counter) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_small(numerator, 5u)) + { + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); + } + } + binary_exponent_adjust = decimal_exponent; + } + else + { + auto const pow5_exponent{static_cast<::std::uint_least64_t>(-decimal_exponent)}; + if (!::fast_io::details::scan_decfloat_bigint_pow5(denominator, pow5_exponent)) + { + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::overflow; + } + binary_exponent_adjust = decimal_exponent; + } + + ::std::int_least64_t binary_exponent{}; + if (decimal_exponent >= 0) + { + binary_exponent = static_cast<::std::int_least64_t>( + ::fast_io::details::scan_decfloat_bigint_bit_width(numerator)) - + 1 + binary_exponent_adjust; + } + else + { + binary_exponent = + ::fast_io::details::scan_decfloat_bigint_floor_log2_ratio(numerator, denominator) + + binary_exponent_adjust; + } + + bool subnormal{}; + ::std::int_least64_t target_exponent{binary_exponent}; + ::std::int_least64_t scale_exponent{}; + if (binary_exponent >= min_exponent) + { + scale_exponent = binary_exponent - static_cast<::std::int_least64_t>(precision_bits - 1u); + } + else + { + subnormal = true; + target_exponent = min_exponent; + scale_exponent = min_exponent - static_cast<::std::int_least64_t>(precision_bits - 1u); + } + + auto const division{::fast_io::details::scan_decfloat_bigint_div_shifted_to_u128( + numerator, denominator, binary_exponent_adjust - scale_exponent)}; + if (division.quotient_overflow) + { + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); + } + auto significand{division.quotient}; + if (::fast_io::details::scan_decfloat_big_round_up( + negative, significand, division.twice_remainder_compare, + division.remainder_nonzero, state.exact_truncated_nonzero)) + { + ++significand; + } + auto const hidden_bit{static_cast<__uint128_t>(1u) << mbits}; + auto const carry_bit{hidden_bit << 1u}; + if (!subnormal) + { + if (significand >= carry_bit) + { + significand >>= 1u; + ++target_exponent; + } + if (target_exponent > max_exponent) + { + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); + } + if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + ::fast_io::details::fp_assign_float80_bits( + value, static_cast<::std::uint_least64_t>(significand), + static_cast<::std::uint_least32_t>(target_exponent + bias), negative); + } + else + { + auto fraction{static_cast(significand - hidden_bit)}; + auto bits{static_cast( + (static_cast(static_cast<::std::uint_least64_t>(target_exponent + bias)) << mbits) | + fraction)}; + if (negative) + { + bits |= static_cast(static_cast(1u) << (mbits + ebits)); + } + ::fast_io::details::fp_assign_bits(value, bits); + } + return ::fast_io::parse_code::ok; + } + if (!significand) + { + value = negative ? -static_cast(0.0) : static_cast(0.0); + return ::fast_io::parse_code::overflow; + } + if (significand >= hidden_bit) + { + if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + ::fast_io::details::fp_assign_float80_bits(value, static_cast<::std::uint_least64_t>(hidden_bit), + 1u, negative); + } + else + { + mantissa_type bits{static_cast(mantissa_type{1u} << mbits)}; + if (negative) + { + bits |= static_cast(static_cast(1u) << (mbits + ebits)); + } + ::fast_io::details::fp_assign_bits(value, bits); + } + } + else if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + ::fast_io::details::fp_assign_float80_bits( + value, static_cast<::std::uint_least64_t>(significand), 0u, negative); + } + else + { + auto bits{static_cast(significand)}; + if (negative) + { + bits |= static_cast(static_cast(1u) << (mbits + ebits)); + } + ::fast_io::details::fp_assign_bits(value, bits); + } + return ::fast_io::parse_code::ok; + } + } +#endif + + inline constexpr void scan_decfloat_state_from_u64(scan_decfloat_significand_state &state, + ::std::uint_least64_t significand) noexcept + { + auto const original{significand}; + char8_t buffer[20]{}; + ::std::size_t size{}; + for (; significand; significand /= 10u) + { + buffer[size] = static_cast(significand % 10u); + ++size; + } + state.has_digit = true; + state.has_nonzero_digit = size != 0u; + state.significant_digits = size; + state.stored_digits = size; + state.significand = original; + for (auto index{size}; index != 0u;) + { + --index; + ::fast_io::details::scan_decfloat_append_exact_digit(state, buffer[index]); + } + } + template <::fast_io::manipulators::floating_rounding rounding> [[nodiscard]] inline constexpr bool scan_decfloat_decimal_round_up(bool negative, ::std::uint_least64_t rounded_down, @@ -791,6 +1495,7 @@ inline constexpr void scan_decfloat_append_digit(scan_decfloat_significand_state state.has_nonzero_digit = true; } ++state.significant_digits; + ::fast_io::details::scan_decfloat_append_exact_digit(state, digit); if (state.stored_digits != digit_limit) { state.significand = state.significand * 10u + static_cast<::std::uint_least64_t>(digit); @@ -847,6 +1552,7 @@ inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significan state.has_nonzero_digit = true; } state.significant_digits += 8u; + ::fast_io::details::scan_decfloat_append_exact_eight_digits(state, digits); if (state.stored_digits == digit_limit) { if (digits != 0) @@ -926,8 +1632,19 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d state.fractional_digits += 8u; } state.significant_digits += 8u; - if (!state.truncated_nonzero && - ::fast_io::details::scan_decfloat_ascii8_has_nonzero_digit(val)) + auto const has_nonzero{ + ::fast_io::details::scan_decfloat_ascii8_has_nonzero_digit(val)}; + if (state.exact_stored_digits != + ::fast_io::details::scan_decfloat_exact_digit_capacity) + { + ::fast_io::details::scan_decfloat_append_exact_eight_digits( + state, ::fast_io::details::scan_decfloat_ascii8_parse(val)); + } + else if (!state.exact_truncated_nonzero && has_nonzero) + { + state.exact_truncated_nonzero = true; + } + if (!state.truncated_nonzero && has_nonzero) { state.truncated_nonzero = true; } @@ -998,28 +1715,17 @@ template ( - adjusted_exponent, state.significand, negative, adjusted) && - ::fast_io::details::scan_decfloat_compute_adjusted( - adjusted_exponent, state.significand + 1u, negative, adjusted_next) && - adjusted.mantissa == adjusted_next.mantissa && adjusted.power2 == adjusted_next.power2) - { - return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); - } - } } +#ifdef __SIZEOF_INT128__ + return ::fast_io::details::scan_decfloat_assign_big(value, negative, state, exponent); +#else if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) { return ::fast_io::details::scan_decfloat_assign_native_wide( value, negative, state.significand, adjusted_exponent); } - else - { - return ::fast_io::parse_code::partial; - } + return ::fast_io::parse_code::partial; +#endif } template (value, negative, state, adjusted_exponent); +#else if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) { return ::fast_io::details::scan_decfloat_assign_native_wide( value, negative, significand, adjusted_exponent); } - else - { - return ::fast_io::parse_code::partial; - } + return ::fast_io::parse_code::partial; +#endif } template (value, negative, state, adjusted_exponent); +#else if constexpr (::fast_io::details::scan_decfloat_native_wide_supported) { return ::fast_io::details::scan_decfloat_assign_native_wide( value, negative, significand, adjusted_exponent); } - else - { - return ::fast_io::parse_code::partial; - } + return ::fast_io::parse_code::partial; +#endif } template diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index 014ef68b7..2736f724e 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -409,25 +409,74 @@ inline constexpr void scan_hexfloat_ascii8_pack(::std::uint_least64_t val, ::std return true; } -template +template <::std::integral char_type, typename state_type> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) [[msvc::forceinline]] #endif -inline constexpr void scan_hexfloat_skip_ascii8_after_storage_limit(state_type &state, bool after_decimal, - bool has_nonzero_digit) noexcept +inline constexpr char_type const *scan_hexfloat_skip_after_storage_limit_run(char_type const *first, + char_type const *last, + bool after_decimal, + state_type &state) noexcept { - state.has_digit = true; - if (after_decimal) + auto const *const original_first{first}; + auto truncated_nonzero{state.truncated_nonzero}; +#ifdef __cpp_if_consteval + if !consteval +#else + if (!__builtin_is_constant_evaluated()) +#endif { - state.fractional_hex_digits += 8u; + if constexpr (!::fast_io::details::is_ebcdic && sizeof(char_type) == sizeof(char8_t) && + ::std::numeric_limits<::std::uint_least64_t>::digits == 64u) + { + for (; static_cast<::std::size_t>(last - first) >= sizeof(::std::uint_least64_t);) + { + ::std::uint_least64_t val; + ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); + if constexpr (::std::endian::little != ::std::endian::native) + { + val = ::fast_io::little_endian(val); + } + if (!::fast_io::details::scan_hexfloat_ascii8_is_xdigits(val)) + { + break; + } + if (!truncated_nonzero && + ::fast_io::details::scan_hexfloat_ascii8_has_nonzero_digit(val)) + { + truncated_nonzero = true; + } + first += sizeof(::std::uint_least64_t); + } + } } - state.significant_hex_digits += 8u; - if (!state.truncated_nonzero && has_nonzero_digit) + using unsigned_char_type = ::fast_io::details::my_make_unsigned_t; + for (; first != last; ++first) { - state.truncated_nonzero = true; + auto uch{static_cast(*first)}; + if (::fast_io::details::char_digit_to_literal<16, char_type>(uch)) + { + break; + } + if (!truncated_nonzero && uch != 0) + { + truncated_nonzero = true; + } } + auto const skipped{static_cast<::std::int_least64_t>(first - original_first)}; + if (skipped) + { + state.has_digit = true; + if (after_decimal) + { + state.fractional_hex_digits += skipped; + } + state.significant_hex_digits += skipped; + state.truncated_nonzero = truncated_nonzero; + } + return first; } template <::std::size_t stored_hex_digits_limit, typename state_type> @@ -508,6 +557,10 @@ inline constexpr char_type const *scan_hexfloat_significand_run(char_type const { for (; static_cast<::std::size_t>(last - first) >= sizeof(::std::uint_least64_t);) { + if (state.has_nonzero_digit && state.stored_hex_digits == stored_hex_digits_limit) + { + return ::fast_io::details::scan_hexfloat_skip_after_storage_limit_run(first, last, after_decimal, state); + } ::std::uint_least64_t val; ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); if constexpr (::std::endian::little != ::std::endian::native) @@ -515,17 +568,6 @@ inline constexpr char_type const *scan_hexfloat_significand_run(char_type const val = ::fast_io::little_endian(val); } ::std::uint_least64_t nibbles{}; - if (state.has_nonzero_digit && state.stored_hex_digits == stored_hex_digits_limit) - { - if (!::fast_io::details::scan_hexfloat_ascii8_is_xdigits(val)) - { - break; - } - ::fast_io::details::scan_hexfloat_skip_ascii8_after_storage_limit(state, after_decimal, - ::fast_io::details::scan_hexfloat_ascii8_has_nonzero_digit(val)); - first += sizeof(::std::uint_least64_t); - continue; - } ::std::uint_least64_t packed{}; if (!::fast_io::details::scan_hexfloat_ascii8_nibbles(val, nibbles, packed)) { @@ -537,6 +579,10 @@ inline constexpr char_type const *scan_hexfloat_significand_run(char_type const } } } + if (state.has_nonzero_digit && state.stored_hex_digits == stored_hex_digits_limit) + { + return ::fast_io::details::scan_hexfloat_skip_after_storage_limit_run(first, last, after_decimal, state); + } using unsigned_char_type = ::fast_io::details::my_make_unsigned_t; for (; first != last; ++first) { @@ -645,7 +691,8 @@ template <::fast_io::details::my_unsigned_integral storage_type> return static_cast((storage_type{1} << static_cast(bits)) - storage_type{1}); } -template <::fast_io::details::my_unsigned_integral storage_type> +template <::fast_io::manipulators::floating_rounding rounding, + ::fast_io::details::my_unsigned_integral storage_type> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -654,8 +701,31 @@ template <::fast_io::details::my_unsigned_integral storage_type> [[nodiscard]] inline constexpr storage_type scan_hexfloat_round_shift(storage_type stored, ::std::int_least64_t remaining_bits, bool truncated_nonzero, + bool negative, ::std::int_least64_t shift) noexcept { + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::current_environment) + { + switch (::fast_io::details::current_floating_rounding()) + { + case ::fast_io::manipulators::floating_rounding::toward_plus_infinity: + return ::fast_io::details::scan_hexfloat_round_shift< + ::fast_io::manipulators::floating_rounding::toward_plus_infinity>( + stored, remaining_bits, truncated_nonzero, negative, shift); + case ::fast_io::manipulators::floating_rounding::toward_minus_infinity: + return ::fast_io::details::scan_hexfloat_round_shift< + ::fast_io::manipulators::floating_rounding::toward_minus_infinity>( + stored, remaining_bits, truncated_nonzero, negative, shift); + case ::fast_io::manipulators::floating_rounding::toward_zero: + return ::fast_io::details::scan_hexfloat_round_shift< + ::fast_io::manipulators::floating_rounding::toward_zero>( + stored, remaining_bits, truncated_nonzero, negative, shift); + default: + return ::fast_io::details::scan_hexfloat_round_shift< + ::fast_io::manipulators::floating_rounding::nearest_to_even>( + stored, remaining_bits, truncated_nonzero, negative, shift); + } + } constexpr auto storage_bits{static_cast<::std::int_least64_t>(sizeof(storage_type) * ::std::numeric_limits::digits)}; auto const stored_bits{::fast_io::details::scan_hexfloat_bit_width(stored)}; @@ -674,6 +744,14 @@ template <::fast_io::details::my_unsigned_integral storage_type> } if (shift_in_stored > stored_bits) { + if constexpr (!::fast_io::details::floating_rounding_is_nearest) + { + if ((stored != 0u || truncated_nonzero) && + ::fast_io::details::floating_rounding_directed_round_up(negative)) + { + return 1u; + } + } return 0u; } @@ -696,14 +774,30 @@ template <::fast_io::details::my_unsigned_integral storage_type> below_half = below_half || (stored & below_mask) != 0u; } } - if (half && (below_half || (quotient & 1u) != 0u)) + bool const discarded_nonzero{half || below_half}; + if constexpr (::fast_io::details::floating_rounding_is_nearest) { - ++quotient; + if (half && + (below_half || + ::fast_io::details::floating_rounding_nearest_tie_round_up( + negative, static_cast<::std::uint_least64_t>(quotient) << 1u))) + { + ++quotient; + } + } + else + { + if (discarded_nonzero && ::fast_io::details::floating_rounding_directed_round_up(negative)) + { + ++quotient; + } } return quotient; } -template +template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] #elif __has_cpp_attribute(msvc::forceinline) @@ -746,8 +840,9 @@ inline constexpr ::fast_io::parse_code scan_hexfloat_assign_ieee_result(T &value { if (exponent2 >= min_exponent) { - auto significand{::fast_io::details::scan_hexfloat_round_shift( + auto significand{::fast_io::details::scan_hexfloat_round_shift( stored, remaining_bits, truncated_nonzero, + negative, total_bits - static_cast<::std::int_least64_t>(precision_bits))}; auto exponent_field{static_cast<::std::uint_least32_t>(exponent2 + bias)}; if (significand == static_cast(storage_type{1} << precision_bits)) @@ -766,7 +861,8 @@ inline constexpr ::fast_io::parse_code scan_hexfloat_assign_ieee_result(T &value auto const subnormal_shift{(min_exponent - static_cast<::std::int_least64_t>(mbits)) - binary_exponent}; auto const fraction{ - ::fast_io::details::scan_hexfloat_round_shift(stored, remaining_bits, truncated_nonzero, subnormal_shift)}; + ::fast_io::details::scan_hexfloat_round_shift(stored, remaining_bits, truncated_nonzero, + negative, subnormal_shift)}; if (fraction == 0u) { return ::fast_io::parse_code::overflow; @@ -792,8 +888,9 @@ inline constexpr ::fast_io::parse_code scan_hexfloat_assign_ieee_result(T &value if (exponent2 >= min_exponent) { - auto significand{::fast_io::details::scan_hexfloat_round_shift( + auto significand{::fast_io::details::scan_hexfloat_round_shift( stored, remaining_bits, truncated_nonzero, + negative, total_bits - static_cast<::std::int_least64_t>(precision_bits))}; if (significand == static_cast(storage_type{1} << precision_bits)) { @@ -818,7 +915,8 @@ inline constexpr ::fast_io::parse_code scan_hexfloat_assign_ieee_result(T &value auto const subnormal_shift{(min_exponent - static_cast<::std::int_least64_t>(mbits)) - binary_exponent}; auto const fraction{ - ::fast_io::details::scan_hexfloat_round_shift(stored, remaining_bits, truncated_nonzero, subnormal_shift)}; + ::fast_io::details::scan_hexfloat_round_shift(stored, remaining_bits, truncated_nonzero, + negative, subnormal_shift)}; if (fraction == 0u) { return ::fast_io::parse_code::overflow; @@ -927,7 +1025,7 @@ scan_hexfloat_contiguous_scalar_define_impl(char_type const *begin, char_type co : significand_state.fractional_hex_digits * 4}; auto const binary_exponent{exponent < int64_min + fractional_exponent ? int64_min : exponent - fractional_exponent}; return {exponent_result.iter, - ::fast_io::details::scan_hexfloat_assign_ieee_result( + ::fast_io::details::scan_hexfloat_assign_ieee_result( value, negative, significand_state.stored, significand_state.stored_hex_digits, significand_state.significant_hex_digits, significand_state.truncated_nonzero, binary_exponent)}; } @@ -1028,7 +1126,7 @@ scan_hexfloat_contiguous_define_impl(char_type const *begin, char_type const *en auto const binary_exponent{exponent < int64_min + fractional_exponent ? int64_min : exponent - fractional_exponent}; return {exponent_result.iter, - ::fast_io::details::scan_hexfloat_assign_ieee_result( + ::fast_io::details::scan_hexfloat_assign_ieee_result( value, negative, significand_state.stored, significand_state.stored_hex_digits, significand_state.significant_hex_digits, significand_state.truncated_nonzero, binary_exponent)}; } @@ -1465,7 +1563,7 @@ scan_hexfloat_context_assign( auto const exponent{::fast_io::details::scan_hexfloat_context_exponent(state)}; auto const binary_exponent{exponent < int64_min + fractional_exponent ? int64_min : exponent - fractional_exponent}; - return ::fast_io::details::scan_hexfloat_assign_ieee_result( + return ::fast_io::details::scan_hexfloat_assign_ieee_result( value, state.negative, state.significand_state.stored, state.significand_state.stored_hex_digits, state.significand_state.significant_hex_digits, state.significand_state.truncated_nonzero, binary_exponent); @@ -1832,6 +1930,21 @@ inline constexpr scalar_manip_t<::fast_io::manipulators::scalar_flags{.showbase return {t}; } +template <::fast_io::manipulators::floating_rounding rounding_policy, bool noskipws = false, bool prefix = false, + bool allow_leading_plus = false, ::fast_io::details::my_floating_point scalar_type> +inline constexpr scalar_manip_t<::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::manipulators::scalar_flags{ + .showbase = prefix, + .noskipws = noskipws, + .floating = ::fast_io::manipulators::floating_format::hexfloat, + .allow_leading_plus = allow_leading_plus}, + ::fast_io::manipulators::floating_precision::significant, rounding_policy>, + scalar_type &> + hexfloat_get(scalar_type &t) noexcept +{ + return {t}; +} + template inline constexpr scalar_manip_t<::fast_io::manipulators::scalar_flags{.showbase = true, @@ -1844,6 +1957,21 @@ inline constexpr scalar_manip_t<::fast_io::manipulators::scalar_flags{.showbase return {t}; } +template <::fast_io::manipulators::floating_rounding rounding_policy, bool noskipws = false, + bool allow_leading_plus = false, ::fast_io::details::my_floating_point scalar_type> +inline constexpr scalar_manip_t<::fast_io::details::floating_precision_rounding_mani_flags_cache< + ::fast_io::manipulators::scalar_flags{ + .showbase = true, + .noskipws = noskipws, + .floating = ::fast_io::manipulators::floating_format::hexfloat, + .allow_leading_plus = allow_leading_plus}, + ::fast_io::manipulators::floating_precision::significant, rounding_policy>, + scalar_type &> + hexfloat0x_get(scalar_type &t) noexcept +{ + return {t}; +} + } // namespace manipulators template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, From 78ebb27f2ec4bbd2d76ec84f7bc394e665eb79a8 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 18:54:07 +0800 Subject: [PATCH 25/45] Refactor overflow handling in decfloat parsing - Updated the logic for handling overflow values in decimal floating-point parsing to improve clarity and maintainability. - Introduced a boolean flag to streamline the conditions under which maximum finite values are assigned, enhancing the readability of the code. - This change aims to ensure more accurate parsing results while maintaining the overall performance of the fast_io library. --- .../include/fast_io_unit/floating/decfloat.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 0afe59593..c227c0617 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -1193,9 +1193,20 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t template <::fast_io::manipulators::floating_rounding rounding, typename T> inline constexpr ::fast_io::parse_code scan_decfloat_assign_overflow_value(T &value, bool negative) noexcept { - if constexpr (rounding == ::fast_io::manipulators::floating_rounding::toward_zero || - (rounding == ::fast_io::manipulators::floating_rounding::toward_plus_infinity && negative) || - (rounding == ::fast_io::manipulators::floating_rounding::toward_minus_infinity && !negative)) + bool assign_max_finite{}; + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::toward_zero) + { + assign_max_finite = true; + } + else if constexpr (rounding == ::fast_io::manipulators::floating_rounding::toward_plus_infinity) + { + assign_max_finite = negative; + } + else if constexpr (rounding == ::fast_io::manipulators::floating_rounding::toward_minus_infinity) + { + assign_max_finite = !negative; + } + if (assign_max_finite) { ::fast_io::details::scan_decfloat_assign_max_finite(value, negative); } From e132ee4ca3666fcabc009f0cc9a1c90a1eb27fed Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 19:21:12 +0800 Subject: [PATCH 26/45] Enhance decfloat and hexfloat functionality in fast_io - Introduced new functions for handling floating-point operations, including bigint multiplication and addition, improving performance and accuracy in decimal and hexadecimal parsing. - Added support for extended mantissa structures and adjusted significand calculations, enhancing the precision of floating-point representations. - Refactored existing logic to streamline the parsing process and improve maintainability, ensuring robust handling of edge cases in floating-point formats. --- .../include/fast_io_unit/floating/decfloat.h | 318 +++++++++++++++++- .../include/fast_io_unit/floating/hexfloat.h | 78 +++++ 2 files changed, 382 insertions(+), 14 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index c227c0617..4a4d1ce6b 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -241,6 +241,24 @@ template } } } + else if constexpr (::fast_io::details::fp_floating_point_is_float80) + { + if (-27 <= exponent && exponent <= 27) + { + no_cvref_t result{static_cast(significand)}; + auto const &table{::fast_io::details::scan_decfloat_power10_table}; + if (exponent < 0) + { + result /= table.index_unchecked(static_cast<::std::size_t>(-exponent)); + } + else + { + result *= table.index_unchecked(static_cast<::std::size_t>(exponent)); + } + value = negative ? -result : result; + return true; + } + } return false; } @@ -340,6 +358,13 @@ struct scan_decfloat_adjusted_mantissa ::std::int_least32_t power2{}; }; +[[nodiscard]] inline constexpr bool +scan_decfloat_adjusted_mantissa_equal(scan_decfloat_adjusted_mantissa left, + scan_decfloat_adjusted_mantissa right) noexcept +{ + return left.mantissa == right.mantissa && left.power2 == right.power2; +} + struct scan_decfloat_uint128 { ::std::uint_least64_t low{}; @@ -830,6 +855,33 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return true; } + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_mul_u64(scan_decfloat_bigint &value, + ::std::uint_least64_t multiplier) noexcept + { + if (!multiplier || !value.size) + { + value = {}; + return true; + } + __uint128_t carry{}; + for (::std::size_t index{}; index != value.size; ++index) + { + auto const product{static_cast<__uint128_t>(value.limb[index]) * multiplier + carry}; + value.limb[index] = static_cast<::std::uint_least64_t>(product); + carry = product >> 64u; + } + if (carry) + { + if (value.size == ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + value.limb[value.size] = static_cast<::std::uint_least64_t>(carry); + ++value.size; + } + return true; + } + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_add_small(scan_decfloat_bigint &value, ::std::uint_least32_t addend) noexcept { @@ -859,15 +911,67 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return true; } + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_add_u64(scan_decfloat_bigint &value, + ::std::uint_least64_t addend) noexcept + { + if (!addend) + { + return true; + } + if (!value.size) + { + ::fast_io::details::scan_decfloat_bigint_set_u64(value, addend); + return true; + } + __uint128_t sum{static_cast<__uint128_t>(value.limb[0]) + addend}; + value.limb[0] = static_cast<::std::uint_least64_t>(sum); + auto carry{sum >> 64u}; + for (::std::size_t index{1u}; carry && index != value.size; ++index) + { + sum = static_cast<__uint128_t>(value.limb[index]) + carry; + value.limb[index] = static_cast<::std::uint_least64_t>(sum); + carry = sum >> 64u; + } + if (carry) + { + if (value.size == ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + value.limb[value.size] = static_cast<::std::uint_least64_t>(carry); + ++value.size; + } + return true; + } + inline constexpr bool scan_decfloat_bigint_from_digits(scan_decfloat_bigint &value, scan_decfloat_significand_state const &state) noexcept { value = {}; + constexpr ::std::uint_least64_t chunk_digits{19u}; + constexpr ::std::uint_least64_t chunk_power{10000000000000000000ull}; + ::std::uint_least64_t chunk{}; + ::std::uint_least64_t count{}; for (::std::size_t index{}; index != state.exact_stored_digits; ++index) { - if (!::fast_io::details::scan_decfloat_bigint_mul_small(value, 10u) || - !::fast_io::details::scan_decfloat_bigint_add_small( - value, static_cast<::std::uint_least32_t>(state.exact_digits[index]))) + chunk = chunk * 10u + static_cast<::std::uint_least64_t>(state.exact_digits[index]); + ++count; + if (count == chunk_digits) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_u64(value, chunk_power) || + !::fast_io::details::scan_decfloat_bigint_add_u64(value, chunk)) + { + return false; + } + chunk = 0; + count = 0; + } + } + if (count) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_u64( + value, ::fast_io::details::print_rsv_fp_pow10_0_to_19_table[count]) || + !::fast_io::details::scan_decfloat_bigint_add_u64(value, chunk)) { return false; } @@ -994,13 +1098,52 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return true; } - [[nodiscard]] inline constexpr bool scan_decfloat_bigint_pow5(scan_decfloat_bigint &value, - ::std::uint_least64_t exponent) noexcept - { - ::fast_io::details::scan_decfloat_bigint_set_u64(value, 1u); - for (; exponent; --exponent) + inline constexpr ::std::uint_least64_t scan_decfloat_pow5_0_to_27_table[]{ + 1u, + 5u, + 25u, + 125u, + 625u, + 3125u, + 15625u, + 78125u, + 390625u, + 1953125u, + 9765625u, + 48828125u, + 244140625u, + 1220703125u, + 6103515625u, + 30517578125u, + 152587890625u, + 762939453125u, + 3814697265625u, + 19073486328125u, + 95367431640625u, + 476837158203125u, + 2384185791015625u, + 11920928955078125u, + 59604644775390625u, + 298023223876953125u, + 1490116119384765625u, + 7450580596923828125u}; + + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_mul_pow5(scan_decfloat_bigint &value, + ::std::uint_least64_t exponent) noexcept + { + constexpr ::std::uint_least64_t chunk{27u}; + for (; exponent >= chunk; exponent -= chunk) + { + if (!::fast_io::details::scan_decfloat_bigint_mul_u64( + value, ::fast_io::details::scan_decfloat_pow5_0_to_27_table[chunk])) + { + return false; + } + } + if (exponent != 0) { - if (!::fast_io::details::scan_decfloat_bigint_mul_small(value, 5u)) + if (!::fast_io::details::scan_decfloat_bigint_mul_u64( + value, ::fast_io::details::scan_decfloat_pow5_0_to_27_table[exponent])) { return false; } @@ -1008,6 +1151,13 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return true; } + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_pow5(scan_decfloat_bigint &value, + ::std::uint_least64_t exponent) noexcept + { + ::fast_io::details::scan_decfloat_bigint_set_u64(value, 1u); + return ::fast_io::details::scan_decfloat_bigint_mul_pow5(value, exponent); + } + [[nodiscard]] inline constexpr ::std::int_least64_t scan_decfloat_bigint_floor_log2_ratio(scan_decfloat_bigint const &numerator, scan_decfloat_bigint const &denominator) noexcept @@ -1140,6 +1290,119 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t } } + template + [[nodiscard]] inline constexpr ::std::uint_least64_t + scan_decfloat_adjusted_significand(scan_decfloat_adjusted_mantissa adjusted) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + if (adjusted.power2 == 0) + { + return adjusted.mantissa; + } + return adjusted.mantissa | (::std::uint_least64_t{1u} << trait::mbits); + } + + struct scan_decfloat_extended_mantissa + { + ::std::uint_least64_t mantissa{}; + ::std::int_least64_t power2{}; + }; + + template + [[nodiscard]] inline constexpr scan_decfloat_extended_mantissa + scan_decfloat_adjusted_to_extended_halfway(scan_decfloat_adjusted_mantissa adjusted) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + constexpr auto bias{static_cast<::std::int_least64_t>(trait::mbits) - + static_cast<::std::int_least64_t>( + ::fast_io::details::scan_decfloat_minimum_exponent())}; + ::fast_io::details::scan_decfloat_extended_mantissa extended; + if (adjusted.power2 == 0) + { + extended.mantissa = adjusted.mantissa; + extended.power2 = 1 - bias; + } + else + { + extended.mantissa = + ::fast_io::details::scan_decfloat_adjusted_significand(adjusted); + extended.power2 = static_cast<::std::int_least64_t>(adjusted.power2) - bias; + } + extended.mantissa = static_cast<::std::uint_least64_t>((extended.mantissa << 1u) | 1u); + --extended.power2; + return extended; + } + + template + [[nodiscard]] inline constexpr bool + scan_decfloat_try_nearest_even_compare(T &value, bool negative, + scan_decfloat_significand_state const &state, + ::std::int_least64_t decimal_exponent, + scan_decfloat_adjusted_mantissa lower, + scan_decfloat_adjusted_mantissa upper, + ::fast_io::parse_code &code) noexcept + { + using no_cvref_t = ::std::remove_cvref_t; + if constexpr (!::fast_io::details::scan_decfloat_compute_supported) + { + return false; + } + else + { + if (decimal_exponent >= 0 || state.exact_truncated_nonzero) + { + return false; + } + ::fast_io::details::scan_decfloat_bigint real_digits; + if (!::fast_io::details::scan_decfloat_bigint_from_digits(real_digits, state)) + { + return false; + } + auto const halfway{ + ::fast_io::details::scan_decfloat_adjusted_to_extended_halfway(lower)}; + ::fast_io::details::scan_decfloat_bigint theor_digits; + ::fast_io::details::scan_decfloat_bigint_set_u64(theor_digits, halfway.mantissa); + auto const pow5_exponent{static_cast<::std::uint_least64_t>(-decimal_exponent)}; + if (!::fast_io::details::scan_decfloat_bigint_mul_pow5(theor_digits, pow5_exponent)) + { + return false; + } + auto const pow2_exponent{halfway.power2 - decimal_exponent}; + if (pow2_exponent > 0) + { + ::fast_io::details::scan_decfloat_bigint shifted; + if (!::fast_io::details::scan_decfloat_bigint_shift_left( + shifted, theor_digits, static_cast<::std::size_t>(pow2_exponent))) + { + return false; + } + theor_digits = shifted; + } + else if (pow2_exponent < 0) + { + ::fast_io::details::scan_decfloat_bigint shifted; + if (!::fast_io::details::scan_decfloat_bigint_shift_left( + shifted, real_digits, static_cast<::std::size_t>(-pow2_exponent))) + { + return false; + } + real_digits = shifted; + } + auto const order{::fast_io::details::scan_decfloat_bigint_compare(real_digits, theor_digits)}; + auto const lower_significand{ + ::fast_io::details::scan_decfloat_adjusted_significand(lower)}; + auto const selected{(order > 0 || (order == 0 && (lower_significand & 1u) != 0u)) ? upper : lower}; + ::fast_io::details::scan_decfloat_to_float(negative, selected, value); + code = ((state.significand != 0 && selected.mantissa == 0 && selected.power2 == 0) || + selected.power2 == ::fast_io::details::scan_decfloat_infinite_power()) + ? ::fast_io::parse_code::overflow + : ::fast_io::parse_code::ok; + return true; + } + } + template inline constexpr void scan_decfloat_assign_min_subnormal(T &value, bool negative) noexcept { @@ -1300,12 +1563,10 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t ::std::int_least64_t binary_exponent_adjust{}; if (decimal_exponent >= 0) { - for (::std::int_least64_t counter{}; counter != decimal_exponent; ++counter) + if (!::fast_io::details::scan_decfloat_bigint_mul_pow5( + numerator, static_cast<::std::uint_least64_t>(decimal_exponent))) { - if (!::fast_io::details::scan_decfloat_bigint_mul_small(numerator, 5u)) - { - return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); - } + return ::fast_io::details::scan_decfloat_assign_overflow_value(value, negative); } binary_exponent_adjust = decimal_exponent; } @@ -1726,6 +1987,35 @@ template ::max)()) + { + ::fast_io::details::scan_decfloat_adjusted_mantissa upper_adjusted; + if (::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, state.significand, negative, adjusted) && + ::fast_io::details::scan_decfloat_compute_adjusted( + adjusted_exponent, state.significand + 1u, negative, upper_adjusted)) + { + if (::fast_io::details::scan_decfloat_adjusted_mantissa_equal(adjusted, upper_adjusted)) + { + return ::fast_io::details::scan_decfloat_assign_adjusted(value, negative, state.significand, adjusted); + } + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_even) + { + auto decimal_exponent{::fast_io::details::scan_decfloat_saturating_add( + exponent, -static_cast<::std::int_least64_t>(state.fractional_digits))}; + decimal_exponent = ::fast_io::details::scan_decfloat_saturating_add( + decimal_exponent, + static_cast<::std::int_least64_t>(state.significant_digits - state.exact_stored_digits)); + ::fast_io::parse_code code{}; + if (::fast_io::details::scan_decfloat_try_nearest_even_compare( + value, negative, state, decimal_exponent, adjusted, upper_adjusted, code)) + { + return code; + } + } + } + } } #ifdef __SIZEOF_INT128__ return ::fast_io::details::scan_decfloat_assign_big(value, negative, state, exponent); diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index 2736f724e..0c44a6424 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -375,6 +375,77 @@ inline constexpr void scan_hexfloat_ascii8_pack(::std::uint_least64_t val, ::std return (val ^ 0x3030303030303030) != 0; } +template <::std::size_t vec_size, ::std::integral char_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type const * +scan_hexfloat_skip_after_storage_limit_simd(char_type const *first, char_type const *last, + bool &truncated_nonzero) noexcept +{ + static_assert(sizeof(char_type) == sizeof(char8_t)); + using unsigned_char_type = ::std::make_unsigned_t<::std::remove_cvref_t>; + using signed_char_type = ::std::make_signed_t; + constexpr unsigned N{vec_size / sizeof(char_type)}; + using simd_vector_type = ::fast_io::intrinsics::simd_vector; +#if (__cpp_lib_bit_cast >= 201806L) && !defined(__clang__) + constexpr simd_vector_type zeroes{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; + constexpr simd_vector_type nines{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; + constexpr simd_vector_type lower_as{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; + constexpr simd_vector_type lower_fs{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; + constexpr simd_vector_type case_bits{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; +#else + simd_vector_type zeroes; + zeroes.load(::fast_io::details::characters_array_impl.data()); + simd_vector_type nines; + nines.load(::fast_io::details::characters_array_impl.data()); + simd_vector_type lower_as; + lower_as.load(::fast_io::details::characters_array_impl.data()); + simd_vector_type lower_fs; + lower_fs.load(::fast_io::details::characters_array_impl.data()); + simd_vector_type case_bits; + case_bits.load(::fast_io::details::characters_array_impl.data()); +#endif + for (; N <= static_cast<::std::size_t>(last - first);) + { + simd_vector_type vec; + vec.load(first); + auto const lower{vec | case_bits}; + auto const valid{((zeroes <= vec) & (vec <= nines)) | + ((lower_as <= lower) & (lower <= lower_fs))}; + auto const invalid{~valid}; + if (!::fast_io::intrinsics::is_all_zeros(invalid)) + { + auto const valid_count{::fast_io::intrinsics::vector_mask_countr_zero(invalid)}; + auto const *const invalid_pos{first + valid_count}; + using char_unsigned_type = ::fast_io::details::my_make_unsigned_t; + for (; first != invalid_pos; ++first) + { + auto digit{static_cast(*first)}; + (void)::fast_io::details::char_digit_to_literal<16, char_type>(digit); + if (!truncated_nonzero && digit != 0) + { + truncated_nonzero = true; + } + } + return first; + } + if (!truncated_nonzero && !::fast_io::intrinsics::is_all_zeros(vec != zeroes)) + { + truncated_nonzero = true; + } + first += N; + } + return first; +} + [[nodiscard]] inline constexpr bool scan_hexfloat_ascii8_nibbles_only(::std::uint_least64_t val, ::std::uint_least64_t &nibbles) noexcept { @@ -431,6 +502,13 @@ inline constexpr char_type const *scan_hexfloat_skip_after_storage_limit_run(cha if constexpr (!::fast_io::details::is_ebcdic && sizeof(char_type) == sizeof(char8_t) && ::std::numeric_limits<::std::uint_least64_t>::digits == 64u) { + constexpr auto simd_size{ + ::fast_io::intrinsics::optimal_simd_vector_run_with_cpu_instruction_size_with_mask_countr}; + if constexpr (simd_size != 0) + { + first = ::fast_io::details::scan_hexfloat_skip_after_storage_limit_simd( + first, last, truncated_nonzero); + } for (; static_cast<::std::size_t>(last - first) >= sizeof(::std::uint_least64_t);) { ::std::uint_least64_t val; From c04f0e7c4fc19ae90d60755adc8557a886db4028 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 19:41:43 +0800 Subject: [PATCH 27/45] Refactor decfloat and hexfloat parsing for improved clarity and performance - Simplified the calculation of significand digit limits in decfloat by removing unnecessary lambda functions, enhancing readability. - Introduced new utility functions for clearing and copying bigint structures, improving code maintainability and reducing redundancy. - Added force-inlining attributes to critical parsing functions in both decfloat and hexfloat, optimizing performance during floating-point operations. --- .../include/fast_io_unit/floating/decfloat.h | 62 ++++++++++++------- .../include/fast_io_unit/floating/hexfloat.h | 5 ++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 4a4d1ce6b..30c8e96c9 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -296,20 +296,9 @@ struct scan_decfloat_fast_result inline constexpr ::std::uint_least64_t scan_decfloat_uint64_significand_digit_limit{19u}; -template +template inline constexpr ::std::uint_least64_t scan_decfloat_significand_digit_limit{ - []() constexpr noexcept { - using trait = ::fast_io::details::iec559_traits<::std::remove_cvref_t>; - constexpr ::std::uint_least64_t digits{static_cast<::std::uint_least64_t>(trait::m10digits) + 2u}; - if constexpr (digits < ::fast_io::details::scan_decfloat_uint64_significand_digit_limit) - { - return digits; - } - else - { - return ::fast_io::details::scan_decfloat_uint64_significand_digit_limit; - } - }()}; + ::fast_io::details::scan_decfloat_uint64_significand_digit_limit}; inline constexpr ::std::int_least32_t scan_decfloat_dragonbox_min_power10{-342}; inline constexpr ::std::int_least32_t scan_decfloat_dragonbox_max_power10{326}; inline constexpr ::std::uint_least64_t scan_decfloat_pow10_0_to_8_table[]{ @@ -597,6 +586,11 @@ scan_decfloat_mul_64x128_high(::std::uint_least64_t value, ::fast_io::details::u template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif [[nodiscard]] inline constexpr bool scan_decfloat_compute_adjusted(::std::int_least64_t exponent, ::std::uint_least64_t significand, bool negative, @@ -815,10 +809,25 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t struct scan_decfloat_bigint { - ::std::uint_least64_t limb[scan_decfloat_bigint_limb_capacity]{}; + ::std::uint_least64_t limb[scan_decfloat_bigint_limb_capacity]; ::std::size_t size{}; }; + inline constexpr void scan_decfloat_bigint_clear(scan_decfloat_bigint &value) noexcept + { + value.size = 0u; + } + + inline constexpr void scan_decfloat_bigint_copy(scan_decfloat_bigint &out, + scan_decfloat_bigint const &in) noexcept + { + out.size = in.size; + for (::std::size_t index{}; index != in.size; ++index) + { + out.limb[index] = in.limb[index]; + } + } + inline constexpr void scan_decfloat_bigint_normalize(scan_decfloat_bigint &value) noexcept { for (; value.size && value.limb[value.size - 1u] == 0u; --value.size) @@ -860,7 +869,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t { if (!multiplier || !value.size) { - value = {}; + ::fast_io::details::scan_decfloat_bigint_clear(value); return true; } __uint128_t carry{}; @@ -947,7 +956,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t inline constexpr bool scan_decfloat_bigint_from_digits(scan_decfloat_bigint &value, scan_decfloat_significand_state const &state) noexcept { - value = {}; + ::fast_io::details::scan_decfloat_bigint_clear(value); constexpr ::std::uint_least64_t chunk_digits{19u}; constexpr ::std::uint_least64_t chunk_power{10000000000000000000ull}; ::std::uint_least64_t chunk{}; @@ -1065,7 +1074,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t scan_decfloat_bigint const &in, ::std::size_t shift) noexcept { - out = {}; + ::fast_io::details::scan_decfloat_bigint_clear(out); if (!in.size) { return true; @@ -1212,7 +1221,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t } else { - divisor = denominator; + ::fast_io::details::scan_decfloat_bigint_copy(divisor, denominator); } auto const dividend_bits{ ::fast_io::details::scan_decfloat_bigint_bit_width(numerator) + @@ -1251,7 +1260,8 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t } } } - ::fast_io::details::scan_decfloat_bigint twice_remainder{remainder}; + ::fast_io::details::scan_decfloat_bigint twice_remainder; + ::fast_io::details::scan_decfloat_bigint_copy(twice_remainder, remainder); (void)::fast_io::details::scan_decfloat_bigint_shl1_add_bit(twice_remainder, false); return {.quotient = quotient, .twice_remainder_compare = @@ -1378,7 +1388,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t { return false; } - theor_digits = shifted; + ::fast_io::details::scan_decfloat_bigint_copy(theor_digits, shifted); } else if (pow2_exponent < 0) { @@ -1388,7 +1398,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t { return false; } - real_digits = shifted; + ::fast_io::details::scan_decfloat_bigint_copy(real_digits, shifted); } auto const order{::fast_io::details::scan_decfloat_bigint_compare(real_digits, theor_digits)}; auto const lower_significand{ @@ -1939,6 +1949,11 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d } template <::std::integral char_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif [[nodiscard]] inline constexpr ::fast_io::parse_result scan_decfloat_exponent(char_type const *first, char_type const *last, ::std::int_least64_t &exponent) noexcept { @@ -2183,6 +2198,11 @@ scan_decfloat_assign_precision(T &value, bool negative, scan_decfloat_significan template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif [[nodiscard]] inline constexpr ::fast_io::parse_code scan_decfloat_assign_short(T &value, bool negative, ::std::uint_least64_t significand, ::std::uint_least64_t fractional_digits, diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index 0c44a6424..d6eed28a0 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -676,6 +676,11 @@ inline constexpr char_type const *scan_hexfloat_significand_run(char_type const } template <::std::integral char_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif [[nodiscard]] inline constexpr ::fast_io::parse_result scan_hexfloat_exponent(char_type const *first, char_type const *last, ::std::int_least64_t &exponent) noexcept { From fceb48390218f3c41c65964bd55769e6c8d18ac2 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 20:00:31 +0800 Subject: [PATCH 28/45] Implement new functions for optimized decfloat digit handling - Added `scan_decfloat_append_exact_ascii8_digits` and its slow variant to improve the efficiency of appending exact digits in decimal floating-point parsing. - Introduced `scan_decfloat_bigint_shift_left_inplace` for in-place shifting of bigint structures, enhancing performance and reducing memory overhead. - Refactored existing logic to streamline the handling of significand calculations, ensuring better performance and maintainability in floating-point operations. --- .../include/fast_io_unit/floating/decfloat.h | 306 +++++++++++++++++- 1 file changed, 292 insertions(+), 14 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 30c8e96c9..317a6355d 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -283,7 +283,7 @@ struct scan_decfloat_significand_state bool has_nonzero_digit{}; bool truncated_nonzero{}; bool exact_truncated_nonzero{}; - char8_t exact_digits[scan_decfloat_exact_digit_capacity]{}; + char8_t exact_digits[scan_decfloat_exact_digit_capacity]; }; template <::std::integral char_type> @@ -341,6 +341,49 @@ inline constexpr void scan_decfloat_append_exact_eight_digits(scan_decfloat_sign } } +#if __has_cpp_attribute(__gnu__::__cold__) +[[__gnu__::__cold__]] +#endif +#if __has_cpp_attribute(__gnu__::__noinline__) +[[__gnu__::__noinline__]] +#elif __has_cpp_attribute(msvc::noinline) +[[msvc::noinline]] +#endif +inline constexpr void scan_decfloat_append_exact_ascii8_digits_slow(scan_decfloat_significand_state &state, + ::std::uint_least64_t val) noexcept +{ + for (::std::size_t offset{}; offset != 8u; ++offset) + { + auto const ch{static_cast((val >> (offset * 8u)) & 0xFFu)}; + auto const digit{static_cast(ch - u8'0')}; + ::fast_io::details::scan_decfloat_append_exact_digit(state, digit); + } +} + +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void scan_decfloat_append_exact_ascii8_digits(scan_decfloat_significand_state &state, + ::std::uint_least64_t val) noexcept +{ + if constexpr (::std::endian::native == ::std::endian::little && + ::std::numeric_limits<::std::uint_least64_t>::digits == 64u) + { + if (state.exact_stored_digits + 8u <= ::fast_io::details::scan_decfloat_exact_digit_capacity) + { + auto const digits{static_cast<::std::uint_least64_t>(val - 0x3030303030303030u)}; + ::fast_io::freestanding::my_memcpy( + state.exact_digits + state.exact_stored_digits, __builtin_addressof(digits), + sizeof(::std::uint_least64_t)); + state.exact_stored_digits += 8u; + return; + } + } + ::fast_io::details::scan_decfloat_append_exact_ascii8_digits_slow(state, val); +} + struct scan_decfloat_adjusted_mantissa { ::std::uint_least64_t mantissa{}; @@ -1107,6 +1150,57 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t return true; } +#if __has_cpp_attribute(__gnu__::__always_inline__) + [[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) + [[msvc::forceinline]] +#endif + [[nodiscard]] inline constexpr bool scan_decfloat_bigint_shift_left_inplace(scan_decfloat_bigint &value, + ::std::size_t shift) noexcept + { + if (!value.size || !shift) + { + return true; + } + auto const limb_shift{shift / 64u}; + auto const bit_shift{shift % 64u}; + if (value.size + limb_shift + (bit_shift != 0u) > + ::fast_io::details::scan_decfloat_bigint_limb_capacity) + { + return false; + } + if (limb_shift) + { + for (auto index{value.size}; index != 0u;) + { + --index; + value.limb[index + limb_shift] = value.limb[index]; + } + for (::std::size_t index{}; index != limb_shift; ++index) + { + value.limb[index] = 0u; + } + value.size += limb_shift; + } + if (bit_shift) + { + ::std::uint_least64_t carry{}; + for (::std::size_t index{limb_shift}; index != value.size; ++index) + { + auto const word{value.limb[index]}; + value.limb[index] = static_cast<::std::uint_least64_t>((word << bit_shift) | carry); + carry = static_cast<::std::uint_least64_t>(word >> (64u - bit_shift)); + } + if (carry) + { + value.limb[value.size] = carry; + ++value.size; + } + } + ::fast_io::details::scan_decfloat_bigint_normalize(value); + return true; + } + inline constexpr ::std::uint_least64_t scan_decfloat_pow5_0_to_27_table[]{ 1u, 5u, @@ -1382,23 +1476,19 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t auto const pow2_exponent{halfway.power2 - decimal_exponent}; if (pow2_exponent > 0) { - ::fast_io::details::scan_decfloat_bigint shifted; - if (!::fast_io::details::scan_decfloat_bigint_shift_left( - shifted, theor_digits, static_cast<::std::size_t>(pow2_exponent))) + if (!::fast_io::details::scan_decfloat_bigint_shift_left_inplace( + theor_digits, static_cast<::std::size_t>(pow2_exponent))) { return false; } - ::fast_io::details::scan_decfloat_bigint_copy(theor_digits, shifted); } else if (pow2_exponent < 0) { - ::fast_io::details::scan_decfloat_bigint shifted; - if (!::fast_io::details::scan_decfloat_bigint_shift_left( - shifted, real_digits, static_cast<::std::size_t>(-pow2_exponent))) + if (!::fast_io::details::scan_decfloat_bigint_shift_left_inplace( + real_digits, static_cast<::std::size_t>(-pow2_exponent))) { return false; } - ::fast_io::details::scan_decfloat_bigint_copy(real_digits, shifted); } auto const order{::fast_io::details::scan_decfloat_bigint_compare(real_digits, theor_digits)}; auto const lower_significand{ @@ -1710,7 +1800,7 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t ::std::uint_least64_t significand) noexcept { auto const original{significand}; - char8_t buffer[20]{}; + char8_t buffer[20]; ::std::size_t size{}; for (; significand; significand /= 10u) { @@ -1802,6 +1892,133 @@ scan_decfloat_ascii8_has_nonzero_digit(::std::uint_least64_t val) noexcept return (val ^ 0x3030303030303030u) != 0; } +template <::std::size_t vec_size, ::std::integral char_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type const * +scan_decfloat_skip_after_exact_limit_simd(char_type const *first, char_type const *last, + bool &tail_nonzero) noexcept +{ + static_assert(sizeof(char_type) == sizeof(char8_t)); + using unsigned_char_type = ::std::make_unsigned_t<::std::remove_cvref_t>; + using signed_char_type = ::std::make_signed_t; + constexpr unsigned N{vec_size / sizeof(char_type)}; + using simd_vector_type = ::fast_io::intrinsics::simd_vector; +#if (__cpp_lib_bit_cast >= 201806L) && !defined(__clang__) + constexpr simd_vector_type zeroes{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; + constexpr simd_vector_type nines{ + ::std::bit_cast(::fast_io::details::characters_array_impl)}; +#else + simd_vector_type zeroes; + zeroes.load(::fast_io::details::characters_array_impl.data()); + simd_vector_type nines; + nines.load(::fast_io::details::characters_array_impl.data()); +#endif + for (; N <= static_cast<::std::size_t>(last - first);) + { + simd_vector_type vec; + vec.load(first); + auto const valid{(zeroes <= vec) & (vec <= nines)}; + auto const invalid{~valid}; + if (!::fast_io::intrinsics::is_all_zeros(invalid)) + { + auto const valid_count{::fast_io::intrinsics::vector_mask_countr_zero(invalid)}; + auto const *const invalid_pos{first + valid_count}; + for (; first != invalid_pos; ++first) + { + if (!tail_nonzero && *first != ::fast_io::char_literal_v) + { + tail_nonzero = true; + } + } + return first; + } + if (!tail_nonzero && !::fast_io::intrinsics::is_all_zeros(vec != zeroes)) + { + tail_nonzero = true; + } + first += N; + } + return first; +} + +template <::std::integral char_type> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type const *scan_decfloat_skip_after_exact_limit_run( + char_type const *first, char_type const *last, bool after_decimal, + scan_decfloat_significand_state &state) noexcept +{ + auto const *const original_first{first}; + auto tail_nonzero{state.exact_truncated_nonzero}; +#ifdef __cpp_if_consteval + if !consteval +#else + if (!__builtin_is_constant_evaluated()) +#endif + { + if constexpr (!::fast_io::details::is_ebcdic && sizeof(char_type) == sizeof(char8_t) && + ::std::numeric_limits<::std::uint_least64_t>::digits == 64u) + { + constexpr auto simd_size{ + ::fast_io::intrinsics::optimal_simd_vector_run_with_cpu_instruction_size_with_mask_countr}; + if constexpr (simd_size != 0) + { + first = ::fast_io::details::scan_decfloat_skip_after_exact_limit_simd( + first, last, tail_nonzero); + } + for (; static_cast<::std::size_t>(last - first) >= sizeof(::std::uint_least64_t);) + { + ::std::uint_least64_t val; + ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); + if constexpr (::std::endian::little != ::std::endian::native) + { + val = ::fast_io::little_endian(val); + } + if (!::fast_io::details::scan_decfloat_ascii8_is_digits(val)) + { + break; + } + if (!tail_nonzero && ::fast_io::details::scan_decfloat_ascii8_has_nonzero_digit(val)) + { + tail_nonzero = true; + } + first += sizeof(::std::uint_least64_t); + } + } + } + char8_t digit{}; + for (; first != last && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) + { + if (!tail_nonzero && digit != 0) + { + tail_nonzero = true; + } + } + auto const skipped{static_cast<::std::uint_least64_t>(first - original_first)}; + if (skipped) + { + if (after_decimal) + { + state.fractional_digits += skipped; + } + state.significant_digits += skipped; + if (tail_nonzero) + { + state.exact_truncated_nonzero = true; + state.truncated_nonzero = true; + } + } + return first; +} + [[nodiscard]] inline constexpr ::std::uint_least32_t scan_decfloat_ascii8_parse(::std::uint_least64_t val) noexcept { @@ -1863,6 +2080,61 @@ inline constexpr void scan_decfloat_append_eight_digits(scan_decfloat_significan } } +template +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr void scan_decfloat_append_eight_ascii_digits(scan_decfloat_significand_state &state, + bool after_decimal, + ::std::uint_least64_t val) noexcept +{ + constexpr auto digit_limit{::fast_io::details::scan_decfloat_significand_digit_limit}; + auto const digits{::fast_io::details::scan_decfloat_ascii8_parse(val)}; + state.has_digit = true; + if (after_decimal) + { + state.fractional_digits += 8u; + } + if (!state.has_nonzero_digit) + { + if (digits == 0) + { + return; + } + state.has_nonzero_digit = true; + } + state.significant_digits += 8u; + ::fast_io::details::scan_decfloat_append_exact_ascii8_digits(state, val); + if (state.stored_digits == digit_limit) + { + if (digits != 0) + { + state.truncated_nonzero = true; + } + return; + } + auto const available{digit_limit - state.stored_digits}; + if (8u <= available) + { + state.significand = state.significand * 100000000u + static_cast<::std::uint_least64_t>(digits); + state.stored_digits += 8u; + return; + } + auto const divisor{::fast_io::details::scan_decfloat_pow10_0_to_8_table[8u - available]}; + auto const stored_part{digits / divisor}; + auto const truncated_part{digits - stored_part * divisor}; + state.significand = + state.significand * ::fast_io::details::scan_decfloat_pow10_0_to_8_table[available] + + static_cast<::std::uint_least64_t>(stored_part); + state.stored_digits = digit_limit; + if (truncated_part != 0) + { + state.truncated_nonzero = true; + } +} + template #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -1896,6 +2168,13 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d { for (; static_cast<::std::size_t>(last - first) >= sizeof(::std::uint_least64_t);) { + if (state.has_nonzero_digit && state.stored_digits == digit_limit && + state.exact_stored_digits == ::fast_io::details::scan_decfloat_exact_digit_capacity) + { + first = ::fast_io::details::scan_decfloat_skip_after_exact_limit_run( + first, last, after_decimal, state); + break; + } ::std::uint_least64_t val; ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); if constexpr (::std::endian::little != ::std::endian::native) @@ -1919,8 +2198,7 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d if (state.exact_stored_digits != ::fast_io::details::scan_decfloat_exact_digit_capacity) { - ::fast_io::details::scan_decfloat_append_exact_eight_digits( - state, ::fast_io::details::scan_decfloat_ascii8_parse(val)); + ::fast_io::details::scan_decfloat_append_exact_ascii8_digits(state, val); } else if (!state.exact_truncated_nonzero && has_nonzero) { @@ -1933,8 +2211,8 @@ scan_decfloat_digits(char_type const *first, char_type const *last, bool after_d } else { - ::fast_io::details::scan_decfloat_append_eight_digits( - state, after_decimal, ::fast_io::details::scan_decfloat_ascii8_parse(val)); + ::fast_io::details::scan_decfloat_append_eight_ascii_digits( + state, after_decimal, val); } first += sizeof(::std::uint_least64_t); } From 953c63a91d91326c7b34e9c0fca10fc471c5780a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 20:24:17 +0800 Subject: [PATCH 29/45] Enhance print semantic handling in fast_io - Introduced new structures and functions for bounded size checks in print semantics, improving the handling of various manipulators and types. - Refactored existing print functions to accommodate dynamic reserve and width manipulations, enhancing performance and maintainability. - Updated the logic for width placement and padding in print operations, ensuring more accurate output formatting and better handling of edge cases. --- .../fast_io_core_impl/manipulators/traits.h | 63 ++++ .../printimpl/print_freestanding_cxx20.h | 256 +++++++++++-- .../fast_io/include/fast_io_legacy_impl/io.h | 136 ++++--- .../include/fast_io_unit/floating/impl.h | 26 +- .../include/fast_io_unit/floating/punning.h | 4 +- .../include/fast_io_unit/floating/roundtrip.h | 339 ++++++++++++++---- 6 files changed, 683 insertions(+), 141 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h index 1a97d6914..4cf0fd4e4 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h @@ -246,6 +246,69 @@ struct print_semantic_precise_size_ok_impl>::value> {}; +template <::std::integral char_type, typename T> +inline constexpr bool print_semantic_bounded_leaf_size_ok_v = + ::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v || + ::fast_io::dynamic_reserve_printable>; + +template <::std::integral char_type, typename T> +struct print_semantic_bounded_size_ok_impl + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_bounded_leaf_size_ok_v> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_bounded_size_ok + : ::fast_io::details::decay::print_semantic_bounded_size_ok_impl> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_bounded_size_ok_impl> + : ::fast_io::details::decay::print_semantic_bounded_size_ok +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_bounded_size_ok_impl> + : ::std::bool_constant< + (::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && + ...)> +{}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_bounded_size_ok_impl> + : ::std::bool_constant< + ::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ::fast_io::details::decay::print_semantic_bounded_size_ok>::value> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T> +struct print_semantic_bounded_size_ok_impl> + : ::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, ::fast_io::manipulators::scalar_placement placement, typename T, + ::std::integral ch_type> +struct print_semantic_bounded_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && ::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_bounded_size_ok_impl> + : ::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t> +{}; + +template <::std::integral char_type, typename T, ::std::integral ch_type> +struct print_semantic_bounded_size_ok_impl> + : ::std::bool_constant< + ::std::same_as && ::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value> +{}; + template <::std::integral char_type, typename T> struct print_semantic_params_okay : ::std::false_type {}; diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index f42272e75..92a884b0c 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -4356,6 +4356,11 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, return iter + precise_size; } } + else if constexpr (::fast_io::dynamic_reserve_printable) + { + // Dynamic reserve leaves have already been admitted by a bounded materialization path. + return print_reserve_define(::fast_io::io_reserve_type, iter, ::std::forward(t)); + } else if constexpr (::fast_io::scatter_printable) { // Scatter leaves copy their single contiguous range into the unchecked output buffer. @@ -4484,10 +4489,8 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t } else if constexpr (::fast_io::details::decay::print_semantic_width_v) { - // Width nodes need child length, fill, and placement before writing padded output. + // Width nodes materialize their child before padding so dynamic-reserve children use their actual length. using width_traits = ::fast_io::details::decay::print_semantic_width_traits; - ::std::size_t const len{ - ::fast_io::operations::decay::print_semantic_precise_size_arg(node_ref.reference)}; ::std::size_t const width{node_ref.width}; char_type const fillch{ ::fast_io::operations::decay::print_semantic_width_fill_char(node_ref)}; @@ -4495,52 +4498,50 @@ inline constexpr char_type *print_semantic_emit_unchecked(char_type *iter, T &&t ::fast_io::operations::decay::print_semantic_width_placement(node_ref)}; ::std::size_t const placement_code{ ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; + char_type *const first{iter}; + char_type *const last{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference)}; + ::std::size_t const len{static_cast<::std::size_t>(last - first)}; if (width <= len || placement_code == 0u) { - // Width that is already satisfied, or disabled placement, emits the child without padding. - return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + // Width that is already satisfied, or disabled placement, leaves the child bytes unchanged. + return last; } ::std::size_t const padding{width - len}; if (placement_code == 1u) { - // Left placement emits the child first and appends all padding on the right. - iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); - return ::fast_io::details::my_fill_n(iter, padding, fillch); + // Left placement appends all padding after the already-emitted child. + return ::fast_io::details::my_fill_n(last, padding, fillch); } if (placement_code == 2u) { - // Center placement splits padding around the child, biasing the remainder to the right. + // Center placement shifts the child right, then fills both sides in place. ::std::size_t const left_padding{padding >> 1u}; ::std::size_t const right_padding{padding - left_padding}; - iter = ::fast_io::details::my_fill_n(iter, left_padding, fillch); - iter = ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); - return ::fast_io::details::my_fill_n(iter, right_padding, fillch); + char_type *const child_pos{first + left_padding}; + ::fast_io::details::my_copy(first, last, child_pos); + ::fast_io::details::my_fill_n(first, left_padding, fillch); + ::fast_io::details::my_fill_n(child_pos + len, right_padding, fillch); + return first + width; } if (placement_code == 4u) { // Internal placement inserts padding after the sign or prefix reported by the child. ::std::size_t const internal_len{ ::fast_io::operations::decay::print_semantic_internal_shift_arg(node_ref.reference)}; - if (internal_len != 0) + if (internal_len != 0 && internal_len <= len) { - // A non-zero internal shift allows padding to be inserted inside the already-emitted child bytes. - char_type *const first{iter}; - char_type *const last{ - ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference)}; - if (len < internal_len) - { - // An invalid internal span leaves the child output unchanged instead of moving bytes past it. - return last; - } + // A valid internal shift allows padding to be inserted inside the already-emitted child bytes. char_type *const shift_pos{first + internal_len}; ::fast_io::details::my_copy(shift_pos, last, shift_pos + padding); ::fast_io::details::my_fill_n(shift_pos, padding, fillch); return first + width; } } - // Right placement, or internal placement without a shift point, emits padding before the child. - iter = ::fast_io::details::my_fill_n(iter, padding, fillch); - return ::fast_io::operations::decay::print_semantic_emit_unchecked_arg(iter, node_ref.reference); + // Right placement, or internal placement without a shift point, moves the child behind leading padding. + ::fast_io::details::my_copy(first, last, first + padding); + ::fast_io::details::my_fill_n(first, padding, fillch); + return first + width; } else { @@ -4625,6 +4626,203 @@ inline constexpr char_type *print_semantic_emit_unchecked_run(char_type *iter, A return iter; } +/// @brief Forward declaration for computing a bounded semantic argument size after input forwarding. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to measure +/// @return ::std::size_t an upper bound for the emitted character count +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_bounded_size_arg(T &&t); + +/// @brief Computes an emitted-size upper bound for a semantic leaf. +/// @details Precise leaves use their exact size; dynamic-reserve leaves use their object-specific reserve bound. +/// @tparam char_type the output character type +/// @tparam T the semantic leaf type +/// @param t the leaf to measure +/// @return ::std::size_t an upper bound for the emitted character count +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_bounded_size_leaf(T &&t) +{ + using value_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v) + { + // Existing precise leaves already provide an exact bound. + return ::fast_io::operations::decay::print_semantic_precise_size_leaf(::std::forward(t)); + } + else if constexpr (::fast_io::dynamic_reserve_printable) + { + // Dynamic reserve leaves report the maximum buffer size needed for this object. + return print_reserve_size(::fast_io::io_reserve_type, ::std::forward(t)); + } +} + +/// @brief Continuation that accumulates bounded sizes for expanded semantic pack elements. +/// @tparam char_type the output character type +template <::std::integral char_type> +struct print_semantic_bounded_size_pack_continuation +{ + ::std::size_t *totalptr; + + /// @brief Adds every expanded pack element upper bound to the running total. + /// @tparam PackArgs the expanded pack element types + /// @param pack_args the expanded pack elements + template + inline constexpr void operator()(PackArgs &&...pack_args) const + { + ((*totalptr = ::fast_io::details::intrinsics::add_or_overflow_die( + *totalptr, + ::fast_io::operations::decay::print_semantic_bounded_size_arg( + ::std::forward(pack_args)))), + ...); + } +}; + +/// @brief Computes an emitted-size upper bound for a semantic node or leaf. +/// @tparam char_type the output character type +/// @tparam T the semantic node or leaf type +/// @param t the semantic node or leaf to measure +/// @return ::std::size_t an upper bound for the emitted character count +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_bounded_size(T &&t) +{ + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + using node_type = ::std::remove_cvref_t; + if constexpr (::fast_io::details::print_pack) + { + // Packs sum the upper bound of every stored element. + ::std::size_t total{}; + ::fast_io::details::decay::print_semantic_pack_apply( + ::std::forward(node_ref), + ::fast_io::operations::decay::print_semantic_bounded_size_pack_continuation{ + __builtin_addressof(total)}); + return total; + } + else if constexpr (::fast_io::details::decay::print_semantic_condition_v) + { + // Conditions need only the branch selected for this object. + if (node_ref.pred) + { + return ::fast_io::operations::decay::print_semantic_bounded_size_arg(node_ref.t1); + } + return ::fast_io::operations::decay::print_semantic_bounded_size_arg(node_ref.t2); + } + else if constexpr (::fast_io::details::decay::print_semantic_width_v) + { + // Width nodes use the larger of the child upper bound and the active field width. + using width_traits = ::fast_io::details::decay::print_semantic_width_traits; + ::std::size_t const child_bound{ + ::fast_io::operations::decay::print_semantic_bounded_size_arg(node_ref.reference)}; + ::std::size_t const width{node_ref.width}; + auto const placement{ + ::fast_io::operations::decay::print_semantic_width_placement(node_ref)}; + ::std::size_t const placement_code{ + ::fast_io::operations::decay::print_semantic_width_placement_code(placement)}; + if (width <= child_bound || placement_code == 0u) + { + return child_bound; + } + return width; + } + else + { + // Leaf nodes use either an exact size or a dynamic reserve bound. + return ::fast_io::operations::decay::print_semantic_bounded_size_leaf( + ::std::forward(node_ref)); + } +} + +/// @brief Applies semantic input forwarding before bounded-size measurement. +/// @tparam char_type the output character type +/// @tparam T the semantic input argument type +/// @param t the argument to measure +/// @return ::std::size_t an upper bound for the emitted character count +template <::std::integral char_type, typename T> +inline constexpr ::std::size_t print_semantic_bounded_size_arg(T &&t) +{ + decltype(auto) forwarded{ + ::fast_io::details::decay::print_semantic_input_forward(::std::forward(t))}; + return ::fast_io::operations::decay::print_semantic_bounded_size( + ::std::forward(forwarded)); +} + +/// @brief Computes the run-time bounded size for a semantic print run. +/// @tparam line true when a trailing newline is part of the run +/// @tparam char_type the character type of the output run +/// @tparam Args the argument types in the run +/// @param args the arguments whose semantic bounds are measured +/// @return ::std::size_t the total upper bound needed by the run +template +inline constexpr ::std::size_t print_semantic_bounded_total_size(Args &&...args) +{ + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::operations::decay::print_semantic_bounded_size_arg(args))), + ...); + if constexpr (line) + { + // The line variant includes the final newline in the measured bound. + total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); + } + return total; +} + +/// @brief Attempts to coalesce a bounded semantic print run into one contiguous output operation. +/// @details This path admits dynamic-reserve leaves by allocating for their upper bound and writing the actual length. +/// @tparam line true when a trailing newline is appended +/// @tparam char_type the character type of the output stream +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam Args the argument types in the run +/// @param optstm the output stream reference +/// @param args the arguments to emit +/// @return bool true when the whole run was emitted by this coalescing path +template +inline constexpr bool print_semantic_try_bounded_coalesce(outputstmtype optstm, Args &&...args) +{ + if constexpr ((::fast_io::details::decay::print_semantic_bounded_size_ok< + char_type, ::std::remove_cvref_t>::value && + ...)) + { + // A bounded run can be materialized once when its upper bound fits the available coalescing space. + ::std::size_t const required{ + ::fast_io::operations::decay::print_semantic_bounded_total_size(args...)}; + if (required == 0) + { + return true; + } + if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) + { + // Buffered streams can use the existing put area when it can hold the upper bound. + char_type *const curr{obuffer_curr(optstm)}; + char_type *const end{obuffer_end(optstm)}; + if (static_cast<::std::size_t>(end - curr) >= required) + { + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + curr, ::std::forward(args)...)}; + obuffer_set_curr(optstm, iter); + return true; + } + } + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_full_output_coalesce_threshold()}; + if constexpr (threshold_chars != 0) + { + // Unbuffered streams use a stack buffer when the upper bound is below their full-output threshold. + if (required <= threshold_chars) + { + char_type buffer[threshold_chars]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } + } + } + // At least one requirement for bounded coalescing failed, so the caller must use the normal emit path. + return false; +} + /// @brief Attempts to coalesce a precise semantic print run into one contiguous output operation. /// @details The function first uses existing output-buffer space when available, then falls back to a bounded stack /// buffer according to the full-output coalescing threshold. @@ -5250,7 +5448,7 @@ struct print_semantic_emit_freestanding_continuation }; /// @brief Entry continuation for flattened semantic emission. -/// @details It first tries precise coalescing for the whole filtered run, then falls back to semantic flattening. +/// @details It tries precise and bounded coalescing for the whole filtered run before semantic flattening. /// @tparam line true when a trailing newline is appended /// @tparam char_type the character type of the output stream /// @tparam outputstmtype the decayed output stream reference type @@ -5259,16 +5457,18 @@ struct print_semantic_emit_flat_continuation { outputstmtype optstm; - /// @brief Emits a filtered semantic run, preferring precise coalescing before flattening. + /// @brief Emits a filtered semantic run, preferring coalescing before flattening. /// @tparam FilteredArgs the filtered argument types /// @param filtered_args the filtered arguments template inline constexpr void operator()(FilteredArgs &&...filtered_args) const { if (!::fast_io::operations::decay::print_semantic_try_precise_coalesce( + optstm, ::std::forward(filtered_args)...) && + !::fast_io::operations::decay::print_semantic_try_bounded_coalesce( optstm, ::std::forward(filtered_args)...)) { - // Failed precise coalescing keeps semantics by flattening nodes around ordinary freestanding output. + // Failed coalescing keeps semantics by flattening nodes around ordinary freestanding output. ::fast_io::operations::decay::print_semantic_emit_flat_impl( optstm, ::fast_io::operations::decay::print_semantic_emit_freestanding_continuation{optstm}, diff --git a/third-parties/fast_io/include/fast_io_legacy_impl/io.h b/third-parties/fast_io/include/fast_io_legacy_impl/io.h index 41f8aa935..451a0323d 100644 --- a/third-parties/fast_io/include/fast_io_legacy_impl/io.h +++ b/third-parties/fast_io/include/fast_io_legacy_impl/io.h @@ -34,18 +34,27 @@ inline constexpr void print(T &&t, Args &&...args) !defined(_LIBCPP_FREESTANDING)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) && \ __has_include() - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - ::fast_io::details::print_after_io_print_forward( - ::fast_io::io_print_forward(::fast_io::io_print_alias(t)), - ::fast_io::io_print_forward(::fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for print on the provided output stream"); } else { - // clang-format off + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + ::fast_io::details::print_after_io_print_forward( + ::fast_io::io_print_forward(::fast_io::io_print_alias(t)), + ::fast_io::io_print_forward(::fast_io::io_print_alias(args))...); + } + else + { + // clang-format off static_assert(type_ok, "some types are not printable for print on default C's stdout"); - // clang-format on + // clang-format on + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; @@ -79,16 +88,25 @@ inline constexpr void println(T &&t, Args &&...args) !defined(_LIBCPP_FREESTANDING)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) && \ __has_include() - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - ::fast_io::details::print_after_io_print_forward( - ::fast_io::io_print_forward(::fast_io::io_print_alias(t)), - ::fast_io::io_print_forward(::fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for println on the provided output stream"); } else { - static_assert(type_ok, "some types are not printable for print on default C's stdout"); + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + ::fast_io::details::print_after_io_print_forward( + ::fast_io::io_print_forward(::fast_io::io_print_alias(t)), + ::fast_io::io_print_forward(::fast_io::io_print_alias(args))...); + } + else + { + static_assert(type_ok, "some types are not printable for print on default C's stdout"); + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; @@ -116,18 +134,27 @@ inline constexpr void perr(T &&t, Args &&...args) #if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && !defined(_LIBCPP_FREESTANDING) && \ !defined(__AVR__)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - ::fast_io::details::perr_after_io_print_forward( - fast_io::io_print_forward(fast_io::io_print_alias(t)), - fast_io::io_print_forward(fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for perr on the provided output stream"); } else { - // clang-format off + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + ::fast_io::details::perr_after_io_print_forward( + fast_io::io_print_forward(fast_io::io_print_alias(t)), + fast_io::io_print_forward(fast_io::io_print_alias(args))...); + } + else + { + // clang-format off static_assert(type_ok, "some types are not printable for perr on native err"); - // clang-format on + // clang-format on + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; @@ -155,18 +182,27 @@ inline constexpr void perrln(T &&t, Args &&...args) #if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && !defined(_LIBCPP_FREESTANDING) && \ !defined(__AVR__)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - ::fast_io::details::perr_after_io_print_forward( - fast_io::io_print_forward(fast_io::io_print_alias(t)), - fast_io::io_print_forward(fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for perrln on the provided output stream"); } else { - // clang-format off + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + ::fast_io::details::perr_after_io_print_forward( + fast_io::io_print_forward(fast_io::io_print_alias(t)), + fast_io::io_print_forward(fast_io::io_print_alias(args))...); + } + else + { + // clang-format off static_assert(type_ok, "some types are not printable for perrln on native err"); - // clang-format on + // clang-format on + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; @@ -235,18 +271,27 @@ inline constexpr void debug_print(T &&t, Args &&...args) #if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && \ !defined(_LIBCPP_FREESTANDING)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - fast_io::details::debug_print_after_io_print_forward( - fast_io::io_print_forward(fast_io::io_print_alias(t)), - fast_io::io_print_forward(fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for debug_print on the provided output stream"); } else { - // clang-format off + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + fast_io::details::debug_print_after_io_print_forward( + fast_io::io_print_forward(fast_io::io_print_alias(t)), + fast_io::io_print_forward(fast_io::io_print_alias(args))...); + } + else + { + // clang-format off static_assert(type_ok, "some types are not printable for debug_print on native out"); - // clang-format on + // clang-format on + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; @@ -274,18 +319,27 @@ inline constexpr void debug_println(T &&t, Args &&...args) #if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && \ !defined(_LIBCPP_FREESTANDING)) || \ defined(FAST_IO_ENABLE_HOSTED_FEATURES)) - constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; - if constexpr (type_ok) + constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; + if constexpr (device_ok) { - fast_io::details::debug_print_after_io_print_forward( - fast_io::io_print_forward(fast_io::io_print_alias(t)), - fast_io::io_print_forward(fast_io::io_print_alias(args))...); + static_assert(device_and_type_ok, + "some types are not printable for debug_println on the provided output stream"); } else { - // clang-format off + constexpr bool type_ok{::fast_io::operations::defines::print_freestanding_params_okay}; + if constexpr (type_ok) + { + fast_io::details::debug_print_after_io_print_forward( + fast_io::io_print_forward(fast_io::io_print_alias(t)), + fast_io::io_print_forward(fast_io::io_print_alias(args))...); + } + else + { + // clang-format off static_assert(type_ok, "some types are not printable for debug_println on native out"); - // clang-format on + // clang-format on + } } #else constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; diff --git a/third-parties/fast_io/include/fast_io_unit/floating/impl.h b/third-parties/fast_io/include/fast_io_unit/floating/impl.h index 9ad44ec64..21b6cf1d6 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/impl.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/impl.h @@ -16,17 +16,35 @@ concept print_floating_has_iec559_traits = requires { typename ::fast_io::details::iec559_traits<::std::remove_cvref_t>::mantissa_type; }; +template > +struct print_floating_decimal_direct_supported_impl +{ + inline static constexpr bool value{}; +}; + template -inline constexpr bool print_floating_decimal_direct_supported{ - ::std::same_as<::std::remove_cvref_t, float> || ::std::same_as<::std::remove_cvref_t, double> +struct print_floating_decimal_direct_supported_impl +{ + using no_cvref_t = ::std::remove_cvref_t; + using trait = ::fast_io::details::iec559_traits; + inline static constexpr bool value{ + (trait::mbits <= ::fast_io::details::iec559_traits::mbits && + trait::ebits <= ::fast_io::details::iec559_traits::ebits && + sizeof(no_cvref_t) <= sizeof(float)) || + ::std::same_as #ifdef __STDCPP_FLOAT32_T__ - || ::std::same_as<::std::remove_cvref_t, _Float32> + || ::std::same_as #endif #ifdef __STDCPP_FLOAT64_T__ - || ::std::same_as<::std::remove_cvref_t, _Float64> + || ::std::same_as #endif + }; }; +template +inline constexpr bool print_floating_decimal_direct_supported{ + ::fast_io::details::print_floating_decimal_direct_supported_impl::value}; + template > struct print_floating_decimal_via_float_impl { diff --git a/third-parties/fast_io/include/fast_io_unit/floating/punning.h b/third-parties/fast_io/include/fast_io_unit/floating/punning.h index 7d3f72425..b7ccb0b73 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/punning.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/punning.h @@ -35,7 +35,7 @@ struct iec559_traits<__float16> using mantissa_type = ::std::uint_least16_t; inline static constexpr ::std::size_t mbits{10}; inline static constexpr ::std::size_t ebits{5}; - inline static constexpr ::std::uint_least32_t m10digits{4}; + inline static constexpr ::std::uint_least32_t m10digits{5}; inline static constexpr ::std::uint_least32_t m2hexdigits{3}; inline static constexpr ::std::uint_least32_t e10digits{2}; inline static constexpr ::std::uint_least32_t e2hexdigits{2}; @@ -139,7 +139,7 @@ struct iec559_traits<_Float16> using mantissa_type = ::std::uint_least16_t; inline static constexpr ::std::size_t mbits{10}; inline static constexpr ::std::size_t ebits{5}; - inline static constexpr ::std::uint_least32_t m10digits{4}; + inline static constexpr ::std::uint_least32_t m10digits{5}; inline static constexpr ::std::uint_least32_t m2hexdigits{3}; inline static constexpr ::std::uint_least32_t e10digits{2}; inline static constexpr ::std::uint_least32_t e2hexdigits{2}; diff --git a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h index 8d8e4f975..e80cecf9c 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h @@ -74,6 +74,20 @@ inline constexpr uint64x2 float64_mod5_tb[]{ inline constexpr auto compute_pow10_float64{pow10_float64_tb + 292}; +template +inline constexpr bool dragonbox_uses_binary32_core{ + iec559_traits::mbits <= iec559_traits::mbits && + iec559_traits::ebits <= iec559_traits::ebits}; + +template +inline constexpr ::std::int_least32_t dragonbox_kappa{ + ::fast_io::details::dragonbox_uses_binary32_core ? 1 : 2}; + +template +using dragonbox_decimal_mantissa_type = + ::std::conditional_t<::fast_io::details::dragonbox_uses_binary32_core, ::std::uint_least32_t, + typename iec559_traits::mantissa_type>; + inline constexpr uint64x2 pow10_float64_scan_low_tb[]{ {0xEEF453D6923BD65A, 0x113FAA2906A13B40}, // 1e-342 {0x9558B4661B6565F8, 0x4AC7CA59A424C508}, // 1e-341 @@ -273,7 +287,7 @@ template #if __has_cpp_attribute(__gnu__::__cold__) [[__gnu__::__cold__]] #endif -inline constexpr m10_result::mantissa_type> +inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> schubfach_asymmetric_interval(::std::int_least32_t e2) noexcept { using trait = iec559_traits; @@ -342,7 +356,7 @@ template #if __has_cpp_attribute(__gnu__::__hot__) [[__gnu__::__hot__]] #endif -inline constexpr m10_result::mantissa_type> +inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_main(typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2) noexcept { using trait = iec559_traits; @@ -353,7 +367,7 @@ dragonbox_main(typename iec559_traits::mantissa_type m2, ::std::int_least32 constexpr ::std::uint_least32_t bias{(static_cast<::std::uint_least32_t>(1 << ebits) >> 1) - 1}; constexpr ::std::int_least32_t exponent_bias{bias + mbits}; constexpr mantissa_type mflags{static_cast(static_cast(1) << mbits)}; - constexpr ::std::int_least32_t kappa{(sizeof(flt) == sizeof(::std::uint_least32_t)) ? 1 : 2}; + constexpr ::std::int_least32_t kappa{::fast_io::details::dragonbox_kappa}; constexpr ::std::uint_least32_t big_divisor{kappa == 2 ? 1000 : 100}; constexpr ::std::uint_least32_t small_divisor{big_divisor / 10}; constexpr ::std::uint_least32_t small_divisor_div2{small_divisor / 2}; @@ -372,11 +386,14 @@ dragonbox_main(typename iec559_traits::mantissa_type m2, ::std::int_least32 ::std::uint_least32_t pos_e2{static_cast<::std::uint_least32_t>(-e2)}; if (pos_e2 < mbits && multiple_of_pow2_unchecked(m2, pos_e2)) [[unlikely]] { - return {m2 >> pos_e2, 0}; + return {static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(m2 >> pos_e2), 0}; } - if (raw_m2 == 0 && e2_temp > 1) [[unlikely]] + if constexpr (!(::fast_io::details::dragonbox_uses_binary32_core && sizeof(flt) < sizeof(float))) { - return schubfach_asymmetric_interval(e2); + if (raw_m2 == 0 && e2_temp > 1) [[unlikely]] + { + return schubfach_asymmetric_interval(e2); + } } } bool const is_even{(m2 & 1u) == 0u}; @@ -389,7 +406,8 @@ dragonbox_main(typename iec559_traits::mantissa_type m2, ::std::int_least32 ::std::uint_least64_t const pow10_lo{pow10.lo}; ::std::uint_least64_t const pow10_hi{pow10.hi}; ::std::uint_least32_t const delta{static_cast<::std::uint_least32_t>(pow10_hi >> (63 - beta_minus_1))}; - ::std::uint_least64_t const two_fc{m2 << 1}, two_fl{two_fc - 1}, two_fr{two_fc + 1}; + ::std::uint_least64_t const two_fc{static_cast<::std::uint_least64_t>(m2) << 1}, + two_fl{two_fc - 1}, two_fr{two_fc + 1}; ::std::uint_least64_t const zi{::fast_io::intrinsics::umulh(two_fr << beta_minus_1, ::fast_io::intrinsics::pack_ul64(pow10_lo, pow10_hi))}; ::std::uint_least64_t q; ::std::uint_least32_t r; @@ -429,10 +447,11 @@ dragonbox_main(typename iec559_traits::mantissa_type m2, ::std::int_least32 } return {q, minus_k + kappa}; } - else if constexpr (sizeof(flt) == sizeof(::std::uint_least32_t)) + else { ::std::uint_least64_t const pow10{compute_pow10_float32[plus_k]}; - ::std::uint_least32_t const two_fc{m2 << 1}, two_fl{two_fc - 1}, two_fr{two_fc + 1}; + ::std::uint_least32_t const two_fc{static_cast<::std::uint_least32_t>(m2) << 1}, + two_fl{two_fc - 1}, two_fr{two_fc + 1}; ::std::uint_least32_t const zi{::fast_io::intrinsics::umulh(two_fr << beta_minus_1, pow10)}; ::std::uint_least32_t q{zi / big_divisor}; ::std::uint_least32_t r{zi % big_divisor}; @@ -646,7 +665,7 @@ dragonbox_compute_mul_parity_float64(::std::uint_least64_t two_f, ::std::uint_le } template -[[nodiscard]] inline constexpr m10_result::mantissa_type> +[[nodiscard]] inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_nearest_shorter_interval([[maybe_unused]] typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { @@ -720,7 +739,7 @@ dragonbox_nearest_shorter_interval([[maybe_unused]] typename iec559_traits: } template -[[nodiscard]] inline constexpr m10_result::mantissa_type> +[[nodiscard]] inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { @@ -732,7 +751,7 @@ dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::s constexpr ::std::uint_least32_t bias{(static_cast<::std::uint_least32_t>(1 << ebits) >> 1) - 1}; constexpr ::std::int_least32_t exponent_bias{bias + mbits}; constexpr mantissa_type mflags{static_cast(static_cast(1) << mbits)}; - constexpr ::std::int_least32_t kappa{(sizeof(flt) == sizeof(::std::uint_least32_t)) ? 1 : 2}; + constexpr ::std::int_least32_t kappa{::fast_io::details::dragonbox_kappa}; constexpr ::std::uint_least32_t big_divisor{kappa == 2 ? 1000 : 100}; constexpr ::std::uint_least32_t small_divisor{big_divisor / 10}; constexpr ::std::uint_least32_t small_divisor_div2{small_divisor / 2}; @@ -750,11 +769,14 @@ dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::s ::std::uint_least32_t pos_e2{static_cast<::std::uint_least32_t>(-e2)}; if (pos_e2 < mbits && multiple_of_pow2_unchecked(m2, pos_e2)) [[unlikely]] { - return {m2 >> pos_e2, 0}; + return {static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(m2 >> pos_e2), 0}; } - if (!raw_mantissa) [[unlikely]] + if constexpr (!(::fast_io::details::dragonbox_uses_binary32_core && sizeof(flt) < sizeof(float))) { - return ::fast_io::details::dragonbox_nearest_shorter_interval(m2, e2, negative); + if (!raw_mantissa) [[unlikely]] + { + return ::fast_io::details::dragonbox_nearest_shorter_interval(m2, e2, negative); + } } } bool const is_even{(m2 & 1u) == 0u}; @@ -769,7 +791,8 @@ dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::s ::std::uint_least64_t const pow10_lo{pow10.lo}; ::std::uint_least64_t const pow10_hi{pow10.hi}; ::std::uint_least32_t const delta{static_cast<::std::uint_least32_t>(pow10_hi >> (63 - beta))}; - ::std::uint_least64_t const two_fc{m2 << 1}, two_fl{two_fc - 1}, two_fr{two_fc + 1}; + ::std::uint_least64_t const two_fc{static_cast<::std::uint_least64_t>(m2) << 1}, + two_fl{two_fc - 1}, two_fr{two_fc + 1}; auto const z_result{::fast_io::details::dragonbox_compute_mul_float64( two_fr << static_cast(beta), pow10_lo, pow10_hi)}; ::std::uint_least64_t q{z_result.integer_part / big_divisor}; @@ -816,7 +839,8 @@ dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::s { ::std::uint_least64_t const pow10{compute_pow10_float32[plus_k]}; ::std::uint_least32_t const delta{static_cast<::std::uint_least32_t>(pow10 >> (63 - beta))}; - ::std::uint_least32_t const two_fc{m2 << 1}, two_fl{two_fc - 1}, two_fr{two_fc + 1}; + ::std::uint_least32_t const two_fc{static_cast<::std::uint_least32_t>(m2) << 1}, + two_fl{two_fc - 1}, two_fr{two_fc + 1}; auto const z_result{::fast_io::details::dragonbox_compute_mul_float32( static_cast<::std::uint_least64_t>(two_fr) << static_cast(beta), pow10)}; ::std::uint_least32_t q{static_cast<::std::uint_least32_t>(z_result.integer_part / big_divisor)}; @@ -860,7 +884,7 @@ dragonbox_main_nearest_policy(typename iec559_traits::mantissa_type m2, ::s } template -[[nodiscard]] inline constexpr m10_result::mantissa_type> +[[nodiscard]] inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_main_directed(typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2) noexcept { using trait = iec559_traits; @@ -871,7 +895,7 @@ dragonbox_main_directed(typename iec559_traits::mantissa_type m2, ::std::in constexpr ::std::uint_least32_t bias{(static_cast<::std::uint_least32_t>(1 << ebits) >> 1) - 1}; constexpr ::std::int_least32_t exponent_bias{bias + mbits}; constexpr mantissa_type mflags{static_cast(static_cast(1) << mbits)}; - constexpr ::std::int_least32_t kappa{(sizeof(flt) == sizeof(::std::uint_least32_t)) ? 1 : 2}; + constexpr ::std::int_least32_t kappa{::fast_io::details::dragonbox_kappa}; constexpr ::std::uint_least32_t big_divisor{kappa == 2 ? 1000 : 100}; bool shorter_interval{}; @@ -889,7 +913,7 @@ dragonbox_main_directed(typename iec559_traits::mantissa_type m2, ::std::in ::std::uint_least32_t pos_e2{static_cast<::std::uint_least32_t>(-e2)}; if (pos_e2 < mbits && multiple_of_pow2_unchecked(m2, pos_e2)) [[unlikely]] { - return {m2 >> pos_e2, 0}; + return {static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(m2 >> pos_e2), 0}; } } ::std::uint_least64_t const two_fc{static_cast<::std::uint_least64_t>(m2) << 1u}; @@ -1009,7 +1033,7 @@ dragonbox_main_directed(typename iec559_traits::mantissa_type m2, ::std::in } template -[[nodiscard]] inline constexpr m10_result::mantissa_type> +[[nodiscard]] inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_main_policy(typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { @@ -1049,6 +1073,12 @@ struct dragonbox_decimal_uint128 ::std::uint_least64_t high{}; }; +template +inline constexpr bool dragonbox_decimal_adjusted_supported{ + (iec559_traits::mbits <= iec559_traits::mbits && + iec559_traits::ebits <= iec559_traits::ebits) || + sizeof(flt) == sizeof(::std::uint_least64_t)}; + [[nodiscard]] inline constexpr ::std::int_least32_t dragonbox_decimal_binary_power(::std::int_least32_t exponent) noexcept { @@ -1101,21 +1131,24 @@ dragonbox_decimal_compute_adjusted(::std::int_least64_t exponent, ::std::uint_le dragonbox_decimal_adjusted_mantissa &answer) noexcept { using trait = ::fast_io::details::iec559_traits; - if constexpr (sizeof(flt) != sizeof(::std::uint_least32_t) && sizeof(flt) != sizeof(::std::uint_least64_t)) + if constexpr (!::fast_io::details::dragonbox_decimal_adjusted_supported) { return false; } else { + constexpr bool use_binary32_bounds{ + trait::mbits <= ::fast_io::details::iec559_traits::mbits && + trait::ebits <= ::fast_io::details::iec559_traits::ebits}; constexpr auto mantissa_explicit_bits{static_cast<::std::int_least32_t>(trait::mbits)}; constexpr auto minimum_exponent{ -static_cast<::std::int_least32_t>((static_cast<::std::uint_least32_t>(1u) << (trait::ebits - 1u)) - 1u)}; constexpr auto infinite_power{ static_cast<::std::int_least32_t>((static_cast<::std::uint_least32_t>(1u) << trait::ebits) - 1u)}; - constexpr auto smallest_power10{sizeof(flt) == sizeof(::std::uint_least32_t) ? -65 : -342}; - constexpr auto largest_power10{sizeof(flt) == sizeof(::std::uint_least32_t) ? 38 : 308}; - constexpr auto min_round_to_even_power10{sizeof(flt) == sizeof(::std::uint_least32_t) ? -17 : -4}; - constexpr auto max_round_to_even_power10{sizeof(flt) == sizeof(::std::uint_least32_t) ? 10 : 23}; + constexpr auto smallest_power10{use_binary32_bounds ? -65 : -342}; + constexpr auto largest_power10{use_binary32_bounds ? 38 : 308}; + constexpr auto min_round_to_even_power10{use_binary32_bounds ? -17 : -4}; + constexpr auto max_round_to_even_power10{use_binary32_bounds ? 10 : 23}; if (significand == 0 || exponent < smallest_power10) { answer = {}; @@ -1183,7 +1216,8 @@ dragonbox_decimal_compute_adjusted(::std::int_least64_t exponent, ::std::uint_le template [[nodiscard]] inline constexpr bool dragonbox_decimal_roundtrips_to( - typename iec559_traits::mantissa_type decimal_mantissa, ::std::int_least32_t decimal_exponent, + ::fast_io::details::dragonbox_decimal_mantissa_type decimal_mantissa, + ::std::int_least32_t decimal_exponent, typename iec559_traits::mantissa_type binary_mantissa, ::std::int_least32_t binary_exponent, bool negative) noexcept { @@ -1198,42 +1232,44 @@ template template [[nodiscard]] inline constexpr bool dragonbox_decimal_printable_roundtrips_to( - typename iec559_traits::mantissa_type decimal_mantissa, ::std::int_least32_t decimal_exponent, + ::fast_io::details::dragonbox_decimal_mantissa_type decimal_mantissa, + ::std::int_least32_t decimal_exponent, typename iec559_traits::mantissa_type binary_mantissa, ::std::int_least32_t binary_exponent, bool negative) noexcept { - typename iec559_traits::mantissa_type decimal_mantissa_limit{1u}; + ::std::uint_least64_t decimal_mantissa_limit{1u}; for (::std::uint_least32_t i{}; i != ::fast_io::details::iec559_traits::m10digits; ++i) { - decimal_mantissa_limit = static_cast::mantissa_type>( - decimal_mantissa_limit * 10u); + decimal_mantissa_limit *= 10u; } return decimal_mantissa && - decimal_mantissa < decimal_mantissa_limit && + static_cast<::std::uint_least64_t>(decimal_mantissa) < decimal_mantissa_limit && ::fast_io::details::dragonbox_decimal_roundtrips_to( decimal_mantissa, decimal_exponent, binary_mantissa, binary_exponent, negative); } template [[nodiscard]] inline constexpr bool dragonbox_correct_shortest_roundtrip_extend( - typename iec559_traits::mantissa_type base, ::std::int_least32_t exponent, - typename iec559_traits::mantissa_type &m10, ::std::int_least32_t &e10, + ::fast_io::details::dragonbox_decimal_mantissa_type base, ::std::int_least32_t exponent, + ::fast_io::details::dragonbox_decimal_mantissa_type &m10, ::std::int_least32_t &e10, typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { - typename iec559_traits::mantissa_type add_limit{1u}; + ::fast_io::details::dragonbox_decimal_mantissa_type add_limit{1u}; for (::std::uint_least32_t extension{}; extension != 3u; ++extension) { - auto const next_base{static_cast::mantissa_type>(base * 10u)}; + auto const next_base{ + static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(base * 10u)}; if (next_base / 10u != base) { break; } base = next_base; - add_limit = static_cast::mantissa_type>(add_limit * 10u); + add_limit = static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(add_limit * 10u); --exponent; - for (typename iec559_traits::mantissa_type add{}; add != add_limit; ++add) + for (::fast_io::details::dragonbox_decimal_mantissa_type add{}; add != add_limit; ++add) { - auto const extended{static_cast::mantissa_type>(base + add)}; + auto const extended{ + static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(base + add)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( extended, exponent, m2, e2, negative)) { @@ -1242,9 +1278,10 @@ template return true; } } - for (typename iec559_traits::mantissa_type sub{1u}; sub != add_limit && sub <= base; ++sub) + for (::fast_io::details::dragonbox_decimal_mantissa_type sub{1u}; sub != add_limit && sub <= base; ++sub) { - auto const extended{static_cast::mantissa_type>(base - sub)}; + auto const extended{ + static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(base - sub)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( extended, exponent, m2, e2, negative)) { @@ -1258,18 +1295,19 @@ template } template -inline constexpr void dragonbox_correct_shortest_roundtrip(typename iec559_traits::mantissa_type &m10, +inline constexpr void dragonbox_correct_shortest_roundtrip( + ::fast_io::details::dragonbox_decimal_mantissa_type &m10, ::std::int_least32_t &e10, typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { - if constexpr (sizeof(flt) == sizeof(::std::uint_least32_t) || sizeof(flt) == sizeof(::std::uint_least64_t)) + if constexpr (::fast_io::details::dragonbox_decimal_adjusted_supported) { if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to(m10, e10, m2, e2, negative)) { return; } - auto const next{static_cast::mantissa_type>(m10 + 1u)}; + auto const next{static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(m10 + 1u)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to(next, e10, m2, e2, negative)) { m10 = next; @@ -1277,7 +1315,7 @@ inline constexpr void dragonbox_correct_shortest_roundtrip(typename iec559_trait } if (m10) { - auto const previous{static_cast::mantissa_type>(m10 - 1u)}; + auto const previous{static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(m10 - 1u)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( previous, e10, m2, e2, negative)) { @@ -1295,7 +1333,8 @@ inline constexpr void dragonbox_correct_shortest_roundtrip(typename iec559_trait e10 = nearest_e10; return; } - auto const nearest_next{static_cast::mantissa_type>(nearest_v + 1u)}; + auto const nearest_next{ + static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(nearest_v + 1u)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( nearest_next, nearest_e10, m2, e2, negative)) { @@ -1305,7 +1344,8 @@ inline constexpr void dragonbox_correct_shortest_roundtrip(typename iec559_trait } if (nearest_v) { - auto const nearest_previous{static_cast::mantissa_type>(nearest_v - 1u)}; + auto const nearest_previous{ + static_cast<::fast_io::details::dragonbox_decimal_mantissa_type>(nearest_v - 1u)}; if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( nearest_previous, nearest_e10, m2, e2, negative)) { @@ -1332,7 +1372,7 @@ template ::mantissa_type> +inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_impl(typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { auto [m10, e10] = @@ -1352,7 +1392,8 @@ dragonbox_impl(typename iec559_traits::mantissa_type m2, ::std::int_least32 // m10 should not ==0 auto [v, n] = ::fast_io::bitops::rtz_iec559(m10); e10 += static_cast<::std::int_least32_t>(static_cast<::std::uint_least32_t>(n)); - if constexpr (rounding != ::fast_io::manipulators::floating_rounding::nearest_to_even) + if constexpr (rounding != ::fast_io::manipulators::floating_rounding::nearest_to_even || + (::fast_io::details::dragonbox_uses_binary32_core && sizeof(flt) < sizeof(float))) { ::fast_io::details::dragonbox_correct_shortest_roundtrip(v, e10, m2, e2, negative); auto [v2, n2] = ::fast_io::bitops::rtz_iec559(v); @@ -1365,6 +1406,135 @@ dragonbox_impl(typename iec559_traits::mantissa_type m2, ::std::int_least32 } } +template +inline constexpr void dragonbox_shorten_decimal_to_target( + ::fast_io::details::dragonbox_decimal_mantissa_type &m10, ::std::int_least32_t &e10, + typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept +{ + using decimal_type = ::fast_io::details::dragonbox_decimal_mantissa_type; + if (!m10) + { + return; + } + auto const len{static_cast<::std::uint_least32_t>(chars_len<10, true>(m10))}; + constexpr auto max_digits{::fast_io::details::iec559_traits::m10digits}; + auto const max_candidate_digits{len < max_digits ? len : max_digits}; + for (::std::uint_least32_t desired{1u}; desired <= max_candidate_digits; ++desired) + { + auto const cut{static_cast<::std::uint_least32_t>(len - desired)}; + if (19u < cut) + { + continue; + } + ::std::uint_least64_t divisor{1u}; + for (::std::uint_least32_t i{}; i != cut; ++i) + { + divisor *= 10u; + } + auto const quotient{static_cast(m10 / divisor)}; + auto const remainder{static_cast<::std::uint_least64_t>(m10 - static_cast(quotient * divisor))}; + auto const candidate_e10{static_cast<::std::int_least32_t>(e10 + static_cast<::std::int_least32_t>(cut))}; + auto try_candidate = [&](decimal_type candidate) constexpr noexcept + { + if (!candidate) + { + return false; + } + if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( + candidate, candidate_e10, m2, e2, negative)) + { + m10 = candidate; + e10 = candidate_e10; + auto [trimmed, zeroes] = ::fast_io::bitops::rtz_iec559(m10); + m10 = trimmed; + e10 += static_cast<::std::int_least32_t>(static_cast<::std::uint_least32_t>(zeroes)); + return true; + } + return false; + }; + auto const round_up{remainder && (divisor - remainder < remainder)}; + auto const lower{quotient}; + auto const upper{static_cast(quotient + static_cast(remainder != 0u))}; + if (round_up) + { + if (try_candidate(upper) || try_candidate(lower)) + { + return; + } + if (try_candidate(static_cast(upper + 1u))) + { + return; + } + if (lower && try_candidate(static_cast(lower - 1u))) + { + return; + } + } + else + { + if (try_candidate(lower) || try_candidate(upper)) + { + return; + } + if (lower && try_candidate(static_cast(lower - 1u))) + { + return; + } + if (try_candidate(static_cast(upper + 1u))) + { + return; + } + } + } +} + +template +inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> +dragonbox_impl_narrow_from_float(flt f, typename iec559_traits::mantissa_type m2, + ::std::int_least32_t e2, bool negative) noexcept +{ + auto [float_mantissa, float_exponent, float_sign] = get_punned_result(static_cast(f)); + (void)float_sign; + auto [m10, e10] = ::fast_io::details::dragonbox_impl( + float_mantissa, static_cast<::std::int_least32_t>(float_exponent), negative); + ::fast_io::details::dragonbox_shorten_decimal_to_target(m10, e10, m2, e2, negative); + return {m10, e10}; +} + +template +inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> +dragonbox_impl_narrow_hybrid(flt f, typename iec559_traits::mantissa_type m2, + ::std::int_least32_t e2, bool negative) noexcept +{ + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_even) + { + auto direct{::fast_io::details::dragonbox_main(m2, e2)}; + if (direct.m10) + { + auto [trimmed, zeroes] = ::fast_io::bitops::rtz_iec559(direct.m10); + auto const trimmed_e10{ + static_cast<::std::int_least32_t>(direct.e10 + static_cast<::std::int_least32_t>( + static_cast<::std::uint_least32_t>(zeroes)))}; + if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( + trimmed, trimmed_e10, m2, e2, negative)) + { + return {trimmed, trimmed_e10}; + } + } + } + else + { + auto direct{::fast_io::details::dragonbox_impl(m2, e2, negative)}; + if (direct.m10 && + ::fast_io::details::dragonbox_decimal_printable_roundtrips_to( + direct.m10, direct.e10, m2, e2, negative)) + { + return direct; + } + } + return ::fast_io::details::dragonbox_impl_narrow_from_float(f, m2, e2, negative); +} + template inline constexpr char_type *print_rsv_fp_decimal_scientific_common_impl(char_type *iter, U m10, ::std::uint_least32_t m10len) noexcept @@ -1474,7 +1644,8 @@ inline constexpr char_type *fill_zero_point_impl(char_type *iter) noexcept } template -inline constexpr char_type *fixed_case0_full_integer(char_type *iter, typename iec559_traits::mantissa_type m10, +inline constexpr char_type *fixed_case0_full_integer(char_type *iter, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t olength, ::std::int_least32_t real_exp) noexcept { @@ -1495,7 +1666,7 @@ inline constexpr char_type *print_rsv_fp_append_json_float_zero(char_type *iter) template inline constexpr char_type *fixed_case0_full_integer_maybe_json( - char_type *iter, typename iec559_traits::mantissa_type m10, ::std::int_least32_t olength, + char_type *iter, ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t olength, ::std::int_least32_t real_exp) noexcept { iter = fixed_case0_full_integer(iter, m10, olength, real_exp); @@ -1511,7 +1682,7 @@ inline constexpr char_type *fixed_case0_full_integer_maybe_json( template inline constexpr char_type * -fixed_case1_integer_and_point(char_type *iter, typename iec559_traits::mantissa_type m10, +fixed_case1_integer_and_point(char_type *iter, ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t olength, ::std::int_least32_t real_exp) noexcept { auto eposition(real_exp + 1); @@ -1533,7 +1704,8 @@ fixed_case1_integer_and_point(char_type *iter, typename iec559_traits::mant template inline constexpr char_type * -fixed_case1_integer_and_point_maybe_json(char_type *iter, typename iec559_traits::mantissa_type m10, +fixed_case1_integer_and_point_maybe_json(char_type *iter, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t olength, ::std::int_least32_t real_exp) noexcept { @@ -1559,7 +1731,8 @@ fixed_case1_integer_and_point_maybe_json(char_type *iter, typename iec559_traits } template -inline constexpr char_type *fixed_case2_all_point(char_type *iter, typename iec559_traits::mantissa_type m10, +inline constexpr char_type *fixed_case2_all_point(char_type *iter, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t olength, ::std::int_least32_t real_exp) noexcept { iter = fill_zero_point_impl(iter); @@ -1571,7 +1744,7 @@ inline constexpr char_type *fixed_case2_all_point(char_type *iter, typename iec5 template inline constexpr char_type *print_rsv_fp_fixed_decision_impl(char_type *iter, - typename iec559_traits::mantissa_type m10, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t e10) noexcept { ::std::int_least32_t olength(static_cast<::std::int_least32_t>(chars_len<10, true>(m10))); @@ -1592,7 +1765,8 @@ inline constexpr char_type *print_rsv_fp_fixed_decision_impl(char_type *iter, template -inline constexpr char_type *print_rsv_fp_decision_impl(char_type *iter, typename iec559_traits::mantissa_type m10, +inline constexpr char_type *print_rsv_fp_decision_impl(char_type *iter, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t e10) noexcept { if constexpr (mt == ::fast_io::manipulators::floating_format::general) @@ -1751,10 +1925,11 @@ template <::fast_io::manipulators::floating_rounding rounding> template inline constexpr void print_rsv_fp_round_to_significant( - typename iec559_traits::mantissa_type &m10, ::std::int_least32_t &e10, ::std::size_t precision, + ::fast_io::details::dragonbox_decimal_mantissa_type &m10, ::std::int_least32_t &e10, + ::std::size_t precision, bool negative) noexcept { - using mantissa_type = typename iec559_traits::mantissa_type; + using mantissa_type = ::fast_io::details::dragonbox_decimal_mantissa_type; if (!m10) { return; @@ -1809,10 +1984,11 @@ inline constexpr void print_rsv_fp_round_to_significant( template inline constexpr void print_rsv_fp_round_to_fractional( - typename iec559_traits::mantissa_type &m10, ::std::int_least32_t &e10, ::std::size_t precision, + ::fast_io::details::dragonbox_decimal_mantissa_type &m10, ::std::int_least32_t &e10, + ::std::size_t precision, bool negative) noexcept { - using mantissa_type = typename iec559_traits::mantissa_type; + using mantissa_type = ::fast_io::details::dragonbox_decimal_mantissa_type; if (!m10) { return; @@ -1877,7 +2053,7 @@ inline constexpr char_type *print_rsv_fp_append_point_zeros(char_type *iter, ::s template inline constexpr char_type *print_rsv_fp_fixed_precision_impl(char_type *iter, - typename iec559_traits::mantissa_type m10, + ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t e10, ::std::size_t precision) noexcept { @@ -1958,7 +2134,7 @@ inline constexpr char_type *print_rsv_fp_fixed_precision_impl(char_type *iter, template inline constexpr char_type *print_rsv_fp_scientific_precision_impl( - char_type *iter, typename iec559_traits::mantissa_type m10, ::std::int_least32_t e10, + char_type *iter, ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t e10, ::std::size_t precision) noexcept { auto const olength{static_cast<::std::int_least32_t>(chars_len<10, true>(m10))}; @@ -2007,7 +2183,7 @@ template inline constexpr char_type *print_rsv_fp_precision_decision_impl( - char_type *iter, typename iec559_traits::mantissa_type m10, ::std::int_least32_t e10, + char_type *iter, ::fast_io::details::dragonbox_decimal_mantissa_type m10, ::std::int_least32_t e10, ::std::size_t precision, bool negative) noexcept { constexpr bool uses_significant_precision{ @@ -2147,7 +2323,23 @@ inline constexpr char_type *print_rsvflt_define_impl(char_type *iter, flt f) noe return prsv_fp_dece0(iter); } } - auto [m10, e10] = dragonbox_impl(mantissa, static_cast<::std::int_least32_t>(exponent), sign); + auto [m10, e10] = + []([[maybe_unused]] flt value, + [[maybe_unused]] mantissa_type binary_mantissa, + [[maybe_unused]] ::std::int_least32_t binary_exponent, + [[maybe_unused]] bool negative) constexpr noexcept + { + if constexpr (::fast_io::details::dragonbox_uses_binary32_core && sizeof(flt) < sizeof(float)) + { + return ::fast_io::details::dragonbox_impl_narrow_hybrid( + value, binary_mantissa, binary_exponent, negative); + } + else + { + return ::fast_io::details::dragonbox_impl( + binary_mantissa, binary_exponent, negative); + } + }(f, mantissa, static_cast<::std::int_least32_t>(exponent), sign); if constexpr (mt == ::fast_io::manipulators::floating_format::fixed) { return print_rsv_fp_fixed_decision_impl(iter, m10, e10); @@ -2279,8 +2471,23 @@ inline constexpr char_type *print_rsvflt_precision_define_impl(char_type *iter, return iter; } } - auto [m10, e10] = dragonbox_impl( - mantissa, static_cast<::std::int_least32_t>(exponent), sign); + auto [m10, e10] = + []([[maybe_unused]] flt value, + [[maybe_unused]] mantissa_type binary_mantissa, + [[maybe_unused]] ::std::int_least32_t binary_exponent, + [[maybe_unused]] bool negative) constexpr noexcept + { + if constexpr (::fast_io::details::dragonbox_uses_binary32_core && sizeof(flt) < sizeof(float)) + { + return ::fast_io::details::dragonbox_impl_narrow_hybrid( + value, binary_mantissa, binary_exponent, negative); + } + else + { + return ::fast_io::details::dragonbox_impl( + binary_mantissa, binary_exponent, negative); + } + }(f, mantissa, static_cast<::std::int_least32_t>(exponent), sign); return ::fast_io::details::print_rsv_fp_precision_decision_impl< flt, comma, uppercase_e, mt, precision_mode, rounding, json_float>( iter, m10, e10, precision, sign); From 5c7b7495e02e5be6dd01c242867204d1b86dd7e2 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 20:39:53 +0800 Subject: [PATCH 30/45] Add bfloat16 high fallback support in dragonbox implementation - Introduced new structures and functions for handling bfloat16 high fallback scenarios in floating-point operations. - Enhanced the dragonbox implementation with fallback mechanisms for nearest-to-even rounding, improving accuracy for specific exponent and mantissa combinations. - Refactored existing functions to integrate the new fallback logic, ensuring better performance and maintainability in floating-point calculations. --- .../include/fast_io_unit/floating/roundtrip.h | 202 +++++++++++++++++- 1 file changed, 193 insertions(+), 9 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h index e80cecf9c..d03f44c7f 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h @@ -1488,19 +1488,182 @@ inline constexpr void dragonbox_shorten_decimal_to_target( } } +template <::std::size_t n> +struct dragonbox_bfloat16_high_fallback_table +{ + ::std::uint_least16_t values[n]{}; +}; + +inline constexpr ::std::int_least32_t dragonbox_bfloat16_high_fallback_min_exponent{244}; +inline constexpr ::std::int_least32_t dragonbox_bfloat16_high_fallback_max_exponent{254}; +inline constexpr ::std::uint_least32_t dragonbox_bfloat16_high_fallback_exponent_count{ + static_cast<::std::uint_least32_t>(dragonbox_bfloat16_high_fallback_max_exponent - + dragonbox_bfloat16_high_fallback_min_exponent + 1)}; +inline constexpr ::std::uint_least32_t dragonbox_bfloat16_mantissa_count{128u}; +inline constexpr ::std::int_least32_t dragonbox_bfloat16_high_fallback_e10_bias{33}; +inline constexpr ::std::uint_least32_t dragonbox_bfloat16_high_fallback_m10_mask{0x0FFFu}; + +template +[[nodiscard]] inline constexpr dragonbox_bfloat16_high_fallback_table< + dragonbox_bfloat16_high_fallback_exponent_count * dragonbox_bfloat16_mantissa_count> +dragonbox_make_bfloat16_high_fallback_table() noexcept +{ + dragonbox_bfloat16_high_fallback_table + table; + for (::std::int_least32_t exponent{dragonbox_bfloat16_high_fallback_min_exponent}; + exponent <= dragonbox_bfloat16_high_fallback_max_exponent; ++exponent) + { + for (::std::uint_least32_t mantissa{}; mantissa != dragonbox_bfloat16_mantissa_count; ++mantissa) + { + auto [m10, e10] = ::fast_io::details::dragonbox_impl< + float, ::fast_io::manipulators::floating_rounding::nearest_to_even>( + static_cast<::fast_io::details::iec559_traits::mantissa_type>(mantissa << 16u), + static_cast<::std::int_least32_t>(exponent), false); + ::fast_io::details::dragonbox_shorten_decimal_to_target< + flt, ::fast_io::manipulators::floating_rounding::nearest_to_even>( + m10, e10, static_cast::mantissa_type>(mantissa), + exponent, false); + table.values[static_cast<::std::uint_least32_t>(exponent - dragonbox_bfloat16_high_fallback_min_exponent) * + dragonbox_bfloat16_mantissa_count + + mantissa] = static_cast<::std::uint_least16_t>( + (static_cast<::std::uint_least32_t>(e10 - dragonbox_bfloat16_high_fallback_e10_bias) << 12u) | m10); + } + } + return table; +} + +template +inline constexpr auto dragonbox_bfloat16_high_fallback_nearest_to_even_table{ + ::fast_io::details::dragonbox_make_bfloat16_high_fallback_table()}; + +template +[[nodiscard]] inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> +dragonbox_bfloat16_high_fallback_nearest_to_even( + typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2) noexcept +{ + using decimal_type = ::fast_io::details::dragonbox_decimal_mantissa_type; + auto const packed{ + ::fast_io::details::dragonbox_bfloat16_high_fallback_nearest_to_even_table.values + [static_cast<::std::uint_least32_t>(e2 - dragonbox_bfloat16_high_fallback_min_exponent) * + dragonbox_bfloat16_mantissa_count + + static_cast<::std::uint_least32_t>(m2)]}; + return {static_cast(packed & dragonbox_bfloat16_high_fallback_m10_mask), + static_cast<::std::int_least32_t>(dragonbox_bfloat16_high_fallback_e10_bias + + static_cast<::std::int_least32_t>(packed >> 12u))}; +} + +template +[[nodiscard]] inline constexpr punning_result dragonbox_narrow_float_punned( + flt f, typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept +{ + using trait = ::fast_io::details::iec559_traits; + if constexpr (trait::mbits == 7u && trait::ebits == 8u) + { + return {static_cast<::fast_io::details::iec559_traits::mantissa_type>( + static_cast<::std::uint_least32_t>(m2) << 16u), + static_cast<::std::uint_least32_t>(e2), negative}; + } + else if constexpr (trait::mbits == 10u && trait::ebits == 5u) + { + if (e2 != 0) + { + return {static_cast<::fast_io::details::iec559_traits::mantissa_type>( + static_cast<::std::uint_least32_t>(m2) << 13u), + static_cast<::std::uint_least32_t>(e2 + 112), negative}; + } + } + return get_punned_result(static_cast(f)); +} + template +#if __has_cpp_attribute(__gnu__::__cold__) +[[__gnu__::__cold__]] +#endif +#if __has_cpp_attribute(__gnu__::__noinline__) +[[__gnu__::__noinline__]] +#endif inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_impl_narrow_from_float(flt f, typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2, bool negative) noexcept { - auto [float_mantissa, float_exponent, float_sign] = get_punned_result(static_cast(f)); - (void)float_sign; + auto [float_mantissa, float_exponent, float_sign] = + ::fast_io::details::dragonbox_narrow_float_punned(f, m2, e2, negative); auto [m10, e10] = ::fast_io::details::dragonbox_impl( - float_mantissa, static_cast<::std::int_least32_t>(float_exponent), negative); + float_mantissa, static_cast<::std::int_least32_t>(float_exponent), float_sign); ::fast_io::details::dragonbox_shorten_decimal_to_target(m10, e10, m2, e2, negative); return {m10, e10}; } +template +[[nodiscard]] inline constexpr bool dragonbox_narrow_raw_candidate_needs_fallback( + typename iec559_traits::mantissa_type m2, ::std::int_least32_t e2) noexcept +{ + using trait = ::fast_io::details::iec559_traits; + if constexpr (trait::mbits == 10u && trait::ebits == 5u) + { + return m2 == 0u && (e2 == 8 || e2 == 9); + } + else if constexpr (trait::mbits == 7u && trait::ebits == 8u) + { + if (244 <= e2) + { + return true; + } + if (m2 != 0u) + { + return false; + } + switch (e2) + { + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 21: + case 44: + case 48: + case 49: + case 50: + case 51: + case 62: + case 63: + case 64: + case 68: + case 92: + case 93: + case 94: + case 107: + case 108: + case 109: + case 110: + case 136: + case 137: + case 157: + case 164: + case 187: + case 188: + case 189: + case 190: + case 191: + case 211: + case 223: + case 224: + return true; + default: + return false; + } + } + else + { + return true; + } +} + template inline constexpr m10_result<::fast_io::details::dragonbox_decimal_mantissa_type> dragonbox_impl_narrow_hybrid(flt f, typename iec559_traits::mantissa_type m2, @@ -1508,18 +1671,39 @@ dragonbox_impl_narrow_hybrid(flt f, typename iec559_traits::mantissa_type m { if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_even) { + if constexpr (::fast_io::details::iec559_traits::mbits == 7u && + ::fast_io::details::iec559_traits::ebits == 8u) + { + if (dragonbox_bfloat16_high_fallback_min_exponent <= e2 && + e2 <= dragonbox_bfloat16_high_fallback_max_exponent) [[unlikely]] + { + return ::fast_io::details::dragonbox_bfloat16_high_fallback_nearest_to_even(m2, e2); + } + } + if constexpr (::fast_io::details::iec559_traits::mbits == 10u && + ::fast_io::details::iec559_traits::ebits == 5u) + { + if (m2 == 0u) + { + if (e2 == 8) + { + return {7812u, -6}; + } + if (e2 == 9) + { + return {1563u, -5}; + } + } + } auto direct{::fast_io::details::dragonbox_main(m2, e2)}; - if (direct.m10) + if (direct.m10 && + !::fast_io::details::dragonbox_narrow_raw_candidate_needs_fallback(m2, e2)) { auto [trimmed, zeroes] = ::fast_io::bitops::rtz_iec559(direct.m10); auto const trimmed_e10{ static_cast<::std::int_least32_t>(direct.e10 + static_cast<::std::int_least32_t>( static_cast<::std::uint_least32_t>(zeroes)))}; - if (::fast_io::details::dragonbox_decimal_printable_roundtrips_to( - trimmed, trimmed_e10, m2, e2, negative)) - { - return {trimmed, trimmed_e10}; - } + return {trimmed, trimmed_e10}; } } else From bc338e6c46354d2839b77bdccf4ebb39691eef18 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 21:34:31 +0800 Subject: [PATCH 31/45] Enhance floating-point rounding logic in fast_io - Refactored rounding conditions in decfloat and roundtrip implementations to improve clarity and accuracy. - Introduced additional checks for mantissa adjustments based on rounding rules, ensuring correct handling of edge cases. - Updated the logic for handling zero significands and exponent limits, enhancing overall performance and maintainability in floating-point operations. --- .../include/fast_io_unit/floating/decfloat.h | 44 +++++++++++++++++-- .../include/fast_io_unit/floating/roundtrip.h | 44 +++++++++++++++++-- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 317a6355d..a9a84e5f3 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -604,10 +604,16 @@ scan_decfloat_round_mantissa(bool negative, ::std::uint_least64_t mantissa, bool } else { - if (((mantissa & 1u) != 0u || has_tail) && - ::fast_io::details::scan_decfloat_directed_round_up(negative)) + if (::fast_io::details::scan_decfloat_directed_round_up(negative)) { - ++mantissa; + if ((mantissa & 1u) != 0u) + { + ++mantissa; + } + else if (has_tail) + { + mantissa += ::std::uint_least64_t{2u}; + } } } return mantissa >> 1u; @@ -650,13 +656,35 @@ template (trait::mbits)}; constexpr auto minimum_exponent{::fast_io::details::scan_decfloat_minimum_exponent()}; constexpr auto infinite_power{::fast_io::details::scan_decfloat_infinite_power()}; - if (significand == 0 || exponent < ::fast_io::details::scan_decfloat_smallest_power10()) + constexpr auto max_finite_mantissa{(::std::uint_least64_t{1u} << mantissa_explicit_bits) - 1u}; + if (significand == 0) { answer = {}; return true; } + if (exponent < ::fast_io::details::scan_decfloat_smallest_power10()) + { + if constexpr (!::fast_io::details::scan_decfloat_nearest_rounding) + { + if (::fast_io::details::scan_decfloat_directed_round_up(negative)) + { + answer = {.mantissa = 1u, .power2 = 0}; + return true; + } + } + answer = {}; + return true; + } if (exponent > ::fast_io::details::scan_decfloat_largest_power10()) { + if constexpr (!::fast_io::details::scan_decfloat_nearest_rounding) + { + if (!::fast_io::details::scan_decfloat_directed_round_up(negative)) + { + answer = {.mantissa = max_finite_mantissa, .power2 = infinite_power - 1}; + return true; + } + } answer = {.mantissa = 0, .power2 = infinite_power}; return true; } @@ -735,6 +763,14 @@ template = infinite_power) { + if constexpr (!::fast_io::details::scan_decfloat_nearest_rounding) + { + if (!::fast_io::details::scan_decfloat_directed_round_up(negative)) + { + answer = {.mantissa = max_finite_mantissa, .power2 = infinite_power - 1}; + return true; + } + } answer = {.mantissa = 0, .power2 = infinite_power}; return true; } diff --git a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h index d03f44c7f..5350954a2 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h @@ -1115,10 +1115,16 @@ dragonbox_decimal_round_mantissa(bool negative, ::std::uint_least64_t mantissa, } else { - if (((mantissa & 1u) != 0u || has_tail) && - ::fast_io::details::floating_rounding_directed_round_up(negative)) + if (::fast_io::details::floating_rounding_directed_round_up(negative)) { - ++mantissa; + if ((mantissa & 1u) != 0u) + { + ++mantissa; + } + else if (has_tail) + { + mantissa += ::std::uint_least64_t{2u}; + } } } return mantissa >> 1u; @@ -1149,13 +1155,35 @@ dragonbox_decimal_compute_adjusted(::std::int_least64_t exponent, ::std::uint_le constexpr auto largest_power10{use_binary32_bounds ? 38 : 308}; constexpr auto min_round_to_even_power10{use_binary32_bounds ? -17 : -4}; constexpr auto max_round_to_even_power10{use_binary32_bounds ? 10 : 23}; - if (significand == 0 || exponent < smallest_power10) + constexpr auto max_finite_mantissa{(::std::uint_least64_t{1u} << mantissa_explicit_bits) - 1u}; + if (significand == 0) { answer = {}; return true; } + if (exponent < smallest_power10) + { + if constexpr (!::fast_io::details::floating_rounding_is_nearest) + { + if (::fast_io::details::floating_rounding_directed_round_up(negative)) + { + answer = {.mantissa = 1u, .power2 = 0}; + return true; + } + } + answer = {}; + return true; + } if (exponent > largest_power10) { + if constexpr (!::fast_io::details::floating_rounding_is_nearest) + { + if (!::fast_io::details::floating_rounding_directed_round_up(negative)) + { + answer = {.mantissa = max_finite_mantissa, .power2 = infinite_power - 1}; + return true; + } + } answer = {.mantissa = 0, .power2 = infinite_power}; return true; } @@ -1206,6 +1234,14 @@ dragonbox_decimal_compute_adjusted(::std::int_least64_t exponent, ::std::uint_le mantissa &= ~(::std::uint_least64_t{1} << mantissa_explicit_bits); if (power2 >= infinite_power) { + if constexpr (!::fast_io::details::floating_rounding_is_nearest) + { + if (!::fast_io::details::floating_rounding_directed_round_up(negative)) + { + answer = {.mantissa = max_finite_mantissa, .power2 = infinite_power - 1}; + return true; + } + } answer = {.mantissa = 0, .power2 = infinite_power}; return true; } From dde05b93de7431c10a89946dbf0786072f2bfcca Mon Sep 17 00:00:00 2001 From: MacroModel Date: Wed, 8 Jul 2026 21:51:21 +0800 Subject: [PATCH 32/45] Enhance scanning capabilities in fast_io - Introduced new concepts for handling EOF scenarios in context scanning, allowing for rewind capabilities when a partial prefix is invalid. - Added functions to manage EOF rewind size, improving the robustness of scanning operations. - Updated existing scanning logic to incorporate new EOF handling mechanisms, enhancing overall performance and maintainability in parsing operations. --- .../fast_io_core_impl/concepts/operation.h | 3 + .../operations/scan_freestanding.h | 41 +++++++++--- .../include/fast_io_unit/floating/decfloat.h | 62 ++++++++++++++++--- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index 9062af6e8..6136b2cb0 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -55,6 +55,9 @@ concept contiguous_scannable = requires(char_type const *begin, char_type const /// @param your_context_type& the context object /// @param T the object to be scanned, can be any passing style /// @return ::fast_io::parse_code a parse code indicating parsing state +/// @note If EOF makes a partial prefix invalid, a context scanner may have +/// already consumed that prefix. Scanners that can rewind within the +/// current buffer may additionally provide scan_context_eof_rewind_size. template concept context_scannable = requires(char_type const *begin, char_type const *end, T t) { requires requires( diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/scan_freestanding.h b/third-parties/fast_io/include/fast_io_core_impl/operations/scan_freestanding.h index fe9bfe5cc..7530fddcb 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/scan_freestanding.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/scan_freestanding.h @@ -5,6 +5,32 @@ namespace fast_io namespace details { + +template +concept scan_context_eof_rewindable = requires(state_type &state, P arg) { + { + scan_context_eof_rewind_size(io_reserve_type, state, arg) + } -> ::std::convertible_to<::std::size_t>; +}; + +template +inline constexpr void scan_context_eof_rewind_if_available(input in, state_type &state, P arg, + iter_type chunk_begin, iter_type chunk_end) +{ + using char_type = typename input::input_char_type; + if constexpr (::fast_io::details::scan_context_eof_rewindable) + { + auto const rewind{static_cast<::std::size_t>( + scan_context_eof_rewind_size(io_reserve_type, state, arg))}; + if (rewind) + { + auto const chunk_size{static_cast<::std::size_t>(chunk_end - chunk_begin)}; + auto const rewinded{rewind < chunk_size ? chunk_end - rewind : chunk_begin}; + ibuffer_set_curr(in, rewinded); + } + } +} + #if 0 template #if __has_cpp_attribute(__gnu__::__cold__) @@ -72,6 +98,7 @@ inline constexpr bool scan_context_status_impl(input in, P arg) { break; } + ::fast_io::details::scan_context_eof_rewind_if_available(in, state, arg, curr, end); throw_parse_code(ec); } } @@ -256,11 +283,7 @@ template auto curr{ibuffer_curr(in)}; auto end{ibuffer_end(in)}; auto [it, ec] = scan_contiguous_define(io_reserve_type, curr, end, arg); - if (it == end) - { - return scan_context_status_impl(in, arg); - } - else + if (ec == parse_code::ok && it != end) { if constexpr (::std::same_as) { @@ -270,12 +293,9 @@ template { ibuffer_set_curr(in, it - curr + curr); } - if (ec != parse_code::ok) - { - throw_parse_code(ec); - } + return true; } - return true; + return scan_context_status_impl(in, arg); } else if constexpr (context_scannable) { @@ -311,6 +331,7 @@ template { break; } + ::fast_io::details::scan_context_eof_rewind_if_available(in, state, arg, curr, end); throw_parse_code(ec); } } diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index a9a84e5f3..c4bbbaccf 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -2274,6 +2274,13 @@ scan_decfloat_exponent(char_type const *first, char_type const *last, ::std::int return ::fast_io::details::scan_hexfloat_exponent(first, last, exponent); } +template <::std::integral char_type> +[[nodiscard]] inline constexpr bool scan_decfloat_special_start_char(char_type ch) noexcept +{ + return ::fast_io::details::scan_hexfloat_caseless_equal(ch) || + ::fast_io::details::scan_hexfloat_caseless_equal(ch); +} + template [[nodiscard]] inline constexpr ::fast_io::parse_code scan_decfloat_assign_adjusted(T &value, bool negative, ::std::uint_least64_t significand, @@ -2796,12 +2803,16 @@ scan_decfloat_contiguous_define_impl(char_type const *begin, char_type const *en } } - auto const special_result{ - ::fast_io::details::scan_hexfloat_special_value( - first, end, negative, value)}; - if (special_result.matched) + if (!numeric_candidate && first != end && + ::fast_io::details::scan_decfloat_special_start_char(*first)) { - return {special_result.iter, special_result.code}; + auto const special_result{ + ::fast_io::details::scan_hexfloat_special_value( + first, end, negative, value)}; + if (special_result.matched) + { + return {special_result.iter, special_result.code}; + } } ::fast_io::details::scan_decfloat_significand_state significand_state; @@ -3223,9 +3234,13 @@ scan_decfloat_context_numeric_define(::fast_io::details::scan_decfloat_context( - state, first, end, value, precision); + if (::fast_io::details::scan_decfloat_special_start_char(*first)) + { + state.phase = ::fast_io::details::scan_decfloat_context_phase::special; + return ::fast_io::details::scan_decfloat_context_special_define( + state, first, end, value, precision); + } + return {first, ::fast_io::parse_code::invalid}; } if (first != end && *first == dot) { @@ -3424,6 +3439,21 @@ scan_context_eof_define(io_reserve_type_t(state, value.reference); } +template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, + details::scan_decfloat_supported_floating_point T> + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::std::size_t +scan_context_eof_rewind_size(io_reserve_type_t>, + ::fast_io::details::scan_decfloat_context &state, + ::fast_io::manipulators::scalar_manip_t) noexcept +{ + if (state.phase == ::fast_io::details::scan_decfloat_context_phase::special) + { + return state.special_buffer.size; + } + return 0u; +} + template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, details::scan_decfloat_supported_floating_point T> requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) @@ -3461,6 +3491,22 @@ scan_context_eof_define(io_reserve_type_t + requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat) +inline constexpr ::std::size_t +scan_context_eof_rewind_size(io_reserve_type_t>, + ::fast_io::details::scan_decfloat_context &state, + ::fast_io::manipulators::scalar_manip_precision_t) noexcept +{ + if (state.phase == ::fast_io::details::scan_decfloat_context_phase::special) + { + return state.special_buffer.size; + } + return 0u; +} + template <::std::integral char_type, ::fast_io::manipulators::scalar_flags flags, details::my_floating_point T> requires(flags.floating != ::fast_io::manipulators::floating_format::hexfloat && !details::scan_decfloat_supported_floating_point) From a325471f43fab0aaa099f17eea7b3ef038bd17c4 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 00:32:16 +0800 Subject: [PATCH 33/45] Refactor floating precision and print semantics in fast_io - Updated floating precision enumerations to correct naming conventions for trailing zero preservation. - Enhanced print semantic handling by introducing static assertions for raw character and pointer types, improving type safety during print operations. - Added new utility functions for checking raw print arguments, ensuring better handling of character and pointer types in print functions. - Refactored existing print functions to incorporate new static assertions, enhancing maintainability and clarity in error handling. --- .../include/fast_io_core_impl/integers/impl.h | 104 +++++++++- .../fast_io_core_impl/manipulators/traits.h | 3 +- .../printimpl/print_freestanding_cxx20.h | 31 ++- .../fast_io/include/fast_io_legacy_impl/io.h | 186 ++++++++++++++++-- .../include/fast_io_unit/floating/roundtrip.h | 2 +- 5 files changed, 301 insertions(+), 25 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index d9f466668..01e24d501 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -65,8 +65,8 @@ enum class floating_precision : ::std::uint_least8_t { significant, fractional, - significant_preserve_tailing_zero, - fractional_preserve_tailing_zero + significant_preserve_trailing_zero, + fractional_preserve_trailing_zero }; enum class floating_rounding : ::std::uint_least8_t @@ -378,17 +378,17 @@ inline constexpr ::fast_io::manipulators::scalar_flags floating_precision_roundi template <::fast_io::manipulators::floating_precision precision> inline constexpr bool floating_precision_is_significant{ precision == ::fast_io::manipulators::floating_precision::significant || - precision == ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero}; + precision == ::fast_io::manipulators::floating_precision::significant_preserve_trailing_zero}; template <::fast_io::manipulators::floating_precision precision> inline constexpr bool floating_precision_is_fractional{ precision == ::fast_io::manipulators::floating_precision::fractional || - precision == ::fast_io::manipulators::floating_precision::fractional_preserve_tailing_zero}; + precision == ::fast_io::manipulators::floating_precision::fractional_preserve_trailing_zero}; template <::fast_io::manipulators::floating_precision precision> inline constexpr bool floating_precision_preserves_trailing_zero{ - precision == ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero || - precision == ::fast_io::manipulators::floating_precision::fractional_preserve_tailing_zero}; + precision == ::fast_io::manipulators::floating_precision::significant_preserve_trailing_zero || + precision == ::fast_io::manipulators::floating_precision::fractional_preserve_trailing_zero}; inline constexpr ::fast_io::manipulators::scalar_flags set_json_float_flag(::fast_io::manipulators::scalar_flags flags, bool json_float) noexcept @@ -918,6 +918,14 @@ inline constexpr auto base(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache>(t); } +template <::std::size_t bs, bool shbase = false, bool full = false, bool oct_c2y = false, typename scalar_type> + requires((2 <= bs && bs <= 36) && (::fast_io::details::character_integral)) +inline constexpr auto base(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache>(t); +} + template <::std::size_t bs, bool shbase = false, bool full = false, bool oct_c2y = false, typename scalar_type> requires((2 <= bs && bs <= 36) && (::fast_io::details::scalar_integrals)) inline constexpr auto baseupper(scalar_type t) noexcept @@ -926,6 +934,14 @@ inline constexpr auto baseupper(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache>(t); } +template <::std::size_t bs, bool shbase = false, bool full = false, bool oct_c2y = false, typename scalar_type> + requires((2 <= bs && bs <= 36) && (::fast_io::details::character_integral)) +inline constexpr auto baseupper(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto hex(scalar_type t) noexcept @@ -934,6 +950,14 @@ inline constexpr auto hex(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<16, false, shbase, full>>(t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto hex(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<16, false, shbase, full>>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto hexupper(scalar_type t) noexcept @@ -942,6 +966,14 @@ inline constexpr auto hexupper(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<16, true, shbase, full>>(t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto hexupper(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<16, true, shbase, full>>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto hex0x(scalar_type t) noexcept @@ -950,6 +982,14 @@ inline constexpr auto hex0x(scalar_type t) noexcept t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto hex0x(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache<::fast_io::details::base_mani_flags_cache<16, false, true, full>>( + t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto hex0xupper(scalar_type t) noexcept @@ -958,6 +998,14 @@ inline constexpr auto hex0xupper(scalar_type t) noexcept t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto hex0xupper(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache<::fast_io::details::base_mani_flags_cache<16, true, true, full>>( + t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto uhexfull(scalar_type t) noexcept @@ -1081,7 +1129,7 @@ inline constexpr auto uhexupperfull(scalar_type t) noexcept // Character code units are deliberately separated from the default numeric // print path even though they satisfy ::std::integral. Use chvw for a character -// view and char_as_integer when the numeric value of a character code unit is intended. +// view, and dec when the numeric value of a code unit is intended. template requires(::fast_io::details::scalar_integrals) inline constexpr auto dec(scalar_type t) noexcept @@ -1092,7 +1140,7 @@ inline constexpr auto dec(scalar_type t) noexcept template requires(::fast_io::details::character_integral) -inline constexpr auto char_as_integer(scalar_type t) noexcept +inline constexpr auto dec(scalar_type t) noexcept { return ::fast_io::details::scalar_flags_int_cache< ::fast_io::details::base_mani_flags_cache<10, false, shbase, full>>(t); @@ -1106,6 +1154,14 @@ inline constexpr auto oct(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<8, uppercase_showbase, shbase, full, false, false, oct_c2y>>(t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto oct(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<8, uppercase_showbase, shbase, full, false, false, oct_c2y>>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto oct0(scalar_type t) noexcept @@ -1114,6 +1170,14 @@ inline constexpr auto oct0(scalar_type t) noexcept t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto oct0(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache<::fast_io::details::base_mani_flags_cache<8, false, true, full>>( + t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto oct0o(scalar_type t) noexcept @@ -1122,6 +1186,14 @@ inline constexpr auto oct0o(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<8, false, true, full, false, false, true>>(t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto oct0o(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<8, false, true, full, false, false, true>>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto bin(scalar_type t) noexcept @@ -1130,6 +1202,14 @@ inline constexpr auto bin(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<2, false, shbase, full>>(t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto bin(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<2, false, shbase, full>>(t); +} + template requires(::fast_io::details::scalar_integrals) inline constexpr auto bin0b(scalar_type t) noexcept @@ -1138,6 +1218,14 @@ inline constexpr auto bin0b(scalar_type t) noexcept t); } +template + requires(::fast_io::details::character_integral) +inline constexpr auto bin0b(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache<::fast_io::details::base_mani_flags_cache<2, false, true, full>>( + t); +} + template requires(((2 <= flags.base && flags.base <= 36 && (::fast_io::details::scalar_integrals)) || (flags.base == 10 && ::fast_io::details::my_floating_point))) diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h index 4cf0fd4e4..f476d8543 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h @@ -249,7 +249,8 @@ struct print_semantic_precise_size_ok_impl inline constexpr bool print_semantic_bounded_leaf_size_ok_v = ::fast_io::details::decay::print_semantic_precise_leaf_size_ok_v || - ::fast_io::dynamic_reserve_printable>; + ::fast_io::dynamic_reserve_printable> || + ::fast_io::reserve_printable>; template <::std::integral char_type, typename T> struct print_semantic_bounded_size_ok_impl diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 92a884b0c..043838248 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -3358,6 +3358,20 @@ struct print_semantic_lvalue_prefix_continuation } }; +template +[[nodiscard]] inline constexpr ::fast_io::containers::tuple +print_semantic_pack_pointer_tuple(Args... args) noexcept +{ +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + return ::fast_io::containers::tuple{args...}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +} + /// @brief Completes semantic pack expansion when no input arguments remain. /// @tparam already_forwarded true when non-pack inputs were already print-forwarded /// @tparam char_type the print character type @@ -3454,7 +3468,9 @@ struct print_semantic_pack_expand_middle_continuation return operator_impl( ::fast_io::details::decay::print_semantic_pack_expand_tail_continuation< already_forwarded, char_type, continuation, ExpandedPackArgs...>{ - contptr, {__builtin_addressof(expanded_pack_args)...}}, + contptr, + ::fast_io::details::decay::print_semantic_pack_pointer_tuple( + __builtin_addressof(expanded_pack_args)...)}, ::std::make_index_sequence{}); } }; @@ -3509,7 +3525,8 @@ inline constexpr decltype(auto) print_semantic_pack_expand(continuation &&cont, ::std::forward(t), ::fast_io::details::decay::print_semantic_pack_expand_initial_continuation< already_forwarded, char_type, continuation, Args...>{__builtin_addressof(cont), - {__builtin_addressof(args)...}}); + ::fast_io::details::decay::print_semantic_pack_pointer_tuple( + __builtin_addressof(args)...)}); } else if constexpr (already_forwarded) { @@ -4361,6 +4378,11 @@ inline constexpr char_type *print_semantic_emit_unchecked_leaf(char_type *iter, // Dynamic reserve leaves have already been admitted by a bounded materialization path. return print_reserve_define(::fast_io::io_reserve_type, iter, ::std::forward(t)); } + else if constexpr (::fast_io::reserve_printable) + { + // Static reserve leaves have already been admitted by a bounded materialization path. + return print_reserve_define(::fast_io::io_reserve_type, iter, ::std::forward(t)); + } else if constexpr (::fast_io::scatter_printable) { // Scatter leaves copy their single contiguous range into the unchecked output buffer. @@ -4654,6 +4676,11 @@ inline constexpr ::std::size_t print_semantic_bounded_size_leaf(T &&t) // Dynamic reserve leaves report the maximum buffer size needed for this object. return print_reserve_size(::fast_io::io_reserve_type, ::std::forward(t)); } + else if constexpr (::fast_io::reserve_printable) + { + // Static reserve leaves expose a compile-time object-independent upper bound. + return print_reserve_size(::fast_io::io_reserve_type); + } } /// @brief Continuation that accumulates bounded sizes for expanded semantic pack elements. diff --git a/third-parties/fast_io/include/fast_io_legacy_impl/io.h b/third-parties/fast_io/include/fast_io_legacy_impl/io.h index 451a0323d..5376f3c74 100644 --- a/third-parties/fast_io/include/fast_io_legacy_impl/io.h +++ b/third-parties/fast_io/include/fast_io_legacy_impl/io.h @@ -9,6 +9,82 @@ namespace fast_io { +namespace details +{ + +template +inline constexpr bool raw_character_scalar_print_arg{ + ::fast_io::details::character_integral<::std::remove_cvref_t>}; + +template +using raw_character_pointer_pointee_t = + ::std::remove_cv_t<::std::remove_pointer_t<::std::remove_cvref_t>>; + +template +inline constexpr bool raw_character_pointer_print_arg{ + ::std::is_pointer_v<::std::remove_cvref_t> && + ::fast_io::details::character_integral>}; + +template +inline constexpr bool raw_non_character_pointer_print_arg{ + ::std::is_pointer_v<::std::remove_cvref_t> && (!raw_character_pointer_print_arg)}; + +template +inline constexpr bool has_raw_character_scalar_print_arg{(... || raw_character_scalar_print_arg)}; + +template +inline constexpr bool has_raw_character_pointer_print_arg{(... || raw_character_pointer_print_arg)}; + +template +inline constexpr bool has_raw_non_character_pointer_print_arg{(... || raw_non_character_pointer_print_arg)}; + +template +inline constexpr bool has_raw_print_arg{has_raw_character_scalar_print_arg || + has_raw_character_pointer_print_arg || + has_raw_non_character_pointer_print_arg}; + +template +inline constexpr void print_raw_character_scalar_static_assert() noexcept +{ + static_assert(!has_raw_character_scalar, + "fast_io: raw character scalar is ambiguous. Use mnp::chvw(ch) for character text or " + "mnp::dec(ch) for its code value."); +} + +template +inline constexpr void print_raw_character_pointer_static_assert() noexcept +{ + static_assert(!has_raw_character_pointer, + "fast_io: raw character pointer is ambiguous. Use mnp::pointervw(ptr) for pointer value or " + "mnp::os_c_str(ptr) for OS/C string text."); +} + +template +inline constexpr void print_raw_non_character_pointer_static_assert() noexcept +{ + static_assert(!has_raw_non_character_pointer, + "fast_io: raw pointer is not printable directly. Use mnp::pointervw(ptr) for pointer value."); +} + +template +inline constexpr void print_raw_static_assert() noexcept +{ + if constexpr (has_raw_character_scalar_print_arg) + { + print_raw_character_scalar_static_assert>(); + } + else if constexpr (has_raw_character_pointer_print_arg) + { + print_raw_character_pointer_static_assert>(); + } + else if constexpr (has_raw_non_character_pointer_print_arg) + { + print_raw_non_character_pointer_static_assert>(); + } +} + +} // namespace details + inline namespace io { @@ -37,8 +113,15 @@ inline constexpr void print(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for print on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for print on the provided output stream"); + } } else { @@ -51,9 +134,16 @@ inline constexpr void print(T &&t, Args &&...args) } else { + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { // clang-format off static_assert(type_ok, "some types are not printable for print on default C's stdout"); // clang-format on + } } } #else @@ -91,8 +181,15 @@ inline constexpr void println(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for println on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for println on the provided output stream"); + } } else { @@ -105,7 +202,14 @@ inline constexpr void println(T &&t, Args &&...args) } else { - static_assert(type_ok, "some types are not printable for print on default C's stdout"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(type_ok, "some types are not printable for print on default C's stdout"); + } } } #else @@ -137,8 +241,15 @@ inline constexpr void perr(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for perr on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for perr on the provided output stream"); + } } else { @@ -151,9 +262,16 @@ inline constexpr void perr(T &&t, Args &&...args) } else { + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { // clang-format off static_assert(type_ok, "some types are not printable for perr on native err"); // clang-format on + } } } #else @@ -185,8 +303,15 @@ inline constexpr void perrln(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for perrln on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for perrln on the provided output stream"); + } } else { @@ -199,9 +324,16 @@ inline constexpr void perrln(T &&t, Args &&...args) } else { + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { // clang-format off static_assert(type_ok, "some types are not printable for perrln on native err"); // clang-format on + } } } #else @@ -274,8 +406,15 @@ inline constexpr void debug_print(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for debug_print on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for debug_print on the provided output stream"); + } } else { @@ -288,9 +427,16 @@ inline constexpr void debug_print(T &&t, Args &&...args) } else { + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { // clang-format off static_assert(type_ok, "some types are not printable for debug_print on native out"); // clang-format on + } } } #else @@ -322,8 +468,15 @@ inline constexpr void debug_println(T &&t, Args &&...args) constexpr bool device_ok{::fast_io::operations::defines::has_output_or_io_stream_ref_define}; if constexpr (device_ok) { - static_assert(device_and_type_ok, - "some types are not printable for debug_println on the provided output stream"); + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { + static_assert(device_and_type_ok, + "some types are not printable for debug_println on the provided output stream"); + } } else { @@ -336,9 +489,16 @@ inline constexpr void debug_println(T &&t, Args &&...args) } else { + if constexpr (::fast_io::details::has_raw_print_arg) + { + ::fast_io::details::print_raw_static_assert(); + } + else + { // clang-format off static_assert(type_ok, "some types are not printable for debug_println on native out"); // clang-format on + } } } #else diff --git a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h index 5350954a2..bbbeaa1ff 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/roundtrip.h @@ -2666,7 +2666,7 @@ inline constexpr char_type *print_rsvflt_precision_define_impl(char_type *iter, return iter; } else if constexpr (precision_mode == - ::fast_io::manipulators::floating_precision::significant_preserve_tailing_zero) + ::fast_io::manipulators::floating_precision::significant_preserve_trailing_zero) { *iter = char_literal_v; ++iter; From 92eac1e9e75bfb3c0389d2199097168cf96d9b63 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 00:41:54 +0800 Subject: [PATCH 34/45] Enhance pointer printing semantics in fast_io - Added comments to clarify the behavior of pointer printing functions, distinguishing between object pointers, function pointers, and member pointers. - Updated the implementation to utilize the new pointer printing functions for better clarity and consistency in output. - Introduced static assertions for raw function and member pointer types to improve type safety during print operations. --- .../include/fast_io_core_impl/integers/impl.h | 5 ++ .../fast_io_core_impl/integers/pointer.h | 2 +- .../fast_io/include/fast_io_legacy_impl/io.h | 65 ++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index 01e24d501..e3d8fa455 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -1037,6 +1037,7 @@ template (!::std::is_function_v<::std::remove_cvref_t>)) inline constexpr auto pointervw(scalar_type t) noexcept { + // Object pointers and contiguous iterators: print the address value, not pointee text. return ::fast_io::details::scalar_flags_int_cache< ::fast_io::details::base_mani_flags_cache<16, uppercase, true, true, false>>(t); } @@ -1045,6 +1046,7 @@ template requires(::std::is_function_v) inline constexpr auto funcvw(scalar_type *t) noexcept { + // Function pointer entry addresses are kept separate from object/iterator addresses. return ::fast_io::details::scalar_flags_int_cache< ::fast_io::details::base_mani_flags_cache<16, uppercase, true, true, false>>(::std::bit_cast<::std::size_t>(t)); } @@ -1053,6 +1055,7 @@ template requires(::std::is_member_object_pointer_v) inline constexpr auto fieldptrvw(scalar_type t) noexcept { + // Pointer-to-member fields are ABI encodings, not object addresses. if constexpr (sizeof(t) == sizeof(::fast_io::manipulators::member_function_pointer_holder_t)) { return ::fast_io::details::scalar_flags_int_cache< @@ -1079,6 +1082,7 @@ template requires(::std::is_member_function_pointer_v && (sizeof(scalar_type) % sizeof(::std::size_t) == 0)) inline constexpr auto methodvw(scalar_type t) noexcept { + // Pointer-to-member functions may be multi-word ABI encodings. if constexpr (sizeof(scalar_type) == sizeof(::std::size_t)) { return ::fast_io::details::scalar_flags_int_cache< @@ -1754,6 +1758,7 @@ inline constexpr auto scientific_get(scalar_type &value, ::std::size_t n) noexce template <::std::integral inttype> inline constexpr ::std::remove_cvref_t bitfieldvw(inttype v) noexcept { + // Copy bit-fields into an ordinary integer value before formatting. return v; } diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h index a56083053..78e631e0a 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/pointer.h @@ -13,7 +13,7 @@ Instead, we print out its pointer value We extend print pointers to print contiguous_iterator. No we can write things like ::std::vector<::std::size_t> vec(100,2); -println("vec.begin():",fast_io::mnp::funcvw(vec.begin())," vec.end()",fast_io::mnp::funcvw(vec.end())); +println("vec.begin():",fast_io::mnp::pointervw(vec.begin())," vec.end()",fast_io::mnp::pointervw(vec.end())); */ namespace manipulators { diff --git a/third-parties/fast_io/include/fast_io_legacy_impl/io.h b/third-parties/fast_io/include/fast_io_legacy_impl/io.h index 5376f3c74..cf394aca0 100644 --- a/third-parties/fast_io/include/fast_io_legacy_impl/io.h +++ b/third-parties/fast_io/include/fast_io_legacy_impl/io.h @@ -25,9 +25,23 @@ inline constexpr bool raw_character_pointer_print_arg{ ::std::is_pointer_v<::std::remove_cvref_t> && ::fast_io::details::character_integral>}; +template +inline constexpr bool raw_function_pointer_print_arg{ + ::std::is_pointer_v<::std::remove_cvref_t> && + ::std::is_function_v>}; + template inline constexpr bool raw_non_character_pointer_print_arg{ - ::std::is_pointer_v<::std::remove_cvref_t> && (!raw_character_pointer_print_arg)}; + ::std::is_pointer_v<::std::remove_cvref_t> && (!raw_character_pointer_print_arg) && + (!raw_function_pointer_print_arg)}; + +template +inline constexpr bool raw_member_object_pointer_print_arg{ + ::std::is_member_object_pointer_v<::std::remove_cvref_t>}; + +template +inline constexpr bool raw_member_function_pointer_print_arg{ + ::std::is_member_function_pointer_v<::std::remove_cvref_t>}; template inline constexpr bool has_raw_character_scalar_print_arg{(... || raw_character_scalar_print_arg)}; @@ -35,13 +49,25 @@ inline constexpr bool has_raw_character_scalar_print_arg{(... || raw_character_s template inline constexpr bool has_raw_character_pointer_print_arg{(... || raw_character_pointer_print_arg)}; +template +inline constexpr bool has_raw_function_pointer_print_arg{(... || raw_function_pointer_print_arg)}; + template inline constexpr bool has_raw_non_character_pointer_print_arg{(... || raw_non_character_pointer_print_arg)}; +template +inline constexpr bool has_raw_member_object_pointer_print_arg{(... || raw_member_object_pointer_print_arg)}; + +template +inline constexpr bool has_raw_member_function_pointer_print_arg{(... || raw_member_function_pointer_print_arg)}; + template inline constexpr bool has_raw_print_arg{has_raw_character_scalar_print_arg || has_raw_character_pointer_print_arg || - has_raw_non_character_pointer_print_arg}; + has_raw_function_pointer_print_arg || + has_raw_non_character_pointer_print_arg || + has_raw_member_object_pointer_print_arg || + has_raw_member_function_pointer_print_arg}; template inline constexpr void print_raw_character_scalar_static_assert() noexcept @@ -66,6 +92,29 @@ inline constexpr void print_raw_non_character_pointer_static_assert() noexcept "fast_io: raw pointer is not printable directly. Use mnp::pointervw(ptr) for pointer value."); } +template +inline constexpr void print_raw_function_pointer_static_assert() noexcept +{ + static_assert(!has_raw_function_pointer, + "fast_io: raw function pointer is not printable directly. Use mnp::funcvw(fn) for function address."); +} + +template +inline constexpr void print_raw_member_object_pointer_static_assert() noexcept +{ + static_assert(!has_raw_member_object_pointer, + "fast_io: raw member object pointer is not printable directly. Use mnp::fieldptrvw(ptr) for its " + "member-pointer representation."); +} + +template +inline constexpr void print_raw_member_function_pointer_static_assert() noexcept +{ + static_assert(!has_raw_member_function_pointer, + "fast_io: raw member function pointer is not printable directly. Use mnp::methodvw(ptr) for its " + "member-pointer representation."); +} + template inline constexpr void print_raw_static_assert() noexcept { @@ -77,10 +126,22 @@ inline constexpr void print_raw_static_assert() noexcept { print_raw_character_pointer_static_assert>(); } + else if constexpr (has_raw_function_pointer_print_arg) + { + print_raw_function_pointer_static_assert>(); + } else if constexpr (has_raw_non_character_pointer_print_arg) { print_raw_non_character_pointer_static_assert>(); } + else if constexpr (has_raw_member_object_pointer_print_arg) + { + print_raw_member_object_pointer_static_assert>(); + } + else if constexpr (has_raw_member_function_pointer_print_arg) + { + print_raw_member_function_pointer_static_assert>(); + } } } // namespace details From 9636252e2cca0b1131d6b3c23efb39e27806f2a3 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 01:05:44 +0800 Subject: [PATCH 35/45] Refactor print semantic handling in fast_io - Updated print functions to utilize new type checks for forwarded arguments, enhancing type safety and clarity in bounded and precise coalescing operations. - Introduced additional logic for managing stack buffer limits, improving performance and memory efficiency during print operations. - Enhanced the handling of floating-point rounding conditions in decfloat, ensuring accurate rounding behavior based on specified manipulator settings. --- .../printimpl/print_freestanding_cxx20.h | 23 ++++++++++++++++--- .../include/fast_io_unit/floating/decfloat.h | 23 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 043838248..fcd919285 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -4806,7 +4806,8 @@ template >::value && + char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ...)) { // A bounded run can be materialized once when its upper bound fits the available coalescing space. @@ -4844,6 +4845,21 @@ inline constexpr bool print_semantic_try_bounded_coalesce(outputstmtype optstm, ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); return true; } + constexpr ::std::size_t materialize_limit{ + ::fast_io::details::decay::print_stack_buffer_max_size()}; + if constexpr (materialize_limit != 0) + { + if (required <= materialize_limit) + { + ::fast_io::details::buffer_alloc_arr_ptr buffer(required); + char_type *const first{buffer.get()}; + char_type *const last{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + first, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, first, last); + return true; + } + } } } // At least one requirement for bounded coalescing failed, so the caller must use the normal emit path. @@ -4864,7 +4880,8 @@ template >::value && + char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t>::value && ...)) { // A fully precise run can be measured before choosing a coalesced output strategy. @@ -4898,7 +4915,7 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, // A non-zero full-output threshold permits bounded stack-buffer coalescing. constexpr ::std::size_t static_total{ ::fast_io::operations::decay::print_semantic_static_precise_total_size...>()}; + ::fast_io::details::decay::print_semantic_forwarded_arg_t...>()}; if constexpr (static_total != SIZE_MAX && static_total <= threshold_chars) { // A compile-time bounded run uses an exactly-sized stack buffer and one write operation. diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index c4bbbaccf..c74a432cb 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -592,7 +592,16 @@ template <::fast_io::manipulators::floating_rounding rounding> [[nodiscard]] inline constexpr ::std::uint_least64_t scan_decfloat_round_mantissa(bool negative, ::std::uint_least64_t mantissa, bool has_tail, bool is_tie) noexcept { - if constexpr (::fast_io::details::scan_decfloat_nearest_rounding) + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_odd) + { + auto rounded_down{mantissa >> 1u}; + if (((mantissa & 1u) != 0u || has_tail) && ((rounded_down & 1u) == 0u)) + { + ++rounded_down; + } + return rounded_down; + } + else if constexpr (::fast_io::details::scan_decfloat_nearest_rounding) { if ((mantissa & 1u) != 0u) { @@ -1406,7 +1415,11 @@ scan_decfloat_assign_native_wide(T &value, bool negative, ::std::uint_least64_t bool remainder_nonzero, bool tail_nonzero) noexcept { - if constexpr (::fast_io::details::floating_rounding_is_nearest) + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_odd) + { + return (remainder_nonzero || tail_nonzero) && ((quotient & 1u) == 0u); + } + else if constexpr (::fast_io::details::floating_rounding_is_nearest) { if (twice_remainder_compare < 0) { @@ -1865,7 +1878,11 @@ scan_decfloat_decimal_round_up(bool negative, ::std::uint_least64_t rounded_down { return false; } - if constexpr (::fast_io::details::floating_rounding_is_nearest) + if constexpr (rounding == ::fast_io::manipulators::floating_rounding::nearest_to_odd) + { + return (rounded_down & 1u) == 0u; + } + else if constexpr (::fast_io::details::floating_rounding_is_nearest) { auto const half{divisor >> 1u}; if (remainder < half) From af4ce2e24718c2f4a191dac42a3bab55a66ed5c8 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 02:28:59 +0800 Subject: [PATCH 36/45] Enhance scanning and printing capabilities in fast_io - Introduced new functions for scanning hexadecimal floating-point exponents, improving parsing accuracy and handling of edge cases. - Added logic to manage scatter-printable arguments in print operations, optimizing memory usage and performance. - Enhanced existing scanning functions to support extended parsing scenarios, ensuring robust handling of special cases in floating-point contexts. --- .../integers/sto/sto_contiguous.h | 48 ++++++ .../printimpl/print_freestanding_cxx20.h | 146 ++++++++++++++++++ .../include/fast_io_unit/floating/decfloat.h | 89 ++++++++++- .../include/fast_io_unit/floating/hexfloat.h | 89 +++++++++-- 4 files changed, 361 insertions(+), 11 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h index 0347d7e93..ea4d487d8 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h @@ -593,6 +593,46 @@ inline constexpr ::fast_io::freestanding::array generate_pow_table() noexc template inline constexpr ::fast_io::freestanding::array pow_table_n{::fast_io::details::generate_pow_table()}; +template <::std::integral char_type, my_unsigned_integral T> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr parse_result +scan_int_contiguous_ascii_hex_space_part_define_impl(char_type const *first, char_type const *last, T &out) noexcept +{ + using unsigned_char_type = ::std::make_unsigned_t; + using unsigned_type = my_make_unsigned_t<::std::remove_cvref_t>; + constexpr ::std::size_t max_size{::fast_io::details::max_int_size_result}; + ::std::size_t const diff{static_cast<::std::size_t>(last - first)}; + ::std::size_t mn_val{max_size}; + if (diff < mn_val) + { + mn_val = diff; + } + auto first_phase_last{first + mn_val}; + T res{out}; + for (; first != first_phase_last; ++first) [[likely]] + { + auto const digit{::fast_io::details::sto_ascii_digit_table_lookup( + static_cast(*first))}; + if (15u < digit) [[unlikely]] + { + break; + } + res = static_cast((static_cast(res) << 4u) | static_cast(digit)); + } + if (first == last) + { + out = res; + return {first, parse_code::ok}; + } + auto ret{scan_int_contiguous_none_simd_space_part_check_overflow_impl<16, char_type, T>(first, last, res)}; + out = res; + return ret; +} + template inline parse_result runtime_scan_int_contiguous_none_simd_space_part_define_impl(char_type const *first, char_type const *last, T &out) noexcept @@ -1030,6 +1070,14 @@ scan_int_contiguous_none_simd_space_part_define_impl(char_type const *first, cha if (!__builtin_is_constant_evaluated()) #endif { + using unsigned_type = my_make_unsigned_t<::std::remove_cvref_t>; + if constexpr (base == 16 && sizeof(char_type) == sizeof(char8_t) && + !::fast_io::details::is_ebcdic && + sizeof(unsigned_type) <= sizeof(::std::uint_least64_t)) + { + return ::fast_io::details::scan_int_contiguous_ascii_hex_space_part_define_impl( + first, last, res); + } return runtime_scan_int_contiguous_none_simd_space_part_define_impl(first, last, res); } else diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index fcd919285..74f232b07 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -2001,6 +2001,147 @@ inline constexpr void print_n_scatters(basic_io_scatter_t *pscatter } } +/// @brief Computes the total character count for N scatter-printable/null arguments. +/// @tparam n the number of argument positions to consume +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param t the current argument +/// @param args the remaining arguments +/// @return ::std::size_t the combined scatter character count +template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr ::std::size_t print_n_scatter_total_size( +#if __has_cpp_attribute(maybe_unused) + [[maybe_unused]] +#endif + T t, +#if __has_cpp_attribute(maybe_unused) + [[maybe_unused]] +#endif + Args... args) +{ + if constexpr (n == 0) + { + return 0; + } + else + { + using nocvreft = ::std::remove_cvref_t; + ::std::size_t current{}; + if constexpr (!::std::same_as) + { + current = print_scatter_define(::fast_io::io_reserve_type, t).len; + } + if constexpr (n == 1) + { + return current; + } + else + { + return ::fast_io::details::intrinsics::add_or_overflow_die( + current, ::fast_io::details::decay::print_n_scatter_total_size(args...)); + } + } +} + +/// @brief Copies N scatter-printable/null arguments into a contiguous buffer. +/// @tparam n the number of argument positions to consume +/// @tparam char_type the print character type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param ptr the current output cursor +/// @param t the current argument +/// @param args the remaining arguments +/// @return char_type* one past the copied scatter output +template <::std::size_t n, ::std::integral char_type, typename T, typename... Args> +#if __has_cpp_attribute(__gnu__::__always_inline__) +[[__gnu__::__always_inline__]] +#elif __has_cpp_attribute(msvc::forceinline) +[[msvc::forceinline]] +#endif +inline constexpr char_type *print_n_scatter_materialize(char_type *ptr, +#if __has_cpp_attribute(maybe_unused) + [[maybe_unused]] +#endif + T t, +#if __has_cpp_attribute(maybe_unused) + [[maybe_unused]] +#endif + Args... args) +{ + if constexpr (n == 0) + { + return ptr; + } + else + { + using nocvreft = ::std::remove_cvref_t; + if constexpr (!::std::same_as) + { + auto scatter{print_scatter_define(::fast_io::io_reserve_type, t)}; + ptr = ::fast_io::details::decay::print_small_scatter_copy_n(scatter.base, scatter.len, ptr); + } + if constexpr (n == 1) + { + return ptr; + } + else + { + return ::fast_io::details::decay::print_n_scatter_materialize(ptr, args...); + } + } +} + +/// @brief Tries to emit a scatter-only prefix as one contiguous output range. +/// @tparam needprintlf true when the final emitted output should append a newline +/// @tparam position the number of argument positions included in the scatter prefix +/// @tparam char_type the print character type +/// @tparam outputstmtype the decayed output stream reference type +/// @tparam T the current argument type +/// @tparam Args the remaining argument types +/// @param optstm the output stream reference +/// @param t the current argument +/// @param args the remaining arguments +/// @return bool true when the prefix was emitted without building scatter descriptors +template +inline constexpr bool print_controls_scatters_try_materialize(outputstmtype optstm, T t, Args... args) +{ + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_full_output_coalesce_threshold()}; + if constexpr (threshold_chars != 0 && + ::fast_io::details::decay::print_has_direct_write_operations && + ::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + ::std::size_t const total_size{::fast_io::details::intrinsics::add_or_overflow_die( + ::fast_io::details::decay::print_n_scatter_total_size(t, args...), + static_cast<::std::size_t>(needprintlf))}; + if (total_size == 0) + { + return true; + } + if (total_size <= threshold_chars) + { + char_type buffer[threshold_chars]; + char_type *ptr{ + ::fast_io::details::decay::print_n_scatter_materialize(buffer, t, args...)}; + if constexpr (needprintlf) + { + *ptr = ::fast_io::char_literal_v; + ++ptr; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); + return true; + } + } + return false; +} + /// @brief Computes the combined run-time reserve size for dynamic reserve arguments in a prefix. /// @tparam n the number of argument positions to inspect /// @tparam char_type the print character type @@ -2493,6 +2634,11 @@ inline constexpr void print_controls_scatters(outputstmtype optstm, T t, Args... using scatter_type = ::std::conditional_t< ::fast_io::operations::decay::defines::has_any_of_write_or_seek_pwrite_bytes_operations, ::fast_io::io_scatter_t, ::fast_io::basic_io_scatter_t>; + if (::fast_io::details::decay::print_controls_scatters_try_materialize( + optstm, t, args...)) + { + return; + } if constexpr (::fast_io::details::decay::print_stack_buffer_size_within_limit) { // Small scatter descriptor runs are built on the stack and written once. diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index c74a432cb..58d15d0e9 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -2288,6 +2288,67 @@ template <::std::integral char_type> [[nodiscard]] inline constexpr ::fast_io::parse_result scan_decfloat_exponent(char_type const *first, char_type const *last, ::std::int_least64_t &exponent) noexcept { + if constexpr (sizeof(char_type) == sizeof(char8_t) && !::fast_io::details::is_ebcdic) + { +#ifdef __cpp_if_consteval + if !consteval +#else + if (!__builtin_is_constant_evaluated()) +#endif + { + auto const *const original_first{first}; + if (first == last) + { + return {first, ::fast_io::parse_code::invalid}; + } + using unsigned_char_type = ::fast_io::details::my_make_unsigned_t; + constexpr auto plus{static_cast(u8'+')}; + constexpr auto minus{static_cast(u8'-')}; + constexpr auto zero{static_cast(u8'0')}; + bool negative{}; + auto uch{static_cast(*first)}; + if (uch == minus) + { + negative = true; + ++first; + } + else if (uch == plus) + { + ++first; + } + if (first == last) + { + return {first, ::fast_io::parse_code::invalid}; + } + uch = static_cast(*first); + auto digit{static_cast(uch - zero)}; + if (10u <= digit) + { + return {first, ::fast_io::parse_code::invalid}; + } + ::std::uint_least64_t value{static_cast<::std::uint_least64_t>(digit)}; + ++first; + ::std::size_t digit_count{1u}; + for (; first != last; ++first) + { + uch = static_cast(*first); + digit = static_cast(uch - zero); + if (10u <= digit) + { + break; + } + if (digit_count == 18u) + { + return ::fast_io::details::scan_hexfloat_exponent(original_first, last, exponent); + } + value = value * 10u + static_cast<::std::uint_least64_t>(digit); + ++digit_count; + } + exponent = negative ? -static_cast<::std::int_least64_t>(value) : + static_cast<::std::int_least64_t>(value); + return {first, ::fast_io::parse_code::ok}; + } + } return ::fast_io::details::scan_hexfloat_exponent(first, last, exponent); } @@ -3078,7 +3139,7 @@ scan_decfloat_context_special_define(::fast_io::details::scan_decfloat_context( + buffer_begin, buffer_end)) + { + if (append_result.truncated) + { + return {append_result.iter, ::fast_io::parse_code::overflow}; + } + return {end, ::fast_io::parse_code::partial}; + } + value = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, append_result.iter, parse_result.iter), + ::fast_io::parse_code::ok}; } if (::fast_io::details::scan_hexfloat_special_parse_may_extend( parse_result.iter, buffer_end)) @@ -3112,11 +3185,19 @@ scan_decfloat_context_special_define(::fast_io::details::scan_decfloat_context +[[nodiscard]] inline constexpr bool +scan_hexfloat_special_end_may_extend(char_type const *first, char_type const *last) noexcept +{ + if (first == last) + { + return false; + } + auto scan{first}; + constexpr auto plus{::fast_io::char_literal_v}; + constexpr auto minus{::fast_io::char_literal_v}; + if (*scan == plus || *scan == minus) + { + ++scan; + } + auto const len{static_cast<::std::size_t>(last - scan)}; + if (len == 3u) + { + if (::fast_io::details::scan_hexfloat_caseless_equal(scan[0]) && + ::fast_io::details::scan_hexfloat_caseless_equal(scan[1]) && + ::fast_io::details::scan_hexfloat_caseless_equal(scan[2])) + { + return true; + } + if constexpr (nan_payload_scan != ::fast_io::manipulators::floating_nan_payload_scan::none) + { + if (::fast_io::details::scan_hexfloat_caseless_equal(scan[0]) && + ::fast_io::details::scan_hexfloat_caseless_equal(scan[1]) && + ::fast_io::details::scan_hexfloat_caseless_equal(scan[2])) + { + return true; + } + } + } + return false; +} + template struct scan_hexfloat_significand_state { @@ -1511,6 +1548,14 @@ struct scan_floating_context ::std::size_t size{}; }; +template <::std::integral char_type> +struct scan_floating_context_append_result +{ + char_type const *iter{}; + ::fast_io::parse_code code{}; + bool truncated{}; +}; + template <::std::integral char_type, bool noskipws> inline constexpr char_type const *scan_floating_context_skip_space(char_type const *first, char_type const *last) noexcept @@ -1546,22 +1591,24 @@ scan_floating_context_map_iter(scan_floating_context const &state, :: } template <::std::integral char_type> -[[nodiscard]] inline constexpr ::fast_io::parse_result +[[nodiscard]] inline constexpr ::fast_io::details::scan_floating_context_append_result scan_floating_context_append(scan_floating_context &state, char_type const *chunk_begin, char_type const *chunk_end) noexcept { auto const chunk_size{static_cast<::std::size_t>(chunk_end - chunk_begin)}; if (!chunk_size) { - return {chunk_end, ::fast_io::parse_code::partial}; + return {chunk_end, ::fast_io::parse_code::partial, false}; } - if (scan_floating_context::capacity - state.size < chunk_size) + auto const available{scan_floating_context::capacity - state.size}; + if (!available) { - return {chunk_begin, ::fast_io::parse_code::overflow}; + return {chunk_begin, ::fast_io::parse_code::overflow, true}; } - ::fast_io::freestanding::non_overlapped_copy_n(chunk_begin, chunk_size, state.buffer.data() + state.size); - state.size += chunk_size; - return {chunk_end, ::fast_io::parse_code::partial}; + auto const copied{chunk_size < available ? chunk_size : available}; + ::fast_io::freestanding::non_overlapped_copy_n(chunk_begin, copied, state.buffer.data() + state.size); + state.size += copied; + return {chunk_begin + copied, ::fast_io::parse_code::partial, copied != chunk_size}; } enum class scan_hexfloat_context_phase : ::std::uint_least8_t @@ -1687,7 +1734,7 @@ scan_hexfloat_context_special_define( auto const append_result{::fast_io::details::scan_floating_context_append(state.special_buffer, begin, end)}; if (append_result.code != ::fast_io::parse_code::partial) { - return append_result; + return {append_result.iter, append_result.code}; } T parsed_value{}; auto const *buffer_begin{state.special_buffer.buffer.data()}; @@ -1698,7 +1745,19 @@ scan_hexfloat_context_special_define( { if (parse_result.iter == buffer_end) { - return {end, ::fast_io::parse_code::partial}; + if (::fast_io::details::scan_hexfloat_special_end_may_extend( + buffer_begin, buffer_end)) + { + if (append_result.truncated) + { + return {append_result.iter, ::fast_io::parse_code::overflow}; + } + return {end, ::fast_io::parse_code::partial}; + } + value = parsed_value; + return {::fast_io::details::scan_floating_context_map_iter( + state.special_buffer, old_size, begin, append_result.iter, parse_result.iter), + ::fast_io::parse_code::ok}; } if (::fast_io::details::scan_hexfloat_special_parse_may_extend( parse_result.iter, buffer_end)) @@ -1712,11 +1771,19 @@ scan_hexfloat_context_special_define( } if (parse_result.iter == buffer_end) { + if (append_result.truncated) + { + return {append_result.iter, ::fast_io::parse_code::overflow}; + } return {end, ::fast_io::parse_code::partial}; } if (parse_result.code == ::fast_io::parse_code::end_of_file || parse_result.code == ::fast_io::parse_code::partial) { + if (append_result.truncated) + { + return {append_result.iter, ::fast_io::parse_code::overflow}; + } return {end, ::fast_io::parse_code::partial}; } if (parse_result.code == ::fast_io::parse_code::invalid) @@ -1724,6 +1791,10 @@ scan_hexfloat_context_special_define( if (buffer_begin != buffer_end && !::fast_io::char_category::is_c_space(*(buffer_end - 1))) { + if (append_result.truncated) + { + return {append_result.iter, ::fast_io::parse_code::overflow}; + } return {end, ::fast_io::parse_code::partial}; } } From 0f25fb8710a4fa1fb3be87698408794737c99e4a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 02:36:14 +0800 Subject: [PATCH 37/45] Update scatter fallback and coalescing thresholds in fast_io - Added comments to clarify the behavior and rationale behind scatter fallback and full-output coalescing thresholds. - Adjusted the full-output coalescing threshold to 2048 bytes, optimizing performance for larger payloads while maintaining efficiency for smaller runs. - Enhanced documentation to provide insights into the performance implications of these thresholds based on benchmarking results. --- .../fast_io/include/fast_io_hosted/platforms/posix.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 23ee87bb4..1cf8b3acf 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -549,6 +549,9 @@ template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t scatter_fallback_full_output_threshold( ::fast_io::io_reserve_type_t>) noexcept { + // Keep scatter fallback conservative: this path first builds scatter descriptors, then copies the payload + // into a stack buffer only to avoid writev overhead for very small runs. POSIX write/writev measurements + // put the useful crossover around a 256-byte payload budget. return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); } @@ -556,7 +559,10 @@ template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t full_output_coalesce_threshold( ::fast_io::io_reserve_type_t>) noexcept { - return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); + // Full-output coalescing happens before scatter descriptors are materialized, so it avoids both descriptor + // setup and writev for tiny multi-part prints. Benchmarks showed wins through roughly 2 KiB, while larger + // stack buffers brought little extra benefit and more stack pressure. + return static_cast<::std::size_t>(2048u / sizeof(char_type) + 1u); } #if defined(__CYGWIN__) From fcad7129d0c012805bec38fdbc373364d0dc0875 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 03:11:19 +0800 Subject: [PATCH 38/45] Implement scatter chain handling and enhance concatenation logic in fast_io - Added functions to calculate total size of scatter chains and copy them to buffers, improving memory management during concatenation. - Introduced a new implementation for handling scatter-printable arguments in concatenation, optimizing performance and memory usage. - Enhanced existing concatenation functions to support the new scatter handling logic, ensuring robust and efficient string operations. --- .../fast_io_core_impl/concat/concat_general.h | 84 +++++++++++++++++-- .../include/fast_io_core_impl/integers/impl.h | 3 +- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h index c80d19610..9cb8a88e5 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h @@ -127,6 +127,61 @@ inline constexpr basic_io_scatter_t print_scatter_define_extract_one(T return print_scatter_define(io_reserve_type>, t); } +template <::std::integral ch_type> +inline constexpr ::std::size_t calculate_scatter_total_size(basic_io_scatter_t const *first, + basic_io_scatter_t const *last) +{ + ::std::size_t total_size{}; + for (; first != last; ++first) + { + total_size = ::fast_io::details::intrinsics::add_or_overflow_die(total_size, first->len); + } + return total_size; +} + +template <::std::integral ch_type> +inline constexpr ch_type *copy_scatter_chain_to_buffer(ch_type *iter, basic_io_scatter_t const *first, + basic_io_scatter_t const *last) +{ + for (; first != last; ++first) + { + iter = non_overlapped_copy_n(first->base, first->len, iter); + } + return iter; +} + +template +inline constexpr void basic_general_concat_decay_ref_impl_all_scatter(T &str, Args... args) +{ + basic_io_scatter_t scatters[]{ + print_scatter_define(io_reserve_type>, args)...}; + ::std::size_t total_size{calculate_scatter_total_size(scatters, scatters + sizeof...(Args))}; + if constexpr (line) + { + total_size = ::fast_io::details::intrinsics::add_or_overflow_die(total_size, static_cast<::std::size_t>(1u)); + } + if constexpr (sso_buffer_strlike) + { + constexpr ::std::size_t local_cap{strlike_sso_size(io_strlike_type)}; + if (local_cap < total_size) + { + strlike_reserve(io_strlike_type, str, total_size); + } + } + else + { + strlike_reserve(io_strlike_type, str, total_size); + } + auto ptr{copy_scatter_chain_to_buffer(strlike_begin(io_strlike_type, str), scatters, + scatters + sizeof...(Args))}; + if constexpr (line) + { + *ptr = char_literal_v; + ++ptr; + } + strlike_set_curr(io_strlike_type, str, ptr); +} + template inline constexpr T basic_general_concat_decay_impl_precise(T &str, Arg arg) { @@ -219,6 +274,12 @@ inline constexpr T basic_general_concat_decay_impl(Args... args) basic_io_scatter_t scatter{print_scatter_define_extract_one(args...)}; return strlike_construct_define(io_strlike_type(scatter.base, scatter.base + scatter.len)); } + else if constexpr ((scatter_printable && ...)) + { + T str; + basic_general_concat_decay_ref_impl_all_scatter(str, args...); + return str; + } else { ::std::size_t total_size{::fast_io::details::intrinsics::add_or_overflow_die( @@ -301,8 +362,8 @@ inline constexpr void basic_general_concat_decay_ref_impl(T &str, Args... args) basic_general_concat_decay_ref_impl_semantic_precise(str, args...); } else if constexpr (((reserve_printable || scatter_printable || - dynamic_reserve_printable) && - ...)) + dynamic_reserve_printable) && + ...)) { constexpr ::std::size_t sz{calculate_scatter_reserve_size()}; if constexpr (line) @@ -342,12 +403,19 @@ inline constexpr void basic_general_concat_decay_ref_impl(T &str, Args... args) } else { - ::std::size_t total_size{::fast_io::details::intrinsics::add_or_overflow_die( - sz_with_line, calculate_scatter_dynamic_reserve_size_with_scatter(args...))}; - strlike_reserve(io_strlike_type, str, total_size); - strlike_set_curr(io_strlike_type, str, - print_reserve_define_chain_scatter_impl( - strlike_begin(io_strlike_type, str), args...)); + if constexpr ((scatter_printable && ...)) + { + basic_general_concat_decay_ref_impl_all_scatter(str, args...); + } + else + { + ::std::size_t total_size{::fast_io::details::intrinsics::add_or_overflow_die( + sz_with_line, calculate_scatter_dynamic_reserve_size_with_scatter(args...))}; + strlike_reserve(io_strlike_type, str, total_size); + strlike_set_curr(io_strlike_type, str, + print_reserve_define_chain_scatter_impl( + strlike_begin(io_strlike_type, str), args...)); + } } } else diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index e3d8fa455..716a5b946 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -2333,7 +2333,8 @@ inline constexpr void print_reserve_integral_define_precise(char_type *start, :: } if constexpr (base == 10 && (::std::numeric_limits<::std::uint_least32_t>::digits == 32u)) { - return ::fast_io::details::jeaiii::jeaiii_main_len(first, u, static_cast<::std::uint_least32_t>(n)); + return ::fast_io::details::jeaiii::jeaiii_main_len( + first, u, static_cast<::std::uint_least32_t>((start + n) - first)); } else { From cac460dc8a70df2643bcae288af6570b011330a8 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 11:59:39 +0800 Subject: [PATCH 39/45] Enhance scatter handling and ASCII hex parsing in fast_io - Introduced new functions for efficient copying of small scatter chains, optimizing memory usage during concatenation operations. - Enhanced existing scatter handling logic to support direct and generic implementations based on argument types, improving performance. - Added functions for parsing ASCII hexadecimal digits and validating hexadecimal word formats, enhancing the robustness of integer scanning operations. - Updated existing parsing functions to utilize new mechanisms for handling large data chunks, improving efficiency in scanning operations. --- .../fast_io_core_impl/concat/concat_general.h | 67 ++++++++++- .../integers/sto/sto_contiguous.h | 105 ++++++++++++++++-- .../fast_io_core_impl/integers/uprsv/dec.h | 2 +- .../include/fast_io_unit/floating/decfloat.h | 19 ++++ 4 files changed, 179 insertions(+), 14 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h index 9cb8a88e5..a14f5843c 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h @@ -139,19 +139,69 @@ inline constexpr ::std::size_t calculate_scatter_total_size(basic_io_scatter_t +inline constexpr ch_type *concat_small_scatter_copy_n(ch_type const *first, ::std::size_t count, + ch_type *result) noexcept +{ + if (count <= 16u) + { + for (::std::size_t i{}; i != count; ++i) + { + result[i] = first[i]; + } + return result + count; + } + return non_overlapped_copy_n(first, count, result); +} + template <::std::integral ch_type> inline constexpr ch_type *copy_scatter_chain_to_buffer(ch_type *iter, basic_io_scatter_t const *first, basic_io_scatter_t const *last) { for (; first != last; ++first) { - iter = non_overlapped_copy_n(first->base, first->len, iter); + iter = concat_small_scatter_copy_n(first->base, first->len, iter); } return iter; } +template <::std::integral ch_type, typename T> +inline constexpr bool direct_scatter_view_printable = + ::std::same_as<::std::remove_cvref_t, basic_io_scatter_t>; + template -inline constexpr void basic_general_concat_decay_ref_impl_all_scatter(T &str, Args... args) +inline constexpr void basic_general_concat_decay_ref_impl_all_scatter_direct(T &str, Args... args) +{ + ::std::size_t total_size{}; + ((total_size = ::fast_io::details::intrinsics::add_or_overflow_die(total_size, args.len)), ...); + if constexpr (line) + { + total_size = ::fast_io::details::intrinsics::add_or_overflow_die(total_size, static_cast<::std::size_t>(1u)); + } + if constexpr (sso_buffer_strlike) + { + constexpr ::std::size_t local_cap{strlike_sso_size(io_strlike_type)}; + if (local_cap < total_size) + { + strlike_reserve(io_strlike_type, str, total_size); + } + } + else + { + strlike_reserve(io_strlike_type, str, total_size); + } + auto ptr{strlike_begin(io_strlike_type, str)}; + ((ptr = concat_small_scatter_copy_n(args.base, args.len, ptr)), ...); + if constexpr (line) + { + *ptr = char_literal_v; + ++ptr; + } + strlike_set_curr(io_strlike_type, str, ptr); +} + +template +inline constexpr void basic_general_concat_decay_ref_impl_all_scatter_generic(T &str, Args... args) { basic_io_scatter_t scatters[]{ print_scatter_define(io_reserve_type>, args)...}; @@ -182,6 +232,19 @@ inline constexpr void basic_general_concat_decay_ref_impl_all_scatter(T &str, Ar strlike_set_curr(io_strlike_type, str, ptr); } +template +inline constexpr void basic_general_concat_decay_ref_impl_all_scatter(T &str, Args... args) +{ + if constexpr ((direct_scatter_view_printable && ...)) + { + basic_general_concat_decay_ref_impl_all_scatter_direct(str, args...); + } + else + { + basic_general_concat_decay_ref_impl_all_scatter_generic(str, args...); + } +} + template inline constexpr T basic_general_concat_decay_impl_precise(T &str, Arg arg) { diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h index ea4d487d8..8944ca950 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/sto/sto_contiguous.h @@ -437,7 +437,7 @@ inline simd_parse_result sse_parse(char unsigned const *buffer, char unsigned co ::std::memcpy(__builtin_addressof(chunk0), __builtin_addressof(chunk), sizeof(chunk0)); #endif ::std::uint_least64_t result{ - static_cast<::std::uint_least64_t>(((chunk0 & 0xffffffff) * UINT64_C(100000000)) + (chunk0 >> 32))}; + static_cast<::std::uint_least64_t>(((chunk0 & 0xffffffff) * static_cast<::std::uint_least64_t>(100000000)) + (chunk0 >> 32))}; if (digits == 16) [[unlikely]] { if constexpr (less_than_64_bits) @@ -476,8 +476,8 @@ inline simd_parse_result sse_parse(char unsigned const *buffer, char unsigned co } case 4: { - constexpr ::std::uint_least64_t risky_value{UINT_LEAST64_MAX / UINT64_C(10000)}; - constexpr ::std::uint_fast16_t risky_mod{UINT_LEAST64_MAX % UINT64_C(10000)}; + constexpr ::std::uint_least64_t risky_value{UINT_LEAST64_MAX / static_cast<::std::uint_least64_t>(10000)}; + constexpr ::std::uint_fast16_t risky_mod{UINT_LEAST64_MAX % static_cast<::std::uint_least64_t>(10000)}; if (result > risky_value) { return {20, parse_code::overflow}; @@ -593,6 +593,66 @@ inline constexpr ::fast_io::freestanding::array generate_pow_table() noexc template inline constexpr ::fast_io::freestanding::array pow_table_n{::fast_io::details::generate_pow_table()}; +template <::std::integral char_type> + requires(!::fast_io::details::is_ebcdic && sizeof(char_type) == sizeof(char8_t)) +inline constexpr char8_t ascii_hex_digit_value(my_make_unsigned_t ch) noexcept +{ + my_make_unsigned_t digit{ch}; + digit -= static_cast>(u8'0'); + if (digit < 10u) + { + return static_cast(digit); + } + ch |= static_cast>(0x20u); + ch -= static_cast>(u8'a'); + if (ch < 6u) + { + return static_cast(ch + 10u); + } + return static_cast(0xFFu); +} + +inline constexpr ::std::uint_least64_t ascii_hex_word_invalid_mask(::std::uint_least64_t val) noexcept +{ + return (((((val + static_cast<::std::uint_least64_t>(0x4646464646464646)) | (val - static_cast<::std::uint_least64_t>(0x3030303030303030))) & + ((val + static_cast<::std::uint_least64_t>(0x3939393939393939)) | (val - static_cast<::std::uint_least64_t>(0x4040404040404040))) & + ((val + static_cast<::std::uint_least64_t>(0x1919191919191919)) | (val - static_cast<::std::uint_least64_t>(0x6060606060606060)))) | + ~(((val + static_cast<::std::uint_least64_t>(0x3f3f3f3f3f3f3f3f)) | (val - static_cast<::std::uint_least64_t>(0x4040404040404040))) & + ((val + static_cast<::std::uint_least64_t>(0x1f1f1f1f1f1f1f1f)) | (val - static_cast<::std::uint_least64_t>(0x6060606060606060))))) & + static_cast<::std::uint_least64_t>(0x8080808080808080)); +} + +inline constexpr ::std::uint_least32_t ascii_hex_word_to_u32(::std::uint_least64_t val) noexcept +{ + constexpr ::std::uint_least64_t mask{static_cast<::std::uint_least64_t>(0x000000FF000000FF)}; + constexpr ::std::uint_least64_t mul1{static_cast<::std::uint_least64_t>(0x0100000000000100)}; + constexpr ::std::uint_least64_t mul2{static_cast<::std::uint_least64_t>(0x0001000000000001)}; + val -= static_cast<::std::uint_least64_t>(0x3030303030303030); + val = (val & static_cast<::std::uint_least64_t>(0x0f0f0f0f0f0f0f0f)) + ((val & static_cast<::std::uint_least64_t>(0x1010101010101010)) >> 4u) * 9u; + val = (val * 16u) + (val >> 8u); + return static_cast<::std::uint_least32_t>((((val & mask) * mul1) + (((val >> 16u) & mask) * mul2)) >> 32u); +} + +template <::std::integral char_type, my_unsigned_integral T> + requires(!::fast_io::details::is_ebcdic && sizeof(char_type) == sizeof(char8_t)) +inline constexpr char_type const *scan_ascii_hex_digits_scalar(char_type const *first, char_type const *last, + T &res) noexcept +{ + using unsigned_char_type = ::std::make_unsigned_t; + using unsigned_type = my_make_unsigned_t<::std::remove_cvref_t>; + for (; first != last; ++first) + { + auto const digit{ + ::fast_io::details::ascii_hex_digit_value(static_cast(*first))}; + if (15u < digit) [[unlikely]] + { + break; + } + res = static_cast((static_cast(res) << 4u) | static_cast(digit)); + } + return first; +} + template <::std::integral char_type, my_unsigned_integral T> #if __has_cpp_attribute(__gnu__::__always_inline__) [[__gnu__::__always_inline__]] @@ -602,7 +662,6 @@ template <::std::integral char_type, my_unsigned_integral T> inline constexpr parse_result scan_int_contiguous_ascii_hex_space_part_define_impl(char_type const *first, char_type const *last, T &out) noexcept { - using unsigned_char_type = ::std::make_unsigned_t; using unsigned_type = my_make_unsigned_t<::std::remove_cvref_t>; constexpr ::std::size_t max_size{::fast_io::details::max_int_size_result}; ::std::size_t const diff{static_cast<::std::size_t>(last - first)}; @@ -613,16 +672,40 @@ scan_int_contiguous_ascii_hex_space_part_define_impl(char_type const *first, cha } auto first_phase_last{first + mn_val}; T res{out}; - for (; first != first_phase_last; ++first) [[likely]] + if constexpr (::std::numeric_limits<::std::uint_least64_t>::digits == 64u && 8u <= max_size) { - auto const digit{::fast_io::details::sto_ascii_digit_table_lookup( - static_cast(*first))}; - if (15u < digit) [[unlikely]] + while (static_cast<::std::size_t>(first_phase_last - first) >= sizeof(::std::uint_least64_t)) [[likely]] { - break; + ::std::uint_least64_t val; + ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); + if constexpr (::std::endian::little != ::std::endian::native) + { + val = ::fast_io::little_endian(val); + } + if (::std::uint_least64_t const invalid_mask{::fast_io::details::ascii_hex_word_invalid_mask(val)}; + invalid_mask != 0) [[unlikely]] + { + auto const valid_bytes{ + static_cast<::std::size_t>(static_cast(::std::countr_zero(invalid_mask)) >> 3u)}; + first = ::fast_io::details::scan_ascii_hex_digits_scalar(first, first + valid_bytes, res); + goto finish; + } + auto const chunk{::fast_io::details::ascii_hex_word_to_u32(val)}; + if constexpr (sizeof(unsigned_type) <= sizeof(::std::uint_least32_t)) + { + res = static_cast(chunk); + } + else + { + res = static_cast((static_cast(res) << 32u) | + static_cast(chunk)); + } + first += sizeof(::std::uint_least64_t); } - res = static_cast((static_cast(res) << 4u) | static_cast(digit)); } + first = ::fast_io::details::scan_ascii_hex_digits_scalar(first, first_phase_last, res); + +finish: if (first == last) { out = res; @@ -1388,7 +1471,7 @@ inline constexpr auto scan_context_type_impl_int() noexcept using unsigned_type = details::my_make_unsigned_t<::std::remove_cvref_t>; constexpr ::std::size_t max_size{ (::fast_io::details::print_integer_reserved_size_cache, - false, unsigned_type>) + 2}; + false, unsigned_type>)+2}; struct scan_integer_context { ::fast_io::freestanding::array buffer; diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/uprsv/dec.h b/third-parties/fast_io/include/fast_io_core_impl/integers/uprsv/dec.h index 1fc9427db..a39f0ab94 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/uprsv/dec.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/uprsv/dec.h @@ -232,7 +232,7 @@ inline constexpr char_type *uprsv128_impl(char_type *iter, __uint128_t value) no return uprsv64_impl(iter, low); } - constexpr ::std::uint_least64_t onee18{UINT64_C(1000000000000000000)}; + constexpr ::std::uint_least64_t onee18{static_cast<::std::uint_least64_t>(1000000000000000000)}; constexpr __uint128_t onee36{static_cast<__uint128_t>(onee18) * static_cast<__uint128_t>(onee18)}; diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index 58d15d0e9..d6791a40f 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -2731,6 +2731,25 @@ scan_decfloat_contiguous_short_define_impl(char_type const *begin, char_type con ::std::uint_least64_t digit_count{}; ::std::uint_least64_t fractional_digits{}; char8_t digit{}; + for (; digit_count + 8u <= digit_limit && + static_cast<::std::size_t>(end - first) >= sizeof(::std::uint_least64_t);) + { + ::std::uint_least64_t val; + ::fast_io::freestanding::my_memcpy(__builtin_addressof(val), first, sizeof(::std::uint_least64_t)); + if constexpr (::std::endian::little != ::std::endian::native) + { + val = ::fast_io::little_endian(val); + } + if (!::fast_io::details::scan_decfloat_ascii8_is_digits(val)) + { + break; + } + significand = significand * 100000000u + + static_cast<::std::uint_least64_t>( + ::fast_io::details::scan_decfloat_ascii8_parse(val)); + digit_count += 8u; + first += sizeof(::std::uint_least64_t); + } for (; first != end && ::fast_io::details::scan_decfloat_decimal_digit(*first, digit); ++first) { if (digit_count == digit_limit) From 1d9fc6a0ab99657d51fae0ebcb60427c78d54b0d Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 12:37:51 +0800 Subject: [PATCH 40/45] Add small scatter coalescing support in fast_io - Introduced the `small_scatter_coalesce_threshold_stream` concept to optimize stream handling for small scatter repacking. - Implemented `print_small_scatter_repack_size` and related functions to enhance memory efficiency during scatter write operations. - Updated POSIX platform support with a new `small_scatter_coalesce_threshold` function to manage small scatter repacking effectively. - Enhanced existing scatter handling logic to improve performance and reduce unnecessary memory copies during output operations. --- .../fast_io_core_impl/concepts/operation.h | 18 + .../printimpl/print_freestanding_cxx20.h | 458 ++++++++++++++++++ .../include/fast_io_hosted/platforms/posix.h | 16 + 3 files changed, 492 insertions(+) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h index 6136b2cb0..41a7e81db 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concepts/operation.h @@ -243,6 +243,24 @@ concept full_output_coalesce_threshold_stream = } -> ::std::same_as<::std::size_t>; }; +/// @brief small_scatter_coalesce_threshold_stream +/// @details Opts a stream into small-scatter repacking and customizes the +/// maximum individual scatter size, in char_type units, that may be +/// copied into temporary coalescing storage. Returning 0 disables +/// this path. Streams should only opt in when reducing writev/iovec +/// pressure is known to beat the extra memcpy work. +/// @fn small_scatter_coalesce_threshold +/// @brief Returns the small-scatter element threshold, in char_type units. +template +concept small_scatter_coalesce_threshold_stream = + ::std::integral && requires { + typename ::fast_io::details::reserve_static_stack_size_constant< + small_scatter_coalesce_threshold(io_reserve_type>)>; + { + small_scatter_coalesce_threshold(io_reserve_type>) + } -> ::std::same_as<::std::size_t>; + }; + /// @brief printable_internal_shift /// @details Defines the behaviour when printed with ::fast_io::mnp::width<::fast_io::mnp::scalar_placement::internal> /// @fn print_define_internal_shift diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 74f232b07..0a02f1f1e 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -578,6 +578,448 @@ inline constexpr char_type *print_small_scatter_copy_n(char_type const *first, : return ::fast_io::details::non_overlapped_copy_n(first, count, result); } +template <::std::integral char_type, typename outputstmtype> +inline constexpr ::std::size_t print_small_scatter_repack_size() noexcept +{ + if constexpr (::fast_io::small_scatter_coalesce_threshold_stream) + { + return small_scatter_coalesce_threshold( + ::fast_io::io_reserve_type>); + } + else + { + return 0u; + } +} + +inline constexpr ::std::size_t print_scatter_repack_min_saved_scatters{8u}; + +template +inline constexpr ::std::size_t print_scatter_repack_stack_scatter_count() noexcept +{ + constexpr ::std::size_t stack_count{ + ::fast_io::details::decay::print_stack_buffer_max_element_count()}; + if constexpr (stack_count < 64u) + { + return stack_count; + } + else + { + return 64u; + } +} + +template +inline constexpr bool print_scatter_write_all_bytes_try_repack_small( + outputstmtype outstm, ::fast_io::io_scatter_t const *scatters, ::std::size_t n) +{ + using char_type = typename outputstmtype::output_char_type; + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_scatter_full_output_threshold()}; + constexpr ::std::size_t small_chars{ + ::fast_io::details::decay::print_small_scatter_repack_size()}; + if constexpr (threshold_chars == 0 || small_chars == 0 || + !::fast_io::details::decay::print_has_direct_scatter_write_bytes_operations) + { + return false; + } + else + { + constexpr ::std::size_t chunk_bytes{threshold_chars * sizeof(char_type) - 1u}; + constexpr ::std::size_t small_bytes{small_chars * sizeof(char_type)}; + if constexpr (chunk_bytes == 0) + { + return false; + } + else + { + ::std::size_t output_count{}; + ::std::size_t dynamic_buffer_size{}; + ::std::size_t current_size{}; + ::std::size_t current_count{}; + bool stack_chunk_used{}; + bool has_coalesced_chunk{}; + auto flush_plan = [&]() constexpr { + if (current_count == 0) + { + return; + } + ++output_count; + if (1u < current_count) + { + has_coalesced_chunk = true; + if (stack_chunk_used) + { + dynamic_buffer_size = + ::fast_io::details::intrinsics::add_or_overflow_die(dynamic_buffer_size, current_size); + } + else + { + stack_chunk_used = true; + } + } + current_size = 0; + current_count = 0; + }; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (len == 0) + { + continue; + } + if (small_bytes < len) + { + flush_plan(); + ++output_count; + continue; + } + if (chunk_bytes - current_size < len) + { + flush_plan(); + } + current_size += len; + ++current_count; + } + flush_plan(); + if (!has_coalesced_chunk || output_count > n || + n - output_count < print_scatter_repack_min_saved_scatters) + { + return false; + } + + ::std::byte stack_buffer[chunk_bytes]; + ::fast_io::details::local_operator_new_array_ptr<::std::byte> dynamic_buffer; + if (dynamic_buffer_size != 0) + { + dynamic_buffer.allocate_new(dynamic_buffer_size); + } + + auto emit_repacked = [&](::fast_io::io_scatter_t *out_scatters) constexpr { + auto out_curr{out_scatters}; + ::std::byte *dynamic_curr{dynamic_buffer.ptr}; + ::std::byte *chunk_begin{}; + ::std::byte *chunk_curr{}; + ::std::size_t chunk_size{}; + ::std::size_t chunk_count{}; + bool chunk_is_stack{}; + bool stack_used{}; + ::fast_io::io_scatter_t pending{}; + auto begin_chunk = [&]() constexpr { + if (!stack_used) + { + chunk_begin = stack_buffer; + chunk_curr = stack_buffer; + chunk_is_stack = true; + stack_used = true; + } + else + { + chunk_begin = dynamic_curr; + chunk_curr = dynamic_curr; + chunk_is_stack = false; + } + }; + auto flush_chunk = [&]() constexpr { + if (chunk_count == 0) + { + return; + } + if (chunk_count == 1) + { + *out_curr = pending; + ++out_curr; + } + else + { + *out_curr = {chunk_begin, chunk_size}; + ++out_curr; + if (!chunk_is_stack) + { + dynamic_curr = chunk_curr; + } + } + chunk_begin = {}; + chunk_curr = {}; + chunk_size = 0; + chunk_count = 0; + chunk_is_stack = false; + }; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (len == 0) + { + continue; + } + if (small_bytes < len) + { + flush_chunk(); + *out_curr = *iter; + ++out_curr; + continue; + } + if (chunk_bytes - chunk_size < len) + { + flush_chunk(); + } + if (chunk_count == 0) + { + pending = *iter; + chunk_size = len; + chunk_count = 1; + continue; + } + if (chunk_count == 1) + { + begin_chunk(); + auto const first{static_cast<::std::byte const *>(pending.base)}; + chunk_curr = ::fast_io::details::decay::print_small_scatter_copy_n(first, pending.len, + chunk_curr); + } + auto const first{static_cast<::std::byte const *>(iter->base)}; + chunk_curr = ::fast_io::details::decay::print_small_scatter_copy_n(first, len, chunk_curr); + chunk_size += len; + ++chunk_count; + } + flush_chunk(); + ::std::size_t const repacked_count{static_cast<::std::size_t>(out_curr - out_scatters)}; + if constexpr (::fast_io::details::decay::print_has_direct_write_bytes_operations) + { + if (repacked_count == 1) + { + auto first{static_cast<::std::byte const *>(out_scatters->base)}; + ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, + first + out_scatters->len); + return; + } + } + ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, out_scatters, repacked_count); + }; + + constexpr ::std::size_t stack_scatter_count{ + ::fast_io::details::decay::print_scatter_repack_stack_scatter_count<::fast_io::io_scatter_t>()}; + if constexpr (stack_scatter_count != 0) + { + if (output_count <= stack_scatter_count) + { + ::fast_io::io_scatter_t out_scatters[stack_scatter_count]; + emit_repacked(out_scatters); + return true; + } + } + ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> out_scatters(output_count); + emit_repacked(out_scatters.ptr); + return true; + } + } +} + +template +inline constexpr bool print_scatter_write_all_try_repack_small( + outputstmtype outstm, + ::fast_io::basic_io_scatter_t const *scatters, ::std::size_t n) +{ + using char_type = typename outputstmtype::output_char_type; + constexpr ::std::size_t threshold_chars{ + ::fast_io::details::decay::print_scatter_full_output_threshold()}; + constexpr ::std::size_t small_chars{ + ::fast_io::details::decay::print_small_scatter_repack_size()}; + if constexpr (threshold_chars == 0 || small_chars == 0 || + !::fast_io::details::decay::print_has_direct_scatter_write_operations) + { + return false; + } + else + { + constexpr ::std::size_t chunk_chars{threshold_chars - 1u}; + if constexpr (chunk_chars == 0) + { + return false; + } + else + { + using scatter_type = ::fast_io::basic_io_scatter_t; + ::std::size_t output_count{}; + ::std::size_t dynamic_buffer_size{}; + ::std::size_t current_size{}; + ::std::size_t current_count{}; + bool stack_chunk_used{}; + bool has_coalesced_chunk{}; + auto flush_plan = [&]() constexpr { + if (current_count == 0) + { + return; + } + ++output_count; + if (1u < current_count) + { + has_coalesced_chunk = true; + if (stack_chunk_used) + { + dynamic_buffer_size = + ::fast_io::details::intrinsics::add_or_overflow_die(dynamic_buffer_size, current_size); + } + else + { + stack_chunk_used = true; + } + } + current_size = 0; + current_count = 0; + }; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (len == 0) + { + continue; + } + if (small_chars < len) + { + flush_plan(); + ++output_count; + continue; + } + if (chunk_chars - current_size < len) + { + flush_plan(); + } + current_size += len; + ++current_count; + } + flush_plan(); + if (!has_coalesced_chunk || output_count > n || + n - output_count < print_scatter_repack_min_saved_scatters) + { + return false; + } + + char_type stack_buffer[chunk_chars]; + ::fast_io::details::local_operator_new_array_ptr dynamic_buffer; + if (dynamic_buffer_size != 0) + { + dynamic_buffer.allocate_new(dynamic_buffer_size); + } + + auto emit_repacked = [&](scatter_type *out_scatters) constexpr { + auto out_curr{out_scatters}; + char_type *dynamic_curr{dynamic_buffer.ptr}; + char_type *chunk_begin{}; + char_type *chunk_curr{}; + ::std::size_t chunk_size{}; + ::std::size_t chunk_count{}; + bool chunk_is_stack{}; + bool stack_used{}; + scatter_type pending{}; + auto begin_chunk = [&]() constexpr { + if (!stack_used) + { + chunk_begin = stack_buffer; + chunk_curr = stack_buffer; + chunk_is_stack = true; + stack_used = true; + } + else + { + chunk_begin = dynamic_curr; + chunk_curr = dynamic_curr; + chunk_is_stack = false; + } + }; + auto flush_chunk = [&]() constexpr { + if (chunk_count == 0) + { + return; + } + if (chunk_count == 1) + { + *out_curr = pending; + ++out_curr; + } + else + { + *out_curr = {chunk_begin, chunk_size}; + ++out_curr; + if (!chunk_is_stack) + { + dynamic_curr = chunk_curr; + } + } + chunk_begin = {}; + chunk_curr = {}; + chunk_size = 0; + chunk_count = 0; + chunk_is_stack = false; + }; + for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) + { + ::std::size_t const len{iter->len}; + if (len == 0) + { + continue; + } + if (small_chars < len) + { + flush_chunk(); + *out_curr = *iter; + ++out_curr; + continue; + } + if (chunk_chars - chunk_size < len) + { + flush_chunk(); + } + if (chunk_count == 0) + { + pending = *iter; + chunk_size = len; + chunk_count = 1; + continue; + } + if (chunk_count == 1) + { + begin_chunk(); + chunk_curr = ::fast_io::details::decay::print_small_scatter_copy_n( + pending.base, pending.len, chunk_curr); + } + chunk_curr = + ::fast_io::details::decay::print_small_scatter_copy_n(iter->base, len, chunk_curr); + chunk_size += len; + ++chunk_count; + } + flush_chunk(); + ::std::size_t const repacked_count{static_cast<::std::size_t>(out_curr - out_scatters)}; + if constexpr (::fast_io::details::decay::print_has_direct_write_operations) + { + if (repacked_count == 1) + { + auto [base, len] = *out_scatters; + ::fast_io::operations::decay::write_all_decay(outstm, base, base + len); + return; + } + } + ::fast_io::operations::decay::scatter_write_all_decay(outstm, out_scatters, repacked_count); + }; + + constexpr ::std::size_t stack_scatter_count{ + ::fast_io::details::decay::print_scatter_repack_stack_scatter_count()}; + if constexpr (stack_scatter_count != 0) + { + if (output_count <= stack_scatter_count) + { + scatter_type out_scatters[stack_scatter_count]; + emit_repacked(out_scatters); + return true; + } + } + ::fast_io::details::local_operator_new_array_ptr out_scatters(output_count); + emit_repacked(out_scatters.ptr); + return true; + } + } +} + /// @brief Writes byte scatter descriptors, optionally coalescing small scatter runs first. /// @details Empty and single-scatter runs are handled directly. Multi-scatter runs are copied into a bounded stack /// buffer only when the stream supports both direct byte writes and direct byte scatter writes. @@ -625,6 +1067,10 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype if (remaining < len) { // The byte run exceeds the coalescing threshold, so preserve scatter output directly. + if (::fast_io::details::decay::print_scatter_write_all_bytes_try_repack_small(outstm, scatters, n)) + { + return; + } ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); return; } @@ -644,6 +1090,10 @@ inline constexpr void print_scatter_write_all_bytes_maybe_coalesce(outputstmtype return; } // Streams that cannot coalesce this path emit the original byte scatter descriptors directly. + if (::fast_io::details::decay::print_scatter_write_all_bytes_try_repack_small(outstm, scatters, n)) + { + return; + } ::fast_io::operations::decay::scatter_write_all_bytes_decay(outstm, scatters, n); } @@ -691,6 +1141,10 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( if (remaining < len) { // The character run exceeds the coalescing threshold, so preserve scatter output directly. + if (::fast_io::details::decay::print_scatter_write_all_try_repack_small(outstm, scatters, n)) + { + return; + } ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); return; } @@ -709,6 +1163,10 @@ inline constexpr void print_scatter_write_all_maybe_coalesce( return; } // Streams that cannot coalesce this path emit the original character scatter descriptors directly. + if (::fast_io::details::decay::print_scatter_write_all_try_repack_small(outstm, scatters, n)) + { + return; + } ::fast_io::operations::decay::scatter_write_all_decay(outstm, scatters, n); } diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 1cf8b3acf..13554c09c 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -565,6 +565,22 @@ inline constexpr ::std::size_t full_output_coalesce_threshold( return static_cast<::std::size_t>(2048u / sizeof(char_type) + 1u); } +template <::fast_io::posix_family family, ::std::integral char_type> +inline constexpr ::std::size_t small_scatter_coalesce_threshold( + ::fast_io::io_reserve_type_t>) noexcept +{ + // Only POSIX opts into the secondary small-scatter repack path. Keep the per-element copy budget tiny so + // larger scatter payloads continue to flow through writev without an extra memcpy. + if constexpr (16u < sizeof(char_type)) + { + return 1u; + } + else + { + return static_cast<::std::size_t>(16u / sizeof(char_type)); + } +} + #if defined(__CYGWIN__) // https://github.com/cygwin/cygwin/blob/c43ec5f5951c7f4b882a0f8e619601a45ae70a91/newlib/libc/include/sys/_default_fcntl.h#L168 From 72e1ddf83281b69252301e10e8aeed732b6b0e4b Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 13:16:30 +0800 Subject: [PATCH 41/45] Refactor scatter fallback and coalescing thresholds in fast_io - Updated the `scatter_fallback_full_output_threshold`, `full_output_coalesce_threshold`, and `small_scatter_coalesce_threshold` functions to return zero, optimizing performance by leveraging native POSIX `writev` capabilities. - Removed conservative fallback logic and adjusted comments to reflect the new approach, enhancing clarity on performance implications for scatter operations. - Ensured that small scatter repacking remains disabled by default, allowing for more efficient direct writes in POSIX environments. --- .../include/fast_io_hosted/platforms/posix.h | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 13554c09c..65a78c4d6 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -545,41 +545,34 @@ io_bytes_stream_ref_define(basic_posix_family_io_observer other return {other.fd}; } +#if 0 template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t scatter_fallback_full_output_threshold( ::fast_io::io_reserve_type_t>) noexcept { - // Keep scatter fallback conservative: this path first builds scatter descriptors, then copies the payload - // into a stack buffer only to avoid writev overhead for very small runs. POSIX write/writev measurements - // put the useful crossover around a 256-byte payload budget. - return static_cast<::std::size_t>(256u / sizeof(char_type) + 1u); + // POSIX has native writev. /dev/null measurements show that copying scatter payloads into temporary + // contiguous storage loses to writing the original iovec chain, even for tiny scatter runs. + return 0u; } template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t full_output_coalesce_threshold( ::fast_io::io_reserve_type_t>) noexcept { - // Full-output coalescing happens before scatter descriptors are materialized, so it avoids both descriptor - // setup and writev for tiny multi-part prints. Benchmarks showed wins through roughly 2 KiB, while larger - // stack buffers brought little extra benefit and more stack pressure. - return static_cast<::std::size_t>(2048u / sizeof(char_type) + 1u); + // Scatter-only public print paths are faster when they build descriptors and call writev directly. Full + // materialization can still be useful for streams without native scatter output, but POSIX does not opt in. + return 0u; } template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t small_scatter_coalesce_threshold( ::fast_io::io_reserve_type_t>) noexcept { - // Only POSIX opts into the secondary small-scatter repack path. Keep the per-element copy budget tiny so - // larger scatter payloads continue to flow through writev without an extra memcpy. - if constexpr (16u < sizeof(char_type)) - { - return 1u; - } - else - { - return static_cast<::std::size_t>(16u / sizeof(char_type)); - } + // Repacking small scatter elements is a memcpy tradeoff. POSIX defaults to direct writev, so this remains + // disabled unless a more specialized stream type opts in with its own measured threshold. + return 0u; } +#endif #if defined(__CYGWIN__) From d3a4a5514818770770e2f0c50ec7a7d846a45b82 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 13:57:49 +0800 Subject: [PATCH 42/45] Refactor integer printing and scanning logic in fast_io - Updated the implementation of integral printing functions to utilize a more efficient power calculation method, enhancing performance. - Improved handling of high digit printing based on the base, ensuring correct character representation for bases greater than 10. - Introduced a new function to check for potential extension of decimal float exponent prefixes, improving parsing accuracy. - Enhanced hex float precision handling by refining conditions for total precision adjustments, optimizing output formatting. --- .../include/fast_io_core_impl/integers/impl.h | 28 +++++++++++++++---- .../integers/jeaiii_method.h | 4 ++- .../include/fast_io_unit/floating/decfloat.h | 26 +++++++++++++++++ .../include/fast_io_unit/floating/hexfloat.h | 9 ++++-- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h index 716a5b946..eb7e54382 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/impl.h @@ -2037,7 +2037,7 @@ inline constexpr void print_reserve_integral_main_impl(char_type *iter, T t, ::s { constexpr ::std::size_t sizetdigitsm1{sizetdigits - 1}; constexpr ::std::size_t remain_digits{basetdigits - sizetdigitsm1 * 2}; - constexpr T maxhighdigits{compile_pow10}; + constexpr T maxhighdigits{compile_pow_n}; if constexpr (!ryu_mode && remain_digits != 0) { static_assert(remain_digits < 3); @@ -2047,7 +2047,16 @@ inline constexpr void print_reserve_integral_main_impl(char_type *iter, T t, ::s { T high{t / maxhighdigits}; t = t % maxhighdigits; - *(iter - basetdigits) = ::fast_io::char_literal_add(high); + if constexpr (base <= 10) + { + *(iter - basetdigits) = ::fast_io::char_literal_add(high); + } + else + { + constexpr auto tb{::fast_io::details::digits_table}; + *(iter - basetdigits) = + static_cast(tb[(static_cast<::std::size_t>(high) << 1u) + 1u]); + } --len; } } @@ -2058,7 +2067,7 @@ inline constexpr void print_reserve_integral_main_impl(char_type *iter, T t, ::s { T high{t / maxhighdigits}; t = t % maxhighdigits; - ::std::uint_least8_t rem{static_cast<::std::uint_least8_t>(high)}; + ::std::size_t rem{static_cast<::std::size_t>(high)}; if (len == basetdigits) { constexpr auto tb{::fast_io::details::digits_table}; @@ -2067,7 +2076,16 @@ inline constexpr void print_reserve_integral_main_impl(char_type *iter, T t, ::s } else { - *(iter + 1 - basetdigits) = ::fast_io::char_literal_add(high); + if constexpr (base <= 10) + { + *(iter + 1 - basetdigits) = ::fast_io::char_literal_add(high); + } + else + { + constexpr auto tb{::fast_io::details::digits_table}; + *(iter + 1 - basetdigits) = + static_cast(tb[(static_cast<::std::size_t>(high) << 1u) + 1u]); + } --len; } } @@ -2076,7 +2094,7 @@ inline constexpr void print_reserve_integral_main_impl(char_type *iter, T t, ::s optimal_print_unsigned_type low; if (len > sizetdigitsm1) { - constexpr T halfdigits{compile_pow10}; + constexpr T halfdigits{compile_pow_n}; optimal_print_unsigned_type high{static_cast(t / halfdigits)}; low = static_cast(t % halfdigits); print_reserve_integral_main_impl(iter - sizetdigitsm1, high, len - sizetdigitsm1); diff --git a/third-parties/fast_io/include/fast_io_core_impl/integers/jeaiii_method.h b/third-parties/fast_io/include/fast_io_core_impl/integers/jeaiii_method.h index cec27bad7..1afebf112 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/integers/jeaiii_method.h +++ b/third-parties/fast_io/include/fast_io_core_impl/integers/jeaiii_method.h @@ -517,7 +517,9 @@ inline constexpr void jeaiii_main_len(char_type *iter, U n, ::std::uint_least32_ ::std::uint_least64_t alow{static_cast<::std::uint_least64_t>(a)}; if constexpr (ryu_mode) { - iter = jeaiii_main_len(iter, static_cast<::std::uint_least64_t>(alow), len - full_length); + ::std::uint_least32_t len_sub{len - full_length}; + jeaiii_main_len(iter, static_cast<::std::uint_least64_t>(alow), len_sub); + iter += len_sub; } else { diff --git a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h index d6791a40f..a81108b3e 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/decfloat.h @@ -2352,6 +2352,24 @@ scan_decfloat_exponent(char_type const *first, char_type const *last, ::std::int return ::fast_io::details::scan_hexfloat_exponent(first, last, exponent); } +template <::std::integral char_type> +[[nodiscard]] inline constexpr bool scan_decfloat_exponent_prefix_may_extend(char_type const *first, + char_type const *last) noexcept +{ + constexpr auto plus{::fast_io::char_literal_v}; + constexpr auto minus{::fast_io::char_literal_v}; + ++first; + if (first == last) + { + return true; + } + if ((*first == plus || *first == minus) && first + 1 == last) + { + return true; + } + return false; +} + template <::std::integral char_type> [[nodiscard]] inline constexpr bool scan_decfloat_special_start_char(char_type ch) noexcept { @@ -2815,6 +2833,10 @@ scan_decfloat_contiguous_short_define_impl(char_type const *begin, char_type con { first = exponent_result.iter; } + else if (::fast_io::details::scan_decfloat_exponent_prefix_may_extend(exponent_begin, end)) + { + return {end, ::fast_io::parse_code::partial, true}; + } else if constexpr (flags.floating == ::fast_io::manipulators::floating_format::scientific) { return {exponent_result.iter, exponent_result.code, true}; @@ -2940,6 +2962,10 @@ scan_decfloat_contiguous_define_impl(char_type const *begin, char_type const *en { first = exponent_result.iter; } + else if (::fast_io::details::scan_decfloat_exponent_prefix_may_extend(exponent_begin, end)) + { + return {end, ::fast_io::parse_code::partial}; + } else if constexpr (flags.floating == ::fast_io::manipulators::floating_format::scientific) { return exponent_result; diff --git a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h index 745bf7f50..406ffbc5b 100644 --- a/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h +++ b/third-parties/fast_io/include/fast_io_unit/floating/hexfloat.h @@ -1416,7 +1416,10 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i ::std::size_t total_precision{precision}; if constexpr (fractional_precision) { - ++total_precision; + if (total_precision != static_cast<::std::size_t>(-1)) + { + ++total_precision; + } } else if (!total_precision) { @@ -1494,7 +1497,7 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i if (exponent != 0 && digits[0] == 2u) { digits[0] = 1u; - for (::std::size_t index{1u}; index != retained_digits; ++index) + for (::std::size_t index{1u}; index < retained_digits; ++index) { digits[index] = 0u; } @@ -1519,7 +1522,7 @@ inline constexpr char_type *print_rsvhexfloat_precision_define_impl(char_type *i *iter = char_literal_v<(comma ? u8',' : u8'.'), char_type>; ++iter; auto const digit_limit{digits_to_print < retained_digits ? digits_to_print : retained_digits}; - for (::std::size_t index{1u}; index != digit_limit; ++index) + for (::std::size_t index{1u}; index < digit_limit; ++index) { iter = print_rsvhexfloat_digit_impl(iter, digits[index]); } From ca70bb1cca737a7c28e5dd1aa76d4b3f9c4da912 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 14:40:53 +0800 Subject: [PATCH 43/45] Refactor scatter fallback and coalescing thresholds in fast_io - Updated the `scatter_fallback_full_output_threshold` and `full_output_coalesce_threshold` functions to improve performance by adjusting the thresholds and refining comments for clarity. - Set the `full_output_coalesce_threshold` to 2048 bytes to optimize output for larger payloads while ensuring efficient handling of smaller runs. - Enhanced documentation to reflect the new approach and its implications for POSIX environments. --- .../include/fast_io_hosted/platforms/posix.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h index 65a78c4d6..76db493e4 100644 --- a/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h +++ b/third-parties/fast_io/include/fast_io_hosted/platforms/posix.h @@ -550,20 +550,23 @@ template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t scatter_fallback_full_output_threshold( ::fast_io::io_reserve_type_t>) noexcept { - // POSIX has native writev. /dev/null measurements show that copying scatter payloads into temporary - // contiguous storage loses to writing the original iovec chain, even for tiny scatter runs. + // POSIX has native writev. Measurements show that scatter-fallback copying is not a good default once + // whole-run materialization is available for compact output. return 0u; } +#endif template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t full_output_coalesce_threshold( ::fast_io::io_reserve_type_t>) noexcept { - // Scatter-only public print paths are faster when they build descriptors and call writev directly. Full - // materialization can still be useful for streams without native scatter output, but POSIX does not opt in. - return 0u; + // Compact whole-output runs are copied into one contiguous buffer before a single write. This improves real + // file/log output patterns on measured POSIX kernels; syscall-shell sinks such as /dev/null-like streams should + // opt out with a zero threshold in their own stream policy. + return 2048u; } +#if 0 template <::fast_io::posix_family family, ::std::integral char_type> inline constexpr ::std::size_t small_scatter_coalesce_threshold( ::fast_io::io_reserve_type_t>) noexcept From 206a6ad4f2c65d0888f10baf5bd84a05fe4f77e2 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 15:00:08 +0800 Subject: [PATCH 44/45] Enhance small scatter repacking logic in fast_io - Introduced new functions to manage small scatter repacking, including `print_small_scatter_repack_size` and `print_scatter_write_all_bytes_try_repack_small`, optimizing memory usage during output operations. - Improved documentation with detailed comments explaining the behavior and thresholds for small scatter repacking, ensuring clarity on performance implications. - Enhanced logic to conditionally repack small byte scatter segments, allowing for more efficient output handling based on stream capabilities. --- .../printimpl/print_freestanding_cxx20.h | 168 ++++++++++++++++-- 1 file changed, 156 insertions(+), 12 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 0a02f1f1e..6a62d0f7a 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -578,22 +578,33 @@ inline constexpr char_type *print_small_scatter_copy_n(char_type const *first, : return ::fast_io::details::non_overlapped_copy_n(first, count, result); } +/// @brief Returns the per-segment size threshold used by small-scatter repacking. +/// @details Streams without the customization keep small-scatter repacking disabled. +/// @tparam char_type the output character type +/// @tparam outputstmtype the decayed output stream reference type +/// @return ::std::size_t the maximum segment size in characters, or zero when disabled template <::std::integral char_type, typename outputstmtype> inline constexpr ::std::size_t print_small_scatter_repack_size() noexcept { if constexpr (::fast_io::small_scatter_coalesce_threshold_stream) { + // Stream-provided repack thresholds opt into copying small scatter segments. return small_scatter_coalesce_threshold( ::fast_io::io_reserve_type>); } else { + // Streams without the customization keep native scatter descriptors untouched. return 0u; } } +/// @brief Minimum descriptor reduction required before small-scatter repacking is considered worthwhile. inline constexpr ::std::size_t print_scatter_repack_min_saved_scatters{8u}; +/// @brief Caps temporary scatter descriptor storage used by the repack emitter. +/// @tparam scatter_type the scatter descriptor type being emitted +/// @return ::std::size_t the stack descriptor count, capped at 64 descriptors template inline constexpr ::std::size_t print_scatter_repack_stack_scatter_count() noexcept { @@ -601,14 +612,24 @@ inline constexpr ::std::size_t print_scatter_repack_stack_scatter_count() noexce ::fast_io::details::decay::print_stack_buffer_max_element_count()}; if constexpr (stack_count < 64u) { + // Very small stack policies use their exact descriptor capacity. return stack_count; } else { + // Larger stack policies are capped to keep the hot repack frame bounded. return 64u; } } +/// @brief Tries to repack small byte scatter segments before byte scatter output. +/// @details The first pass plans descriptor reduction and buffer needs; the second pass emits either preserved +/// descriptors or copied coalesced chunks. Repacking is skipped unless enough descriptors are saved. +/// @tparam outputstmtype the decayed output stream reference type +/// @param outstm the output stream reference +/// @param scatters the first byte scatter descriptor +/// @param n the number of descriptors +/// @return bool true when this function emitted the output through the repacked descriptor list template inline constexpr bool print_scatter_write_all_bytes_try_repack_small( outputstmtype outstm, ::fast_io::io_scatter_t const *scatters, ::std::size_t n) @@ -621,6 +642,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( if constexpr (threshold_chars == 0 || small_chars == 0 || !::fast_io::details::decay::print_has_direct_scatter_write_bytes_operations) { + // Repacking requires both stream opt-in and a native byte-scatter output path. return false; } else @@ -629,6 +651,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( constexpr ::std::size_t small_bytes{small_chars * sizeof(char_type)}; if constexpr (chunk_bytes == 0) { + // A zero-capacity chunk cannot hold any repacked byte payload. return false; } else @@ -642,19 +665,23 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( auto flush_plan = [&]() constexpr { if (current_count == 0) { + // No pending small descriptors are available for the current planned chunk. return; } ++output_count; if (1u < current_count) { + // Multiple small descriptors in one chunk mean the final output descriptor count can shrink. has_coalesced_chunk = true; if (stack_chunk_used) { + // The first copied chunk uses stack storage; later copied chunks need dynamic storage. dynamic_buffer_size = ::fast_io::details::intrinsics::add_or_overflow_die(dynamic_buffer_size, current_size); } else { + // Reserve the first copied chunk for the fixed stack buffer. stack_chunk_used = true; } } @@ -663,19 +690,23 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( }; for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { + // The planning pass preserves large segments and groups adjacent small segments into chunks. ::std::size_t const len{iter->len}; if (len == 0) { + // Empty descriptors do not affect output or descriptor pressure. continue; } if (small_bytes < len) { + // Large byte segments remain native scatter descriptors. flush_plan(); ++output_count; continue; } if (chunk_bytes - current_size < len) { + // The current copied chunk is full enough; start a new planned chunk. flush_plan(); } current_size += len; @@ -685,6 +716,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( if (!has_coalesced_chunk || output_count > n || n - output_count < print_scatter_repack_min_saved_scatters) { + // Avoid extra copying unless the plan actually reduces enough scatter descriptors. return false; } @@ -692,10 +724,12 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( ::fast_io::details::local_operator_new_array_ptr<::std::byte> dynamic_buffer; if (dynamic_buffer_size != 0) { + // Additional copied chunks beyond the first stack chunk use one dynamic byte buffer. dynamic_buffer.allocate_new(dynamic_buffer_size); } auto emit_repacked = [&](::fast_io::io_scatter_t *out_scatters) constexpr { + // The emit pass repeats the plan while producing the final descriptor chain. auto out_curr{out_scatters}; ::std::byte *dynamic_curr{dynamic_buffer.ptr}; ::std::byte *chunk_begin{}; @@ -708,6 +742,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( auto begin_chunk = [&]() constexpr { if (!stack_used) { + // The first copied chunk uses the fixed stack buffer. chunk_begin = stack_buffer; chunk_curr = stack_buffer; chunk_is_stack = true; @@ -715,6 +750,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( } else { + // Later copied chunks append into the pre-sized dynamic buffer. chunk_begin = dynamic_curr; chunk_curr = dynamic_curr; chunk_is_stack = false; @@ -723,19 +759,23 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( auto flush_chunk = [&]() constexpr { if (chunk_count == 0) { + // No pending chunk exists, so the output descriptor cursor is unchanged. return; } if (chunk_count == 1) { + // A single small descriptor was not copied; preserve it as-is. *out_curr = pending; ++out_curr; } else { + // Multiple small descriptors were copied into one contiguous output chunk. *out_curr = {chunk_begin, chunk_size}; ++out_curr; if (!chunk_is_stack) { + // Commit the dynamic-buffer cursor only after a dynamic chunk is emitted. dynamic_curr = chunk_curr; } } @@ -747,13 +787,16 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( }; for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { + // Rebuild the planned descriptor chain while copying only coalesced small chunks. ::std::size_t const len{iter->len}; if (len == 0) { + // Empty descriptors are skipped in the emitted chain. continue; } if (small_bytes < len) { + // Large byte segments flush pending small chunks and remain direct scatter entries. flush_chunk(); *out_curr = *iter; ++out_curr; @@ -761,10 +804,12 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( } if (chunk_bytes - chunk_size < len) { + // The pending copied chunk cannot fit this segment. flush_chunk(); } if (chunk_count == 0) { + // The first small descriptor is kept pending until a second one proves copying is useful. pending = *iter; chunk_size = len; chunk_count = 1; @@ -772,6 +817,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( } if (chunk_count == 1) { + // The second small descriptor starts an actual copied chunk. begin_chunk(); auto const first{static_cast<::std::byte const *>(pending.base)}; chunk_curr = ::fast_io::details::decay::print_small_scatter_copy_n(first, pending.len, @@ -788,6 +834,7 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( { if (repacked_count == 1) { + // A single repacked byte range can bypass scatter output entirely. auto first{static_cast<::std::byte const *>(out_scatters->base)}; ::fast_io::operations::decay::write_all_bytes_decay(outstm, first, first + out_scatters->len); @@ -803,11 +850,13 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( { if (output_count <= stack_scatter_count) { + // The final descriptor chain fits in bounded stack storage. ::fast_io::io_scatter_t out_scatters[stack_scatter_count]; emit_repacked(out_scatters); return true; } } + // Large repacked descriptor chains allocate descriptor storage before emission. ::fast_io::details::local_operator_new_array_ptr<::fast_io::io_scatter_t> out_scatters(output_count); emit_repacked(out_scatters.ptr); return true; @@ -815,6 +864,13 @@ inline constexpr bool print_scatter_write_all_bytes_try_repack_small( } } +/// @brief Tries to repack small character scatter segments before character scatter output. +/// @details This mirrors the byte-scatter repack path, but sizes chunks and descriptors in char_type units. +/// @tparam outputstmtype the decayed output stream reference type +/// @param outstm the output stream reference +/// @param scatters the first character scatter descriptor +/// @param n the number of descriptors +/// @return bool true when this function emitted the output through the repacked descriptor list template inline constexpr bool print_scatter_write_all_try_repack_small( outputstmtype outstm, @@ -828,6 +884,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( if constexpr (threshold_chars == 0 || small_chars == 0 || !::fast_io::details::decay::print_has_direct_scatter_write_operations) { + // Repacking requires both stream opt-in and a native character-scatter output path. return false; } else @@ -835,6 +892,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( constexpr ::std::size_t chunk_chars{threshold_chars - 1u}; if constexpr (chunk_chars == 0) { + // A zero-capacity chunk cannot hold any repacked character payload. return false; } else @@ -849,19 +907,23 @@ inline constexpr bool print_scatter_write_all_try_repack_small( auto flush_plan = [&]() constexpr { if (current_count == 0) { + // No pending small descriptors are available for the current planned chunk. return; } ++output_count; if (1u < current_count) { + // Multiple small descriptors in one chunk mean the final output descriptor count can shrink. has_coalesced_chunk = true; if (stack_chunk_used) { + // The first copied chunk uses stack storage; later copied chunks need dynamic storage. dynamic_buffer_size = ::fast_io::details::intrinsics::add_or_overflow_die(dynamic_buffer_size, current_size); } else { + // Reserve the first copied chunk for the fixed stack buffer. stack_chunk_used = true; } } @@ -870,19 +932,23 @@ inline constexpr bool print_scatter_write_all_try_repack_small( }; for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { + // The planning pass preserves large segments and groups adjacent small segments into chunks. ::std::size_t const len{iter->len}; if (len == 0) { + // Empty descriptors do not affect output or descriptor pressure. continue; } if (small_chars < len) { + // Large character segments remain native scatter descriptors. flush_plan(); ++output_count; continue; } if (chunk_chars - current_size < len) { + // The current copied chunk is full enough; start a new planned chunk. flush_plan(); } current_size += len; @@ -892,6 +958,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( if (!has_coalesced_chunk || output_count > n || n - output_count < print_scatter_repack_min_saved_scatters) { + // Avoid extra copying unless the plan actually reduces enough scatter descriptors. return false; } @@ -899,10 +966,12 @@ inline constexpr bool print_scatter_write_all_try_repack_small( ::fast_io::details::local_operator_new_array_ptr dynamic_buffer; if (dynamic_buffer_size != 0) { + // Additional copied chunks beyond the first stack chunk use one dynamic character buffer. dynamic_buffer.allocate_new(dynamic_buffer_size); } auto emit_repacked = [&](scatter_type *out_scatters) constexpr { + // The emit pass repeats the plan while producing the final descriptor chain. auto out_curr{out_scatters}; char_type *dynamic_curr{dynamic_buffer.ptr}; char_type *chunk_begin{}; @@ -915,6 +984,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( auto begin_chunk = [&]() constexpr { if (!stack_used) { + // The first copied chunk uses the fixed stack buffer. chunk_begin = stack_buffer; chunk_curr = stack_buffer; chunk_is_stack = true; @@ -922,6 +992,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( } else { + // Later copied chunks append into the pre-sized dynamic buffer. chunk_begin = dynamic_curr; chunk_curr = dynamic_curr; chunk_is_stack = false; @@ -930,19 +1001,23 @@ inline constexpr bool print_scatter_write_all_try_repack_small( auto flush_chunk = [&]() constexpr { if (chunk_count == 0) { + // No pending chunk exists, so the output descriptor cursor is unchanged. return; } if (chunk_count == 1) { + // A single small descriptor was not copied; preserve it as-is. *out_curr = pending; ++out_curr; } else { + // Multiple small descriptors were copied into one contiguous output chunk. *out_curr = {chunk_begin, chunk_size}; ++out_curr; if (!chunk_is_stack) { + // Commit the dynamic-buffer cursor only after a dynamic chunk is emitted. dynamic_curr = chunk_curr; } } @@ -954,13 +1029,16 @@ inline constexpr bool print_scatter_write_all_try_repack_small( }; for (auto iter{scatters}, last{scatters + n}; iter != last; ++iter) { + // Rebuild the planned descriptor chain while copying only coalesced small chunks. ::std::size_t const len{iter->len}; if (len == 0) { + // Empty descriptors are skipped in the emitted chain. continue; } if (small_chars < len) { + // Large character segments flush pending small chunks and remain direct scatter entries. flush_chunk(); *out_curr = *iter; ++out_curr; @@ -968,10 +1046,12 @@ inline constexpr bool print_scatter_write_all_try_repack_small( } if (chunk_chars - chunk_size < len) { + // The pending copied chunk cannot fit this segment. flush_chunk(); } if (chunk_count == 0) { + // The first small descriptor is kept pending until a second one proves copying is useful. pending = *iter; chunk_size = len; chunk_count = 1; @@ -979,6 +1059,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( } if (chunk_count == 1) { + // The second small descriptor starts an actual copied chunk. begin_chunk(); chunk_curr = ::fast_io::details::decay::print_small_scatter_copy_n( pending.base, pending.len, chunk_curr); @@ -994,6 +1075,7 @@ inline constexpr bool print_scatter_write_all_try_repack_small( { if (repacked_count == 1) { + // A single repacked character range can bypass scatter output entirely. auto [base, len] = *out_scatters; ::fast_io::operations::decay::write_all_decay(outstm, base, base + len); return; @@ -1008,11 +1090,13 @@ inline constexpr bool print_scatter_write_all_try_repack_small( { if (output_count <= stack_scatter_count) { + // The final descriptor chain fits in bounded stack storage. scatter_type out_scatters[stack_scatter_count]; emit_repacked(out_scatters); return true; } } + // Large repacked descriptor chains allocate descriptor storage before emission. ::fast_io::details::local_operator_new_array_ptr out_scatters(output_count); emit_repacked(out_scatters.ptr); return true; @@ -2485,22 +2569,27 @@ inline constexpr ::std::size_t print_n_scatter_total_size( { if constexpr (n == 0) { + // An empty scatter prefix contributes no characters. return 0; } else { + // A non-empty scatter prefix accumulates the current descriptor length and the tail length. using nocvreft = ::std::remove_cvref_t; ::std::size_t current{}; if constexpr (!::std::same_as) { + // Non-null scatter-printable arguments expose their character length through print_scatter_define. current = print_scatter_define(::fast_io::io_reserve_type, t).len; } if constexpr (n == 1) { + // The current argument is the final consumed scatter position. return current; } else { + // More scatter positions remain, so add the tail length with overflow checking. return ::fast_io::details::intrinsics::add_or_overflow_die( current, ::fast_io::details::decay::print_n_scatter_total_size(args...)); } @@ -2534,22 +2623,27 @@ inline constexpr char_type *print_n_scatter_materialize(char_type *ptr, { if constexpr (n == 0) { + // An empty scatter prefix leaves the materialization cursor unchanged. return ptr; } else { + // A non-empty scatter prefix copies the current range when it is not null output. using nocvreft = ::std::remove_cvref_t; if constexpr (!::std::same_as) { + // Scatter-printable arguments expose a contiguous range that can be copied into the output buffer. auto scatter{print_scatter_define(::fast_io::io_reserve_type, t)}; ptr = ::fast_io::details::decay::print_small_scatter_copy_n(scatter.base, scatter.len, ptr); } if constexpr (n == 1) { + // The current argument is the final consumed scatter position. return ptr; } else { + // More scatter positions remain, so continue from the advanced cursor. return ::fast_io::details::decay::print_n_scatter_materialize(ptr, args...); } } @@ -2572,31 +2666,63 @@ inline constexpr bool print_controls_scatters_try_materialize(outputstmtype opts { constexpr ::std::size_t threshold_chars{ ::fast_io::details::decay::print_full_output_coalesce_threshold()}; - if constexpr (threshold_chars != 0 && - ::fast_io::details::decay::print_has_direct_write_operations && - ::fast_io::details::decay::print_stack_buffer_size_within_limit) + if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations || + (threshold_chars != 0 && + ::fast_io::details::decay::print_has_direct_write_operations && + ::fast_io::details::decay::print_stack_buffer_size_within_limit)) { + // A coalescing-capable stream measures the complete scatter prefix before choosing storage. ::std::size_t const total_size{::fast_io::details::intrinsics::add_or_overflow_die( ::fast_io::details::decay::print_n_scatter_total_size(t, args...), static_cast<::std::size_t>(needprintlf))}; if (total_size == 0) { + // A zero-sized scatter prefix completes without touching the output stream. return true; } - if (total_size <= threshold_chars) + if constexpr (::fast_io::operations::decay::defines::has_obuffer_basic_operations) { - char_type buffer[threshold_chars]; - char_type *ptr{ - ::fast_io::details::decay::print_n_scatter_materialize(buffer, t, args...)}; - if constexpr (needprintlf) + // Buffered streams prefer direct materialization into the current put area. + char_type *const curr{obuffer_curr(optstm)}; + char_type *const end{obuffer_end(optstm)}; + if (static_cast<::std::size_t>(end - curr) >= total_size) { - *ptr = ::fast_io::char_literal_v; - ++ptr; + // The put area can hold the full scatter prefix, so no temporary stack buffer is needed. + char_type *ptr{ + ::fast_io::details::decay::print_n_scatter_materialize(curr, t, args...)}; + if constexpr (needprintlf) + { + // The line variant appends the trailing newline inside the same output-buffer commit. + *ptr = ::fast_io::char_literal_v; + ++ptr; + } + obuffer_set_curr(optstm, ptr); + return true; + } + } + if constexpr (threshold_chars != 0 && + ::fast_io::details::decay::print_has_direct_write_operations && + ::fast_io::details::decay::print_stack_buffer_size_within_limit) + { + // Unbuffered streams can still coalesce into a bounded stack buffer. + if (total_size <= threshold_chars) + { + // The full scatter prefix fits the stream's whole-output coalescing threshold. + char_type buffer[threshold_chars]; + char_type *ptr{ + ::fast_io::details::decay::print_n_scatter_materialize(buffer, t, args...)}; + if constexpr (needprintlf) + { + // The line variant appends the trailing newline before the stack buffer is written. + *ptr = ::fast_io::char_literal_v; + ++ptr; + } + ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); + return true; } - ::fast_io::operations::decay::write_all_decay(optstm, buffer, ptr); - return true; } } + // The scatter prefix could not be coalesced by this helper; callers should use the normal scatter path. return false; } @@ -3962,6 +4088,11 @@ struct print_semantic_lvalue_prefix_continuation } }; +/// @brief Builds a tuple of pointers used to preserve expanded semantic pack element lifetimes. +/// @details The helper centralizes the clang missing-braces suppression needed by fast_io's tuple aggregate form. +/// @tparam Args the pointer argument types stored in the tuple +/// @param args the pointers to store +/// @return ::fast_io::containers::tuple containing the supplied pointers template [[nodiscard]] inline constexpr ::fast_io::containers::tuple print_semantic_pack_pointer_tuple(Args... args) noexcept @@ -5951,6 +6082,7 @@ inline constexpr void print_semantic_emit_width(outputstmtype optstm, T &&t) } /// @brief Emits one semantic node to an output stream. +/// @brief Emits one semantic node through the checked stream path. /// @details The checked node dispatcher handles packs, conditions, and width nodes before returning control to the /// flattened semantic emitter. /// @tparam line true when this node is responsible for the caller's trailing newline @@ -6271,11 +6403,17 @@ inline constexpr decltype(auto) print_freestanding_decay_cold(outputstmtype opts namespace decay::defines { +/// @brief Validates already-forwarded freestanding print parameters for one character type. +/// @tparam char_type the output character type +/// @tparam Args the already-forwarded argument types template concept print_freestanding_params_okay = ::std::integral && (::fast_io::details::decay::print_freestanding_decay_param_okay_single::value && ...); +/// @brief Validates already-decayed freestanding print arguments for one output stream reference. +/// @tparam output the decayed output stream reference type +/// @tparam Args the already-forwarded argument types template concept print_freestanding_okay = ::fast_io::operations::decay::defines::print_freestanding_params_okay; @@ -6285,10 +6423,16 @@ concept print_freestanding_okay = namespace defines { +/// @brief Validates public freestanding print parameters after aliasing and print forwarding. +/// @tparam char_type the output character type +/// @tparam Args the original public argument types template concept print_freestanding_params_okay = ::fast_io::operations::decay::defines::print_freestanding_params_okay(::fast_io::io_print_alias(::std::declval())))...>; +/// @brief Validates public freestanding print arguments for an output stream object. +/// @tparam output the public output stream object type +/// @tparam Args the original public argument types template concept print_freestanding_okay = ::fast_io::operations::decay::defines::print_freestanding_okay< decltype(::fast_io::operations::output_stream_ref(::std::declval())), From 18dd980b176df2e13919095daf6a8fc4ffcb3ef7 Mon Sep 17 00:00:00 2001 From: MacroModel Date: Thu, 9 Jul 2026 16:36:07 +0800 Subject: [PATCH 45/45] Enhance concatenation and print semantic handling in fast_io - Introduced new templates and structures to improve the handling of concatenation conditions, allowing for more precise control over output based on argument types. - Added functions to compute static bounded sizes for print operations, optimizing memory usage and ensuring accurate size calculations during semantic printing. - Enhanced existing logic to support top-level conditions in concatenation, improving the efficiency of output operations and reducing unnecessary complexity. - Updated documentation to clarify the new functionalities and their implications for performance in various scenarios. --- .../fast_io_core_impl/concat/concat_general.h | 142 +++++++++++++++++- .../fast_io_core_impl/manipulators/traits.h | 104 +++++++++++++ .../printimpl/print_freestanding_cxx20.h | 87 +++++++++-- 3 files changed, 320 insertions(+), 13 deletions(-) diff --git a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h index a14f5843c..62c05e3e0 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h +++ b/third-parties/fast_io/include/fast_io_core_impl/concat/concat_general.h @@ -395,6 +395,130 @@ inline constexpr bool basic_general_concat_semantic_precise_ok = (false || ... || ::fast_io::details::decay::print_semantic_node) && (::fast_io::details::decay::print_semantic_precise_size_ok>::value && ...); +template +inline constexpr void basic_general_concat_decay_ref_impl(T &str, Args... args); + +template +inline constexpr T basic_general_concat_phase1_decay_impl(Args... args); + +template +inline constexpr bool basic_general_concat_top_level_condition_v = + ::fast_io::details::decay::print_semantic_node && + ::fast_io::details::decay::print_semantic_condition_v<::std::remove_cvref_t()))>>; + +template +inline constexpr bool basic_general_concat_has_top_level_condition_v = + (false || ... || ::fast_io::details::decay::basic_general_concat_top_level_condition_v); + +template +struct basic_general_concat_condition_prefix_continuation +{ + ::std::remove_reference_t *contptr; + ::std::remove_reference_t *valueptr; + + template + inline constexpr decltype(auto) operator()(TailArgs &&...tail_args) const + { + return (*contptr)(::std::forward(*valueptr), ::std::forward(tail_args)...); + } +}; + +template <::std::integral ch_type, typename continuation> +inline constexpr decltype(auto) basic_general_concat_select_condition(continuation &&cont) +{ + return ::std::forward(cont)(); +} + +template <::std::integral ch_type, typename continuation, typename T, typename... Args> +inline constexpr decltype(auto) basic_general_concat_select_condition(continuation &&cont, T &&t, Args &&...args) +{ + if constexpr (::fast_io::details::decay::basic_general_concat_top_level_condition_v) + { + auto &&node_ref{::fast_io::details::decay::print_semantic_node_ref(::std::forward(t))}; + if (node_ref.pred) + { + using branch_type = decltype(node_ref.t1); + if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::std::forward(cont), ::std::forward(args)...); + } + else if constexpr (::fast_io::details::decay::basic_general_concat_top_level_condition_v) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::std::forward(cont), node_ref.t1, ::std::forward(args)...); + } + else + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::fast_io::details::decay::basic_general_concat_condition_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(node_ref.t1)}, + ::std::forward(args)...); + } + } + else + { + using branch_type = decltype(node_ref.t2); + if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::std::forward(cont), ::std::forward(args)...); + } + else if constexpr (::fast_io::details::decay::basic_general_concat_top_level_condition_v) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::std::forward(cont), node_ref.t2, ::std::forward(args)...); + } + else + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::fast_io::details::decay::basic_general_concat_condition_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(node_ref.t2)}, + ::std::forward(args)...); + } + } + } + else if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::std::forward(cont), ::std::forward(args)...); + } + else + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::fast_io::details::decay::basic_general_concat_condition_prefix_continuation{ + __builtin_addressof(cont), __builtin_addressof(t)}, + ::std::forward(args)...); + } +} + +template +struct basic_general_concat_select_condition_ref_continuation +{ + T *strptr; + + template + inline constexpr void operator()(Args &&...args) const + { + ::fast_io::details::decay::basic_general_concat_decay_ref_impl( + *strptr, ::std::forward(args)...); + } +}; + +template +struct basic_general_concat_select_condition_phase1_continuation +{ + template + inline constexpr T operator()(Args &&...args) const + { + return ::fast_io::details::decay::basic_general_concat_phase1_decay_impl( + ::std::forward(args)...); + } +}; + template inline constexpr void basic_general_concat_decay_ref_impl_semantic_precise(T &str, Args... args) { @@ -420,7 +544,14 @@ inline constexpr void basic_general_concat_decay_ref_impl_semantic_precise(T &st template inline constexpr void basic_general_concat_decay_ref_impl(T &str, Args... args) { - if constexpr (basic_general_concat_semantic_precise_ok) + if constexpr (basic_general_concat_has_top_level_condition_v) + { + ::fast_io::details::decay::basic_general_concat_select_condition( + ::fast_io::details::decay::basic_general_concat_select_condition_ref_continuation{ + __builtin_addressof(str)}, + args...); + } + else if constexpr (basic_general_concat_semantic_precise_ok) { basic_general_concat_decay_ref_impl_semantic_precise(str, args...); } @@ -514,7 +645,14 @@ inline constexpr T basic_general_concat_phase1_decay_impl(Args... args) } else { - if constexpr (basic_general_concat_semantic_precise_ok) + if constexpr (basic_general_concat_has_top_level_condition_v) + { + return ::fast_io::details::decay::basic_general_concat_select_condition( + ::fast_io::details::decay::basic_general_concat_select_condition_phase1_continuation{}, + args...); + } + else if constexpr (basic_general_concat_semantic_precise_ok) { if constexpr (buffer_strlike) { diff --git a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h index f476d8543..dfdca5ec5 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h +++ b/third-parties/fast_io/include/fast_io_core_impl/manipulators/traits.h @@ -180,6 +180,110 @@ struct print_semantic_static_precise_size_impl +struct print_semantic_static_bounded_size_impl +{ + inline static constexpr bool available = + ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || + (!::fast_io::details::decay::print_semantic_parameter_object_v && + (::fast_io::static_precise_reserve_printable || + ::fast_io::reserve_printable)); + + inline static consteval ::std::size_t static_size() noexcept + { + if constexpr (::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t>) + { + return 0u; + } + else if constexpr (!::fast_io::details::decay::print_semantic_parameter_object_v && + ::fast_io::static_precise_reserve_printable) + { + return print_reserve_static_precise_size(::fast_io::io_reserve_type); + } + else if constexpr (!::fast_io::details::decay::print_semantic_parameter_object_v && + ::fast_io::reserve_printable) + { + return print_reserve_size(::fast_io::io_reserve_type); + } + else + { + return SIZE_MAX; + } + } + + inline static constexpr ::std::size_t size{static_size()}; +}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_bounded_size + : ::fast_io::details::decay::print_semantic_static_bounded_size_impl> +{}; + +template <::std::integral char_type, typename T> +struct print_semantic_static_bounded_size_impl> + : ::fast_io::details::decay::print_semantic_static_bounded_size +{}; + +template <::std::integral char_type, typename... Args> +struct print_semantic_static_bounded_size_impl> +{ + inline static constexpr bool available = + (::fast_io::details::decay::print_semantic_static_bounded_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>::available && + ...); + + inline static consteval ::std::size_t static_size() noexcept + { + if constexpr (available) + { + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::details::decay::print_semantic_static_bounded_size< + char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t>::size)), + ...); + return total; + } + else + { + return SIZE_MAX; + } + } + + inline static constexpr ::std::size_t size{static_size()}; +}; + +template <::std::integral char_type, typename T1, typename T2> +struct print_semantic_static_bounded_size_impl> +{ + using first_size = ::fast_io::details::decay::print_semantic_static_bounded_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + using second_size = ::fast_io::details::decay::print_semantic_static_bounded_size< + char_type, ::fast_io::details::decay::print_semantic_forwarded_arg_t>; + inline static constexpr bool available = first_size::available && second_size::available; + + inline static consteval ::std::size_t static_size() noexcept + { + if constexpr (available) + { + if constexpr (first_size::size < second_size::size) + { + return second_size::size; + } + else + { + return first_size::size; + } + } + else + { + return SIZE_MAX; + } + } + + inline static constexpr ::std::size_t size{static_size()}; +}; + template <::std::integral char_type, typename T> inline constexpr bool print_semantic_precise_leaf_size_ok_v = ::std::same_as<::std::remove_cvref_t, ::fast_io::io_null_t> || diff --git a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h index 6a62d0f7a..1f669d386 100644 --- a/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h +++ b/third-parties/fast_io/include/fast_io_core_impl/operations/printimpl/print_freestanding_cxx20.h @@ -5338,6 +5338,33 @@ inline consteval ::std::size_t print_semantic_static_precise_total_size() noexce } } +/// @brief Computes the total static upper bound for a semantic print run. +/// @details The value is available when every argument has a compile-time maximum semantic size. +/// @tparam line true when a trailing newline is part of the run +/// @tparam char_type the character type of the output run +/// @tparam Args the argument types in the run +/// @return ::std::size_t the static total upper bound, or SIZE_MAX when any argument is not statically bounded +template +inline consteval ::std::size_t print_semantic_static_bounded_total_size() noexcept +{ + if constexpr ((::fast_io::details::decay::print_semantic_static_bounded_size::available && ...)) + { + ::std::size_t total{}; + ((total = ::fast_io::details::intrinsics::add_or_overflow_die( + total, ::fast_io::details::decay::print_semantic_static_bounded_size::size)), + ...); + if constexpr (line) + { + total = ::fast_io::details::intrinsics::add_or_overflow_die(total, static_cast<::std::size_t>(1u)); + } + return total; + } + else + { + return SIZE_MAX; + } +} + /// @brief Computes the run-time precise size for a semantic print run. /// @details This is used by checked coalescing paths before allocating or reserving contiguous output space. /// @tparam line true when a trailing newline is part of the run @@ -5570,15 +5597,36 @@ inline constexpr bool print_semantic_try_bounded_coalesce(outputstmtype optstm, ::fast_io::details::decay::print_full_output_coalesce_threshold()}; if constexpr (threshold_chars != 0) { - // Unbuffered streams use a stack buffer when the upper bound is below their full-output threshold. - if (required <= threshold_chars) + constexpr ::std::size_t static_bound{ + ::fast_io::operations::decay::print_semantic_static_bounded_total_size< + line, char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t...>()}; + if constexpr (static_bound != SIZE_MAX && static_bound <= threshold_chars) { - char_type buffer[threshold_chars]; - char_type *const iter{ - ::fast_io::operations::decay::print_semantic_emit_unchecked_run( - buffer, ::std::forward(args)...)}; - ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); - return true; + // Statically bounded runs use the proven maximum instead of the stream threshold as frame size. + if (required <= static_bound) + { + constexpr ::std::size_t buffer_size{static_bound == 0 ? 1u : static_bound}; + char_type buffer[buffer_size]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } + } + else + { + // Runs without a static upper bound preserve the historical threshold-sized stack buffer. + if (required <= threshold_chars) + { + char_type buffer[threshold_chars]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } } constexpr ::std::size_t materialize_limit{ ::fast_io::details::decay::print_stack_buffer_max_size()}; @@ -5664,10 +5712,27 @@ inline constexpr bool print_semantic_try_precise_coalesce(outputstmtype optstm, } else { - // Non-static precise runs can still coalesce when the measured size fits the stack threshold. - if (required <= threshold_chars) + constexpr ::std::size_t static_bound{ + ::fast_io::operations::decay::print_semantic_static_bounded_total_size< + line, char_type, + ::fast_io::details::decay::print_semantic_forwarded_arg_t...>()}; + if constexpr (static_bound != SIZE_MAX && static_bound <= threshold_chars) + { + // Unequal static alternatives are not precise as a type, but their maximum still bounds the frame. + if (required <= static_bound) + { + constexpr ::std::size_t buffer_size{static_bound == 0 ? 1u : static_bound}; + char_type buffer[buffer_size]; + char_type *const iter{ + ::fast_io::operations::decay::print_semantic_emit_unchecked_run( + buffer, ::std::forward(args)...)}; + ::fast_io::operations::decay::write_all_decay(optstm, buffer, iter); + return true; + } + } + else if (required <= threshold_chars) { - // A run-time bounded run uses the threshold-sized stack buffer and one write operation. + // Non-static precise runs can still coalesce when the measured size fits the stack threshold. char_type buffer[threshold_chars]; char_type *const iter{ ::fast_io::operations::decay::print_semantic_emit_unchecked_run(