1// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2// Distributed under the MIT License (http://opensource.org/licenses/MIT)
3#pragma once
4
5#include <chrono>
6#include <iterator>
7#include <spdlog/common.h>
8#include <spdlog/fmt/fmt.h>
9#include <type_traits>
10
11#ifdef SPDLOG_USE_STD_FORMAT
12 #include <charconv>
13 #include <limits>
14#endif
15
16// Some fmt helpers to efficiently format and pad ints and strings
17namespace spdlog {
18namespace details {
19namespace fmt_helper {
20
21inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
22 auto *buf_ptr = view.data();
23 dest.append(first: buf_ptr, last: buf_ptr + view.size());
24}
25
26#ifdef SPDLOG_USE_STD_FORMAT
27template <typename T>
28inline void append_int(T n, memory_buf_t &dest) {
29 // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
30 SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
31 char buf[BUF_SIZE];
32
33 auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
34 if (ec == std::errc()) {
35 dest.append(buf, ptr);
36 } else {
37 throw_spdlog_ex(msg: "Failed to format int", last_errno: static_cast<int>(ec));
38 }
39}
40#else
41template <typename T>
42inline void append_int(T n, memory_buf_t &dest) {
43 fmt::format_int i(n);
44 dest.append(i.data(), i.data() + i.size());
45}
46#endif
47
48template <typename T>
49SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) {
50 // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
51 unsigned int count = 1;
52 for (;;) {
53 // Integer division is slow so do it for a group of four digits instead
54 // of for every digit. The idea comes from the talk by Alexandrescu
55 // "Three Optimization Tips for C++". See speed-test for a comparison.
56 if (n < 10) return count;
57 if (n < 100) return count + 1;
58 if (n < 1000) return count + 2;
59 if (n < 10000) return count + 3;
60 n /= 10000u;
61 count += 4;
62 }
63}
64
65template <typename T>
66inline unsigned int count_digits(T n) {
67 using count_type =
68 typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
69#ifdef SPDLOG_USE_STD_FORMAT
70 return count_digits_fallback(static_cast<count_type>(n));
71#else
72 return static_cast<unsigned int>(fmt::
73 // fmt 7.0.0 renamed the internal namespace to detail.
74 // See: https://github.com/fmtlib/fmt/issues/1538
75 #if FMT_VERSION < 70000
76 internal
77 #else
78 detail
79 #endif
80 ::count_digits(static_cast<count_type>(n)));
81#endif
82}
83
84inline void pad2(int n, memory_buf_t &dest) {
85 if (n >= 0 && n < 100) // 0-99
86 {
87 dest.push_back(c: static_cast<char>('0' + n / 10));
88 dest.push_back(c: static_cast<char>('0' + n % 10));
89 } else // unlikely, but just in case, let fmt deal with it
90 {
91 fmt_lib::format_to(out: std::back_inserter(x&: dest), SPDLOG_FMT_STRING("{:02}"), args&: n);
92 }
93}
94
95template <typename T>
96inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
97 static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
98 for (auto digits = count_digits(n); digits < width; digits++) {
99 dest.push_back(c: '0');
100 }
101 append_int(n, dest);
102}
103
104template <typename T>
105inline void pad3(T n, memory_buf_t &dest) {
106 static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
107 if (n < 1000) {
108 dest.push_back(c: static_cast<char>(n / 100 + '0'));
109 n = n % 100;
110 dest.push_back(c: static_cast<char>((n / 10) + '0'));
111 dest.push_back(c: static_cast<char>((n % 10) + '0'));
112 } else {
113 append_int(n, dest);
114 }
115}
116
117template <typename T>
118inline void pad6(T n, memory_buf_t &dest) {
119 pad_uint(n, 6, dest);
120}
121
122template <typename T>
123inline void pad9(T n, memory_buf_t &dest) {
124 pad_uint(n, 9, dest);
125}
126
127// return fraction of a second of the given time_point.
128// e.g.
129// fraction<std::milliseconds>(tp) -> will return the millis part of the second
130template <typename ToDuration>
131inline ToDuration time_fraction(log_clock::time_point tp) {
132 using std::chrono::duration_cast;
133 using std::chrono::seconds;
134 auto duration = tp.time_since_epoch();
135 auto secs = duration_cast<seconds>(duration);
136 return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
137}
138
139} // namespace fmt_helper
140} // namespace details
141} // namespace spdlog
142