3 #include "fly/concepts/concepts.hpp"
5 #include "fly/types/string/concepts.hpp"
6 #include "fly/types/string/detail/classifier.hpp"
7 #include "fly/types/string/detail/format_specifier.hpp"
8 #include "fly/types/string/detail/unicode.hpp"
10 #if !defined(FLY_COMPILER_SUPPORTS_FP_CHARCONV)
11 # include "fly/types/string/detail/stream_util.hpp"
19 #include <system_error>
20 #include <type_traits>
22 #if !defined(FLY_COMPILER_SUPPORTS_FP_CHARCONV)
41 template <
typename T, StandardCharacter CharType =
char>
45 template <FormattableString T, StandardCharacter CharType>
48 FLY_DEFINE_FORMATTER(CharType, detail::ParameterType::String)
58 template <
typename FormatContext>
59 void format(
const T &value, FormatContext &context)
61 const std::size_t min_width = FormatSpecifier::width(context, 0);
62 const std::size_t max_width = FormatSpecifier::precision(context, string_type::npos);
65 const std::size_t value_size = std::min(max_width, actual_size);
67 const std::size_t padding_size = std::max(value_size, min_width) - value_size;
68 const auto padding_char = m_fill.value_or(s_space);
70 auto append_padding = [&context, padding_char](std::size_t count) {
71 for (std::size_t i = 0; i < count; ++i)
73 *context.out()++ = padding_char;
79 case FormatSpecifier::Alignment::Left:
80 case FormatSpecifier::Alignment::Default:
81 append_string(value, value_size, context);
82 append_padding(padding_size);
85 case FormatSpecifier::Alignment::Right:
86 append_padding(padding_size);
87 append_string(value, value_size, context);
90 case FormatSpecifier::Alignment::Center:
92 const std::size_t left_padding = padding_size / 2;
93 const std::size_t right_padding =
94 (padding_size % 2 == 0) ? left_padding : left_padding + 1;
96 append_padding(left_padding);
97 append_string(value, value_size, context);
98 append_padding(right_padding);
117 template <
typename FormatContext>
118 static void append_string(
const T &value, std::size_t value_size, FormatContext &context)
120 using standard_character_type = StandardCharacterType<T>;
121 using standard_view_type = std::basic_string_view<standard_character_type>;
123 standard_view_type view;
125 if constexpr (std::is_array_v<T> || std::is_pointer_v<T>)
127 view = standard_view_type(value, value_size);
131 view = standard_view_type(value).substr(0, value_size);
134 if constexpr (fly::SameAs<CharType, standard_character_type>)
136 for (
const auto &ch : view)
138 *context.out()++ = ch;
145 if (
auto converted = unicode::template convert_encoding<string_type>(view); converted)
147 for (
const auto &ch : *converted)
149 *context.out()++ = ch;
156 using string_type = std::basic_string<CharType>;
158 static constexpr
const auto s_space = FLY_CHR(CharType,
' ');
162 template <FormattablePo
inter T, StandardCharacter CharType>
163 struct Formatter<T, CharType> :
public detail::BasicFormatSpecifier<CharType>
165 FLY_DEFINE_FORMATTER(CharType, detail::ParameterType::Pointer)
175 template <
typename FormatContext>
176 inline void format(T value, FormatContext &context)
178 m_alternate_form =
true;
179 m_type = FormatSpecifier::Type::Hex;
183 reinterpret_cast<std::uintptr_t
>(
static_cast<const void *
>(value)),
189 template <FormattableIntegral T, StandardCharacter CharType>
190 struct Formatter<T, CharType> :
public detail::BasicFormatSpecifier<CharType>
192 FLY_DEFINE_FORMATTER(CharType, detail::ParameterType::Integral)
202 template <
typename FormatContext>
203 inline void format(T value, FormatContext &context)
205 if constexpr (std::is_signed_v<T>)
207 using U = std::make_unsigned_t<std::remove_cvref_t<T>>;
212 const T sign = value >> std::numeric_limits<T>::digits;
213 const U unsigned_value =
static_cast<U
>(
static_cast<U
>(value ^ sign) + (sign & 1));
215 format(unsigned_value,
static_cast<bool>(sign), context);
219 format(value,
false, context);
224 using string_type = std::basic_string<CharType>;
236 template <fly::Un
signedIntegral U,
typename FormatContext>
237 void format(U value,
bool is_negative, FormatContext &context)
239 if (m_type == FormatSpecifier::Type::Character)
241 format_as_character(value, is_negative, context);
245 std::size_t prefix_size = 0;
247 if (is_negative || (m_sign == FormatSpecifier::Sign::Always) ||
248 (m_sign == FormatSpecifier::Sign::NegativeOnlyWithPositivePadding))
252 if (m_alternate_form)
256 if ((m_type == FormatSpecifier::Type::Binary) || (m_type == FormatSpecifier::Type::Hex))
262 const int base =
static_cast<int>(m_type);
263 const std::size_t value_size = count_digits(value, base) + prefix_size;
265 const std::size_t width = FormatSpecifier::width(context, 0);
266 const std::size_t padding_size = std::max(value_size, width) - value_size;
267 const auto padding_char = m_fill.value_or(s_space);
269 auto append_prefix = [
this, is_negative, &context]() {
272 *context.out()++ = s_minus_sign;
274 else if (m_sign == FormatSpecifier::Sign::Always)
276 *context.out()++ = s_plus_sign;
278 else if (m_sign == FormatSpecifier::Sign::NegativeOnlyWithPositivePadding)
280 *context.out()++ = s_space;
283 if (m_alternate_form)
285 const bool is_upper_case = m_case == FormatSpecifier::Case::Upper;
286 *context.out()++ = s_zero;
288 if (m_type == FormatSpecifier::Type::Binary)
290 *context.out()++ = is_upper_case ? s_upper_b : s_lower_b;
292 else if (m_type == FormatSpecifier::Type::Hex)
294 *context.out()++ = is_upper_case ? s_upper_x : s_lower_x;
299 auto append_padding = [&context](std::size_t count, CharType pad) {
300 for (std::size_t i = 0; i < count; ++i)
302 *context.out()++ = pad;
308 case FormatSpecifier::Alignment::Left:
310 append_number(value, base, context);
311 append_padding(padding_size, padding_char);
314 case FormatSpecifier::Alignment::Right:
315 append_padding(padding_size, padding_char);
317 append_number(value, base, context);
320 case FormatSpecifier::Alignment::Center:
322 const std::size_t left_padding = padding_size / 2;
323 const std::size_t right_padding =
324 (padding_size % 2 == 0) ? left_padding : left_padding + 1;
326 append_padding(left_padding, padding_char);
328 append_number(value, base, context);
329 append_padding(right_padding, padding_char);
333 case FormatSpecifier::Alignment::Default:
337 append_padding(padding_size, s_zero);
338 append_number(value, base, context);
342 append_padding(padding_size, padding_char);
344 append_number(value, base, context);
363 template <fly::Un
signedIntegral U,
typename FormatContext>
364 void format_as_character(U value,
bool is_negative, FormatContext &context)
366 if (is_negative || (value >
static_cast<U
>(std::numeric_limits<CharType>::max())))
371 const std::size_t width = FormatSpecifier::width(context, 0);
372 const std::size_t padding_size = width > 1 ? width - 1 : 0;
373 const auto padding_char = m_fill.value_or(s_space);
375 auto append_padding = [&context, padding_char](std::size_t count) {
376 for (std::size_t i = 0; i < count; ++i)
378 *context.out()++ = padding_char;
384 case FormatSpecifier::Alignment::Left:
385 *context.out()++ =
static_cast<CharType
>(value);
386 append_padding(padding_size);
389 case FormatSpecifier::Alignment::Right:
390 append_padding(padding_size);
391 *context.out()++ =
static_cast<CharType
>(value);
394 case FormatSpecifier::Alignment::Center:
396 const std::size_t left_padding = padding_size / 2;
397 const std::size_t right_padding =
398 (padding_size % 2 == 0) ? left_padding : left_padding + 1;
400 append_padding(left_padding);
401 *context.out()++ =
static_cast<CharType
>(value);
402 append_padding(right_padding);
406 case FormatSpecifier::Alignment::Default:
407 append_padding(padding_size);
408 *context.out()++ =
static_cast<CharType
>(value);
430 template <
typename U,
typename FormatContext>
431 void append_number(U value,
int base, FormatContext &context)
433 static thread_local std::array<char, std::numeric_limits<std::uintmax_t>::digits> s_buffer;
435 char *begin = s_buffer.data();
436 char *end = begin + s_buffer.size();
438 const auto result = std::to_chars(begin, end, value, base);
440 if ((m_type == FormatSpecifier::Type::Hex) && (m_case == FormatSpecifier::Case::Upper))
442 for (
char *it = begin; it != result.ptr; ++it)
448 if constexpr (fly::SameAs<string_type, std::string>)
450 for (
const char *it = begin; it != result.ptr; ++it)
452 *context.out()++ = *it;
457 using unicode = detail::BasicUnicode<char>;
459 std::string_view view(
461 static_cast<std::size_t
>(std::distance(begin, result.ptr)));
463 unicode::template convert_encoding_into<string_type>(view, context.out());
477 template <
typename U>
478 static constexpr std::size_t count_digits(U value,
int base)
480 std::size_t digits = 0;
485 }
while ((value /=
static_cast<U
>(base)) != 0);
490 static constexpr
const auto s_plus_sign = FLY_CHR(CharType,
'+');
491 static constexpr
const auto s_minus_sign = FLY_CHR(CharType,
'-');
492 static constexpr
const auto s_space = FLY_CHR(CharType,
' ');
493 static constexpr
const auto s_zero = FLY_CHR(CharType,
'0');
494 static constexpr
const auto s_lower_b = FLY_CHR(CharType,
'b');
495 static constexpr
const auto s_upper_b = FLY_CHR(CharType,
'B');
496 static constexpr
const auto s_lower_x = FLY_CHR(CharType,
'x');
497 static constexpr
const auto s_upper_x = FLY_CHR(CharType,
'X');
501 template <FormattableFloatingPo
int T, StandardCharacter CharType>
502 struct Formatter<T, CharType> :
public detail::BasicFormatSpecifier<CharType>
504 FLY_DEFINE_FORMATTER(CharType, detail::ParameterType::FloatingPoint)
506 #if defined(FLY_COMPILER_SUPPORTS_FP_CHARCONV)
521 template <
typename FormatContext>
522 void format(T value, FormatContext &context)
524 const bool is_negative = std::signbit(value);
525 value = std::abs(value);
527 std::size_t prefix_size = 0;
529 if (is_negative || (m_sign == FormatSpecifier::Sign::Always) ||
530 (m_sign == FormatSpecifier::Sign::NegativeOnlyWithPositivePadding))
535 const int precision =
static_cast<int>(FormatSpecifier::precision(context, 6));
536 const FloatConversionResult result = convert_value(value, precision);
538 auto append_prefix = [
this, &is_negative, &context]() {
541 *context.out()++ = s_minus_sign;
543 else if (m_sign == FormatSpecifier::Sign::Always)
545 *context.out()++ = s_plus_sign;
547 else if (m_sign == FormatSpecifier::Sign::NegativeOnlyWithPositivePadding)
549 *context.out()++ = s_space;
553 auto append_padding = [&context](std::size_t count, CharType pad) {
554 for (std::size_t i = 0; i < count; ++i)
556 *context.out()++ = pad;
560 auto append_number = [
this, &context, &result]() {
561 if constexpr (fly::SameAs<string_type, std::string>)
563 for (
auto ch : result.m_digits)
565 *context.out()++ = ch;
567 if (result.m_append_decimal)
569 *context.out()++ =
'.';
571 for (std::size_t i = 0; i < result.m_zeroes_to_append; ++i)
573 *context.out()++ =
'0';
575 for (
auto ch : result.m_exponent)
577 *context.out()++ = ch;
582 using unicode = detail::BasicUnicode<char>;
584 unicode::template convert_encoding_into<string_type>(
588 if (result.m_append_decimal)
590 *context.out()++ = FLY_CHR(CharType,
'.');
592 for (std::size_t i = 0; i < result.m_zeroes_to_append; ++i)
594 *context.out()++ = FLY_CHR(CharType,
'0');
597 unicode::template convert_encoding_into<string_type>(
603 const std::size_t value_size = prefix_size + result.m_digits.size() +
604 result.m_exponent.size() +
static_cast<std::size_t
>(result.m_append_decimal) +
605 result.m_zeroes_to_append;
606 const std::size_t width = FormatSpecifier::width(context, 0);
607 const std::size_t padding_size = std::max(value_size, width) - value_size;
608 const auto padding_char = m_fill.value_or(s_space);
612 case FormatSpecifier::Alignment::Left:
615 append_padding(padding_size, padding_char);
618 case FormatSpecifier::Alignment::Right:
619 append_padding(padding_size, padding_char);
624 case FormatSpecifier::Alignment::Center:
626 const std::size_t left_padding = padding_size / 2;
627 const std::size_t right_padding =
628 (padding_size % 2 == 0) ? left_padding : left_padding + 1;
630 append_padding(left_padding, padding_char);
633 append_padding(right_padding, padding_char);
637 case FormatSpecifier::Alignment::Default:
641 append_padding(padding_size, s_zero);
646 append_padding(padding_size, padding_char);
665 template <
typename FormatContext>
666 void format(T value, FormatContext &context)
668 static thread_local std::stringstream s_stream;
671 if (m_alignment == FormatSpecifier::Alignment::Default)
673 m_alignment = FormatSpecifier::Alignment::Right;
678 case FormatSpecifier::Sign::Always:
679 modifiers.
setf(std::ios_base::showpos);
682 case FormatSpecifier::Sign::NegativeOnlyWithPositivePadding:
683 modifiers.template locale<detail::PositivePaddingFacet<char>>();
684 modifiers.
setf(std::ios_base::showpos);
691 if (m_alternate_form)
693 modifiers.
setf(std::ios_base::showpoint);
698 modifiers.
setf(std::ios_base::internal, std::ios_base::adjustfield);
699 modifiers.
fill(
static_cast<char>(s_zero));
700 modifiers.
width(
static_cast<std::streamsize
>(FormatSpecifier::width(context, 0)));
703 modifiers.
precision(
static_cast<std::streamsize
>(FormatSpecifier::precision(context, 6)));
704 m_precision = std::nullopt;
705 m_precision_position = std::nullopt;
709 case FormatSpecifier::Type::HexFloat:
710 modifiers.
setf(std::ios_base::fixed | std::ios_base::scientific);
713 case FormatSpecifier::Type::Scientific:
714 modifiers.
setf(std::ios_base::scientific, std::ios::floatfield);
717 case FormatSpecifier::Type::Fixed:
720 if (!std::isnan(value) && !std::isinf(value))
722 modifiers.
setf(std::ios_base::fixed, std::ios::floatfield);
730 if (m_case == FormatSpecifier::Case::Upper)
732 modifiers.
setf(std::ios_base::uppercase);
738 formatter.format(s_stream.str(), context);
745 using string_type = std::basic_string<CharType>;
747 #if defined(FLY_COMPILER_SUPPORTS_FP_CHARCONV)
752 struct FloatConversionResult
754 std::string_view m_digits;
755 std::string_view m_exponent;
756 bool m_append_decimal {
false};
757 std::size_t m_zeroes_to_append {0};
773 FloatConversionResult convert_value(T value,
int precision)
775 static thread_local std::array<char, std::numeric_limits<T>::digits> s_buffer;
777 char *begin = s_buffer.data();
778 char *end = begin + s_buffer.size();
780 std::chars_format fmt = std::chars_format::general;
781 char exponent =
'\0';
785 case FormatSpecifier::Type::HexFloat:
786 fmt = std::chars_format::hex;
789 case FormatSpecifier::Type::Scientific:
790 fmt = std::chars_format::scientific;
793 case FormatSpecifier::Type::Fixed:
794 fmt = std::chars_format::fixed;
801 const auto to_chars_result = std::to_chars(begin, end, value, fmt, precision);
803 FloatConversionResult conversion_result;
804 conversion_result.m_digits =
805 std::string_view(begin,
static_cast<std::size_t
>(to_chars_result.ptr - begin));
807 if (m_alternate_form)
809 conversion_result.m_append_decimal =
true;
811 for (
const char *it = begin; it != to_chars_result.ptr; ++it)
815 conversion_result.m_append_decimal =
false;
817 else if (*it == exponent)
819 const auto position =
static_cast<std::size_t
>(it - begin);
821 conversion_result.m_exponent = conversion_result.m_digits.substr(position);
822 conversion_result.m_digits = conversion_result.m_digits.substr(0, position);
826 if (m_type == FormatSpecifier::Type::General)
828 const auto digits = conversion_result.m_digits.size() -
829 static_cast<std::size_t
>(!conversion_result.m_append_decimal);
831 if (
static_cast<std::size_t
>(precision) > digits)
833 conversion_result.m_zeroes_to_append =
834 static_cast<std::size_t
>(precision) - digits;
839 if (m_case == FormatSpecifier::Case::Upper)
841 for (
char *it = begin; it != to_chars_result.ptr; ++it)
847 return conversion_result;
852 static constexpr
const auto s_plus_sign = FLY_CHR(CharType,
'+');
853 static constexpr
const auto s_minus_sign = FLY_CHR(CharType,
'-');
854 static constexpr
const auto s_space = FLY_CHR(CharType,
' ');
855 static constexpr
const auto s_zero = FLY_CHR(CharType,
'0');
859 template <FormattableBoolean T, StandardCharacter CharType>
860 struct Formatter<T, CharType> :
public detail::BasicFormatSpecifier<CharType>
862 FLY_DEFINE_FORMATTER(CharType, detail::ParameterType::Boolean)
872 template <
typename FormatContext>
873 inline void format(T value, FormatContext &context)
875 if (m_type == FormatSpecifier::Type::String)
878 formatter.format(value ? s_true : s_false, context);
883 formatter.format(
static_cast<unsigned>(value), context);
888 static constexpr
const CharType *s_true = FLY_STR(CharType,
"true");
889 static constexpr
const CharType *s_false = FLY_STR(CharType,
"false");
static constexpr CharType to_upper(CharType ch)
Definition: classifier.hpp:237
static constexpr size_type size(T &&value)
Definition: unicode.hpp:31
Definition: stream_util.hpp:21
void width(std::streamsize size)
Definition: stream_util.hpp:187
void setf(std::ios_base::fmtflags flag)
Definition: stream_util.hpp:158
void precision(std::streamsize size)
Definition: stream_util.hpp:194
void fill(char ch)
Definition: stream_util.hpp:180