General Utility Library for C++14  2.8
hexdump.h
Go to the documentation of this file.
1 
23 #ifndef GUL14_HEXDUMP_H_
24 #define GUL14_HEXDUMP_H_
25 
26 #include <iomanip>
27 #include <sstream>
28 #include <type_traits>
29 #include <utility>
30 #include "gul14/internal.h"
31 #include "gul14/string_view.h"
32 
34 //
35 // std::string hexdump(IteratorT begin, IteratorT end, string_view prompt = "")
36 // std::string hexdump(const ContainerT& cont, string_view prompt = "")
37 //
38 // struct HexdumpParameterForward
39 //
40 // HexdumpParameterForward<...> hexdump_stream(IteratorT begin, IteratorT end, std::string prompt = "")
41 // HexdumpParameterForward<...> hexdump_stream(const ContainerT& cont, std::string prompt = "")
42 // HexdumpParameterForward<...> hexdump_stream(ContainerT&& cont, std::string prompt = "")
43 //
44 // std::ostream& operator<< (std::ostream& os, const HexdumpParameterForward<...>& hdp)
45 //
47 
48 namespace gul14 {
49 
50 namespace detail {
51 
53 //
54 // Until we have concepts ;-)
55 //
56 
57 // Helper to identify types that have cbegin() and cend() and have integral data
58 template <typename T, typename = int>
59 struct IsHexDumpContainer : std::false_type { };
60 
61 template <typename T>
62 struct IsHexDumpContainer <T,
63  typename std::enable_if_t<
64  std::is_integral<typename std::iterator_traits<decltype(
65  std::declval<T>().cbegin())>::value_type>::value,
66  decltype(std::declval<T>().cbegin(),
67  std::declval<T>().cend(),
68  0)
69  >>
70  : std::true_type { };
71 
72 // Helper to identify types that are ForwardIterators or Pointers
73 //
74 // We enforce that the data pointed to is integer.
75 // We assume that every iterator that is not an input_iterator is at least
76 // a forward_iterator.
77 // We also check the existence of dereference and increment operators (this might be superfluous).
78 // (Type int is used as a dummy.)
79 template <typename T, typename = int>
80 struct IsHexDumpIterator : std::false_type { };
81 
82 template <typename T>
83 struct IsHexDumpIterator <T,
84  typename std::enable_if_t<
85  std::is_integral<typename std::iterator_traits<T>::value_type>::value
86  and not std::is_same<typename std::iterator_traits<T>::iterator_category,
87  std::input_iterator_tag>::value,
88  decltype(std::declval<T>().operator*(),
89  std::declval<T>().operator++(),
90  0)
91  >>
92  : std::true_type { };
93 
94 template <typename T>
95 struct IsHexDumpIterator <T,
96  typename std::enable_if_t<
97  std::is_pointer<T>::value
98  and std::is_integral<typename std::remove_pointer<T>::type>::value,
99  int
100  >>
101  : std::true_type { };
102 
103 // Helper to identify stream types that we can use for output
104 template <typename StreamT,
105  typename = std::enable_if_t<std::is_convertible<
106  StreamT*,
107  std::basic_ostream<typename StreamT::char_type,
108  typename StreamT::traits_type>*>::value>>
109 struct IsHexDumpStream : std::true_type { };
110 
112 
113 // Here is the template actually doing the hexdump
114 // It is called by the different hexdump*() versions
115 template<typename StreamT, typename IteratorT,
116  typename = std::enable_if_t<detail::IsHexDumpStream<StreamT>::value>,
117  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
118 StreamT& hexdump_stream(StreamT& dest, const IteratorT& begin, const IteratorT& end,
119  string_view prompt = "")
120 {
121  constexpr auto maxelem = 1000ul * 16; // 1000 lines with 16 elements each
122 
123  // Get the number of hex digits to represent any value of the given integral type
124  constexpr auto nod = sizeof(*begin) * 2;
125  constexpr bool is_char = (nod == 2);
126 
127  const std::string indent(prompt.length(), ' ');
128  const std::string empty(nod + 1, ' ');
129 
130  dest << std::hex << std::setfill('0');
131 
132  auto it = IteratorT{ begin };
133 
134  // Inspired by epatel @ stack overflow, https://stackoverflow.com/a/29865
135  for (size_t i = 0; (it != end and i < maxelem) or (i == 0); i += 16) {
136  dest << (i ? indent : prompt) << std::setw(6) << i << ": ";
137  auto line = it;
138  for (size_t j = 0; j < 16; ++j) {
139  if (it != end) {
140  const unsigned long long ch = static_cast<
141  typename std::make_unsigned<
142  typename std::decay<decltype(*it)>::type
143  >::type
144  >(*it++);
145  dest << std::setw(nod) << ch << ' ';
146  } else {
147  if (!is_char) {
148  break;
149  }
150  dest << empty;
151  }
152  }
153  if (is_char) {
154  // Here we re-visit the iterator from the beginning of the line, thus
155  // requiring ForwardIterators over InputOperators
156  dest << ' ';
157  for (size_t j = 0; j < 16 and line != end; ++j, ++line) {
158  const auto c = static_cast<unsigned char>(*line);
159  dest << static_cast<char>(isprint(c) ? c : '.'); // isprint() is only defined for unsigned char, but only char creates character output
160  }
161  }
162  dest << "\n";
163  }
164  if (it != end) {
165  dest << indent << "[output truncated...]\n";
166  }
167  return dest;
168 }
170 
171 } // namespace detail
172 
174 // Functions returning a string
175 //
176 
211 template<typename IteratorT,
212  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
213 std::string hexdump(IteratorT begin, IteratorT end, string_view prompt = "")
214 {
215  std::stringstream o{ };
216  return detail::hexdump_stream(o, begin, end, prompt).str();
217 }
218 
228 template<typename ContainerT,
229  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
230 std::string hexdump(const ContainerT& cont, string_view prompt = "")
231 {
232  std::stringstream o{ };
233  return detail::hexdump_stream(o, cont.cbegin(), cont.cend(), prompt).str();
234 }
235 
237 // Functions returning a forwarder object
238 // (Support for 'stream << hexdump' without intermediate data image)
239 //
240 
249 template<typename IteratorT, typename ContainerT = void*>
251 public:
253  IteratorT begin_;
255  IteratorT end_;
257  std::string prompt_;
259  ContainerT cont_;
260 
261  HexdumpParameterForward() = default;
262  ~HexdumpParameterForward() = default;
263 
273  IteratorT begin_it, IteratorT end_it, std::string prompt, ContainerT&& cont)
274  : begin_ { begin_it }
275  , end_ { end_it }
276  , prompt_ { std::move(prompt) }
277  , cont_ { std::forward<ContainerT>(cont) }
278  {
279  regenerate_iterators<ContainerT>();
280  }
281 
286  HexdumpParameterForward(const HexdumpParameterForward& other) { *this = other; }
287 
293  {
294  *this = std::move(other);
295  }
296 
302  {
303  if (this == &other)
304  return *this;
305 
306  begin_ = other.begin_;
307  end_ = other.end_;
308  prompt_ = other.prompt_;
309  cont_ = other.cont_;
310 
311  regenerate_iterators<ContainerT>();
312 
313  return *this;
314  }
315 
321  {
322  if (this == &other)
323  return *this;
324 
325  begin_ = std::move(other.begin_);
326  end_ = std::move(other.end_);
327  prompt_ = std::move(other.prompt_);
328  cont_ = std::move(other.cont_);
329 
330  regenerate_iterators<ContainerT>();
331 
332  return *this;
333  }
334 
344  friend std::ostream& operator<<(
345  std::ostream& os, const HexdumpParameterForward<IteratorT, ContainerT>& hdp)
346  {
347  return detail::hexdump_stream(os, hdp.begin_, hdp.end_, hdp.prompt_);
348  }
349 
350 private:
351  template <typename ContType,
352  std::enable_if_t<!detail::IsHexDumpContainer<ContType>::value, int> = 0
353  >
354  void regenerate_iterators() noexcept
355  {
356  // This is empty, but we need this member function template so that we can
357  // regenerate the iterators if need be for the other enable_if case, see
358  // below...
359  }
360 
361  template <typename ContType,
362  std::enable_if_t<detail::IsHexDumpContainer<ContType>::value, int> = 0
363  >
364  void regenerate_iterators() noexcept
365  {
366  begin_ = cont_.begin();
367  end_ = cont_.end();
368  }
369 };
370 
413 template<typename IteratorT,
414  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
415 HexdumpParameterForward<const IteratorT>
416 hexdump_stream(const IteratorT& begin, const IteratorT& end, std::string prompt = "")
417 {
418  return { begin, end, std::move(prompt), nullptr };
419 }
420 
429 template<typename ContainerT,
430  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
431 HexdumpParameterForward<const decltype(std::declval<ContainerT>().cbegin())>
432 hexdump_stream(const ContainerT& cont, std::string prompt = "")
433 {
434  return { cont.cbegin(), cont.cend(), std::move(prompt), nullptr };
435 }
436 
445 template<typename ContainerT,
446  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value,
447  decltype(HexdumpParameterForward<decltype(std::declval<ContainerT>().cbegin()),
448  ContainerT> {}, 0)>>
449 HexdumpParameterForward<decltype(std::declval<ContainerT>().cbegin()), ContainerT>
450 hexdump_stream(ContainerT&& cont, std::string prompt = "")
451 {
452  // The temporary container must be moved to retain the values until we need them
453  // after operator<<.
454  return { cont.cbegin(), cont.cbegin(), std::move(prompt),
455  std::forward<ContainerT>(cont) };
456 }
457 
458 } // namespace gul14
459 
460 #endif
461 
462 // vi:ts=4:sw=4:et
Helper object used to enable a convenient syntax to dump things to a stream.
Definition: hexdump.h:250
HexdumpParameterForward & operator=(HexdumpParameterForward &&other) noexcept
Move assignment (automatically updates the begin_ and end_ interator members if the moved-from object...
Definition: hexdump.h:320
HexdumpParameterForward(const HexdumpParameterForward &other)
Copy constructor (automatically updates the begin_ and end_ interator members if the copied object ho...
Definition: hexdump.h:286
IteratorT begin_
Iterator to begin of elements to be dumped (in iterator mode)
Definition: hexdump.h:253
std::string prompt_
Possible prompt to prepend to the dump.
Definition: hexdump.h:257
HexdumpParameterForward(HexdumpParameterForward &&other) noexcept
Move constructor (automatically updates the begin_ and end_ interator members if the moved-from objec...
Definition: hexdump.h:292
IteratorT end_
Iterator past end of elements to be dumped (in iterator mode)
Definition: hexdump.h:255
HexdumpParameterForward(IteratorT begin_it, IteratorT end_it, std::string prompt, ContainerT &&cont)
Construct a hexdump parameter forwarder object.
Definition: hexdump.h:272
HexdumpParameterForward & operator=(const HexdumpParameterForward &other)
Copy assignment (automatically updates the begin_ and end_ interator members if the copied object hol...
Definition: hexdump.h:301
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:344
ContainerT cont_
A container with the elements to be dumped (in container/temporary mode)
Definition: hexdump.h:259
Definition of macros used internally by GUL.
Namespace gul14 contains all functions and classes of the General Utility Library.
Definition: doxygen.h:26
std::string hexdump(IteratorT begin, IteratorT end, string_view prompt="")
Generate a hexdump of a data range and return it as a string.
Definition: hexdump.h:213
basic_string_view< char > string_view
A view to a contiguous sequence of chars.
Definition: string_view.h:623
HexdumpParameterForward< decltype(std::declval< ContainerT >).cbegin()), ContainerT > hexdump_stream(ContainerT &&cont, std::string prompt="")
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: hexdump.h:450
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:416
Provides a gul14::string_view that is fully compatible with C++17's std::string_view.