diff --git a/libcudacxx/include/cuda/std/__format/buffer.h b/libcudacxx/include/cuda/std/__format/buffer.h index 1b30e5edff4..11732bed8cb 100644 --- a/libcudacxx/include/cuda/std/__format/buffer.h +++ b/libcudacxx/include/cuda/std/__format/buffer.h @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/libcudacxx/include/cuda/std/__format/format_to_n.h b/libcudacxx/include/cuda/std/__format/format_to_n.h new file mode 100644 index 00000000000..232431a94eb --- /dev/null +++ b/libcudacxx/include/cuda/std/__format/format_to_n.h @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#ifndef _CUDA_STD___FORMAT_FORMAT_TO_N_H +#define _CUDA_STD___FORMAT_FORMAT_TO_N_H + +#include + +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) +# pragma GCC system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) +# pragma clang system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) +# pragma system_header +#endif // no system header + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +_CCCL_BEGIN_NAMESPACE_CUDA_STD + +template +struct _CCCL_TYPE_VISIBILITY_DEFAULT format_to_n_result +{ + _OutIt out; + iter_difference_t<_OutIt> size; +}; +_CCCL_CTAD_SUPPORTED_FOR_TYPE(format_to_n_result); + +// A buffer that counts and limits the number of insertions. +template +class __fmt_format_to_n_buffer : __fmt_buffer_select_t<_OutIt, _CharT> +{ + __fmt_max_output_size __max_output_size_; + +public: + using _Base _CCCL_NODEBUG_ALIAS = __fmt_buffer_select_t<_OutIt, _CharT>; + + _CCCL_API constexpr __fmt_format_to_n_buffer(_OutIt __out_it, iter_difference_t<_OutIt> __n) + : _Base{::cuda::std::move(__out_it), &__max_output_size_} + , __max_output_size_{::cuda::std::cmp_less(__n, 0) ? size_t{0} : static_cast(__n)} + {} + + [[nodiscard]] _CCCL_API constexpr auto __make_output_iterator() + { + return _Base::__make_output_iterator(); + } + + [[nodiscard]] _CCCL_API constexpr format_to_n_result<_OutIt> __result() && + { + return {static_cast<_Base&&>(*this).__out_it(), + static_cast>(__max_output_size_.__code_units_written())}; + } +}; + +template +[[nodiscard]] _CCCL_API format_to_n_result<_OutIt> __format_to_n_impl( + _OutIt __out_it, iter_difference_t<_OutIt> __n, basic_string_view<_CharT> __fmt, basic_format_args<_Context> __args) +{ + __fmt_format_to_n_buffer<_OutIt, _CharT> __buffer{::cuda::std::move(__out_it), __n}; + (void) ::cuda::std::__fmt_vformat_to( + basic_format_parse_context{__fmt, __args.__size()}, + ::cuda::std::__fmt_make_format_context(__buffer.__make_output_iterator(), __args)); + return ::cuda::std::move(__buffer).__result(); +} + +_CCCL_TEMPLATE(class _OutIt, class... _Args) +_CCCL_REQUIRES(output_iterator<_OutIt, const char&>) +/*discard*/ _CCCL_API format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, format_string<_Args...> __fmt, _Args&&... __args) +{ + return ::cuda::std::__format_to_n_impl( + ::cuda::std::move(__out_it), __n, __fmt.get(), ::cuda::std::make_format_args(__args...)); +} + +#if _CCCL_HAS_WCHAR_T() +_CCCL_TEMPLATE(class _OutIt, class... _Args) +_CCCL_REQUIRES(output_iterator<_OutIt, const wchar_t&>) +/*discard*/ _CCCL_API format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wformat_string<_Args...> __fmt, _Args&&... __args) +{ + return ::cuda::std::__format_to_n_impl( + ::cuda::std::move(__out_it), __n, __fmt.get(), ::cuda::std::make_wformat_args(__args...)); +} +#endif // _CCCL_HAS_WCHAR_T() + +_CCCL_END_NAMESPACE_CUDA_STD + +#include + +#endif // _CUDA_STD___FORMAT_FORMAT_TO_N_H diff --git a/libcudacxx/include/cuda/std/__format_ b/libcudacxx/include/cuda/std/__format_ index 23740836d76..6a4421c5e40 100644 --- a/libcudacxx/include/cuda/std/__format_ +++ b/libcudacxx/include/cuda/std/__format_ @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/libcudacxx/include/cuda/std/__fwd/format.h b/libcudacxx/include/cuda/std/__fwd/format.h index 90d1d6dd98a..a5a9eac7aab 100644 --- a/libcudacxx/include/cuda/std/__fwd/format.h +++ b/libcudacxx/include/cuda/std/__fwd/format.h @@ -100,6 +100,9 @@ template using wformat_string = basic_format_string...>; #endif // _CCCL_HAS_WCHAR_T() +template +struct _CCCL_TYPE_VISIBILITY_DEFAULT format_to_n_result; + _CCCL_END_NAMESPACE_CUDA_STD #include diff --git a/libcudacxx/test/libcudacxx/std/text/format/format.functions/checkers/format_to_n.h b/libcudacxx/test/libcudacxx/std/text/format/format.functions/checkers/format_to_n.h new file mode 100644 index 00000000000..97ba2721d18 --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/text/format/format.functions/checkers/format_to_n.h @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include + +#include "format_functions_common.h" +#include "test_macros.h" + +// Marking checkers with _CCCL_NOINLINE greatly improves ptxas compile times. + +template +TEST_FUNC _CCCL_NOINLINE bool +check(cuda::std::basic_string_view expected, test_format_string fmt, Args&&... args) +{ + assert(expected.size() < 4096 && "Update the size of the buffer."); + { + cuda::std::inplace_vector out; + const auto result = + cuda::std::format_to_n(cuda::std::__back_insert_iterator{out}, 0, fmt, cuda::std::forward(args)...); + if (cuda::std::cmp_not_equal(result.size, expected.size())) + { + return false; + } + if (!out.empty()) + { + return false; + } + } + { + constexpr auto n = 5; + cuda::std::inplace_vector out; + const auto result = + cuda::std::format_to_n(cuda::std::__back_insert_iterator{out}, n, fmt, cuda::std::forward(args)...); + if (cuda::std::cmp_not_equal(result.size, expected.size())) + { + return false; + } + const auto size = cuda::std::min(cuda::std::size_t{n}, expected.size()); + if (!cuda::std::equal(out.begin(), out.end(), expected.begin(), expected.begin() + size)) + { + return false; + } + } + { + constexpr auto n = 10; + cuda::std::inplace_vector out(n, CharT{' '}); + const auto result = cuda::std::format_to_n(out.begin(), n, fmt, cuda::std::forward(args)...); + if (cuda::std::cmp_not_equal(result.size, expected.size())) + { + return false; + } + const auto size = cuda::std::min(cuda::std::size_t{n}, expected.size()); + if (result.out != out.begin() + size) + { + return false; + } + if (!cuda::std::equal(out.begin(), out.begin() + size, expected.begin(), expected.begin() + size)) + { + return false; + } + if (!cuda::std::all_of(out.begin() + size, out.end(), cuda::equal_to_value{CharT{' '}})) + { + return false; + } + } + { + static_assert(cuda::std::is_signed_v>); + CharT out[]{CharT{0}}; + const auto result = cuda::std::format_to_n(out, -1, fmt, cuda::std::forward(args)...); + if (cuda::std::cmp_not_equal(result.size, expected.size())) + { + return false; + } + if (result.out != out) + { + return false; + } + if (out[0] != CharT{0}) + { + return false; + } + } + return true; +} + +template +TEST_FUNC bool check_exception(cuda::std::string_view, cuda::std::basic_string_view, Args&&...) +{ + // After P2216 most exceptions thrown by std::format become ill-formed. + // Therefore this tests does nothing. + // A basic ill-formed test is done in format.verify.cpp + // The exceptions are tested by other functions that don't use the basic-format-string as fmt argument. + return true; +} diff --git a/libcudacxx/test/libcudacxx/std/text/format/format.functions/format_to_n.pass.cpp b/libcudacxx/test/libcudacxx/std/text/format/format.functions/format_to_n.pass.cpp new file mode 100644 index 00000000000..12b5473b6fb --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/text/format/format.functions/format_to_n.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// + +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// format-string fmt, const Args&... args); +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// wformat-string fmt, const Args&... args); + +#include +#include +#include +#include + +static_assert(cuda::std::is_same_v< + cuda::std::format_to_n_result, + decltype(cuda::std::format_to_n(cuda::std::declval(), cuda::std::iter_difference_t{}, ""))>); +static_assert(!noexcept(cuda::std::format_to_n(cuda::std::declval(), cuda::std::iter_difference_t{}, ""))); +#if _CCCL_HAS_WCHAR_T() +static_assert( + cuda::std::is_same_v< + cuda::std::format_to_n_result, + decltype(cuda::std::format_to_n(cuda::std::declval(), cuda::std::iter_difference_t{}, L""))>); +static_assert( + !noexcept(cuda::std::format_to_n(cuda::std::declval(), cuda::std::iter_difference_t{}, L""))); +#endif // _CCCL_HAS_WCHAR_T() + +#include "checkers/format_to_n.h" +#include "tests/smoke.h"