fmt_helper.h 4.3 KB

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