pabigot  0.1.1
C++ support classes
byteorder.hpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 /* Copyright 2014-2019 Peter A. Bigot */
3 
9 #ifndef PABIGOT_BYTEORDER_HPP
10 #define PABIGOT_BYTEORDER_HPP
11 #pragma once
12 
13 #include <algorithm>
14 #include <cinttypes>
15 #include <climits>
16 #include <cstring>
17 #include <type_traits>
18 
19 #include <pabigot/common.hpp>
21 
22 namespace pabigot {
23 
25 namespace byteorder {
26 
31 enum class byte_order_enum : uint32_t
32 {
34  little_endian = 0x01020304U,
35 
37  big_endian = 0x04030201U,
38 
41 
47  pdp_endian = 0x03040102U,
48 };
49 
55 constexpr byte_order_enum
57 {
58  /* The following test depends on GCC pre-defined macros, because the
59  * alternative approach below violates strict aliasing in a way that
60  * prevents it from being used in a constexpr function:
61  *
62  * using t = union {
63  * char c[4];
64  * byte_order_enum bo;
65  * };
66  * return t{{4, 3, 2, 1}}.bo;
67  */
68  return (__ORDER_LITTLE_ENDIAN__ == __BYTE_ORDER__) ? byte_order_enum::little_endian
69  : (__ORDER_BIG_ENDIAN__ == __BYTE_ORDER__) ? byte_order_enum::big_endian
71 }
72 
74 static constexpr wchar_t BOM = u'\uFFFE';
75 
76 namespace details {
77 
85 template<class T,
86  class U = typename std::make_unsigned<T>::type>
87 constexpr U
88 byteswap (T v)
89 {
90  return external::ccbysa30::bswap<T>(v);
91 }
92 
101 template <class T>
102 constexpr bool
103 is_constexpr_swappable_v{std::is_integral<T>::value};
104 
117 template <class T>
118 constexpr bool
119 is_alias_swappable_v{std::is_scalar_v<T>
120  && !is_constexpr_swappable_v<T>};
121 
122 
128 template <class T>
129 constexpr bool
130 is_other_swappable_v{!is_constexpr_swappable_v<T>
131  && !is_alias_swappable_v<T>};
132 
133 } // ns details
134 
139 template <typename T>
140 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>, T>::type
141 byteswap (const T& t)
142 {
143  return details::byteswap(t);
144 }
145 
148 template <typename T>
149 typename std::enable_if<details::is_alias_swappable_v<T>, T>::type
150 byteswap (const T& t)
151 {
152  union { // @todo c++20 use std::byte
153  T value;
154  uint8_t raw[sizeof(t)];
155  } u = {t};
156  std::reverse(u.raw, u.raw + sizeof(u.raw));
157  return u.value;
158 }
159 
165 template <typename T>
166 typename std::enable_if<details::is_other_swappable_v<T>, T>::type
167 byteswap (const T& t)
168 {
169  static_assert(1 == sizeof(*t.begin()), "cannot byte-swap sequences with non-octet values");
170  auto value = t;
171  std::reverse(value.begin(), value.end());
172  return value;
173 }
174 
179 template <typename T,
180  byte_order_enum endian>
181 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>
182  && (host_byte_order() == endian), T>::type
183 hostswap (const T& t)
184 {
185  return t;
186 }
187 
192 template <typename T,
193  byte_order_enum endian>
194 typename std::enable_if<!details::is_constexpr_swappable_v<T>
195  && (host_byte_order() == endian), T>::type
196 hostswap (const T& t)
197 {
198  return t;
199 }
200 
205 template <typename T,
206  byte_order_enum endian>
207 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>
208  && (host_byte_order() != endian), T>::type
209 hostswap (const T& t)
210 {
211  return byteswap(t);
212 }
213 
218 template <typename T,
219  byte_order_enum endian>
220 typename std::enable_if<!details::is_constexpr_swappable_v<T>
221  && (host_byte_order() != endian), T>::type
222 hostswap (const T& t)
223 {
224  return byteswap(t);
225 }
226 
231 template <typename T>
232 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>, T>::type
233 host_x_le (const T& v)
234 {
235  return hostswap<T, byte_order_enum::little_endian>(v);
236 }
237 
239 template <typename T>
240 typename std::enable_if<!details::is_constexpr_swappable_v<T>, T>::type
241 host_x_le (const T& v)
242 {
243  return hostswap<T, byte_order_enum::little_endian>(v);
244 }
245 
250 template <typename T>
251 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>, T>::type
252 host_x_be (const T& v)
253 {
254  return hostswap<T, byte_order_enum::big_endian>(v);
255 }
256 
258 template <typename T>
259 typename std::enable_if<!details::is_constexpr_swappable_v<T>, T>::type
260 host_x_be (const T& v)
261 {
262  return hostswap<T, byte_order_enum::big_endian>(v);
263 }
264 
269 template <typename T>
270 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>, T>::type
271 be_x_le (const T& v)
272 {
273  return byteswap(v);
274 }
275 
277 template <typename T>
278 typename std::enable_if<!details::is_constexpr_swappable_v<T>, T>::type
279 be_x_le (const T& v)
280 {
281  return byteswap(v);
282 }
283 
288 template <typename T>
289 constexpr typename std::enable_if<details::is_constexpr_swappable_v<T>, T>::type
290 host_x_network (const T& v)
291 {
292  return host_x_be(v);
293 }
294 
296 template <typename T>
297 typename std::enable_if<!details::is_constexpr_swappable_v<T>, T>::type
298 host_x_network (const T& v)
299 {
300  return host_x_be(v);
301 }
302 
311 {
312 public:
314  using size_type = std::size_t;
315 
321  octets_helper (uint8_t* begin,
322  uint8_t* end) :
323  begin_{begin},
324  end_{end}
325  {
326  reset();
327  }
328 
334  octets_helper (uint8_t* begin,
335  size_type count) :
336  octets_helper{begin, begin + count}
337  { }
338 
343  void reset () noexcept
344  {
345  if (begin_) {
346  memset(begin_, 0, end_ - begin_);
347  }
348  bp_ = begin_;
349  }
350 
355  const bool valid () const noexcept
356  {
357  return bp_;
358  }
359 
364  void invalidate () noexcept
365  {
366  bp_ = nullptr;
367  }
368 
372  const void* begin () const noexcept
373  {
374  return bp_ ? begin_ : nullptr;
375  }
376 
382  const void* end () const noexcept
383  {
384  return bp_ ? bp_ : nullptr;
385  }
386 
390  const size_type size () const noexcept
391  {
392  return bp_ ? (bp_ - begin_) : 0U;
393  }
394 
398  const size_type available () const noexcept
399  {
400  return bp_ ? (end_ - bp_) : 0U;
401  }
402 
404  const size_type max_size () const noexcept
405  {
406  return end_ - begin_;
407  }
408 
418  void* advance (size_type s) noexcept
419  {
420  void* rv = nullptr;
421  if (can_advance(s)) {
422  rv = bp_;
423  bp_ += s;
424  } else {
425  bp_ = nullptr;
426  }
427  return rv;
428  }
429 
437  bool can_advance (size_type s) const noexcept
438  {
439  return bp_ ? (static_cast<size_type>(end_ - bp_) >= s) : false;
440  }
441 
452  bool append (const void* sp,
453  size_type span) noexcept
454  {
455  if (auto dp = advance(span)) {
456  memmove(dp, sp, span);
457  }
458  return valid();
459  }
460 
472  template <typename T>
473  bool append (const T& value) noexcept
474  {
475  return append(&value, sizeof(value));
476  }
477 
480  template <typename T>
481  bool append_be (const T& value) noexcept
482  {
483  return append<T>(host_x_be(value));
484  }
485 
488  template <typename T>
489  bool append_le (const T& value) noexcept
490  {
491  return append<T>(host_x_le(value));
492  }
493 
494 protected:
495  friend class oob_helper;
496 
497  /* Pointer to the start of the buffer. If null the buffer cannot be used. */
498  uint8_t* const begin_;
499 
500  /* Pointer to the end of the buffer. If null the buffer cannot be used. */
501  uint8_t* const end_;
502 
503  /* Pointer to the current location in the buffer. If null the buffer is
504  * invalid due to advance() asking for too much. */
505  uint8_t* bp_ = nullptr;
506 };
507 
508 } // ns byteorder
509 } // ns pabigot
510 
511 #endif /* PABIGOT_BYTEORDER_HPP */
pabigot::byteorder::octets_helper::begin
const void * begin() const noexcept
Get a pointer to the start of buffer.
Definition: byteorder.hpp:372
pabigot::byteorder::byte_order_enum::pdp_endian
Representation for PDP byte order.
pabigot::byteorder::octets_helper
Infrastructure to fill an octet buffer with data.
Definition: byteorder.hpp:310
pabigot::byteorder::octets_helper::append
bool append(const void *sp, size_type span) noexcept
Append a value to the buffer.
Definition: byteorder.hpp:452
pabigot::byteorder::details::is_constexpr_swappable_v
constexpr bool is_constexpr_swappable_v
Category identifying types that can be constexpr swapped.
Definition: byteorder.hpp:103
pabigot::byteorder::host_x_be
constexpr std::enable_if< details::is_constexpr_swappable_v< T >, T >::type host_x_be(const T &v)
Convert between host and big-endian byte order.
Definition: byteorder.hpp:252
pabigot::byteorder::octets_helper::octets_helper
octets_helper(uint8_t *begin, size_type count)
Reference an octet sequence into which data will be written.
Definition: byteorder.hpp:334
pabigot::byteorder::host_byte_order
constexpr byte_order_enum host_byte_order()
Return the byte_order_enum value for the host.
Definition: byteorder.hpp:56
pabigot::byteorder::byteswap
constexpr std::enable_if< details::is_constexpr_swappable_v< T >, T >::type byteswap(const T &t)
Byte-swap values at compile-time.
Definition: byteorder.hpp:141
common.hpp
General-use material that applies to all of pabigot.
ccbysa30.hpp
Material from external sources with CC-BY-SA-3.0 licensing.
pabigot::byteorder::octets_helper::end
const void * end() const noexcept
Get a pointer to the end of the filled part of the buffer.
Definition: byteorder.hpp:382
pabigot::byteorder::BOM
static constexpr wchar_t BOM
The Unicode byte order marker.
Definition: byteorder.hpp:74
pabigot::byteorder::octets_helper::size
const size_type size() const noexcept
Number of octets stored in the buffer.
Definition: byteorder.hpp:390
pabigot::byteorder::byte_order_enum::little_endian
Representation for a host using little-endian order.
pabigot::byteorder::byte_order_enum
byte_order_enum
Enumeration of constants representing various known byte orders.
Definition: byteorder.hpp:31
pabigot::byteorder::byteswap
std::enable_if< details::is_other_swappable_v< T >, T >::type byteswap(const T &t)
Byte-swap values by creating a copy and using std::reverse.
Definition: byteorder.hpp:167
pabigot::byteorder::octets_helper::invalidate
void invalidate() noexcept
Explicitly mark the buffer invalid.
Definition: byteorder.hpp:364
pabigot::byteorder::octets_helper::max_size
const size_type max_size() const noexcept
Maximum number of octets supported by the buffer.
Definition: byteorder.hpp:404
pabigot::byteorder::octets_helper::valid
const bool valid() const noexcept
Indicates whether advance() caused an error.
Definition: byteorder.hpp:355
pabigot::byteorder::octets_helper::append
bool append(const T &value) noexcept
Append a value to the buffer.
Definition: byteorder.hpp:473
pabigot::byteorder::byte_order_enum::big_endian
Representation for a host using big-endian order.
pabigot::byteorder::octets_helper::available
const size_type available() const noexcept
Number of unused octets available in the buffer.
Definition: byteorder.hpp:398
pabigot::byteorder::octets_helper::octets_helper
octets_helper(uint8_t *begin, uint8_t *end)
Reference an octet range into which data will be written.
Definition: byteorder.hpp:321
pabigot::byteorder::details::is_other_swappable_v
constexpr bool is_other_swappable_v
Category identifying types that can be swapped by using std::reverse on a copy.
Definition: byteorder.hpp:130
pabigot::byteorder::octets_helper::size_type
std::size_t size_type
Type used for span values.
Definition: byteorder.hpp:314
pabigot::byteorder::byte_order_enum::network
Representation for network byte order, which is big-endian.
pabigot
Root for all pabigot namespaces.
Definition: gap.hpp:15
pabigot::byteorder::host_x_le
constexpr std::enable_if< details::is_constexpr_swappable_v< T >, T >::type host_x_le(const T &v)
Convert between host and little-endian byte order.
Definition: byteorder.hpp:233
pabigot::byteorder::be_x_le
constexpr std::enable_if< details::is_constexpr_swappable_v< T >, T >::type be_x_le(const T &v)
Convert between big-endian and little-endian byte order.
Definition: byteorder.hpp:271
pabigot::byteorder::octets_helper::append_le
bool append_le(const T &value) noexcept
As with append() but stores the value converted to little-endian byte order.
Definition: byteorder.hpp:489
pabigot::byteorder::octets_helper::advance
void * advance(size_type s) noexcept
Allocate a region and return a pointer to it or a nullptr if the advance went too far.
Definition: byteorder.hpp:418
pabigot::byteorder::octets_helper::append_be
bool append_be(const T &value) noexcept
As with append() but stores the value converted to big-endian byte order.
Definition: byteorder.hpp:481
pabigot::byteorder::octets_helper::can_advance
bool can_advance(size_type s) const noexcept
Indicate whether advance() would succeed for a given span.
Definition: byteorder.hpp:437
pabigot::byteorder::hostswap
constexpr std::enable_if< details::is_constexpr_swappable_v< T > &&(host_byte_order()==endian), T >::type hostswap(const T &t)
constexpr-selected byte swap between host and endian.
Definition: byteorder.hpp:183
pabigot::byteorder::octets_helper::reset
void reset() noexcept
Remove all content from the buffer.
Definition: byteorder.hpp:343
pabigot::byteorder::host_x_network
constexpr std::enable_if< details::is_constexpr_swappable_v< T >, T >::type host_x_network(const T &v)
Convert between host and network byte order.
Definition: byteorder.hpp:290
pabigot::byteorder::details::is_alias_swappable_v
constexpr bool is_alias_swappable_v
Category identifying types that can swapped by alias with a byte array.
Definition: byteorder.hpp:119