1 | // |
---|
2 | // Copyright(c) 2015 Gabi Melman. |
---|
3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
---|
4 | // |
---|
5 | |
---|
6 | #pragma once |
---|
7 | |
---|
8 | // |
---|
9 | // Support for logging binary data as hex |
---|
10 | // format flags: |
---|
11 | // {:X} - print in uppercase. |
---|
12 | // {:s} - don't separate each byte with space. |
---|
13 | // {:p} - don't print the position on each line start. |
---|
14 | // {:n} - don't split the output to lines. |
---|
15 | |
---|
16 | // |
---|
17 | // Examples: |
---|
18 | // |
---|
19 | // std::vector<char> v(200, 0x0b); |
---|
20 | // logger->info("Some buffer {}", spdlog::to_hex(v)); |
---|
21 | // char buf[128]; |
---|
22 | // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf))); |
---|
23 | |
---|
24 | namespace spdlog { |
---|
25 | namespace details { |
---|
26 | |
---|
27 | template<typename It> |
---|
28 | class bytes_range |
---|
29 | { |
---|
30 | public: |
---|
31 | bytes_range(It range_begin, It range_end) |
---|
32 | : begin_(range_begin) |
---|
33 | , end_(range_end) |
---|
34 | { |
---|
35 | } |
---|
36 | |
---|
37 | It begin() const |
---|
38 | { |
---|
39 | return begin_; |
---|
40 | } |
---|
41 | It end() const |
---|
42 | { |
---|
43 | return end_; |
---|
44 | } |
---|
45 | |
---|
46 | private: |
---|
47 | It begin_, end_; |
---|
48 | }; |
---|
49 | } // namespace details |
---|
50 | |
---|
51 | // create a bytes_range that wraps the given container |
---|
52 | template<typename Container> |
---|
53 | inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container) |
---|
54 | { |
---|
55 | static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); |
---|
56 | using Iter = typename Container::const_iterator; |
---|
57 | return details::bytes_range<Iter>(std::begin(container), std::end(container)); |
---|
58 | } |
---|
59 | |
---|
60 | // create bytes_range from ranges |
---|
61 | template<typename It> |
---|
62 | inline details::bytes_range<It> to_hex(const It range_begin, const It range_end) |
---|
63 | { |
---|
64 | return details::bytes_range<It>(range_begin, range_end); |
---|
65 | } |
---|
66 | |
---|
67 | } // namespace spdlog |
---|
68 | |
---|
69 | namespace fmt { |
---|
70 | |
---|
71 | template<typename T> |
---|
72 | struct formatter<spdlog::details::bytes_range<T>> |
---|
73 | { |
---|
74 | const std::size_t line_size = 100; |
---|
75 | const char delimiter = ' '; |
---|
76 | |
---|
77 | bool put_newlines = true; |
---|
78 | bool put_delimiters = true; |
---|
79 | bool use_uppercase = false; |
---|
80 | bool put_positions = true; // position on start of each line |
---|
81 | |
---|
82 | // parse the format string flags |
---|
83 | template<typename ParseContext> |
---|
84 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) |
---|
85 | { |
---|
86 | auto it = ctx.begin(); |
---|
87 | while (*it && *it != '}') |
---|
88 | { |
---|
89 | switch (*it) |
---|
90 | { |
---|
91 | case 'X': |
---|
92 | use_uppercase = true; |
---|
93 | break; |
---|
94 | case 's': |
---|
95 | put_delimiters = false; |
---|
96 | break; |
---|
97 | case 'p': |
---|
98 | put_positions = false; |
---|
99 | break; |
---|
100 | case 'n': |
---|
101 | put_newlines = false; |
---|
102 | break; |
---|
103 | } |
---|
104 | |
---|
105 | ++it; |
---|
106 | } |
---|
107 | return it; |
---|
108 | } |
---|
109 | |
---|
110 | // format the given bytes range as hex |
---|
111 | template<typename FormatContext, typename Container> |
---|
112 | auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out()) |
---|
113 | { |
---|
114 | SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF"; |
---|
115 | SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef"; |
---|
116 | const char *hex_chars = use_uppercase ? hex_upper : hex_lower; |
---|
117 | |
---|
118 | std::size_t pos = 0; |
---|
119 | std::size_t column = line_size; |
---|
120 | auto inserter = ctx.begin(); |
---|
121 | |
---|
122 | for (auto &item : the_range) |
---|
123 | { |
---|
124 | auto ch = static_cast<unsigned char>(item); |
---|
125 | pos++; |
---|
126 | |
---|
127 | if (put_newlines && column >= line_size) |
---|
128 | { |
---|
129 | column = put_newline(inserter, pos); |
---|
130 | |
---|
131 | // put first byte without delimiter in front of it |
---|
132 | *inserter++ = hex_chars[(ch >> 4) & 0x0f]; |
---|
133 | *inserter++ = hex_chars[ch & 0x0f]; |
---|
134 | column += 2; |
---|
135 | continue; |
---|
136 | } |
---|
137 | |
---|
138 | if (put_delimiters) |
---|
139 | { |
---|
140 | *inserter++ = delimiter; |
---|
141 | ++column; |
---|
142 | } |
---|
143 | |
---|
144 | *inserter++ = hex_chars[(ch >> 4) & 0x0f]; |
---|
145 | *inserter++ = hex_chars[ch & 0x0f]; |
---|
146 | column += 2; |
---|
147 | } |
---|
148 | return inserter; |
---|
149 | } |
---|
150 | |
---|
151 | // put newline(and position header) |
---|
152 | // return the next column |
---|
153 | template<typename It> |
---|
154 | std::size_t put_newline(It inserter, std::size_t pos) |
---|
155 | { |
---|
156 | #ifdef _WIN32 |
---|
157 | *inserter++ = '\r'; |
---|
158 | #endif |
---|
159 | *inserter++ = '\n'; |
---|
160 | |
---|
161 | if (put_positions) |
---|
162 | { |
---|
163 | fmt::format_to(inserter, "{:<04X}: ", pos - 1); |
---|
164 | return 7; |
---|
165 | } |
---|
166 | else |
---|
167 | { |
---|
168 | return 1; |
---|
169 | } |
---|
170 | } |
---|
171 | }; |
---|
172 | } // namespace fmt |
---|