pstore2
varint.hpp
Go to the documentation of this file.
1 //===- include/pstore/support/varint.hpp ------------------*- mode: C++ -*-===//
2 //* _ _ *
3 //* __ ____ _ _ __(_)_ __ | |_ *
4 //* \ \ / / _` | '__| | '_ \| __| *
5 //* \ V / (_| | | | | | | | |_ *
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 //===----------------------------------------------------------------------===//
54 
55 #ifndef PSTORE_SUPPORT_VARINT_HPP
56 #define PSTORE_SUPPORT_VARINT_HPP
57 
59 
60 namespace pstore {
61  namespace varint {
62 
64  constexpr std::size_t const max_output_length = 9;
65 
66  constexpr unsigned encoded_size (std::uint64_t const x) noexcept {
67  // Each additional byte that we emit steals one bit from the first byte. We therefore
68  // manage 7 bits per byte.
69  constexpr auto nine_byte_threshold = (UINT64_C (1) << (7U * 8U)) - 1U;
70  if (x > nine_byte_threshold) {
71  return 9;
72  }
73 
74  // The input to clz() is ORed with 1 to guarantee we don't pass 0 (which will
75  // require 1 byte to store anyway).
76  unsigned const bits = 64U - bit_count::clz (x | 1U);
77  return (bits - 1U) / 7U + 1U;
78  }
79 
80  template <typename OutputIterator>
81  OutputIterator encode (std::uint64_t x, OutputIterator out) {
82  unsigned const bits = 64U - bit_count::clz (x | 1U);
83  unsigned bytes;
84  if (bits > 56U) {
85  bytes = 8U;
86  *(out++) = 0U;
87  } else {
88  bytes = (bits - 1U) / 7U + 1U;
89  // Encode the number of bytes in the low bits of the value itself.
90  x = (2U * x + 1U) << (bytes - 1U);
91  }
92  PSTORE_ASSERT (bytes < 9);
93  // clang-format off
94  switch (bytes) {
95  // NOLINTNEXTLINE(bugprone-branch-clone)
96  case 8: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
97  case 7: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
98  case 6: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
99  case 5: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
100  case 4: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
101  case 3: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
102  case 2: *(out++) = x & 0xFFU; x >>= 8U; PSTORE_FALLTHROUGH;
103  default:
104  *(out++) = x & 0xFFU;
105  }
106  // clang-format on
107  return out;
108  }
109 
110 
111  template <typename InputIterator>
112  inline unsigned decode_size (InputIterator in) {
113  // (Note that ORing with 0x100 guarantees that bit 8 is set. This in turn ensures that
114  // we won't ever pass 0 to clz() which would result in undefined behavior.
115  return bit_count::ctz (*in | 0x100U) + 1U;
116  }
117 
118  namespace details {
119 
120  template <typename InputIterator>
121  std::uint64_t decode9 (InputIterator in) {
122  ++in; // skip the length byte
123  auto result = std::uint64_t{0};
124  for (auto shift = 0U; shift < 64U; shift += 8U) {
125  result |= std::uint64_t{*(in++)} << shift;
126  }
127  return result;
128  }
129 
130  } // end namespace details
131 
132  template <typename InputIterator>
133  std::uint64_t decode (InputIterator in, unsigned size) {
134  PSTORE_ASSERT (size > 0 && size == decode_size (in));
135  if (size == 9) {
136  return details::decode9 (in);
137  }
138 
139  auto result = std::uint64_t{0};
140  auto shift = 0U;
141  // clang-format off
142  switch (size) {
143  // NOLINTNEXTLINE(bugprone-branch-clone)
144  case 8: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
145  case 7: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
146  case 6: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
147  case 5: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
148  case 4: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
149  case 3: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
150  case 2: result |= std::uint64_t{*(in++)} << shift; shift += 8; PSTORE_FALLTHROUGH;
151  default:
152  result |= std::uint64_t{*(in++)} << shift;
153  }
154  // clang-format on
155  return result >> size; // throw away the unwanted size bytes frm the first byte.
156  }
157 
158  template <typename InputIterator>
159  std::uint64_t decode (InputIterator in) {
160  return decode (in, decode_size (in));
161  }
162 
163  } // end namespace varint
164 } // end namespace pstore
165 
166 #endif // PSTORE_SUPPORT_VARINT_HPP
constexpr std::size_t const max_output_length
The maximum number of bytes that encode() will produce.
Definition: varint.hpp:64
Definition: print.cpp:18
Definition: nonpod2.cpp:40
Implements portable functions for bit twiddling operations including counting leading and trailing ze...