spdlog
Loading...
Searching...
No Matches
bin_to_hex.h
Go to the documentation of this file.
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#include <cctype>
9#include <spdlog/common.h>
10
11//
12// Support for logging binary data as hex
13// format flags, any combination of the followng:
14// {:X} - print in uppercase.
15// {:s} - don't separate each byte with space.
16// {:p} - don't print the position on each line start.
17// {:n} - don't split the output to lines.
18// {:a} - show ASCII if :n is not set
19
20//
21// Examples:
22//
23// std::vector<char> v(200, 0x0b);
24// logger->info("Some buffer {}", spdlog::to_hex(v));
25// char buf[128];
26// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
27// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
28
29namespace spdlog {
30namespace details {
31
32template<typename It>
34{
35public:
36 dump_info(It range_begin, It range_end, size_t size_per_line)
37 : begin_(range_begin)
38 , end_(range_end)
40 {}
41
42 It begin() const
43 {
44 return begin_;
45 }
46 It end() const
47 {
48 return end_;
49 }
50 size_t size_per_line() const
51 {
52 return size_per_line_;
53 }
54
55private:
58};
59} // namespace details
60
61// create a dump_info that wraps the given container
62template<typename Container>
63inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
64{
65 static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
66 using Iter = typename Container::const_iterator;
67 return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
68}
69
70// create dump_info from ranges
71template<typename It>
72inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
73{
74 return details::dump_info<It>(range_begin, range_end, size_per_line);
75}
76
77} // namespace spdlog
78
79namespace fmt {
80
81template<typename T>
82struct formatter<spdlog::details::dump_info<T>>
83{
84 const char delimiter = ' ';
85 bool put_newlines = true;
86 bool put_delimiters = true;
87 bool use_uppercase = false;
88 bool put_positions = true; // position on start of each line
89 bool show_ascii = false;
90
91 // parse the format string flags
92 template<typename ParseContext>
93 FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
94 {
95 auto it = ctx.begin();
96 while (it != ctx.end() && *it != '}')
97 {
98 switch (*it)
99 {
100 case 'X':
101 use_uppercase = true;
102 break;
103 case 's':
104 put_delimiters = false;
105 break;
106 case 'p':
107 put_positions = false;
108 break;
109 case 'n':
110 put_newlines = false;
111 show_ascii = false;
112 break;
113 case 'a':
114 if (put_newlines)
115 {
116 show_ascii = true;
117 }
118 break;
119 }
120
121 ++it;
122 }
123 return it;
124 }
125
126 // format the given bytes range as hex
127 template<typename FormatContext, typename Container>
128 auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
129 {
130 SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
131 SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
132 const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
133
134#if FMT_VERSION < 60000
135 auto inserter = ctx.begin();
136#else
137 auto inserter = ctx.out();
138#endif
139
140 int size_per_line = static_cast<int>(the_range.size_per_line());
141 auto start_of_line = the_range.begin();
142 for (auto i = the_range.begin(); i != the_range.end(); i++)
143 {
144 auto ch = static_cast<unsigned char>(*i);
145
146 if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
147 {
148 if (show_ascii && i != the_range.begin())
149 {
150 *inserter++ = delimiter;
151 *inserter++ = delimiter;
152 for (auto j = start_of_line; j < i; j++)
153 {
154 auto pc = static_cast<unsigned char>(*j);
155 *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
156 }
157 }
158
159 put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
160
161 // put first byte without delimiter in front of it
162 *inserter++ = hex_chars[(ch >> 4) & 0x0f];
163 *inserter++ = hex_chars[ch & 0x0f];
164 start_of_line = i;
165 continue;
166 }
167
168 if (put_delimiters)
169 {
170 *inserter++ = delimiter;
171 }
172
173 *inserter++ = hex_chars[(ch >> 4) & 0x0f];
174 *inserter++ = hex_chars[ch & 0x0f];
175 }
176 if (show_ascii) // add ascii to last line
177 {
178 if (the_range.end() - the_range.begin() > size_per_line)
179 {
180 auto blank_num = size_per_line - (the_range.end() - start_of_line);
181 while (blank_num-- > 0)
182 {
183 *inserter++ = delimiter;
184 *inserter++ = delimiter;
185 if (put_delimiters)
186 {
187 *inserter++ = delimiter;
188 }
189 }
190 }
191 *inserter++ = delimiter;
192 *inserter++ = delimiter;
193 for (auto j = start_of_line; j != the_range.end(); j++)
194 {
195 auto pc = static_cast<unsigned char>(*j);
196 *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
197 }
198 }
199 return inserter;
200 }
201
202 // put newline(and position header)
203 template<typename It>
204 void put_newline(It inserter, std::size_t pos)
205 {
206#ifdef _WIN32
207 *inserter++ = '\r';
208#endif
209 *inserter++ = '\n';
210
211 if (put_positions)
212 {
213 fmt::format_to(inserter, "{:04X}: ", pos);
214 }
215 }
216};
217} // namespace fmt
T begin(T... args)
dump_info(It range_begin, It range_end, size_t size_per_line)
Definition bin_to_hex.h:36
size_t size_per_line() const
Definition bin_to_hex.h:50
#define SPDLOG_CONSTEXPR
Definition common.h:54
#define FMT_CONSTEXPR
Definition core.h:99
T end(T... args)
T make_shared(T... args)
Definition async.h:25
details::dump_info< typename Container::const_iterator > to_hex(const Container &container, size_t size_per_line=32)
Definition bin_to_hex.h:63
auto format(const spdlog::details::dump_info< Container > &the_range, FormatContext &ctx) -> decltype(ctx.out())
Definition bin_to_hex.h:128
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
Definition bin_to_hex.h:93
void put_newline(It inserter, std::size_t pos)
Definition bin_to_hex.h:204