General Utility Library for C++14  2.12
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 
31 #include "gul14/internal.h"
32 #include "gul14/string_view.h"
33 
35 //
36 // std::string hexdump(IteratorT begin, IteratorT end, string_view prompt = "")
37 // std::string hexdump(const ContainerT& cont, 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 
49 namespace gul14 {
50 
57 namespace detail {
58 
60 //
61 // Until we have concepts ;-)
62 //
63 
64 // Helper to identify types that have cbegin() and cend() and have integral data
65 template <typename T, typename = int>
66 struct IsHexDumpContainer : std::false_type { };
67 
68 template <typename T>
69 struct IsHexDumpContainer <T,
70  typename std::enable_if_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.)
86 template <typename T, typename = int>
87 struct IsHexDumpIterator : std::false_type { };
88 
89 template <typename T>
90 struct IsHexDumpIterator <T,
91  typename std::enable_if_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 
101 template <typename T>
102 struct IsHexDumpIterator <T,
103  typename std::enable_if_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
111 template <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>>
116 struct IsHexDumpStream : std::true_type { };
117 
119 
120 // Here is the template actually doing the hexdump
121 // It is called by the different hexdump*() versions
122 template<typename StreamT, typename IteratorT,
123  typename = std::enable_if_t<detail::IsHexDumpStream<StreamT>::value>,
124  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
125 StreamT& hexdump_stream(StreamT& dest, const IteratorT& begin, const IteratorT& end,
126  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 
218 template<typename IteratorT,
219  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
220 std::string hexdump(IteratorT begin, IteratorT end, string_view prompt = "")
221 {
222  std::stringstream o{ };
223  return detail::hexdump_stream(o, begin, end, prompt).str();
224 }
225 
235 template<typename ContainerT,
236  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
237 std::string hexdump(const ContainerT& cont, 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 
256 template<typename IteratorT, typename ContainerT = void*>
258 public:
260  IteratorT begin_;
262  IteratorT end_;
264  std::string prompt_;
266  ContainerT cont_;
267 
268  HexdumpParameterForward() = default;
269  ~HexdumpParameterForward() = default;
270 
280  IteratorT begin_it, IteratorT end_it, std::string prompt, ContainerT&& cont)
281  : begin_ { begin_it }
282  , end_ { end_it }
283  , prompt_ { std::move(prompt) }
284  , cont_ { std::forward<ContainerT>(cont) }
285  {
286  regenerate_iterators<ContainerT>();
287  }
288 
293  HexdumpParameterForward(const HexdumpParameterForward& other) { *this = other; }
294 
300  {
301  *this = std::move(other);
302  }
303 
309  {
310  if (this == &other)
311  return *this;
312 
313  begin_ = other.begin_;
314  end_ = other.end_;
315  prompt_ = other.prompt_;
316  cont_ = other.cont_;
317 
318  regenerate_iterators<ContainerT>();
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 
337  regenerate_iterators<ContainerT>();
338 
339  return *this;
340  }
341 
351  friend std::ostream& operator<<(
352  std::ostream& os, const HexdumpParameterForward<IteratorT, ContainerT>& hdp)
353  {
354  return detail::hexdump_stream(os, hdp.begin_, hdp.end_, hdp.prompt_);
355  }
356 
357 private:
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 
420 template<typename IteratorT,
421  typename = std::enable_if_t<detail::IsHexDumpIterator<IteratorT>::value>>
422 HexdumpParameterForward<const IteratorT>
423 hexdump_stream(const IteratorT& begin, const IteratorT& end, std::string prompt = "")
424 {
425  return { begin, end, std::move(prompt), nullptr };
426 }
427 
436 template<typename ContainerT,
437  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value>>
438 HexdumpParameterForward<const decltype(std::declval<ContainerT>().cbegin())>
439 hexdump_stream(const ContainerT& cont, std::string prompt = "")
440 {
441  return { cont.cbegin(), cont.cend(), std::move(prompt), nullptr };
442 }
443 
452 template<typename ContainerT,
453  typename = std::enable_if_t<detail::IsHexDumpContainer<ContainerT>::value,
454  decltype(HexdumpParameterForward<decltype(std::declval<ContainerT>().cbegin()),
455  ContainerT> {}, 0)>>
456 HexdumpParameterForward<decltype(std::declval<ContainerT>().cbegin()), ContainerT>
457 hexdump_stream(ContainerT&& cont, std::string prompt = "")
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 gul14
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
HexdumpParameterForward & operator=(HexdumpParameterForward &&other) noexcept
Move assignment (automatically updates the begin_ and end_ interator members if the moved-from object...
Definition: hexdump.h:327
HexdumpParameterForward(const HexdumpParameterForward &other)
Copy constructor (automatically updates the begin_ and end_ interator members if the copied object ho...
Definition: hexdump.h:293
IteratorT begin_
Iterator to begin of elements to be dumped (in iterator mode)
Definition: hexdump.h:260
std::string prompt_
Possible prompt to prepend to the dump.
Definition: hexdump.h:264
HexdumpParameterForward(HexdumpParameterForward &&other) noexcept
Move constructor (automatically updates the begin_ and end_ interator members if the moved-from objec...
Definition: hexdump.h:299
IteratorT end_
Iterator past end of elements to be dumped (in iterator mode)
Definition: hexdump.h:262
HexdumpParameterForward(IteratorT begin_it, IteratorT end_it, std::string prompt, ContainerT &&cont)
Construct a hexdump parameter forwarder object.
Definition: hexdump.h:279
HexdumpParameterForward & operator=(const HexdumpParameterForward &other)
Copy assignment (automatically updates the begin_ and end_ interator members if the copied object hol...
Definition: hexdump.h:308
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
ContainerT cont_
A container with the elements to be dumped (in container/temporary mode)
Definition: hexdump.h:266
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:220
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:457
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
basic_string_view< char > string_view
A view to a contiguous sequence of chars.
Definition: string_view.h:624
Definition of macros used internally by GUL.
Namespace gul14 contains all functions and classes of the General Utility Library.
Definition: doxygen.h:26
Provides a gul14::string_view that is fully compatible with C++17's std::string_view.