// // Copyright(c) 2015 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // #pragma once // // Support for logging binary data as hex // format flags: // {:X} - print in uppercase. // {:s} - don't separate each byte with space. // {:p} - don't print the position on each line start. // {:n} - don't split the output to lines. // // Examples: // // std::vector v(200, 0x0b); // logger->info("Some buffer {}", spdlog::to_hex(v)); // char buf[128]; // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf))); namespace spdlog { namespace details { template class bytes_range { public: bytes_range(It range_begin, It range_end) : begin_(range_begin) , end_(range_end) { } It begin() const { return begin_; } It end() const { return end_; } private: It begin_, end_; }; } // namespace details // create a bytes_range that wraps the given container template inline details::bytes_range to_hex(const Container &container) { static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); using Iter = typename Container::const_iterator; return details::bytes_range(std::begin(container), std::end(container)); } // create bytes_range from ranges template inline details::bytes_range to_hex(const It range_begin, const It range_end) { return details::bytes_range(range_begin, range_end); } } // namespace spdlog namespace fmt { template struct formatter> { const std::size_t line_size = 100; const char delimiter = ' '; bool put_newlines = true; bool put_delimiters = true; bool use_uppercase = false; bool put_positions = true; // position on start of each line // parse the format string flags template auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); while (*it && *it != '}') { switch (*it) { case 'X': use_uppercase = true; break; case 's': put_delimiters = false; break; case 'p': put_positions = false; break; case 'n': put_newlines = false; break; } ++it; } return it; } // format the given bytes range as hex template auto format(const spdlog::details::bytes_range &the_range, FormatContext &ctx) -> decltype(ctx.out()) { SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF"; SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef"; const char *hex_chars = use_uppercase ? hex_upper : hex_lower; std::size_t pos = 0; std::size_t column = line_size; auto inserter = ctx.begin(); for (auto &item : the_range) { auto ch = static_cast(item); pos++; if (put_newlines && column >= line_size) { column = put_newline(inserter, pos); // put first byte without delimiter in front of it *inserter++ = hex_chars[(ch >> 4) & 0x0f]; *inserter++ = hex_chars[ch & 0x0f]; column += 2; continue; } if (put_delimiters) { *inserter++ = delimiter; ++column; } *inserter++ = hex_chars[(ch >> 4) & 0x0f]; *inserter++ = hex_chars[ch & 0x0f]; column += 2; } return inserter; } // put newline(and position header) // return the next column template std::size_t put_newline(It inserter, std::size_t pos) { #ifdef _WIN32 *inserter++ = '\r'; #endif *inserter++ = '\n'; if (put_positions) { fmt::format_to(inserter, "{:<04X}: ", pos - 1); return 7; } else { return 1; } } }; } // namespace fmt