General Utility Library for C++17 25.4.1
hexdump.h
Go to the documentation of this file.
1
23#ifndef GUL17_HEXDUMP_H_
24#define GUL17_HEXDUMP_H_
25
26#include <iomanip>
27#include <sstream>
28#include <string_view>
29#include <type_traits>
30#include <utility>
31
32#include "gul17/internal.h"
33
35//
36// std::string hexdump(IteratorT begin, IteratorT end, std::string_view prompt = "")
37// std::string hexdump(const ContainerT& cont, std::string_view prompt = "")
38//
39// struct HexdumpParameterForward
40//
41// HexdumpParameterForward<...> hexdump_stream(IteratorT begin, IteratorT end, std::string prompt = "")
42// HexdumpParameterForward<...> hexdump_stream(const ContainerT& cont, std::string prompt = "")
43// HexdumpParameterForward<...> hexdump_stream(ContainerT&& cont, std::string prompt = "")
44//
45// std::ostream& operator<< (std::ostream& os, const HexdumpParameterForward<...>& hdp)
46//
48
49namespace gul17 {
50
57namespace detail {
58
60//
61// Until we have concepts ;-)
62//
63
64// Helper to identify types that have cbegin() and cend() and have integral data
65template <typename T, typename = int>
66struct IsHexDumpContainer : std::false_type { };
67
68template <typename T>
69struct IsHexDumpContainer <T,
71 std::is_integral<typename std::iterator_traits<decltype(
72 std::declval<T>().cbegin())>::value_type>::value,
73 decltype(std::declval<T>().cbegin(),
74 std::declval<T>().cend(),
75 0)
76 >>
77 : std::true_type { };
78
79// Helper to identify types that are ForwardIterators or Pointers
80//
81// We enforce that the data pointed to is integer.
82// We assume that every iterator that is not an input_iterator is at least
83// a forward_iterator.
84// We also check the existence of dereference and increment operators (this might be superfluous).
85// (Type int is used as a dummy.)
86template <typename T, typename = int>
87struct IsHexDumpIterator : std::false_type { };
88
89template <typename T>
90struct IsHexDumpIterator <T,
92 std::is_integral<typename std::iterator_traits<T>::value_type>::value
93 and not std::is_same<typename std::iterator_traits<T>::iterator_category,
94 std::input_iterator_tag>::value,
95 decltype(std::declval<T>().operator*(),
96 std::declval<T>().operator++(),
97 0)
98 >>
99 : std::true_type { };
100
101template <typename T>
102struct IsHexDumpIterator <T,
104 std::is_pointer<T>::value
105 and std::is_integral<typename std::remove_pointer<T>::type>::value,
106 int
107 >>
108 : std::true_type { };
109
110// Helper to identify stream types that we can use for output
111template <typename StreamT,
112 typename = std::enable_if_t<std::is_convertible<
113 StreamT*,
114 std::basic_ostream<typename StreamT::char_type,
115 typename StreamT::traits_type>*>::value>>
116struct IsHexDumpStream : std::true_type { };
117
119
120// Here is the template actually doing the hexdump
121// It is called by the different hexdump*() versions
122template<typename StreamT, typename IteratorT,
123 typename = std::enable_if_t<detail::IsHexDumpStream<StreamT>::value>,
124 typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
125StreamT& hexdump_stream(StreamT& dest, const IteratorT& begin, const IteratorT& end,
126 std::string_view prompt = "")
127{
128 constexpr auto maxelem = 1000ul * 16; // 1000 lines with 16 elements each
129
130 // Get the number of hex digits to represent any value of the given integral type
131 constexpr auto nod = sizeof(*begin) * 2;
132 constexpr bool is_char = (nod == 2);
133
134 const std::string indent(prompt.length(), ' ');
135 const std::string empty(nod + 1, ' ');
136
137 dest << std::hex << std::setfill('0');
138
139 auto it = IteratorT{ begin };
140
141 // Inspired by epatel @ stack overflow, https://stackoverflow.com/a/29865
142 for (size_t i = 0; (it != end and i < maxelem) or (i == 0); i += 16) {
143 dest << (i ? indent : prompt) << std::setw(6) << i << ": ";
144 auto line = it;
145 for (size_t j = 0; j < 16; ++j) {
146 if (it != end) {
147 const unsigned long long ch = static_cast<
148 typename std::make_unsigned<
149 typename std::decay<decltype(*it)>::type
150 >::type
151 >(*it++);
152 dest << std::setw(nod) << ch << ' ';
153 } else {
154 if (!is_char) {
155 break;
156 }
157 dest << empty;
158 }
159 }
160 if (is_char) {
161 // Here we re-visit the iterator from the beginning of the line, thus
162 // requiring ForwardIterators over InputOperators
163 dest << ' ';
164 for (size_t j = 0; j < 16 and line != end; ++j, ++line) {
165 const auto c = static_cast<unsigned char>(*line);
166 dest << static_cast<char>(isprint(c) ? c : '.'); // isprint() is only defined for unsigned char, but only char creates character output
167 }
168 }
169 dest << "\n";
170 }
171 if (it != end) {
172 dest << indent << "[output truncated...]\n";
173 }
174 return dest;
175}
177
178} // namespace detail
179
181// Functions returning a string
182//
183
218template<typename IteratorT,
219 typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
220std::string hexdump(IteratorT begin, IteratorT end, std::string_view prompt = "")
221{
222 std::stringstream o{ };
223 return detail::hexdump_stream(o, begin, end, prompt).str();
224}
225
235template<typename ContainerT,
236 typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
237std::string hexdump(const ContainerT& cont, std::string_view prompt = "")
238{
239 std::stringstream o{ };
240 return detail::hexdump_stream(o, cont.cbegin(), cont.cend(), prompt).str();
241}
242
244// Functions returning a forwarder object
245// (Support for 'stream << hexdump' without intermediate data image)
246//
247
256template<typename IteratorT, typename ContainerT = void*>
258public:
264 std::string prompt_;
267
268 HexdumpParameterForward() = default;
269 ~HexdumpParameterForward() = default;
270
288
294
300 {
301 *this = std::move(other);
302 }
303
309 {
310 if (this == &other)
311 return *this;
312
314 end_ = other.end_;
315 prompt_ = other.prompt_;
316 cont_ = other.cont_;
317
319
320 return *this;
321 }
322
328 {
329 if (this == &other)
330 return *this;
331
332 begin_ = std::move(other.begin_);
333 end_ = std::move(other.end_);
334 prompt_ = std::move(other.prompt_);
335 cont_ = std::move(other.cont_);
336
338
339 return *this;
340 }
341
351 friend std::ostream& operator<<(
353 {
354 return detail::hexdump_stream(os, hdp.begin_, hdp.end_, hdp.prompt_);
355 }
356
357private:
358 template <typename ContType,
359 std::enable_if_t<!detail::IsHexDumpContainer<ContType>::value, int> = 0
360 >
361 void regenerate_iterators() noexcept
362 {
363 // This is empty, but we need this member function template so that we can
364 // regenerate the iterators if need be for the other enable_if case, see
365 // below...
366 }
367
368 template <typename ContType,
369 std::enable_if_t<detail::IsHexDumpContainer<ContType>::value, int> = 0
370 >
371 void regenerate_iterators() noexcept
372 {
373 begin_ = cont_.begin();
374 end_ = cont_.end();
375 }
376};
377
420template<typename IteratorT,
421 typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
423hexdump_stream(const IteratorT& begin, const IteratorT& end, std::string prompt = "")
424{
425 return { begin, end, std::move(prompt), nullptr };
426}
427
436template<typename ContainerT,
437 typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
439hexdump_stream(const ContainerT& cont, std::string prompt = "")
440{
441 return { cont.cbegin(), cont.cend(), std::move(prompt), nullptr };
442}
443
452template<typename ContainerT,
453 typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value,
455 ContainerT> {}, 0)>>
456HexdumpParameterForward<decltype(std::declval<ContainerT>().cbegin()), ContainerT>
458{
459 // The temporary container must be moved to retain the values until we need them
460 // after operator<<.
461 return { cont.cbegin(), cont.cbegin(), std::move(prompt),
462 std::forward<ContainerT>(cont) };
463}
464
466
467} // namespace gul17
468
469#endif
470
471// vi:ts=4:sw=4:et
Helper object used to enable a convenient syntax to dump things to a stream.
Definition hexdump.h:257
IteratorT begin_
Iterator to begin of elements to be dumped (in iterator mode)
Definition hexdump.h:260
HexdumpParameterForward & operator=(const HexdumpParameterForward &other)
Copy assignment (automatically updates the begin_ and end_ interator members if the copied object hol...
Definition hexdump.h:308
HexdumpParameterForward(IteratorT begin_it, IteratorT end_it, std::string prompt, ContainerT &&cont)
Construct a hexdump parameter forwarder object.
Definition hexdump.h:279
IteratorT end_
Iterator past end of elements to be dumped (in iterator mode)
Definition hexdump.h:262
std::string prompt_
Possible prompt to prepend to the dump.
Definition hexdump.h:264
HexdumpParameterForward & operator=(HexdumpParameterForward &&other) noexcept
Move assignment (automatically updates the begin_ and end_ interator members if the moved-from object...
Definition hexdump.h:327
ContainerT cont_
A container with the elements to be dumped (in container/temporary mode)
Definition hexdump.h:266
HexdumpParameterForward(HexdumpParameterForward &&other) noexcept
Move constructor (automatically updates the begin_ and end_ interator members if the moved-from objec...
Definition hexdump.h:299
HexdumpParameterForward(const HexdumpParameterForward &other)
Copy constructor (automatically updates the begin_ and end_ interator members if the copied object ho...
Definition hexdump.h:293
friend std::ostream & operator<<(std::ostream &os, const HexdumpParameterForward< IteratorT, ContainerT > &hdp)
Overload of std::ostream's operator<< to enable a convenient syntax to dump things to a stream.
Definition hexdump.h:351
auto constexpr bit_set(unsigned bit) noexcept -> ReturnT
Set a bit in an integral type.
Definition bit_manip.h:121
HexdumpParameterForward< const IteratorT > hexdump_stream(const IteratorT &begin, const IteratorT &end, std::string prompt="")
Generate a hexdump of a data range that can be efficiently written to a stream using operator<<.
Definition hexdump.h:423
std::string hexdump(IteratorT begin, IteratorT end, std::string_view prompt="")
Generate a hexdump of a data range and return it as a string.
Definition hexdump.h:220
Definition of macros used internally by GUL.
Namespace gul17 contains all functions and classes of the General Utility Library.
Definition doxygen.h:26