22#include <reloco/fmt.hpp>
68 explicit constexpr span_sink(span<char> buf RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
69 : m_buf(buf), m_pos(0) {}
71#if RELOCO_HAS_STD_SPAN
80 template <std::
size_t Extent>
82 std::span<char, Extent> buf RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
83 : m_buf(buf.data(), buf.size()), m_pos(0) {}
94 return sink{
this, [](
void *ctx, microfmt::string_view sv)
noexcept {
95 auto *self =
static_cast<span_sink *
>(ctx);
96 const size_t avail = (self->m_pos < self->m_buf.size()) ? (self->m_buf.size() - self->m_pos) : 0;
97 const size_t n = std::min(sv.size(), avail);
99 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
100 std::copy_n(sv.data(), n, self->m_buf.data() + self->m_pos);
101 RELOCO_END_UNSAFE_BUFFER_USAGE;
112 [[nodiscard]]
constexpr microfmt::string_view
view() const noexcept RELOCO_LIFETIMEBOUND {
113 return microfmt::string_view(m_buf.data(), m_pos);
121 [[nodiscard]]
constexpr size_t size() const noexcept {
return m_pos; }
128 [[nodiscard]]
constexpr size_t available() const noexcept {
129 return (m_pos < m_buf.size()) ? (m_buf.size() - m_pos) : 0;
135 RELOCO_REINITIALIZES
constexpr void reset() noexcept { m_pos = 0; }
146 std::size_t m_capacity{};
149 constexpr buffer_sink_base(
char *ptr RELOCO_LIFETIMEBOUND, std::size_t cap,
size_t pos) noexcept
150 : m_data(ptr), m_capacity(cap), m_pos(pos) {}
153 buffer_sink_base(
const buffer_sink_base &) =
delete;
154 buffer_sink_base &operator=(
const buffer_sink_base &) =
delete;
155 buffer_sink_base(buffer_sink_base &&) =
delete;
156 buffer_sink_base &operator=(buffer_sink_base &&) =
delete;
158 RELOCO_CONSTEXPR20
static void write_thunk(
void *ctx, microfmt::string_view sv)
noexcept {
159 auto *self =
static_cast<buffer_sink_base *
>(ctx);
160 const std::size_t avail = (self->m_pos < self->m_capacity) ? (self->m_capacity - self->m_pos) : 0;
161 const std::size_t n = std::min(sv.size(), avail);
163 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
164 std::copy_n(sv.data(), n, self->m_data + self->m_pos);
165 RELOCO_END_UNSAFE_BUFFER_USAGE;
170 RELOCO_REINITIALIZES
void reset() noexcept { m_pos = 0; }
185template <std::
size_t N>
class buffer_sink : detail::buffer_sink_base {
190 constexpr buffer_sink() noexcept : detail::buffer_sink_base{m_storage, N, 0} {}
193 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE
196 for (std::size_t i = 0; i < other.m_pos; ++i) {
197 m_storage[i] = other.m_storage[i];
202 for (std::size_t i = 0; i < other.m_pos; ++i) {
203 m_storage[i] = other.m_storage[i];
209 if (
this != &other) {
210 this->m_pos = other.m_pos;
211 for (std::size_t i = 0; i < other.m_pos; ++i) {
212 m_storage[i] = other.m_storage[i];
219 if (
this != &other) {
220 this->m_pos = other.m_pos;
221 for (std::size_t i = 0; i < other.m_pos; ++i) {
222 m_storage[i] = other.m_storage[i];
228 RELOCO_END_UNSAFE_BUFFER_USAGE
236 [[nodiscard]] RELOCO_CONSTEXPR20
sink as_sink() &
noexcept RELOCO_LIFETIMEBOUND {
237 return sink{
this, &detail::buffer_sink_base::write_thunk};
245 [[nodiscard]]
constexpr microfmt::string_view
view() const & noexcept RELOCO_LIFETIMEBOUND {
246 return microfmt::string_view(m_data, m_pos);
249 [[nodiscard]]
constexpr microfmt::string_view
view() const && = delete;
256 [[nodiscard]] constexpr std::
size_t size() const noexcept {
return m_pos; }
263 [[nodiscard]]
constexpr std::size_t
capacity() const noexcept {
return N; }
270 [[nodiscard]]
constexpr std::size_t
available() const noexcept {
return (m_pos < N) ? (N - m_pos) : 0; }
278 [[nodiscard]]
constexpr span<const char>
as_span() const & noexcept RELOCO_LIFETIMEBOUND {
279 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
281 return span<const char>(m_data, m_pos);
283 RELOCO_END_UNSAFE_BUFFER_USAGE;
286 [[nodiscard]]
constexpr span<const char>
as_span() const && = delete;
288#if RELOCO_HAS_STD_SPAN
297 [[nodiscard]]
constexpr std::span<const char> as_std_span() const & noexcept RELOCO_LIFETIMEBOUND {
298 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
300 return std::span<const char>(m_data, m_pos);
302 RELOCO_END_UNSAFE_BUFFER_USAGE;
305 [[nodiscard]]
constexpr std::span<const char> as_std_span() const && = delete;
312 RELOCO_REINITIALIZES
constexpr void reset() noexcept { m_pos = 0; }
344 RELOCO_BEGIN_UNSAFE_CODE
346 return sink{
this, [](
void *ctx, microfmt::string_view sv)
noexcept {
349#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
351 self->m_it = std::copy(sv.begin(), sv.end(), self->m_it);
360 self->m_it = std::copy(sv.begin(), sv.end(), self->m_it);
371 [[nodiscard]]
constexpr OutputIt
current() const noexcept {
return m_it; }
399 RELOCO_BEGIN_UNSAFE_CODE
400 [[nodiscard]]
sink as_sink() & noexcept RELOCO_LIFETIMEBOUND {
401 return sink{
this, [](
void *ctx, microfmt::string_view sv)
noexcept {
403 self->m_count += sv.size();
413 [[nodiscard]]
constexpr std::size_t
count() const noexcept {
return m_count; }
418 RELOCO_REINITIALIZES
constexpr void reset() noexcept { m_count = 0; }
421 std::size_t m_count{0};
439 return sink{
nullptr, [](
void *, microfmt::string_view)
noexcept {}};
447 std::size_t m_max_payload{};
450 constexpr c_string_sink_base(
char *ptr RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS,
size_t max,
452 : m_data(ptr), m_max_payload(max), m_pos(pos) {}
460 RELOCO_CONSTEXPR20
static void write_thunk(
void *ctx, microfmt::string_view sv)
noexcept {
462 const std::size_t avail = (self->m_pos < self->m_max_payload) ? (self->m_max_payload - self->m_pos) : 0;
463 const std::size_t n = std::min(sv.size(), avail);
466 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
467 std::copy_n(sv.data(), n, self->m_data + self->m_pos);
469 self->m_data[self->m_pos] =
'\0';
470 RELOCO_END_UNSAFE_BUFFER_USAGE;
487 static_assert(N > 0,
"c_string_sink buffer size must be at least 1 byte");
496 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE
499 for (std::size_t i = 0; i < other.m_pos; ++i) {
500 m_storage[i] = other.m_storage[i];
502 m_storage[other.m_pos] =
'\0';
506 for (std::size_t i = 0; i < other.m_pos; ++i) {
507 m_storage[i] = other.m_storage[i];
509 m_storage[other.m_pos] =
'\0';
514 if (
this != &other) {
515 this->
m_pos = other.m_pos;
516 for (std::size_t i = 0; i < other.m_pos; ++i) {
517 m_storage[i] = other.m_storage[i];
519 m_storage[other.m_pos] =
'\0';
525 if (
this != &other) {
526 this->
m_pos = other.m_pos;
527 for (std::size_t i = 0; i < other.m_pos; ++i) {
528 m_storage[i] = other.m_storage[i];
530 m_storage[other.m_pos] =
'\0';
535 RELOCO_END_UNSAFE_BUFFER_USAGE
554 RELOCO_BEGIN_UNSAFE_CODE
555 [[nodiscard]]
constexpr const char *
c_str() const & noexcept RELOCO_LIFETIMEBOUND {
return m_data; }
558 [[nodiscard]]
constexpr const char *
c_str() const && = delete;
566 [[nodiscard]] constexpr
microfmt::string_view
view() const & noexcept RELOCO_LIFETIMEBOUND {
570 [[nodiscard]]
constexpr microfmt::string_view
view() const && = delete;
578 [[nodiscard]] constexpr std::
size_t size() const noexcept {
return m_pos; }
586 [[nodiscard]]
static constexpr std::size_t
max_size() noexcept {
return N - 1; }
591 RELOCO_REINITIALIZES
constexpr void reset() noexcept {
614 explicit constexpr callback_sink(Callable &fn RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
624 return sink{m_fn.get(), [](
void *ctx, microfmt::string_view sv)
noexcept {
625 auto *fn =
static_cast<Callable *
>(ctx);
631 value_ptr<Callable> m_fn;
642template <
typename Callable>
654inline constexpr auto digits_lut = []() {
655 array<char, 200> arr{};
656 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
657 for (
int i = 0; i < 100; ++i) {
658 arr.unsafe_at(2 *
static_cast<size_t>(i)) =
static_cast<char>(
'0' + (i / 10));
659 arr.unsafe_at(2 *
static_cast<size_t>(i) + 1) =
static_cast<char>(
'0' + (i % 10));
661 RELOCO_END_UNSAFE_BUFFER_USAGE;
665inline constexpr reloco::array<char, 16> hex_digits_lower = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
666 '8',
'9',
'a',
'b',
'c',
'd',
'e',
'f'};
668inline constexpr reloco::array<char, 16> hex_digits_upper = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
669 '8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'};
682RELOCO_BEGIN_UNSAFE_CODE
683template <detail::radix Radix>
684RELOCO_ALWAYS_INLINE
inline void format_integer_core(
const sink &out, uint64_t val,
bool is_negative,
bool uppercase,
685 int min_width)
noexcept {
686 static_assert(Radix == radix::decimal || Radix == radix::hex,
687 "format_integer_core only supports decimal/hex; use format_binary for radix::binary");
694 constexpr size_t buf_size = (Radix == radix::hex) ? 16 : 21;
695 microfmt::array<char, buf_size> buf;
696 size_t idx = buf.size();
700 }
else if constexpr (Radix == radix::hex) {
701 const auto &hex_digits = uppercase ? hex_digits_upper : hex_digits_lower;
703 buf[--idx] = hex_digits[val & 0xF];
708 const auto rem =
static_cast<uint32_t
>(val % 100);
711 buf[idx] = digits_lut[rem * 2];
712 buf[idx + 1] = digits_lut[rem * 2 + 1];
715 buf[--idx] =
static_cast<char>(
'0' + val);
717 const auto rem =
static_cast<uint32_t
>(val * 2);
719 buf[idx] = digits_lut[rem];
720 buf[idx + 1] = digits_lut[rem + 1];
726 if constexpr (Radix == radix::decimal) {
727 if (is_negative && min_width <= 0) {
732 const size_t digits_len = buf.size() - idx;
735 const size_t total_needed = digits_len + (is_negative ?
size_t{1} :
size_t{0});
739 if (
static_cast<size_t>(min_width) > total_needed) {
740 for (
size_t i = 0; i < static_cast<size_t>(min_width) - total_needed; ++i) {
746 out.write(microfmt::string_view(&buf[idx], digits_len));
757inline void format_binary(
const sink &out, uint64_t val,
int min_width = 0) noexcept {
760 for (uint64_t v = val; (v >>= 1) != 0;) {
764 for (
int i =
bits; i < min_width; ++i) {
767 for (
int i =
bits - 1; i >= 0; --i) {
768 out.put(
static_cast<char>(
'0' + ((val >> i) & 1U)));
775template <detail::radix Radix>
776RELOCO_ALWAYS_INLINE
inline void format_unsigned(
const sink &out, uint64_t val, [[maybe_unused]]
bool uppercase,
777 int min_width = 0) noexcept {
778 if constexpr (Radix == radix::binary) {
779 format_binary(out, val, min_width);
781 format_integer_core<Radix>(out, val,
false, uppercase, min_width);
786RELOCO_BEGIN_UNSAFE_CODE
788inline void format_signed(
const sink &out, int64_t val,
int min_width = 0) noexcept {
791 const uint64_t mag =
static_cast<uint64_t
>(-(val + 1)) + 1ULL;
792 format_integer_core<radix::decimal>(out, mag,
true,
false, min_width);
794 format_integer_core<radix::decimal>(out,
static_cast<uint64_t
>(val),
false,
false, min_width);
811RELOCO_ALWAYS_INLINE
inline void write_escaped_char(
const sink &out,
char ch,
char quote_char,
812 bool escape_quote)
noexcept {
813 const uint8_t b =
static_cast<uint8_t
>(ch);
846 if (ch == quote_char) {
854 if (b >= 32 && b <= 126) {
855 out.put(
static_cast<char>(b));
860 format_unsigned<radix::hex>(out, b,
false, 2);
866RELOCO_ALWAYS_INLINE
inline void write_debug_quoted(
const sink &out, microfmt::string_view data,
867 char quote_char)
noexcept {
869 for (
char ch : data) {
870 write_escaped_char(out, ch, quote_char,
true);
883 using iterator = microfmt::string_view::const_iterator;
889 microfmt::string_view spec RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
893 [[nodiscard]]
constexpr microfmt::string_view
spec() const noexcept RELOCO_LIFETIMEBOUND {
return m_spec; }
894 [[nodiscard]]
constexpr bool empty() const noexcept {
return m_spec.empty(); }
895 [[nodiscard]]
constexpr size_type size() const noexcept {
return m_spec.size(); }
899 [[nodiscard]]
constexpr const_iterator begin() const noexcept RELOCO_LIFETIMEBOUND {
return m_spec.begin(); }
900 [[nodiscard]]
constexpr const_iterator end() const noexcept RELOCO_LIFETIMEBOUND {
return m_spec.end(); }
901 [[nodiscard]]
constexpr const_iterator cbegin() const noexcept RELOCO_LIFETIMEBOUND {
return m_spec.cbegin(); }
902 [[nodiscard]]
constexpr const_iterator cend() const noexcept RELOCO_LIFETIMEBOUND {
return m_spec.cend(); }
905 [[nodiscard]]
constexpr char front() const noexcept {
return m_spec.empty() ?
'\0' : m_spec.front(); }
906 [[nodiscard]]
constexpr char back() const noexcept {
return m_spec.empty() ?
'\0' : m_spec.back(); }
908 return idx < m_spec.size() ? m_spec[idx] :
'\0';
914 RELOCO_BEGIN_UNSAFE_CODE
916 if (it >= m_spec.begin() && it <= m_spec.end()) {
917 m_spec.remove_prefix(
static_cast<size_type>(it - m_spec.begin()));
927 const char ch = m_spec.front();
928 m_spec.remove_prefix(1);
932 [[nodiscard]]
constexpr bool starts_with(
char ch)
const noexcept {
return !m_spec.empty() && m_spec.front() == ch; }
934 [[nodiscard]]
constexpr bool starts_with(microfmt::string_view prefix)
const noexcept {
936 return m_spec.starts_with(prefix);
938 return m_spec.size() >= prefix.size() && m_spec.substr(0, prefix.size()) == prefix;
944 [[nodiscard]]
constexpr microfmt::string_view
946 return m_spec.substr(pos, count);
975 if (!starts_with(
'?'))
982 microfmt::string_view m_spec;
985template <
typename T,
typename Enable =
void>
struct formatter;
993#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1001 void format(microfmt::string_view val,
const sink &out)
const noexcept {
1002#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1004 detail::write_debug_quoted(out, val,
'"');
1014#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1022 void format(std::string_view val,
const sink &out)
const noexcept {
1023#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1025 detail::write_debug_quoted(out, microfmt::string_view(val),
'"');
1029 out.write(microfmt::string_view(val));
1037#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1047 RELOCO_BEGIN_UNSAFE_CODE
1049 const microfmt::string_view sv = val !=
nullptr ? microfmt::string_view(val) : microfmt::string_view(
"(null)", 6);
1050#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1052 detail::write_debug_quoted(out, sv,
'"');
1078template <
typename UInt>
1079RELOCO_ALWAYS_INLINE RELOCO_UNSAFE_BUFFER_USAGE RELOCO_CONSTEXPR20
inline char *
1082 while (value >= 100) {
1083 const auto rem =
static_cast<unsigned>(value % 100);
1086 ptr[0] = digits_lut[(unsigned)(rem * 2)];
1087 ptr[1] = digits_lut[(unsigned)(rem * 2 + 1)];
1092 *--ptr =
static_cast<char>(
'0' + value);
1095 ptr[0] = digits_lut[(unsigned)(value * 2)];
1096 ptr[1] = digits_lut[(unsigned)(value * 2 + 1)];
1105template <
typename UInt>
1106RELOCO_UNSAFE_BUFFER_USAGE RELOCO_ALWAYS_INLINE RELOCO_CONSTEXPR20
inline char *
1108 const auto &lut = uppercase ? hex_digits_upper : hex_digits_lower;
1114 *--ptr = lut[value & 0xF];
1122RELOCO_BEGIN_UNSAFE_CODE
1125 microfmt::string_view prefix, uint8_t width,
bool zero_pad)
noexcept {
1126 const size_t prefix_len = prefix.size();
1127 const size_t total_content_len = digits_len + prefix_len + (is_negative ?
size_t{1} :
size_t{0});
1128 const size_t pad_len = (width > total_content_len) ? (width - total_content_len) : 0;
1135 if (!prefix.empty()) {
1138 for (
size_t i = 0; i < pad_len; ++i) {
1143 for (
size_t i = 0; i < pad_len; ++i) {
1149 if (!prefix.empty()) {
1154 out.write(microfmt::string_view(digits, digits_len));
1184#if defined(MICROFMT_SHARED)
1201 MICROFMT_API void format_int_impl(int64_t val,
const sink &out)
const noexcept;
1202 MICROFMT_API void format_int_impl(uint64_t val,
const sink &out)
const noexcept;
1211 RELOCO_BEGIN_UNSAFE_CODE
1213 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
1214 constexpr size_t BUF_SIZE = (
sizeof(T) <= 4) ? 12 : (
sizeof(T) <= 8) ? 24 : 48;
1215 char buffer[BUF_SIZE];
1216 char *end = buffer + BUF_SIZE;
1219 using unsigned_t = std::make_unsigned_t<T>;
1221 bool is_negative =
false;
1223 if constexpr (std::is_signed_v<T>) {
1226 uval =
static_cast<unsigned_t
>(0) -
static_cast<unsigned_t
>(val);
1228 uval =
static_cast<unsigned_t
>(val);
1234 microfmt::string_view prefix{};
1235 if (flags.is_hex != 0) {
1236 start = detail::format_hex_backward(end, uval, flags.uppercase);
1237 if (flags.alt_form != 0) {
1238 prefix = flags.uppercase != 0 ? microfmt::string_view(
"0X") : microfmt::string_view(
"0x");
1241 start = detail::format_dec_backward(end, uval);
1244 const size_t digits_len =
static_cast<size_t>(end - start);
1245 detail::emit_formatted_int(out, start, digits_len, is_negative, prefix, width, flags.zero_pad != 0);
1246 RELOCO_END_UNSAFE_BUFFER_USAGE;
1253template <
typename T>
1254struct formatter<T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char>>>
1256 RELOCO_ALWAYS_INLINE
void format(T val,
const sink &out)
const noexcept {
1257 if constexpr (
sizeof(T) <=
sizeof(uint64_t)) {
1264 if constexpr (std::is_signed_v<T>) {
1265 format_int_impl(
static_cast<int64_t
>(val), out);
1267 format_int_impl(
static_cast<uint64_t
>(val), out);
1271 format_int_impl(val, out);
1278#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1287#ifndef MICROFMT_DISABLE_DEBUG_FLAG
1290 detail::write_escaped_char(out, val,
'\'',
true);
1301 void format(
bool val,
const sink &out)
const noexcept { out.write(val ? string_view(
"true") : string_view(
"false")); }
1311 const microfmt::string_view s = ctx.spec();
1313 if (!s.empty() && s[p] ==
'0')
1315 while (p < s.size() && s[p] >=
'0' && s[p] <=
'9') {
1316 width = width * 10 + (s[p] -
'0');
1323 RELOCO_BEGIN_UNSAFE_CODE
1326 detail::format_unsigned<detail::radix::hex>(out,
reinterpret_cast<uintptr_t
>(ptr),
false, width);
1333template <
typename T>
1334struct formatter<T *, std::enable_if_t<!std::is_same_v<T, const char> && !std::is_same_v<T, char>>>
1336 void format(T *ptr,
const sink &out)
const noexcept { do_format(ptr, out); }
1341 void format(std::nullptr_t,
const sink &out)
const noexcept { out.write(
"0x0"); }
1348using format_fn_t = void (*)(
const void *val_ptr, microfmt::string_view spec,
const sink &out)
noexcept;
1360template <
typename T,
typename =
void>
struct has_formatter : std::false_type {};
1362template <
typename T>
struct has_formatter<T, std::void_t<decltype(sizeof(formatter<T>))>> : std::true_type {};
1367RELOCO_BEGIN_UNSAFE_CODE
1368template <
typename T>
1371 using DecayedT = std::decay_t<T>;
1377 auto with_value = [&val_ptr](
auto &&handle) {
1378 if constexpr (std::is_array_v<std::remove_reference_t<T>>) {
1379 const auto *decayed_val =
static_cast<const std::remove_all_extents_t<std::remove_reference_t<T>
> *>(val_ptr);
1380 handle(decayed_val);
1382 handle(*
static_cast<const DecayedT *
>(val_ptr));
1393 if constexpr (has_formatter_v<DecayedT>) {
1397 with_value([&f, &out](
const auto &value) { f.format(value, out); });
1398 }
else if constexpr (reloco::has_display_v<DecayedT>) {
1399 with_value([&out](
const auto &value) { reloco::Display<DecayedT>::format(value, out); });
1400 }
else if constexpr (reloco::has_debug_v<DecayedT>) {
1401 with_value([&out](
const auto &value) { reloco::Debug<DecayedT>::format(value, out); });
1410 with_value([&f, &out](
const auto &value) { f.format(value, out); });
1448 if constexpr (has_formatter_v<DecayedT>) {
1450 }
else if constexpr (reloco::has_display_v<DecayedT>) {
1451 reloco::Display<DecayedT>::format(value, out);
1452 }
else if constexpr (reloco::has_debug_v<DecayedT>) {
1453 reloco::Debug<DecayedT>::format(value, out);
1459 f.format(value, out);
1498constexpr bool starts_with(microfmt::string_view sv, microfmt::string_view prefix)
noexcept {
1499 return sv.size() >= prefix.size() && sv.compare(0, prefix.size(), prefix) == 0;
1502constexpr bool starts_with(microfmt::string_view sv,
char c)
noexcept {
return !sv.empty() && sv.front() == c; }
1507 &format_type_thunk<microfmt_remove_cvref_t<Args>>...};
1518RELOCO_BEGIN_UNSAFE_CODE
1524 constexpr size_t max_size =
static_cast<size_t>(-1);
1526 for (
const char c : text) {
1527 if (c <
'0' || c >
'9') {
1531 const size_t digit =
static_cast<size_t>(c -
'0');
1532 if (value > (max_size - digit) / 10) {
1535 value = value * 10 + digit;
1546 microfmt::string_view literal{};
1547 microfmt::string_view spec{};
1548 size_t arg_index{0};
1553 microfmt::array<compiled_piece, MaxPieces>
pieces{};
1557template <
size_t MaxPieces = 32>
1561 size_t lit_start = 0;
1563 for (
size_t i = 0; i < str.size(); ++i) {
1564 if (str[i] ==
'{') {
1565 if (i + 1 < str.size() && str[i + 1] ==
'{') {
1566 if (i > lit_start) {
1567 result.
pieces[result.count++] =
compiled_piece{str.substr(lit_start, i - lit_start), {}, 0,
false};
1569 result.pieces[result.count++] =
compiled_piece{str.substr(i, 1), {}, 0,
false};
1576 if (i > lit_start) {
1577 result.pieces[result.count++] =
compiled_piece{str.substr(lit_start, i - lit_start), {}, 0,
false};
1582 while (end < str.size() && str[end] !=
'}') {
1586 const microfmt::string_view replacement = str.substr(i + 1, end - (i + 1));
1587 const size_t colon_pos = replacement.find(
':');
1588 const microfmt::string_view index =
1589 colon_pos == microfmt::string_view::npos ? replacement : replacement.substr(0, colon_pos);
1590 const microfmt::string_view spec =
1591 colon_pos == microfmt::string_view::npos ? microfmt::string_view{} : replacement.substr(colon_pos + 1);
1592 size_t piece_arg_index = arg_idx;
1596 result.pieces[result.count++] =
compiled_piece{{}, spec, piece_arg_index,
true};
1600 }
else if (str[i] ==
'}') {
1601 if (i + 1 < str.size() && str[i + 1] ==
'}') {
1602 if (i > lit_start) {
1603 result.pieces[result.count++] =
compiled_piece{str.substr(lit_start, i - lit_start), {}, 0,
false};
1605 result.pieces[result.count++] =
compiled_piece{str.substr(i, 1), {}, 0,
false};
1614 if (lit_start < str.size()) {
1615 result.pieces[result.count++] =
compiled_piece{str.substr(lit_start), {}, 0,
false};
1623RELOCO_BEGIN_UNSAFE_CODE
1624template <
size_t TargetIdx,
size_t CurIdx,
typename T,
typename... Rest>
1625RELOCO_ALWAYS_INLINE
inline const void *
get_arg_by_index(
const T &first,
const Rest &...rest)
noexcept {
1626 if constexpr (TargetIdx == CurIdx) {
1627 return static_cast<const void *
>(&first);
1629 return get_arg_by_index<TargetIdx, CurIdx + 1>(rest...);
1635 static constexpr auto source = StrProvider::get();
1640template <
typename StrProvider,
size_t PieceIdx,
typename... Args>
1642 const std::tuple<const Args &...> &arg_tuple)
noexcept {
1645 if constexpr (!piece.is_arg) {
1646 out.write(piece.literal);
1649 const auto &arg = std::get<piece.arg_index>(arg_tuple);
1655 fmt.format(arg, out);
1659template <
typename StrProvider,
typename... Args,
size_t... Is>
1661 const Args &...args)
noexcept {
1662 auto arg_tuple = std::forward_as_tuple(args...);
1664 (emit_piece_by_index<StrProvider, Is>(out, arg_tuple), ...);
1702template <
typename T> [[nodiscard]]
constexpr auto as_debug(
const T &value RELOCO_LIFETIMEBOUND)
noexcept {
1711template <
typename T, std::enable_if_t<!std::is_lvalue_reference_v<T>,
int> = 0>
1712[[nodiscard]]
constexpr auto as_debug(T &&) noexcept = delete;
1714template <typename T> struct
formatter<detail::debug_view<T>> {
1722 struct no_formatter_stub {
1726 std::conditional_t<!reloco::has_debug_v<DecayedT> && detail::has_formatter_v<DecayedT>,
formatter<DecayedT>,
1733 if constexpr (reloco::has_debug_v<DecayedT>) {
1734 reloco::Debug<DecayedT>::format(view.value, out);
1735 }
else if constexpr (detail::has_formatter_v<DecayedT>) {
1736 underlying.format(view.value, out);
1737 }
else if constexpr (reloco::has_display_v<DecayedT>) {
1738 reloco::Display<DecayedT>::format(view.value, out);
1740 static_assert(reloco::has_debug_v<DecayedT> || detail::has_formatter_v<DecayedT> ||
1741 reloco::has_display_v<DecayedT>,
1742 "microfmt::as_debug(value): no Debug<T>, formatter<T>, or Display<T> found for T");
1755 const span<const format_fn_t> arg_fns)
noexcept;
1757#if MICROFMT_SHARED_PROVIDE_DEFINITIONS
1758#include "microfmt.ipp"
1765template <
typename... Args>
1766RELOCO_CONSTEXPR20
inline void format_to(
const sink &out, microfmt::string_view
fmt,
const Args &...args)
noexcept {
1767 if constexpr (
sizeof...(Args) == 0) {
1771 const void *
const arg_ptrs[] = {
static_cast<const void *
>(&args)...};
1774 vformat_to(out,
fmt, span<const void *const>(arg_ptrs,
sizeof...(Args)), thunks);
1778template <
typename OutputIt,
typename... Args>
1779RELOCO_CONSTEXPR20
inline OutputIt
format_to(OutputIt it, microfmt::string_view
fmt,
const Args &...args)
noexcept {
1785template <
size_t N,
typename... Args>
1795 [[nodiscard]]
constexpr microfmt::string_view
get() const & noexcept RELOCO_LIFETIMEBOUND {
return Provider::get(); }
1797 [[nodiscard]]
constexpr microfmt::string_view
get() const && = delete;
1801#define MICROFMT_STRING(s) \
1803 struct str_provider { \
1804 static constexpr microfmt::string_view get() noexcept { return microfmt::string_view{s, sizeof(s) - 1}; } \
1806 return ::microfmt::compile_string_holder<str_provider>{}; \
1823template <
typename StrProvider,
typename... Args>
1825 const Args &...args)
noexcept {
1826#if defined(MICROFMT_SHARED)
1827 format_to(out, StrProvider::get(), args...);
1831 detail::unrolled_format_impl<StrProvider>(out, std::make_index_sequence<num_pieces>{}, args...);
1836template <
typename OutputIt,
typename StrProvider,
typename... Args>
1838 const Args &...args)
noexcept {
1845template <
size_t N,
typename StrProvider,
typename... Args>
1847 const Args &...args)
noexcept {
1863template <
typename TargetContainer,
typename... Args>
1864[[nodiscard]]
inline TargetContainer
1867 TargetContainer result;
1876template <
typename TargetContainer,
typename StrProvider,
typename... Args>
1877[[nodiscard]]
inline TargetContainer
1880 TargetContainer result;
1903 const void *
const ptrs[
sizeof...(Args) == 0 ? 1 : sizeof...(Args)];
1908 : ptrs{
static_cast<const void *
>(&args)...} {}
1911 if constexpr (
sizeof...(Args) == 0) {
1912 return {span<const void *const>{}, span<const format_fn_t>{}};
1914 return {span<const void *const>(ptrs,
sizeof...(Args)),
1921 RELOCO_CONSTEXPR20
operator format_args() const noexcept RELOCO_LIFETIMEBOUND {
return view(); }
1929 return {span<const void *const>{}, span<const format_fn_t>{}};
1933 RELOCO_CONSTEXPR20
operator format_args() const noexcept {
return view(); }
1943template <
typename... Args>
1944[[nodiscard]] RELOCO_CONSTEXPR20
auto make_format_args(
const Args &...args RELOCO_LIFETIMEBOUND)
noexcept {
1973template <
typename TargetContainer>
1974[[nodiscard]]
inline TargetContainer
1977 TargetContainer result;
1978 auto it = std::back_inserter(result);
Re-export of reloco's checked-precondition facility.
Output sink that stores formatted characters in an internal fixed-size stack/inline buffer.
Definition microfmt.hpp:185
constexpr std::size_t available() const noexcept
Returns the remaining unwritten capacity in the buffer.
Definition microfmt.hpp:270
constexpr buffer_sink & operator=(buffer_sink &&other) noexcept
Definition microfmt.hpp:218
constexpr std::size_t capacity() const noexcept
Returns the maximum capacity of the buffer.
Definition microfmt.hpp:263
constexpr buffer_sink & operator=(const buffer_sink &other) noexcept
Definition microfmt.hpp:208
constexpr span< const char > as_span() const &&=delete
constexpr buffer_sink(buffer_sink &&other) noexcept
Definition microfmt.hpp:201
constexpr span< const char > as_span() const &noexcept RELOCO_LIFETIMEBOUND
Returns a microfmt::span view over the written portion of the buffer.
Definition microfmt.hpp:278
RELOCO_REINITIALIZES constexpr void reset() noexcept
Resets the write offset back to the beginning of the buffer.
Definition microfmt.hpp:312
constexpr std::size_t size() const noexcept
Returns the total number of characters written so far.
Definition microfmt.hpp:256
constexpr microfmt::string_view view() const &noexcept RELOCO_LIFETIMEBOUND
Returns a string view over the characters written so far.
Definition microfmt.hpp:245
RELOCO_BEGIN_UNSAFE_BUFFER_USAGE constexpr buffer_sink(const buffer_sink &other) noexcept
Definition microfmt.hpp:194
constexpr buffer_sink() noexcept
Constructs an empty buffer sink.
Definition microfmt.hpp:190
RELOCO_END_UNSAFE_BUFFER_USAGE RELOCO_CONSTEXPR20 sink as_sink() &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to this instance.
Definition microfmt.hpp:236
constexpr microfmt::string_view view() const &&=delete
Output sink that guarantees a null-terminated string within a fixed-size buffer.
Definition microfmt.hpp:486
constexpr c_string_sink(c_string_sink &&other) noexcept
Definition microfmt.hpp:505
constexpr c_string_sink & operator=(const c_string_sink &other) noexcept
Definition microfmt.hpp:513
RELOCO_END_UNSAFE_BUFFER_USAGE sink as_sink() &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to this instance.
Definition microfmt.hpp:543
RELOCO_END_UNSAFE_CODE constexpr const char * c_str() const &&=delete
constexpr microfmt::string_view view() const &&=delete
RELOCO_REINITIALIZES constexpr void reset() noexcept
Resets the sink to an empty, null-terminated state.
Definition microfmt.hpp:591
constexpr std::size_t size() const noexcept
Returns the length of the string in bytes (excluding null terminator).
Definition microfmt.hpp:578
RELOCO_BEGIN_UNSAFE_BUFFER_USAGE constexpr c_string_sink(const c_string_sink &other) noexcept
Definition microfmt.hpp:497
constexpr c_string_sink() noexcept
Constructs an empty, null-terminated string sink.
Definition microfmt.hpp:493
RELOCO_BEGIN_UNSAFE_CODE constexpr const char * c_str() const &noexcept RELOCO_LIFETIMEBOUND
Returns a null-terminated C string pointer.
Definition microfmt.hpp:555
static constexpr std::size_t max_size() noexcept
Returns the maximum characters the sink can store (excluding null terminator).
Definition microfmt.hpp:586
constexpr c_string_sink & operator=(c_string_sink &&other) noexcept
Definition microfmt.hpp:524
constexpr microfmt::string_view view() const &noexcept RELOCO_LIFETIMEBOUND
Returns a string view over the characters written (excluding null terminator).
Definition microfmt.hpp:566
Output sink that adapts any callable object (lambda, functor) to a sink.
Definition microfmt.hpp:607
sink as_sink() noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to the wrapped callable.
Definition microfmt.hpp:623
constexpr callback_sink(Callable &fn RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a callback sink wrapping a reference to a callable.
Definition microfmt.hpp:614
Output sink that counts total formatted bytes without storing characters.
Definition microfmt.hpp:384
constexpr counting_sink() noexcept=default
Constructs a counting sink with a zero byte count.
RELOCO_REINITIALIZES constexpr void reset() noexcept
Resets the internal counter to zero.
Definition microfmt.hpp:418
RELOCO_END_UNSAFE_CODE constexpr std::size_t count() const noexcept
Returns the total number of bytes written to the sink.
Definition microfmt.hpp:413
Definition microfmt.hpp:881
constexpr bool consume_debug_flag() noexcept
Detects and consumes a leading ? – Rust's {:?} Debug flag, as opposed to {}'s Display – returning tru...
Definition microfmt.hpp:974
constexpr const_iterator end() const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:900
RELOCO_END_UNSAFE_CODE constexpr void remove_prefix(size_type n) noexcept
Definition microfmt.hpp:922
constexpr size_type find(char ch, size_type pos=0) const noexcept
Definition microfmt.hpp:942
constexpr microfmt::string_view spec() const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:893
constexpr size_type size() const noexcept
Definition microfmt.hpp:895
constexpr microfmt::string_view substr(size_type pos=0, size_type count=microfmt::string_view::npos) const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:945
constexpr const_iterator cend() const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:902
constexpr bool empty() const noexcept
Definition microfmt.hpp:894
std::size_t size_type
Definition microfmt.hpp:886
microfmt::string_view::const_iterator iterator
Definition microfmt.hpp:883
RELOCO_BEGIN_UNSAFE_CODE constexpr void advance_to(const_iterator it) noexcept
Definition microfmt.hpp:915
constexpr bool starts_with(char ch) const noexcept
Definition microfmt.hpp:932
constexpr char back() const noexcept
Definition microfmt.hpp:906
constexpr bool starts_with(microfmt::string_view prefix) const noexcept
Definition microfmt.hpp:934
constexpr char consume() noexcept
Definition microfmt.hpp:924
constexpr const_iterator cbegin() const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:901
microfmt::string_view::const_iterator const_iterator
Definition microfmt.hpp:884
constexpr char front() const noexcept
Definition microfmt.hpp:905
RELOCO_CHECKED constexpr char operator[](size_type idx) const noexcept
Definition microfmt.hpp:907
constexpr format_parse_context(microfmt::string_view spec RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition microfmt.hpp:888
char value_type
Definition microfmt.hpp:885
constexpr const_iterator begin() const noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:899
Output sink adapter for arbitrary output iterators or raw pointers.
Definition microfmt.hpp:327
constexpr iterator_sink(OutputIt it) noexcept
Constructs an iterator sink wrapping the target output iterator.
Definition microfmt.hpp:334
RELOCO_END_UNSAFE_CODE constexpr OutputIt current() const noexcept
Returns the current state of the output iterator.
Definition microfmt.hpp:371
RELOCO_BEGIN_UNSAFE_CODE sink as_sink() &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to this instance.
Definition microfmt.hpp:345
Null/Discard sink that discards all formatted output.
Definition microfmt.hpp:430
static constexpr sink as_sink() noexcept
Returns a shared, stateless type-erased sink instance that drops writes.
Definition microfmt.hpp:438
Output sink that writes formatted characters into a fixed-size contiguous memory buffer.
Definition microfmt.hpp:60
constexpr span_sink(span< char > buf RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a span sink over a microfmt::span of character storage.
Definition microfmt.hpp:68
RELOCO_REINITIALIZES constexpr void reset() noexcept
Resets the write offset back to the beginning of the buffer.
Definition microfmt.hpp:135
constexpr size_t available() const noexcept
Returns the remaining unwritten capacity in the buffer.
Definition microfmt.hpp:128
sink as_sink() noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to this instance.
Definition microfmt.hpp:93
constexpr size_t size() const noexcept
Returns the total number of characters written so far.
Definition microfmt.hpp:121
constexpr microfmt::string_view view() const noexcept RELOCO_LIFETIMEBOUND
Returns a string view over the characters written so far.
Definition microfmt.hpp:112
Compiler, language-version, and optional-library feature detection.
#define MICROFMT_API_CLASS
Definition compat.hpp:181
#define MICROFMT_API
Definition compat.hpp:135
#define MICROFMT_API_CONSTEXPR
Definition compat.hpp:163
constexpr style reset
Definition ansi.hpp:79
std::remove_cv_t< std::remove_reference_t< T > > microfmt_remove_cvref_t
Definition microfmt.hpp:1467
constexpr bool starts_with(microfmt::string_view sv, microfmt::string_view prefix) noexcept
Definition microfmt.hpp:1498
RELOCO_ALWAYS_INLINE void unrolled_format_impl(const sink &out, std::index_sequence< Is... >, const Args &...args) noexcept
Definition microfmt.hpp:1660
constexpr bool has_formatter_v
Definition microfmt.hpp:1364
RELOCO_BEGIN_UNSAFE_CODE RELOCO_ALWAYS_INLINE void emit_formatted_int(const sink &out, const char *digits, size_t digits_len, bool is_negative, microfmt::string_view prefix, uint8_t width, bool zero_pad) noexcept
Definition microfmt.hpp:1124
RELOCO_ALWAYS_INLINE void emit_piece_by_index(const sink &out, const std::tuple< const Args &... > &arg_tuple) noexcept
Definition microfmt.hpp:1641
RELOCO_BEGIN_UNSAFE_CODE constexpr bool parse_positional_index(microfmt::string_view text, size_t &index) noexcept
Definition microfmt.hpp:1519
constexpr compiled_format< MaxPieces > compile_format_string(microfmt::string_view str) noexcept
Definition microfmt.hpp:1558
RELOCO_ALWAYS_INLINE RELOCO_UNSAFE_BUFFER_USAGE RELOCO_CONSTEXPR20 char * format_dec_backward(char *ptr, UInt value) noexcept
Definition microfmt.hpp:1080
RELOCO_UNSAFE_BUFFER_USAGE RELOCO_ALWAYS_INLINE RELOCO_CONSTEXPR20 char * format_hex_backward(char *ptr, UInt value, bool uppercase) noexcept
Definition microfmt.hpp:1107
RELOCO_BEGIN_UNSAFE_CODE void format_type_thunk(const void *val_ptr, microfmt::string_view spec, const sink &out) noexcept
Definition microfmt.hpp:1369
RELOCO_BEGIN_UNSAFE_CODE RELOCO_ALWAYS_INLINE const void * get_arg_by_index(const T &first, const Rest &...rest) noexcept
Definition microfmt.hpp:1625
typename fix_type< T >::type fix_type_t
Definition microfmt.hpp:1495
TargetContainer vformat_args_as(microfmt::string_view fmt, format_args args)
Formats type-erased arguments into a newly constructed container (e.g., std::string).
Definition microfmt.hpp:1976
constexpr hex_view< T > hex(T val, int width=0, bool prefix=false, bool uppercase=false) noexcept
Definition format_helpers.hpp:29
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
RELOCO_CONSTEXPR20 auto make_format_args(const Args &...args RELOCO_LIFETIMEBOUND) noexcept
Captures arguments into a type-erased storage object.
Definition microfmt.hpp:1944
void(*)(const void *val_ptr, microfmt::string_view spec, const sink &out) noexcept format_fn_t
Definition microfmt.hpp:1348
constexpr bitfield_view bits(uint32_t raw_val, span< const bit_field > fields, bool show_raw_hex=true, microfmt::string_view sep=" | ") noexcept
Definition bitfield.hpp:36
constexpr callback_sink< Callable > make_callback_sink(Callable &fn RELOCO_LIFETIMEBOUND) noexcept
Helper factory function to construct a callback_sink with template deduction.
Definition microfmt.hpp:643
MICROFMT_API void vformat_to(const sink &out, const microfmt::string_view fmt, const span< const void *const > arg_ptrs, const span< const format_fn_t > arg_fns) noexcept
void vformat_args_to(const sink &out, microfmt::string_view fmt, format_args args) noexcept
Type-erased formatting engine entry point.
Definition microfmt.hpp:1958
RELOCO_CONSTEXPR20 buffer_sink< N > format(microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1786
constexpr auto as_debug(const T &value RELOCO_LIFETIMEBOUND) noexcept
Wraps value so it always renders through reloco's Debug<T> customization point when one exists,...
Definition microfmt.hpp:1702
TargetContainer format_as(string_view fmt, const Args &...args)
Formats arguments into a newly constructed container (e.g., std::string) using a runtime format strin...
Definition microfmt.hpp:1866
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
Single re-export of every `reloco` type and annotation microfmt builds on.
Definition microfmt.hpp:1792
Provider provider_type
Definition microfmt.hpp:1793
constexpr microfmt::string_view get() const &noexcept RELOCO_LIFETIMEBOUND
Definition microfmt.hpp:1795
constexpr microfmt::string_view get() const &&=delete
Definition microfmt.hpp:445
static RELOCO_CONSTEXPR20 void write_thunk(void *ctx, microfmt::string_view sv) noexcept
Definition microfmt.hpp:460
c_string_sink_base & operator=(c_string_sink_base &&)=delete
std::size_t m_pos
Definition microfmt.hpp:448
constexpr c_string_sink_base(char *ptr RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS, size_t max, size_t pos) noexcept
Definition microfmt.hpp:450
c_string_sink_base(c_string_sink_base &&)=delete
char * m_data
Definition microfmt.hpp:446
c_string_sink_base & operator=(const c_string_sink_base &)=delete
c_string_sink_base(const c_string_sink_base &)=delete
Definition microfmt.hpp:1545
Definition microfmt.hpp:1634
static constexpr auto source
Definition microfmt.hpp:1635
static constexpr auto compiled
Definition microfmt.hpp:1636
Definition microfmt.hpp:1036
RELOCO_BEGIN_UNSAFE_CODE void format(const char *val, const sink &out) const noexcept
Definition microfmt.hpp:1048
constexpr void parse(format_parse_context &ctx) noexcept
Definition microfmt.hpp:1040
Non-owning wrapper produced by as_debug, carrying no state of its own beyond a reference to the wrapp...
Definition microfmt.hpp:1680
const T & value
Definition microfmt.hpp:1681
char[1] type
Definition microfmt.hpp:1490
const char[1] type
Definition microfmt.hpp:1493
Collapses argument types to a canonical representative before they reach format_type_table,...
Definition microfmt.hpp:1486
T type
Definition microfmt.hpp:1487