42 template <
typename Sink,
typename Format,
auto ArgumentOrder,
typename... Args>
46 using Built = std::remove_cvref_t<Format>;
47 static_assert(ArgumentOrder.size() == Built::ArgumentList().size(),
48 "LibXR::Print::Writer: argument reorder list must match the "
49 "compiled field count");
50 static_assert(Built::template Matches<Args...>(),
51 "LibXR::Print::Writer::RunArgumentOrder: format arguments do not match");
53 return RunTaggedArgumentOrder<Sink, Built::ArgumentList(), ArgumentOrder,
55 sink, Built::Codes().data(), std::forward<Args>(args)...);
64 static constexpr size_t float_buffer_capacity = 512;
65 template <FormatPackKind K>
66 static constexpr bool dependent_false_v =
false;
87 [[nodiscard]]
static constexpr bool HasFlag(uint8_t flags, uint8_t bit)
89 return (flags & bit) != 0;
104 [[nodiscard]]
constexpr bool LeftAlign()
const
109 [[nodiscard]]
constexpr bool ForceSign()
const
114 [[nodiscard]]
constexpr bool CenterAlign()
const
119 [[nodiscard]]
constexpr bool SpaceSign()
const
124 [[nodiscard]]
constexpr bool Alternate()
const
129 [[nodiscard]]
constexpr bool ZeroPad()
const
134 [[nodiscard]]
constexpr bool UpperCase()
const
139 [[nodiscard]]
constexpr bool HasPrecision()
const
145 template <
typename T>
146 [[nodiscard]]
static constexpr std::string_view ToStringView(
const T& text)
148 using Traits = Detail::FormatArgument::TypeTraits<T>;
150 if constexpr (std::is_same_v<typename Traits::Decayed, std::string_view>)
154 else if constexpr (std::is_same_v<typename Traits::Decayed, std::string>)
156 return std::string_view(text.data(), text.size());
158 else if constexpr (std::is_same_v<typename Traits::Decayed, const char*> ||
159 std::is_same_v<typename Traits::Decayed, char*>)
165 return std::string_view(text, std::strlen(text));
167 else if constexpr (Traits::is_char_array)
169 return std::string_view(text, std::strlen(text));
182 template <FormatPackKind pack,
typename T>
183 [[nodiscard]]
static constexpr auto PackValue(T&& value)
186 using Normalized =
typename Traits::Normalized;
188 if constexpr (pack == FormatPackKind::I32)
190 if constexpr (Traits::is_signed_integer)
192 return static_cast<int32_t
>(
static_cast<Normalized
>(value));
195 else if constexpr (pack == FormatPackKind::I64)
197 if constexpr (Traits::is_signed_integer)
199 return static_cast<int64_t
>(
static_cast<Normalized
>(value));
202 else if constexpr (pack == FormatPackKind::U32)
204 if constexpr (std::is_same_v<Normalized, bool>)
206 return static_cast<uint32_t
>(value ? 1U : 0U);
208 else if constexpr (std::is_integral_v<Normalized>)
210 return static_cast<uint32_t
>(
static_cast<std::make_unsigned_t<Normalized>
>(
211 static_cast<Normalized
>(value)));
214 else if constexpr (pack == FormatPackKind::U64)
216 if constexpr (std::is_same_v<Normalized, bool>)
218 return static_cast<uint64_t
>(value ? 1U : 0U);
220 else if constexpr (std::is_integral_v<Normalized>)
222 return static_cast<uint64_t
>(
static_cast<std::make_unsigned_t<Normalized>
>(
223 static_cast<Normalized
>(value)));
226 else if constexpr (pack == FormatPackKind::Pointer)
228 if constexpr (Traits::is_pointer_like)
230 if constexpr (std::is_same_v<typename Traits::Decayed, std::nullptr_t>)
232 return static_cast<uintptr_t
>(0);
236 return (value ==
nullptr) ?
static_cast<uintptr_t
>(0)
237 :
reinterpret_cast<uintptr_t
>(value);
241 else if constexpr (pack == FormatPackKind::Character)
243 if constexpr (Traits::is_character_like)
245 return static_cast<char>(
static_cast<Normalized
>(value));
248 else if constexpr (pack == FormatPackKind::StringView)
250 if constexpr (Traits::is_string_like)
252 return ToStringView(value);
255 else if constexpr (pack == FormatPackKind::F32)
257 if constexpr (std::is_arithmetic_v<Normalized>)
259 return static_cast<float>(value);
262 else if constexpr (pack == FormatPackKind::F64)
264 if constexpr (std::is_arithmetic_v<Normalized>)
266 return static_cast<double>(value);
269 else if constexpr (pack == FormatPackKind::LongDouble)
271 if constexpr (std::is_arithmetic_v<Normalized>)
273 return static_cast<long double>(value);
278 static_assert(dependent_false_v<pack>,
279 "LibXR::Print::Writer::PackValue: unsupported packed argument kind");
283 template <
typename T>
284 static void StoreArgument(uint8_t*& out,
const T& value)
286 std::memcpy(out, &value,
sizeof(T));
290 template <auto ArgumentInfoList>
291 [[nodiscard]]
static consteval size_t PackedArgumentBytes()
294 for (
const auto& argument : ArgumentInfoList)
296 bytes += FormatArgumentBytes(argument.pack);
301 template <auto ArgumentInfoList, auto ArgumentOrder,
typename Tuple>
302 static void StoreArgumentsOrdered(uint8_t*& out, Tuple& tuple)
304 [&]<
size_t... I>(std::index_sequence<I...>) {
305 (StoreArgument(out,
PackValue<ArgumentInfoList[I].pack>(
306 std::get<ArgumentOrder[I]>(tuple))),
308 }(std::make_index_sequence<ArgumentInfoList.size()>{});
311 template <std::
unsigned_
integral UInt>
312 [[nodiscard]]
static size_t AppendUnsigned(
char* out, UInt value, uint8_t base,
315 constexpr char lower_digits[] =
"0123456789abcdef";
316 constexpr char upper_digits[] =
"0123456789ABCDEF";
317 const char* digits = upper_case ? upper_digits : lower_digits;
329 reverse[count++] = digits[value % base];
333 for (
size_t i = 0; i < count; ++i)
335 out[i] = reverse[count - i - 1];
341 [[nodiscard]]
static size_t AppendSmallUnsigned(
char* out, uint8_t value)
343 return AppendUnsigned(out, value, 10,
false);
347 [[nodiscard]]
static constexpr size_t FieldPadding(uint8_t width,
size_t payload_size)
349 return (width > payload_size) ?
static_cast<size_t>(width) - payload_size : 0;
354 const Spec& spec,
size_t digit_count)
356 return (spec.HasPrecision() && spec.
precision > digit_count)
357 ?
static_cast<size_t>(spec.
precision) - digit_count
362 [[nodiscard]]
static constexpr uint8_t
IntegerBase(FormatType type)
366 case FormatType::Unsigned32:
367 case FormatType::Unsigned64:
369 case FormatType::Binary32:
370 case FormatType::Binary64:
372 case FormatType::Octal32:
373 case FormatType::Octal64:
375 case FormatType::HexLower32:
376 case FormatType::HexLower64:
377 case FormatType::HexUpper32:
378 case FormatType::HexUpper64:
388 return type == FormatType::HexUpper32 || type == FormatType::HexUpper64;
392 template <std::
unsigned_
integral UInt>
394 FormatType type,
const Spec& spec, UInt value)
396 if (!spec.Alternate() || value == 0)
400 if (type == FormatType::HexLower32 || type == FormatType::HexLower64)
404 if (type == FormatType::HexUpper32 || type == FormatType::HexUpper64)
408 if (type == FormatType::Binary32 || type == FormatType::Binary64)
410 return spec.UpperCase() ?
"0B" :
"0b";
426 template <std::
unsigned_
integral UInt>
428 const Spec& spec, UInt value)
430 if (!spec.Alternate())
432 return (value == 0 && spec.
precision == 0) ? 0 : digit_count;
445 if (spec.HasPrecision() && spec.
precision > digit_count)
450 digits[digit_count++] =
'0';
451 for (
size_t i = digit_count - 1; i > 0; --i)
453 digits[i] = digits[i - 1];
464 case FormatType::FloatScientific:
465 case FormatType::DoubleScientific:
466 case FormatType::LongDoubleScientific:
467 case FormatType::FloatGeneral:
468 case FormatType::DoubleGeneral:
469 case FormatType::LongDoubleGeneral:
470 case FormatType::FloatFixed:
471 case FormatType::DoubleFixed:
472 case FormatType::LongDoubleFixed:
484 case FormatType::FloatFixed:
485 return Config::enable_float_fixed;
486 case FormatType::FloatScientific:
487 return Config::enable_float_scientific;
488 case FormatType::FloatGeneral:
489 return Config::enable_float_general;
490 case FormatType::DoubleFixed:
491 return Config::enable_float_double && Config::enable_float_fixed;
492 case FormatType::DoubleScientific:
493 return Config::enable_float_double && Config::enable_float_scientific;
494 case FormatType::DoubleGeneral:
495 return Config::enable_float_double && Config::enable_float_general;
496 case FormatType::LongDoubleFixed:
497 return Config::enable_float_long_double && Config::enable_float_fixed;
498 case FormatType::LongDoubleScientific:
499 return Config::enable_float_long_double && Config::enable_float_scientific;
500 case FormatType::LongDoubleGeneral:
501 return Config::enable_float_long_double && Config::enable_float_general;
511 if (size >= capacity)
521 std::string_view text)
523 if (text.size() > capacity - size)
527 std::memcpy(buffer + size, text.data(), text.size());
534 size_t& size, uint32_t value,
538 size_t digit_count = AppendUnsigned(digits, value, 10,
false);
539 size_t zero_count = (width > digit_count) ?
static_cast<size_t>(width) - digit_count : 0;
541 for (
size_t i = 0; i < zero_count; ++i)
549 return AppendBufferText(buffer, capacity, size, std::string_view(digits, digit_count));
555 uint32_t bits = std::bit_cast<uint32_t>(value);
556 uint32_t exponent_bits = (bits >> 23) & 0xFFU;
557 uint32_t fraction_bits = bits & 0x7FFFFFU;
558 uint32_t significand =
559 (exponent_bits == 0) ? fraction_bits : ((1U << 23) | fraction_bits);
561 (exponent_bits == 0) ? -149 :
static_cast<int>(exponent_bits) - 150;
562 uint64_t numerator =
static_cast<uint64_t
>(significand) * scale;
566 return numerator << exponent2;
569 unsigned int shift =
static_cast<unsigned int>(-exponent2);
574 uint64_t quotient = numerator >> shift;
575 uint64_t remainder = numerator & ((uint64_t{1} << shift) - 1U);
576 uint64_t halfway = uint64_t{1} << (shift - 1);
577 if (remainder > halfway || (remainder == halfway && (quotient & 1U) != 0U))
584 template <
typename Float>
591 template <
typename Float>
592 [[nodiscard]]
static Float Power10(
int exponent)
596 unsigned int remaining =
597 static_cast<unsigned int>(exponent < 0 ? -exponent : exponent);
599 while (remaining != 0)
601 if ((remaining & 1U) != 0U)
624 template <
typename Float>
633 int binary_exponent = 0;
634 std::frexp(value, &binary_exponent);
635 constexpr Float log10_of_2 =
636 static_cast<Float
>(0.30102999566398119521373889472449L);
637 normalized.exponent =
638 static_cast<int>(
static_cast<Float
>(binary_exponent - 1) * log10_of_2);
639 normalized.scale = Power10<Float>(normalized.exponent);
641 Float scaled = value / normalized.scale;
644 normalized.scale /= 10;
645 --normalized.exponent;
650 normalized.scale *= 10;
651 ++normalized.exponent;
659 template <
typename Float>
662 Float scaled = value / scale;
663 auto digit =
static_cast<int>(scaled +
static_cast<Float
>(1e-12L));
673 value -=
static_cast<Float
>(digit) * scale;
674 Float epsilon = scale *
static_cast<Float
>(1e-9L);
675 if (value < 0 && value > -epsilon)
680 return static_cast<uint8_t
>(digit);
686 size_t exponent_pos = size;
687 for (
size_t i = 0; i < size; ++i)
689 if (text[i] ==
'e' || text[i] ==
'E')
696 size_t mantissa_end = exponent_pos;
697 while (mantissa_end > 0 && text[mantissa_end - 1] ==
'0')
701 if (mantissa_end > 0 && text[mantissa_end - 1] ==
'.')
706 if (exponent_pos == size)
711 std::memmove(text + mantissa_end, text + exponent_pos, size - exponent_pos);
712 return mantissa_end + (size - exponent_pos);
717 char* out,
size_t& out_size)
723 if (std::isnan(value))
727 if (std::isinf(value))
736 uint32_t integer_part =
static_cast<uint32_t
>(value);
739 uint64_t scaled_integer =
static_cast<uint64_t
>(integer_part) * scale;
740 uint32_t fractional_part =
741 (scaled_total >= scaled_integer)
742 ?
static_cast<uint32_t
>(scaled_total - scaled_integer)
745 if (fractional_part >= scale)
747 fractional_part -= scale;
748 if (integer_part == std::numeric_limits<uint32_t>::max())
762 for (uint8_t i = 0; i < precision; ++i)
792 float rounded = value;
793 float rounding = 0.5f;
794 for (uint8_t i = 0; i < precision; ++i)
809 float integer_scale = 1.0f;
812 float next_scale = integer_scale * 10.0f;
813 if (!std::isfinite(next_scale) || rounded < next_scale)
817 integer_scale = next_scale;
820 while (integer_scale >= 1.0f)
822 int digit =
static_cast<int>(rounded / integer_scale);
833 static_cast<char>(
'0' + digit)))
838 rounded -=
static_cast<float>(digit) * integer_scale;
839 float epsilon = integer_scale * 1e-6f;
840 if (rounded < 0.0f && rounded > -epsilon)
844 integer_scale *= 0.1f;
857 for (uint8_t i = 0; i < precision; ++i)
860 int digit =
static_cast<int>(rounded + 1e-6f);
871 static_cast<char>(
'0' + digit)))
876 rounded -=
static_cast<float>(digit);
877 if (rounded < 0.0f && rounded > -1e-5f)
886 template <
typename Float>
887 [[nodiscard]]
static bool FormatFixedText(Float value, uint8_t precision,
bool alternate,
888 char* out,
size_t& out_size)
891 value +
static_cast<Float
>(0.5L) * Power10<Float>(-
static_cast<int>(precision));
893 int integer_exponent = (rounded == 0) ? 0 : normalized.exponent;
894 int start_pos = (integer_exponent > 0) ? integer_exponent : 0;
895 Float scale = Power10<Float>(start_pos);
898 for (
int pos = start_pos; pos >= 0; --pos)
908 if (precision != 0 || alternate)
916 for (uint8_t i = 0; i < precision; ++i)
929 [[nodiscard]]
static bool AppendExponentText(
char* out,
size_t& out_size,
int exponent,
933 upper_case ?
'E' :
'e'))
938 exponent < 0 ?
'-' :
'+'))
944 unsigned int magnitude =
945 static_cast<unsigned int>(exponent < 0 ? -exponent : exponent);
946 size_t digit_count = AppendUnsigned(digits, magnitude, 10,
false);
947 if (digit_count < 2 &&
954 std::string_view(digits, digit_count));
957 template <
typename Float>
958 [[nodiscard]]
static bool FormatScientificText(Float value, uint8_t precision,
959 bool alternate,
bool upper_case,
char* out,
963 Float rounded = value;
966 rounded +=
static_cast<Float
>(0.5L) *
967 Power10<Float>(initial.exponent -
static_cast<int>(precision));
971 int exponent = (rounded == 0) ? 0 : normalized.exponent;
972 Float scale = Power10<Float>(exponent);
981 if (precision != 0 || alternate)
990 for (uint8_t i = 0; i < precision; ++i)
1000 return AppendExponentText(out, out_size, exponent, upper_case);
1003 template <
typename Float>
1004 [[nodiscard]]
static bool FormatFloatText(FormatType type,
const Spec& spec, Float value,
1005 char* out,
size_t& out_size)
1009 if (std::isnan(value))
1012 spec.UpperCase() ?
"NAN" :
"nan");
1014 if (std::isinf(value))
1017 spec.UpperCase() ?
"INF" :
"inf");
1020 uint8_t precision = spec.HasPrecision() ? spec.precision : 6;
1023 case FormatType::FloatFixed:
1024 case FormatType::DoubleFixed:
1025 case FormatType::LongDoubleFixed:
1026 return FormatFixedText(value, precision, spec.Alternate(), out, out_size);
1027 case FormatType::FloatScientific:
1028 case FormatType::DoubleScientific:
1029 case FormatType::LongDoubleScientific:
1030 return FormatScientificText(value, precision, spec.Alternate(), spec.UpperCase(),
1032 case FormatType::FloatGeneral:
1033 case FormatType::DoubleGeneral:
1034 case FormatType::LongDoubleGeneral:
1036 uint8_t significant = precision == 0 ? 1 : precision;
1038 if (exponent < -4 || exponent >= significant)
1040 if (!FormatScientificText(value,
static_cast<uint8_t
>(significant - 1),
1041 spec.Alternate(), spec.UpperCase(), out, out_size))
1048 int fractional_precision =
static_cast<int>(significant) - (exponent + 1);
1049 if (fractional_precision < 0)
1051 fractional_precision = 0;
1053 if (!FormatFixedText(value,
static_cast<uint8_t
>(fractional_precision),
1054 spec.Alternate(), out, out_size))
1060 if (!spec.Alternate())
1062 out_size = TrimGeneralText(out, out_size);
1072 class ArgumentReader;
1074 template <OutputSink Sink, FormatProfile Profile>
1077 template <OutputSink Sink, FormatProfile Profile>
1078 [[nodiscard]] __attribute__((noinline))
static ErrorCode Execute(
1079 Sink& sink,
const uint8_t* codes,
const uint8_t* args)
1081 return Executor<Sink, Profile>(sink, codes, args).Run();
1084 template <OutputSink Sink,
auto ArgumentInfoList,
auto ArgumentOrder,
1085 FormatProfile Profile,
typename... Args>
1086 [[nodiscard]] __attribute__((noinline))
static ErrorCode RunTaggedArgumentOrder(
1087 Sink& sink,
const uint8_t* codes, Args&&... args)
1089 if constexpr (ArgumentInfoList.size() == 0)
1091 return Execute<Sink, Profile>(sink, codes,
nullptr);
1095 constexpr size_t packed_arg_bytes = PackedArgumentBytes<ArgumentInfoList>();
1096 uint8_t packed[packed_arg_bytes];
1097 auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
1098 auto* cursor = packed;
1099 StoreArgumentsOrdered<ArgumentInfoList, ArgumentOrder>(cursor, tuple);
1100 return Execute<Sink, Profile>(sink, codes, packed);
1206 Executor(Sink& sink,
const uint8_t* codes,
const uint8_t* args)
1218 auto op = codes_.
ReadOp();
1219 if (op == FormatOp::End)
1235 [[nodiscard]]
ErrorCode WriteRaw(std::string_view text) {
return sink_.Write(text); }
1236 [[nodiscard]]
ErrorCode WritePadding(
char fill,
size_t count)
1239 std::memset(chunk, fill,
sizeof(chunk));
1243 size_t step = (count <
sizeof(chunk)) ? count : sizeof(chunk);
1244 auto ec = WriteRaw(std::string_view(chunk, step));
1254 [[nodiscard]]
ErrorCode WriteTextField(std::string_view text,
const Spec& spec)
1257 size_t left_pad = 0;
1258 size_t right_pad = 0;
1259 if (spec.LeftAlign())
1263 else if (spec.CenterAlign())
1266 right_pad = pad - left_pad;
1273 if (
auto ec = WritePadding(spec.fill, left_pad); ec !=
ErrorCode::OK)
1281 return WritePadding(spec.fill, right_pad);
1283 [[nodiscard]]
ErrorCode WriteIntegerField(
char sign_char, std::string_view prefix,
1284 std::string_view digits,
1287 auto write_char = [
this](
char ch) ->
ErrorCode {
1292 return WriteRaw(std::string_view(&ch, 1));
1294 auto write_text = [
this](std::string_view text) ->
ErrorCode {
1299 return WriteRaw(text);
1303 size_t total = digits.size() + zeros + prefix.size() +
1304 static_cast<size_t>(sign_char !=
'\0');
1306 bool zero_fill = spec.ZeroPad() && !spec.LeftAlign() && !spec.CenterAlign() &&
1307 !spec.HasPrecision();
1308 size_t left_pad = 0;
1309 size_t middle_zeros = zero_fill ? pad : 0;
1310 size_t right_pad = 0;
1313 if (spec.LeftAlign())
1317 else if (spec.CenterAlign())
1320 right_pad = pad - left_pad;
1328 if (
auto ec = WritePadding(spec.fill, left_pad); ec !=
ErrorCode::OK)
1340 if (
auto ec = WritePadding(
'0', middle_zeros); ec !=
ErrorCode::OK)
1344 if (
auto ec = WritePadding(
'0', zeros); ec !=
ErrorCode::OK)
1352 return WritePadding(spec.fill, right_pad);
1354 [[nodiscard]]
ErrorCode WriteFloatField(
char sign_char, std::string_view text,
1357 auto write_char = [
this](
char ch) ->
ErrorCode {
1362 return WriteRaw(std::string_view(&ch, 1));
1365 size_t total = text.size() +
static_cast<size_t>(sign_char !=
'\0');
1367 bool zero_fill = spec.ZeroPad() && !spec.LeftAlign() && !spec.CenterAlign();
1368 size_t left_pad = 0;
1369 size_t middle_zeros = zero_fill ? pad : 0;
1370 size_t right_pad = 0;
1373 if (spec.LeftAlign())
1377 else if (spec.CenterAlign())
1380 right_pad = pad - left_pad;
1388 if (
auto ec = WritePadding(spec.fill, left_pad); ec !=
ErrorCode::OK)
1396 if (
auto ec = WritePadding(
'0', middle_zeros); ec !=
ErrorCode::OK)
1404 return WritePadding(spec.fill, right_pad);
1407 template <std::
signed_
integral Int>
1408 [[nodiscard]]
static char ResolveSignChar(Int value,
const Spec& spec)
1414 if (spec.ForceSign())
1418 if (spec.SpaceSign())
1425 template <
typename T>
1426 [[nodiscard]]
static char ResolveFloatSignChar(T value,
const Spec& spec)
1428 if (std::signbit(value))
1432 if (spec.ForceSign())
1436 if (spec.SpaceSign())
1443 template <std::
signed_
integral Int>
1444 [[nodiscard]]
ErrorCode WriteSigned(
const Spec& spec, Int value)
1446 using UInt = std::make_unsigned_t<Int>;
1447 char digit_buffer[32];
1448 UInt bits =
static_cast<UInt
>(value);
1449 UInt magnitude = (value < 0) ? (UInt{0} - bits) : bits;
1450 size_t digit_count = AppendUnsigned(digit_buffer, magnitude, 10,
false);
1452 std::string_view digits(digit_buffer, digit_count);
1453 if (value == 0 && spec.precision == 0)
1458 return WriteIntegerField(ResolveSignChar(value, spec), {}, digits, spec);
1461 template <std::
unsigned_
integral UInt>
1462 [[nodiscard]]
ErrorCode WriteUnsigned(FormatType type,
const Spec& spec, UInt value)
1473 char digit_buffer[33];
1474 size_t digit_count = AppendUnsigned(digit_buffer, value, base, upper_case);
1476 if (type == FormatType::Octal32 || type == FormatType::Octal64)
1480 else if (value == 0 && spec.precision == 0)
1485 std::string_view digits(digit_buffer, digit_count);
1486 return WriteIntegerField(
'\0', prefix, digits, spec);
1488 [[nodiscard]]
ErrorCode WritePointer(
const Spec& spec, uintptr_t value)
1490 char digit_buffer[2 *
sizeof(uintptr_t)];
1491 size_t digit_count = AppendUnsigned(digit_buffer, value, 16,
false);
1494 if (!actual.HasPrecision() || actual.precision == 0)
1496 actual.precision = 1;
1499 return WriteIntegerField(
'\0',
"0x", std::string_view(digit_buffer, digit_count),
1502 [[nodiscard]]
ErrorCode WriteCharacter(
const Spec& spec,
char ch)
1504 return WriteTextField(std::string_view(&ch, 1), spec);
1506 [[nodiscard]]
ErrorCode WriteString(
const Spec& spec, std::string_view text)
1509 if (spec.HasPrecision() && spec.precision < view.size())
1511 view = view.substr(0, spec.precision);
1514 return WriteTextField(view, spec);
1517 template <
typename T>
1518 [[nodiscard]]
ErrorCode WriteFloat(FormatType type,
const Spec& spec, T value)
1525 char sign_char = ResolveFloatSignChar(value, spec);
1526 T magnitude = std::signbit(value) ? -
static_cast<T
>(value) : static_cast<T>(value);
1527 char output_buffer[float_buffer_capacity];
1528 size_t output_size = 0;
1529 if (!FormatFloatText(type, spec, magnitude, output_buffer, output_size))
1534 return WriteFloatField(sign_char, std::string_view(output_buffer, output_size), spec);
1540 char digit_buffer[10];
1541 size_t digit_count = AppendUnsigned(digit_buffer, value, 10,
false);
1542 return WriteRaw(std::string_view(digit_buffer, digit_count));
1548 char digit_buffer[10];
1549 size_t digit_count = AppendUnsigned(digit_buffer, value, 10,
false);
1551 if (
auto ec = WritePadding(
'0', zeros); ec !=
ErrorCode::OK)
1555 return WriteRaw(std::string_view(digit_buffer, digit_count));
1561 return WriteRaw(text);
1567 char sign_char = std::signbit(value) ?
'-' :
'\0';
1568 float magnitude = std::signbit(value) ? -value : value;
1569 char output_buffer[float_buffer_capacity];
1570 size_t output_size = 0;
1576 if (sign_char !=
'\0')
1578 if (
auto ec = WriteRaw(std::string_view(&sign_char, 1)); ec !=
ErrorCode::OK)
1584 return WriteRaw(std::string_view(output_buffer, output_size));
1590 return WriteFloat(FormatType::DoubleFixed,
Spec{.precision = precision}, value);
1598 template <std::
signed_
integral Int>
1599 [[nodiscard]]
ErrorCode DispatchSignedField()
1601 return WriteSigned(codes_.
ReadSpec(), args_.
Read<Int>());
1604 template <FormatType Type, std::
unsigned_
integral UInt>
1605 [[nodiscard]]
ErrorCode DispatchUnsignedField()
1607 return WriteUnsigned(Type, codes_.
ReadSpec(), args_.
Read<UInt>());
1610 template <FormatType Type,
typename Float>
1611 [[nodiscard]]
ErrorCode DispatchFloatField()
1613 return WriteFloat(Type, codes_.
ReadSpec(), args_.
Read<Float>());
1616 [[nodiscard]]
ErrorCode DispatchPointerField()
1618 return WritePointer(codes_.
ReadSpec(), args_.
Read<uintptr_t>());
1621 [[nodiscard]]
ErrorCode DispatchCharacterField()
1623 return WriteCharacter(codes_.
ReadSpec(), args_.
Read<
char>());
1626 [[nodiscard]]
ErrorCode DispatchStringField()
1628 return WriteString(codes_.
ReadSpec(), args_.
Read<std::string_view>());
1636 case FormatType::Signed32:
1637 if constexpr (!Config::enable_integer)
1641 return DispatchSignedField<int32_t>();
1642 case FormatType::Signed64:
1643 if constexpr (!Config::enable_integer || !Config::enable_integer_64bit)
1647 return DispatchSignedField<int64_t>();
1648 case FormatType::Unsigned32:
1649 if constexpr (!Config::enable_integer)
1653 return DispatchUnsignedField<FormatType::Unsigned32, uint32_t>();
1654 case FormatType::Unsigned64:
1655 if constexpr (!Config::enable_integer || !Config::enable_integer_64bit)
1659 return DispatchUnsignedField<FormatType::Unsigned64, uint64_t>();
1660 case FormatType::Binary32:
1661 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16)
1665 return DispatchUnsignedField<FormatType::Binary32, uint32_t>();
1666 case FormatType::Binary64:
1667 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16 ||
1668 !Config::enable_integer_64bit)
1672 return DispatchUnsignedField<FormatType::Binary64, uint64_t>();
1673 case FormatType::Octal32:
1674 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16)
1678 return DispatchUnsignedField<FormatType::Octal32, uint32_t>();
1679 case FormatType::Octal64:
1680 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16 ||
1681 !Config::enable_integer_64bit)
1685 return DispatchUnsignedField<FormatType::Octal64, uint64_t>();
1686 case FormatType::HexLower32:
1687 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16)
1691 return DispatchUnsignedField<FormatType::HexLower32, uint32_t>();
1692 case FormatType::HexLower64:
1693 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16 ||
1694 !Config::enable_integer_64bit)
1698 return DispatchUnsignedField<FormatType::HexLower64, uint64_t>();
1699 case FormatType::HexUpper32:
1700 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16)
1704 return DispatchUnsignedField<FormatType::HexUpper32, uint32_t>();
1705 case FormatType::HexUpper64:
1706 if constexpr (!Config::enable_integer || !Config::enable_integer_base8_16 ||
1707 !Config::enable_integer_64bit)
1711 return DispatchUnsignedField<FormatType::HexUpper64, uint64_t>();
1712 case FormatType::Pointer:
1713 if constexpr (!Config::enable_pointer)
1717 return DispatchPointerField();
1718 case FormatType::Character:
1719 if constexpr (!Config::enable_text)
1723 return DispatchCharacterField();
1724 case FormatType::String:
1725 if constexpr (!Config::enable_text)
1729 return DispatchStringField();
1730 case FormatType::FloatFixed:
1735 return DispatchFloatField<FormatType::FloatFixed, float>();
1736 case FormatType::DoubleFixed:
1741 return DispatchFloatField<FormatType::DoubleFixed, double>();
1742 case FormatType::FloatScientific:
1743 if constexpr (!
FloatEnabled(FormatType::FloatScientific))
1747 return DispatchFloatField<FormatType::FloatScientific, float>();
1748 case FormatType::DoubleScientific:
1749 if constexpr (!
FloatEnabled(FormatType::DoubleScientific))
1753 return DispatchFloatField<FormatType::DoubleScientific, double>();
1754 case FormatType::FloatGeneral:
1759 return DispatchFloatField<FormatType::FloatGeneral, float>();
1760 case FormatType::DoubleGeneral:
1761 if constexpr (!
FloatEnabled(FormatType::DoubleGeneral))
1765 return DispatchFloatField<FormatType::DoubleGeneral, double>();
1766 case FormatType::LongDoubleFixed:
1767 if constexpr (!
FloatEnabled(FormatType::LongDoubleFixed))
1771 return DispatchFloatField<FormatType::LongDoubleFixed, long double>();
1772 case FormatType::LongDoubleScientific:
1773 if constexpr (!
FloatEnabled(FormatType::LongDoubleScientific))
1777 return DispatchFloatField<FormatType::LongDoubleScientific, long double>();
1778 case FormatType::LongDoubleGeneral:
1779 if constexpr (!
FloatEnabled(FormatType::LongDoubleGeneral))
1783 return DispatchFloatField<FormatType::LongDoubleGeneral, long double>();
1784 case FormatType::TextInline:
1785 case FormatType::TextRef:
1786 case FormatType::TextSpace:
1787 case FormatType::End:
1798 case FormatOp::TextInline:
1800 case FormatOp::TextRef:
1802 case FormatOp::TextSpace:
1803 return WriteRaw(
" ");
1804 case FormatOp::U32Dec:
1805 if constexpr (!HasProfile(Profile, FormatProfile::U32) ||
1806 !Config::enable_integer)
1811 case FormatOp::U32ZeroPadWidth:
1812 if constexpr (!HasProfile(Profile, FormatProfile::U32) ||
1813 !Config::enable_integer)
1818 case FormatOp::StringRaw:
1819 if constexpr (!HasProfile(Profile, FormatProfile::TextArg) ||
1820 !Config::enable_text)
1825 case FormatOp::F32FixedPrec:
1826 if constexpr (!HasProfile(Profile, FormatProfile::F32Fixed) ||
1832 case FormatOp::F64FixedPrec:
1833 if constexpr (!HasProfile(Profile, FormatProfile::F64Fixed) ||
1839 case FormatOp::GenericField:
1840 if constexpr (!HasProfile(Profile, FormatProfile::Generic))