Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 172 additions & 1 deletion libcudacxx/include/cuda/std/__format/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@
#include <cuda/std/__fwd/format.h>
#include <cuda/std/__host_stdlib/stdexcept>
#include <cuda/std/__iterator/back_insert_iterator.h>
#include <cuda/std/__iterator/concepts.h>
#include <cuda/std/__iterator/incrementable_traits.h>
#include <cuda/std/__iterator/wrap_iter.h>
#include <cuda/std/__memory/allocate_at_least.h>
#include <cuda/std/__memory/allocator.h>
#include <cuda/std/__memory/destruct_n.h>
#include <cuda/std/__memory/allocator_traits.h>
#include <cuda/std/__type_traits/conditional.h>
#include <cuda/std/__utility/exception_guard.h>
#include <cuda/std/__utility/move.h>
Expand Down Expand Up @@ -540,6 +541,176 @@ using __fmt_buffer_select_t _CCCL_NODEBUG_ALIAS =
__fmt_direct_iterator_buffer<_OutIt, _CharT>,
__fmt_iterator_buffer<_OutIt, _CharT>>>;

// A dynamically growing buffer intended to be used for retargeting a context.
//
// P2286 Formatting ranges adds range formatting support. It allows the user to
// specify the minimum width for the entire formatted range. The width of the
// range is not known until the range is formatted. Formatting is done to an
// output_iterator so there's no guarantee it would be possible to add the fill
// to the front of the output. Instead the range is formatted to a temporary
// buffer and that buffer is formatted as a string.
//
// There is an issue with that approach, the format context used in
// std::formatter<T>::format contains the output iterator used as part of its
// type. So using this output iterator means there needs to be a new format
// context and the format arguments need to be retargeted to the new context.
// This retargeting is done by a basic_format_context specialized for the
// __iterator of this container.
//
// This class uses its own buffer management, since using vector
// would lead to a circular include with formatter for vector<bool>.
template <class _CharT>
class __fmt_retarget_buffer
{
using _Alloc _CCCL_NODEBUG_ALIAS = allocator<_CharT>;

_CharT* __ptr_;
size_t __capacity_;
size_t __size_{0};

_CCCL_API void __grow_buffer()
{
__grow_buffer(__capacity_ * 1.6);
}

_CCCL_API void __grow_buffer(size_t __capacity)
{
_CCCL_ASSERT(__capacity > __capacity_, "the buffer must grow");
_Alloc __alloc;
auto __result = ::cuda::std::__allocate_at_least(__alloc, __capacity);
::cuda::std::copy_n(__ptr_, __size_, __result.ptr);
__alloc.deallocate(__ptr_, __capacity_);

__ptr_ = __result.ptr;
__capacity_ = __result.count;
}

public:
using value_type _CCCL_NODEBUG_ALIAS = _CharT;

struct __iterator
{
using difference_type _CCCL_NODEBUG_ALIAS = ptrdiff_t;
using value_type _CCCL_NODEBUG_ALIAS = _CharT;

__fmt_retarget_buffer* __buffer_;

_CCCL_API constexpr explicit __iterator(__fmt_retarget_buffer& __buffer) noexcept
: __buffer_{&__buffer}
{}

_CCCL_API constexpr __iterator& operator=(const _CharT& __c)
{
__buffer_->push_back(__c);
return *this;
}

_CCCL_API constexpr __iterator& operator=(_CharT&& __c)
{
__buffer_->push_back(__c);
return *this;
}

_CCCL_API constexpr __iterator& operator*() noexcept
{
return *this;
}
_CCCL_API constexpr __iterator& operator++() noexcept
{
return *this;
}
_CCCL_API constexpr __iterator operator++(int) noexcept
{
return *this;
}
};

_CCCL_API explicit __fmt_retarget_buffer(size_t __size_hint)
{
// When the initial size is very small a lot of resizes happen
// when elements are added. So use a hard-coded minimum size.
//
// Note a size < 2 will not work
// - 0 there is no buffer, while push_back requires 1 empty element.
// - 1 multiplied by the grow factor is 1 and thus the buffer never
// grows.
_Alloc __alloc;
auto __result = ::cuda::std::__allocate_at_least(__alloc, ::cuda::std::max(__size_hint, 256 / sizeof(_CharT)));
__ptr_ = __result.ptr;
__capacity_ = __result.count;
}

__fmt_retarget_buffer(const __fmt_retarget_buffer&) = delete;
__fmt_retarget_buffer& operator=(const __fmt_retarget_buffer&) = delete;

_CCCL_API ~__fmt_retarget_buffer()
{
_Alloc{}.deallocate(__ptr_, __capacity_);
}

[[nodiscard]] _CCCL_API __iterator __make_output_iterator() noexcept
{
return __iterator{*this};
}

_CCCL_API void push_back(_CharT __c)
{
__ptr_[__size_++] = __c;
if (__size_ == __capacity_)
{
__grow_buffer();
}
}

template <class _InCharT>
_CCCL_API void __copy(basic_string_view<_InCharT> __str)
{
size_t __n = __str.size();
if (__size_ + __n >= __capacity_)
{
// Push_back requires the buffer to have room for at least one character.
__grow_buffer(__size_ + __n + 1);
}

::cuda::std::copy_n(__str.data(), __n, __ptr_ + __size_);
__size_ += __n;
}

template <class _Iterator, class _UnaryOperation>
_CCCL_API void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation)
{
static_assert(contiguous_iterator<_Iterator>);
_CCCL_ASSERT(__first <= __last, "not a valid range");

size_t __n = static_cast<size_t>(__last - __first);
if (__size_ + __n >= __capacity_)
{
// Push_back requires the buffer to have room for at least one character.
__grow_buffer(__size_ + __n + 1);
}

::cuda::std::transform(__first, __last, __ptr_ + __size_, ::cuda::std::move(__operation));
__size_ += __n;
}

_CCCL_API void __fill(size_t __n, _CharT __value)
{
if (__size_ + __n >= __capacity_)
{
// Push_back requires the buffer to have room for at least one character.
__grow_buffer(__size_ + __n + 1);
}

::cuda::std::fill_n(__ptr_ + __size_, __n, __value);
__size_ += __n;
}

[[nodiscard]] _CCCL_API constexpr basic_string_view<_CharT> __view() noexcept
{
return {__ptr_, __size_};
}
};

_CCCL_END_NAMESPACE_CUDA_STD

#include <cuda/std/__cccl/epilogue.h>
Expand Down
80 changes: 80 additions & 0 deletions libcudacxx/include/cuda/std/__format/format_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@
# pragma system_header
#endif // no system header

#include <cuda/std/__concepts/same_as.h>
#include <cuda/std/__cstddef/types.h>
#include <cuda/std/__format/buffer.h>
#include <cuda/std/__format/format_arg.h>
#include <cuda/std/__format/format_arg_store.h>
#include <cuda/std/__format/format_error.h>
#include <cuda/std/__format/formatter.h>
#include <cuda/std/__fwd/format.h>
#include <cuda/std/__iterator/back_insert_iterator.h>
#include <cuda/std/__iterator/concepts.h>
#include <cuda/std/__memory/addressof.h>
#include <cuda/std/__utility/ctad_support.h>
#include <cuda/std/__utility/monostate.h>
#include <cuda/std/__utility/move.h>

#include <cuda/std/__cccl/prologue.h>
Expand Down Expand Up @@ -77,6 +83,80 @@ class _CCCL_TYPE_VISIBILITY_DEFAULT basic_format_context
basic_format_args<basic_format_context> __args_;
};

// A specialization for __retarget_buffer
//
// See __retarget_buffer for the motivation for this specialization.
//
// This context holds a reference to the instance of the basic_format_context
// that is retargeted. It converts a formatting argument when it is requested
// during formatting. It is expected that the usage of the arguments is rare so
// the lookups are not expected to be used often. An alternative would be to
// convert all elements during construction.
//
// The elements of the retargets context are only used when an underlying
// formatter uses a locale specific formatting or an formatting argument is
// part for the format spec. For example
// format("{:256:{}}", input, 8);
// Here the width of an element in input is determined dynamically.
// Note when the top-level element has no width the retargeting is not needed.
template <class _CharT>
class basic_format_context<typename __fmt_retarget_buffer<_CharT>::__iterator, _CharT>
{
typename __fmt_retarget_buffer<_CharT>::__iterator __out_it_;
void* __ctx_;
basic_format_arg<basic_format_context> (*__arg_)(void* __ctx, size_t __id);

template <class _Context>
[[nodiscard]] _CCCL_API static basic_format_arg<basic_format_context> __arg_impl(void* __ctx, size_t __id)
{
auto __visitor = [&](auto __arg) -> basic_format_arg<basic_format_context> {
if constexpr (same_as<decltype(__arg), monostate>)
{
return {};
}
else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Context>::handle>)
{
// At the moment it's not possible for formatting to use a re-targeted handle.
// TODO FMT add this when support is needed.
::cuda::std::__throw_format_error("Re-targeting handle not supported");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
else
{
return basic_format_arg<basic_format_context>{
::cuda::std::__fmt_determine_arg_t<basic_format_context, decltype(__arg)>(),
__basic_format_arg_value<basic_format_context>(__arg)};
}
};
return ::cuda::std::visit_format_arg(::cuda::std::move(__visitor), static_cast<_Context*>(__ctx)->arg(__id));
}

public:
using iterator = typename __fmt_retarget_buffer<_CharT>::__iterator;
using char_type = _CharT;
template <class _Tp>
using formatter_type = formatter<_Tp, _CharT>;

template <class _Context>
_CCCL_API explicit basic_format_context(iterator __out_it, _Context& __ctx)
: __out_it_{::cuda::std::move(__out_it)}
, __ctx_{::cuda::std::addressof(__ctx)}
, __arg_{__arg_impl<_Context>}
{}

[[nodiscard]] _CCCL_API basic_format_arg<basic_format_context> arg(size_t __id) const
{
return __arg_(__ctx_, __id);
}
[[nodiscard]] _CCCL_API iterator out()
{
return ::cuda::std::move(__out_it_);
}
_CCCL_API void advance_to(iterator __it)
{
__out_it_ = ::cuda::std::move(__it);
}
};

_CCCL_CTAD_SUPPORTED_FOR_TYPE(basic_format_context);

template <class _OutIt, class _CharT>
Expand Down
7 changes: 7 additions & 0 deletions libcudacxx/include/cuda/std/__format/format_spec_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ struct __fmt_spec_fields
__ret.__consume_all_ = true;
return __ret;
}
[[nodiscard]] _CCCL_HOST_DEVICE_API constexpr __fmt_spec_fields __fmt_spec_fields_tuple() noexcept
{
__fmt_spec_fields __ret{};
__ret.__use_range_fill_ = true;
__ret.__clear_brackets_ = true;
return __ret;
}

enum class __fmt_spec_alignment : uint8_t
{
Expand Down
5 changes: 2 additions & 3 deletions libcudacxx/include/cuda/std/__format/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
# pragma system_header
#endif // no system header

#include <cuda/std/__cstddef/types.h>
#include <cuda/std/string_view>
#include <cuda/std/__fwd/format.h>

#include <cuda/std/__cccl/prologue.h>

Expand All @@ -48,7 +47,7 @@ struct __fmt_disabled_formatter
//! - is_move_constructible_v<F>,
//! - is_copy_assignable_v<F>, and
//! - is_move_assignable_v<F>.
template <class _Tp, class _CharT = char>
template <class _Tp, class _CharT>
struct _CCCL_TYPE_VISIBILITY_DEFAULT formatter : __fmt_disabled_formatter
{};

Expand Down
Loading
Loading