pstore2
uint128.hpp
Go to the documentation of this file.
1 //===- include/pstore/support/uint128.hpp -----------------*- mode: C++ -*-===//
2 //* _ _ _ ____ ___ *
3 //* _ _(_)_ __ | |_/ |___ \( _ ) *
4 //* | | | | | '_ \| __| | __) / _ \ *
5 //* | |_| | | | | | |_| |/ __/ (_) | *
6 //* \__,_|_|_| |_|\__|_|_____\___/ *
7 //* *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
18 
19 #ifndef PSTORE_SUPPORT_UINT128_HPP
20 #define PSTORE_SUPPORT_UINT128_HPP
21 
22 #include <array>
23 #include <cstddef>
24 #include <cstdint>
25 #include <functional>
26 #include <limits>
27 #include <ostream>
28 #include <string>
29 #include <utility>
30 
31 #include "pstore/support/maybe.hpp"
32 
33 namespace pstore {
34  class uint128;
35 } // end namespace pstore
36 
37 namespace std {
38 
39  template <>
40  struct is_unsigned<pstore::uint128> : true_type {};
41  template <>
42  struct is_signed<pstore::uint128> : false_type {};
43 
44  // Provide the is_(un)signed<__uint128_t> if we have support for the type in the compiler but
45  // not in the standard library.
46 #if defined(PSTORE_HAVE_UINT128_T) && !defined(PSTORE_HAVE_UINT128_TRAITS_SUPPORT)
47  template <>
48  struct is_unsigned<__uint128_t> : true_type {};
49  template <>
50  struct is_signed<__uint128_t> : false_type {};
51 #endif // PSTORE_HAVE_UINT128_T && !PSTORE_HAVE_UINT128_TRAITS_SUPPORT
52 
53 } // namespace std
54 
55 namespace pstore {
56 
57  namespace details {
58 
61  template <typename T, typename Enable = void>
62  class high {
63  public:
64  constexpr std::uint64_t operator() (T const t) const noexcept {
65  (void) t;
66  return 0;
67  }
68  };
69 
74  template <typename T>
75  class high<T, typename std::enable_if<(sizeof (T) > sizeof (std::uint64_t))>::type> {
76  public:
77  constexpr std::uint64_t operator() (T v) const noexcept {
78  return (v >> 64U) & ((T{1} << 64) - 1U);
79  }
80  };
81 
82  } // end namespace details
83 
84 
85  class alignas (16) uint128 {
86  public:
87  constexpr uint128 () noexcept = default;
88  constexpr uint128 (std::nullptr_t) noexcept = delete;
89 #ifdef PSTORE_HAVE_UINT128_T
90  constexpr uint128 (std::uint64_t const high, std::uint64_t const low) noexcept
91  : v_{__uint128_t{high} << 64U | __uint128_t{low}} {}
92 
94  template <typename IntType,
95  typename = typename std::enable_if<std::is_unsigned<IntType>::value>::type>
96  constexpr uint128 (IntType v) noexcept // NOLINT(hicpp-explicit-conversions)
97  : v_{v} {}
98 
101  explicit constexpr uint128 (std::uint8_t const * const bytes) noexcept
102  : v_{bytes_to_uint128 (bytes)} {}
103 #else
104  constexpr uint128 (std::uint64_t const high, std::uint64_t const low) noexcept
105  : low_{low}
106  , high_{high} {}
107 
109  template <typename IntType,
110  typename = typename std::enable_if<std::is_unsigned<IntType>::value>::type>
111  constexpr uint128 (IntType const v) noexcept
112  : low_{v} {}
113 
116  explicit constexpr uint128 (std::uint8_t const * const bytes) noexcept
117  : low_{bytes_to_uint64 (&bytes[8])}
118  , high_{bytes_to_uint64 (&bytes[0])} {}
119 #endif // PSTORE_HAVE_UINT128_T
120 
121  explicit uint128 (std::array<std::uint8_t, 16> const & bytes) noexcept
122  : uint128 (bytes.data ()) {}
123 
124  constexpr uint128 (uint128 const &) = default;
125  constexpr uint128 (uint128 &&) noexcept = default;
126  ~uint128 () noexcept = default;
127 
128  uint128 & operator= (uint128 const &) = default;
129  uint128 & operator= (uint128 &&) noexcept = default;
130 
131  template <typename T>
132  constexpr bool operator== (T rhs) const noexcept;
133  template <typename T>
134  constexpr bool operator!= (T rhs) const noexcept {
135  return !operator== (rhs);
136  }
137 
138 #ifdef PSTORE_HAVE_UINT128_T
139  constexpr std::uint64_t high () const noexcept {
140  return static_cast<std::uint64_t> (v_ >> 64U);
141  }
142  constexpr std::uint64_t low () const noexcept {
143  return static_cast<std::uint64_t> (v_ & max64);
144  }
145 #else
146  constexpr std::uint64_t high () const noexcept { return high_; }
147  constexpr std::uint64_t low () const noexcept { return low_; }
148 #endif // PSTORE_HAVE_UINT128_T
149 
150  uint128 operator- () const noexcept;
151  constexpr bool operator! () const noexcept;
152  uint128 operator~ () const noexcept;
153 
154  uint128 & operator++ () noexcept;
155  uint128 operator++ (int) noexcept;
156  uint128 & operator-- () noexcept;
157  uint128 operator-- (int) noexcept;
158 
159  uint128 & operator+= (uint128 rhs) noexcept;
160  uint128 & operator-= (uint128 const b) noexcept { return *this += -b; }
161 
162  template <typename T>
163  constexpr uint128 operator& (T rhs) const noexcept;
164  template <typename T>
165  constexpr uint128 operator| (T rhs) const noexcept;
166 
167  template <typename T>
168  uint128 operator<< (T n) const noexcept;
169  uint128 & operator>>= (unsigned n) noexcept;
170 
171  static constexpr std::size_t hex_string_length = std::size_t{32};
172 
173  template <typename OutputIterator>
174  OutputIterator to_hex (OutputIterator out) const noexcept;
175  std::string to_hex_string () const;
176 
177  static maybe<uint128> from_hex_string (std::string const & str);
178 
179  private:
180  static constexpr std::uint64_t max64 = std::numeric_limits<std::uint64_t>::max ();
181 #ifdef PSTORE_HAVE_UINT128_T
182  __uint128_t v_ = 0U;
183  static constexpr __uint128_t bytes_to_uint128 (std::uint8_t const * bytes) noexcept;
184 #else
185  std::uint64_t low_ = 0U;
186  std::uint64_t high_ = 0U;
187  static constexpr std::uint64_t bytes_to_uint64 (std::uint8_t const * bytes) noexcept;
188 #endif // PSTORE_HAVE_UINT128_T
189 
190  static constexpr char digit_to_hex (unsigned const v) noexcept {
191  PSTORE_ASSERT (v < 0x10);
192  return static_cast<char> (v + ((v < 10) ? '0' : 'a' - 10));
193  }
194  };
195 
196  PSTORE_STATIC_ASSERT (sizeof (uint128) == 16);
197  PSTORE_STATIC_ASSERT (alignof (uint128) == 16);
198  PSTORE_STATIC_ASSERT (std::is_standard_layout<uint128>::value);
199 
200  // operator==
201  // ~~~~~~~~~~
202  template <typename T>
203  constexpr bool uint128::operator== (T rhs) const noexcept {
204 #ifdef PSTORE_HAVE_UINT128_T
205  return v_ == rhs;
206 #else
207  return this->high () == details::high<T>{}(rhs) && this->low () == (rhs & max64);
208 #endif
209  }
210 
211  template <>
212  constexpr bool uint128::operator==<uint128> (uint128 const rhs) const noexcept {
213 #ifdef PSTORE_HAVE_UINT128_T
214  return v_ == rhs.v_;
215 #else
216  return this->high () == rhs.high () && this->low () == rhs.low ();
217 #endif
218  }
219 
220  // operator++
221  // ~~~~~~~~~~
222  inline uint128 & uint128::operator++ () noexcept {
223 #ifdef PSTORE_HAVE_UINT128_T
224  ++v_;
225 #else
226  if (++low_ == 0U) {
227  ++high_;
228  }
229 #endif
230  return *this;
231  }
232  inline uint128 uint128::operator++ (int) noexcept {
233  auto const prev = *this;
234  ++(*this);
235  return prev;
236  }
237 
238  // operator--
239  // ~~~~~~~~~~
240  inline uint128 & uint128::operator-- () noexcept {
241 #ifdef PSTORE_HAVE_UINT128_T
242  --v_;
243 #else
244  if (low_ == 0U) {
245  low_ = max64;
246  --high_;
247  } else {
248  --low_;
249  }
250 #endif
251  return *this;
252  }
253 
254  inline uint128 uint128::operator-- (int) noexcept {
255  auto const prev = *this;
256  --(*this);
257  return prev;
258  }
259 
260  // operator+=
261  // ~~~~~~~~~~
262  inline uint128 & uint128::operator+= (uint128 const rhs) noexcept {
263 #ifdef PSTORE_HAVE_UINT128_T
264  v_ += rhs.v_;
265 #else
266  auto const old_low = low_;
267  low_ += rhs.low_;
268  high_ += rhs.high_;
269  if (low_ < old_low) {
270  ++high_;
271  }
272 #endif
273  return *this;
274  }
275 
276  // operator-() (unary negation)
277  // ~~~~~~~~~~~
278  inline uint128 uint128::operator- () const noexcept {
279 #ifdef PSTORE_HAVE_UINT128_T
280  return {-v_};
281 #else
282  auto r = ~(*this);
283  return ++r;
284 #endif
285  }
286 
287  // operator!
288  // ~~~~~~~~~
289  constexpr bool uint128::operator! () const noexcept {
290 #ifdef PSTORE_HAVE_UINT128_T
291  return !v_; // NOLINT(readability-implicit-bool-conversion)
292 #else
293  return !(high_ != 0 || low_ != 0);
294 #endif
295  }
296 
297  // operator~ (binary ones complement)
298  // ~~~~~~~~~
299  inline uint128 uint128::operator~ () const noexcept {
300 #ifdef PSTORE_HAVE_UINT128_T
301  return {~v_};
302 #else
303  auto t = *this;
304  t.low_ = ~t.low_;
305  t.high_ = ~t.high_;
306  return t;
307 #endif
308  }
309 
310  // operator&
311  // ~~~~~~~~~
312  template <typename T>
313  constexpr uint128 uint128::operator& (T const rhs) const noexcept {
314 #ifdef PSTORE_HAVE_UINT128_T
315  return {v_ & rhs};
316 #else
317  return {0U, low () & rhs};
318 #endif
319  }
320 
321  template <>
322  constexpr uint128 uint128::operator&<uint128> (uint128 const rhs) const noexcept {
323 #ifdef PSTORE_HAVE_UINT128_T
324  return {v_ & rhs.v_};
325 #else
326  return {high () & rhs.high (), low () & rhs.low ()};
327 #endif
328  }
329 
330  // operator|
331  // ~~~~~~~~~
332  template <typename T>
333  constexpr uint128 uint128::operator| (T const rhs) const noexcept {
334 #ifdef PSTORE_HAVE_UINT128_T
335  return {v_ | rhs};
336 #else
337  return {0U, low () | rhs};
338 #endif
339  }
340 
341  template <>
342  constexpr uint128 uint128::operator|<uint128> (uint128 const rhs) const noexcept {
343 #ifdef PSTORE_HAVE_UINT128_T
344  return {v_ | rhs.v_};
345 #else
346  return {high () | rhs.high (), low () | rhs.low ()};
347 #endif
348  }
349 
350  // operator<<
351  // ~~~~~~~~~~
352  template <typename Other>
353  uint128 uint128::operator<< (Other const n) const noexcept {
354  PSTORE_ASSERT (n <= 128);
355 #ifdef PSTORE_HAVE_UINT128_T
356  return {v_ << n};
357 #else
358  if (n >= 64U) {
359  return {low () << (n - 64U), 0U};
360  }
361  if (n == 0U) {
362  return *this;
363  }
364  std::uint64_t const mask = (std::uint64_t{1} << n) - 1U;
365  auto const dist = 64U - n;
366  std::uint64_t const top_of_low_mask = mask << dist;
367  std::uint64_t const bottom_of_high = (low () & top_of_low_mask) >> dist;
368  return {(high () << n) | bottom_of_high, low () << n};
369 #endif // PSTORE_HAVE_UINT128_T
370  }
371 
372  // operator>>=
373  // ~~~~~~~~~~~
374  inline uint128 & uint128::operator>>= (unsigned const n) noexcept {
375  PSTORE_ASSERT (n <= 128);
376 #ifdef PSTORE_HAVE_UINT128_T
377  v_ >>= n;
378 #else
379  if (n >= 64) {
380  low_ = high_ >> (n - 64U);
381  high_ = 0U;
382  } else if (n > 0) {
383  std::uint64_t const mask = (std::uint64_t{1} << n) - 1U;
384  low_ = (low_ >> n) | ((high_ & mask) << (64U - n));
385  high_ >>= n;
386  }
387 #endif // PSTORE_HAVE_UINT128_T
388  return *this;
389  }
390 
391  // bytes_to_uintXX
392  // ~~~~~~~~~~~~~~~
393 #ifdef PSTORE_HAVE_UINT128_T
394  constexpr __uint128_t uint128::bytes_to_uint128 (std::uint8_t const * const bytes) noexcept {
395  return __uint128_t{bytes[0]} << 120U | __uint128_t{bytes[1]} << 112U |
396  __uint128_t{bytes[2]} << 104U | __uint128_t{bytes[3]} << 96U |
397  __uint128_t{bytes[4]} << 88U | __uint128_t{bytes[5]} << 80U |
398  __uint128_t{bytes[6]} << 72U | __uint128_t{bytes[7]} << 64U |
399  __uint128_t{bytes[8]} << 56U | __uint128_t{bytes[9]} << 48U |
400  __uint128_t{bytes[10]} << 40U | __uint128_t{bytes[11]} << 32U |
401  __uint128_t{bytes[12]} << 24U | __uint128_t{bytes[13]} << 16U |
402  __uint128_t{bytes[14]} << 8U | __uint128_t{bytes[15]};
403  }
404 #else
405  constexpr std::uint64_t uint128::bytes_to_uint64 (std::uint8_t const * const bytes) noexcept {
406  return std::uint64_t{bytes[0]} << 56 | std::uint64_t{bytes[1]} << 48 |
407  std::uint64_t{bytes[2]} << 40 | std::uint64_t{bytes[3]} << 32 |
408  std::uint64_t{bytes[4]} << 24 | std::uint64_t{bytes[5]} << 16 |
409  std::uint64_t{bytes[6]} << 8 | std::uint64_t{bytes[7]};
410  }
411 #endif // PSTORE_HAVE_UINT128_T
412 
413  template <typename OutputIterator>
414  OutputIterator uint128::to_hex (OutputIterator out) const noexcept {
415 #ifndef NDEBUG
416  auto emitted = 0U;
417 #endif
418 #ifdef PSTORE_HAVE_UINT128_T
419  for (auto shift = 4U; shift <= 128U; shift += 4U) {
420  *(out++) = digit_to_hex ((v_ >> (128U - shift)) & 0x0FU);
421 # ifndef NDEBUG
422  ++emitted;
423 # endif
424  }
425 #else
426  for (auto shift = 4U; shift <= 64; shift += 4U) {
427  *(out++) = digit_to_hex ((high_ >> (64U - shift)) & 0x0FU);
428 # ifndef NDEBUG
429  ++emitted;
430 # endif
431  }
432  for (auto shift = 4U; shift <= 64; shift += 4U) {
433  *(out++) = digit_to_hex ((low_ >> (64U - shift)) & 0x0FU);
434 # ifndef NDEBUG
435  ++emitted;
436 # endif
437  }
438 #endif
439  PSTORE_ASSERT (emitted == hex_string_length);
440  return out;
441  }
442 
443  inline std::ostream & operator<< (std::ostream & os, uint128 const & value) {
444  return os << '{' << value.high () << ',' << value.low () << '}';
445  }
446 
447  constexpr bool operator== (uint128 const & lhs, uint128 const & rhs) noexcept {
448  return lhs.operator== (rhs);
449  }
450  constexpr bool operator!= (uint128 const & lhs, uint128 const & rhs) noexcept {
451  return lhs.operator!= (rhs);
452  }
453 
454  constexpr bool operator< (uint128 const & lhs, uint128 const & rhs) noexcept {
455  return lhs.high () < rhs.high () ||
456  (!(rhs.high () < lhs.high ()) && lhs.low () < rhs.low ());
457  }
458  constexpr bool operator> (uint128 const & lhs, uint128 const & rhs) noexcept {
459  return rhs < lhs;
460  }
461  constexpr bool operator>= (uint128 const & lhs, uint128 const & rhs) noexcept {
462  return !(lhs < rhs);
463  }
464  constexpr bool operator<= (uint128 const & lhs, uint128 const & rhs) noexcept {
465  return !(rhs < lhs);
466  }
467 
468 } // end namespace pstore
469 
470 namespace std {
471  template <>
472  struct hash<pstore::uint128> {
474  using result_type = size_t;
475 
476  result_type operator() (argument_type const & s) const {
477  return std::hash<std::uint64_t>{}(s.low ()) ^ std::hash<std::uint64_t>{}(s.high ());
478  }
479  };
480 
481  template <>
482  class numeric_limits<pstore::uint128> {
483  using type = pstore::uint128;
484 
485  public:
486  static constexpr bool is_specialized = true;
487  static constexpr bool is_signed = false;
488 
489  static constexpr type min () noexcept { return {0U}; }
490  static constexpr type max () noexcept {
491  return {std::numeric_limits<std::uint64_t>::max (),
492  std::numeric_limits<std::uint64_t>::max ()};
493  }
494  static constexpr type lowest () noexcept { return type{0U}; }
495 
496  static constexpr const int digits = 128;
497  static constexpr const int digits10 = 38;
498  static constexpr const int max_digits10 = 0;
499  static constexpr const bool is_integer = true;
500  static constexpr const bool is_exact = true;
501  static constexpr const int radix = 2;
502  static constexpr type epsilon () noexcept { return {0U}; }
503  static constexpr type round_error () noexcept { return {0U}; }
504 
505  static constexpr const int min_exponent = 0;
506  static constexpr const int min_exponent10 = 0;
507  static constexpr const int max_exponent = 0;
508  static constexpr const int max_exponent10 = 0;
509 
510  static constexpr const bool has_infinity = false;
511  static constexpr const bool has_quiet_NaN = false; // NOLINT(readability-identifier-naming)
512  // NOLINTNEXTLINE(readability-identifier-naming)
513  static constexpr const bool has_signaling_NaN = false;
514  static constexpr const float_denorm_style has_denorm = denorm_absent;
515  static constexpr const bool has_denorm_loss = false;
516  static constexpr type infinity () noexcept { return {0U}; }
517  // NOLINTNEXTLINE(readability-identifier-naming)
518  static constexpr type quiet_NaN () noexcept { return {0U}; }
519  // NOLINTNEXTLINE(readability-identifier-naming)
520  static constexpr type signaling_NaN () noexcept { return {0U}; }
521  static constexpr type denorm_min () noexcept { return {0U}; }
522 
523  static constexpr const bool is_iec559 = false;
524  static constexpr const bool is_bounded = true;
525  static constexpr const bool is_modulo = true;
526 
527  static constexpr const bool traps = true;
528  static constexpr const bool tinyness_before = false;
529  static constexpr const float_round_style round_style = std::round_toward_zero;
530  };
531 
532 } // end namespace std
533 
534 #endif // PSTORE_SUPPORT_UINT128_HPP
Definition: uint128.hpp:85
Definition: chunked_sequence.hpp:607
An implementation of the Haskell Maybe type.
constexpr uint128(std::uint8_t const *const bytes) noexcept
Definition: uint128.hpp:116
Definition: print.cpp:18
Definition: nonpod2.cpp:40
A function object which can extract the high 64-bits from an integer type.
Definition: uint128.hpp:62
Definition: maybe.hpp:49
constexpr uint128(IntType const v) noexcept
Construct from an unsigned integer that&#39;s 64-bits wide or fewer.
Definition: uint128.hpp:111