| // |
| // 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<char> 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<typename It> |
| 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<typename Container> |
| inline details::bytes_range<typename Container::const_iterator> 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<Iter>(std::begin(container), std::end(container)); |
| } |
| |
| // create bytes_range from ranges |
| template<typename It> |
| inline details::bytes_range<It> to_hex(const It range_begin, const It range_end) |
| { |
| return details::bytes_range<It>(range_begin, range_end); |
| } |
| |
| } // namespace spdlog |
| |
| namespace fmt { |
| |
| template<typename T> |
| struct formatter<spdlog::details::bytes_range<T>> |
| { |
| 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<typename ParseContext> |
| 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<typename FormatContext, typename Container> |
| auto format(const spdlog::details::bytes_range<Container> &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<unsigned char>(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<typename It> |
| 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 |