fmt_helper.h 4.5 KB

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