Spicy
integer.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <cinttypes>
6 #include <limits>
7 #include <string>
8 #include <tuple>
9 #include <utility>
10 
11 #include <hilti/rt/extension-points.h>
12 #include <hilti/rt/result.h>
13 #include <hilti/rt/safe-int.h>
14 #include <hilti/rt/types/bytes.h>
15 #include <hilti/rt/unpack.h>
16 #include <hilti/rt/util.h>
17 
18 namespace hilti::rt {
19 
20 namespace detail::adl {
21 inline std::string to_string(hilti::rt::integer::safe<uint64_t> x, adl::tag /*unused*/) {
22  return fmt("%" PRIu64, *x.Ptr());
23 }
24 
25 inline std::string to_string(hilti::rt::integer::safe<int64_t> x, adl::tag /*unused*/) {
26  return fmt("%" PRId64, *x.Ptr());
27 }
28 
29 inline std::string to_string(hilti::rt::integer::safe<uint32_t> x, adl::tag /*unused*/) {
30  return fmt("%" PRIu32, *x.Ptr());
31 }
32 
33 inline std::string to_string(hilti::rt::integer::safe<int32_t> x, adl::tag /*unused*/) {
34  return fmt("%" PRId32, *x.Ptr());
35 }
36 
37 inline std::string to_string(hilti::rt::integer::safe<uint16_t> x, adl::tag /*unused*/) {
38  return fmt("%" PRIu16, *x.Ptr());
39 }
40 
41 inline std::string to_string(hilti::rt::integer::safe<int16_t> x, adl::tag /*unused*/) {
42  return fmt("%" PRId16, *x.Ptr());
43 }
44 
45 inline std::string to_string(hilti::rt::integer::safe<uint8_t> x, adl::tag /*unused*/) {
46  return fmt("%" PRIu8, *x.Ptr());
47 }
48 
49 inline std::string to_string(hilti::rt::integer::safe<int8_t> x, adl::tag /*unused*/) {
50  return fmt("%" PRId8, *x.Ptr());
51 }
52 
53 inline std::string to_string(hilti::rt::integer::safe<char> x, adl::tag /*unused*/) { return fmt("%" PRId8, *x.Ptr()); }
54 
55 inline std::string to_string(uint64_t x, adl::tag /*unused*/) { return fmt("%" PRIu64, x); }
56 
57 inline std::string to_string(int64_t x, adl::tag /*unused*/) { return fmt("%" PRId64, x); }
58 
59 inline std::string to_string(uint32_t x, adl::tag /*unused*/) { return fmt("%" PRIu32, x); }
60 
61 inline std::string to_string(int32_t x, adl::tag /*unused*/) { return fmt("%" PRId32, x); }
62 
63 inline std::string to_string(uint16_t x, adl::tag /*unused*/) { return fmt("%" PRIu16, x); }
64 
65 inline std::string to_string(int16_t x, adl::tag /*unused*/) { return fmt("%" PRId16, x); }
66 
67 inline std::string to_string(uint8_t x, adl::tag /*unused*/) { return fmt("%" PRIu8, x); }
68 
69 inline std::string to_string(int8_t x, adl::tag /*unused*/) { return fmt("%" PRId8, x); }
70 
71 } // namespace detail::adl
72 
73 namespace integer {
74 namespace detail {
75 
76 template<typename T, typename D>
77 inline Result<std::tuple<integer::safe<T>, D>> unpack(D b, const uint8_t* dst, std::initializer_list<int> bytes) {
78  T x = 0;
79  for ( auto i : bytes ) {
80  x <<= 8U;
81  x |= (static_cast<T>(dst[i]));
82  }
83 
84  return std::make_tuple(static_cast<integer::safe<T>>(x), std::move(b));
85 }
86 
87 } // namespace detail
88 
89 template<typename T, typename D>
90 inline Result<std::tuple<integer::safe<T>, D>> unpack(D b, ByteOrder fmt) {
91  if ( fmt == ByteOrder::Host )
92  return unpack<T>(b, systemByteOrder());
93 
94  if ( b.size() < static_cast<int64_t>(sizeof(T)) )
95  return result::Error("insufficient data to unpack integer");
96 
97  uint8_t raw[sizeof(T)];
98  b = b.extract(raw, sizeof(raw));
99 
100  switch ( fmt ) {
101  case ByteOrder::Big:
102  case ByteOrder::Network:
103  if constexpr ( std::is_same<T, uint8_t>::value )
104  return std::make_tuple(static_cast<integer::safe<uint8_t>>(raw[0]), std::move(b));
105 
106  if constexpr ( std::is_same<T, int8_t>::value ) {
107  auto x = static_cast<int8_t>(raw[0]); // Forced cast to skip safe<T> range check.
108  return std::make_tuple(static_cast<integer::safe<int8_t>>(x), std::move(b));
109  }
110 
111  if constexpr ( std::is_same<T, uint16_t>::value || std::is_same<T, int16_t>::value )
112  return detail::unpack<T>(std::move(b), raw, {0, 1});
113 
114  if constexpr ( std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value )
115  return detail::unpack<T>(std::move(b), raw, {0, 1, 2, 3});
116 
117  if constexpr ( std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value )
118  return detail::unpack<T>(std::move(b), raw, {0, 1, 2, 3, 4, 5, 6, 7});
119 
121 
122  case ByteOrder::Little:
123  if constexpr ( std::is_same<T, uint8_t>::value )
124  return std::make_tuple(static_cast<integer::safe<uint8_t>>(raw[0]), std::move(b));
125 
126  if constexpr ( std::is_same<T, int8_t>::value ) {
127  auto x = static_cast<int8_t>(raw[0]); // Forced cast to skip safe<T> range check.
128  return std::make_tuple(static_cast<integer::safe<int8_t>>(x), std::move(b));
129  }
130 
131  if constexpr ( std::is_same<T, uint16_t>::value || std::is_same<T, int16_t>::value )
132  return detail::unpack<T>(std::move(b), raw, {1, 0});
133 
134  if constexpr ( std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value )
135  return detail::unpack<T>(std::move(b), raw, {3, 2, 1, 0});
136 
137  if constexpr ( std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value )
138  return detail::unpack<T>(std::move(b), raw, {7, 6, 5, 4, 3, 2, 1, 0});
139 
141 
142  case ByteOrder::Host:
143  // Cannot reach, we check this above.
145 
146  case ByteOrder::Undef: return result::Error("undefined byte order");
147  }
148 
150 }
151 
157 extern uint64_t hton64(uint64_t v);
158 
164 extern uint32_t hton32(uint32_t v);
165 
171 extern uint16_t hton16(uint16_t v);
172 
178 extern uint64_t ntoh64(uint64_t v);
179 
185 extern uint32_t ntoh32(uint32_t v);
186 
192 extern uint16_t ntoh16(uint16_t v);
193 
199 extern uint16_t flip16(uint16_t v);
200 
206 extern uint32_t flip32(uint32_t v);
207 
213 extern uint64_t flip64(uint64_t v);
214 
222 inline int64_t flip(int64_t v, uint64_t n) {
223  if ( n == 0 )
224  return v;
225  auto i = static_cast<uint64_t>(v);
226  i = flip64(i) >> (64 - n * 8);
227  return static_cast<int64_t>(i);
228 }
229 
237 inline uint64_t flip(uint64_t v, uint64_t n) {
238  if ( n == 0 )
239  return v;
240  return (flip64(v) >> (64 - n * 8));
241 }
242 
244 enum class BitOrder : int64_t { LSB0, MSB0, Undef };
245 
247 template<typename UINT>
248 inline hilti::rt::integer::safe<UINT> bits(hilti::rt::integer::safe<UINT> v, uint64_t lower, uint64_t upper,
249  BitOrder bo) {
250  constexpr auto width = std::numeric_limits<UINT>::digits;
251 
252  if ( lower > upper )
253  throw InvalidArgument("lower limit needs to be less or equal the upper limit");
254 
255  if ( upper >= width )
256  throw InvalidArgument("upper limit needs to be less or equal the input width");
257 
258  switch ( bo ) {
259  case BitOrder::LSB0: break;
260 
261  case BitOrder::MSB0:
262  lower = (width - lower - 1);
263  upper = (width - upper - 1);
264  std::swap(lower, upper);
265  break;
266 
267  case BitOrder::Undef: throw RuntimeError("undefined bit order");
268  }
269 
270  assert(lower <= upper);
271  const auto range = upper - lower + 1;
272 
273  // If the range to extract equals the width there is no work to do.
274  //
275  // NOTE: Not returning early here would lead to a shift beyond the width below.
276  if ( range == width )
277  return v;
278 
279  const auto mask = ((uint64_t(1) << range) - uint64_t(1U)) << lower;
280  return (v & mask) >> lower;
281 }
282 
283 } // namespace integer
284 
285 namespace detail::adl {
286 std::string to_string(const integer::BitOrder& x, tag /*unused*/);
287 }
288 
289 } // namespace hilti::rt
std::string to_string(T &&x)
Definition: extension-points.h:26
Definition: any.h:7
auto range(const T &t)
Definition: iterator.h:37
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:52
ByteOrder
Definition: util.h:506
ByteOrder systemByteOrder()
Definition: util.cc:390
void abort_with_backtrace() __attribute__((noreturn))
Definition: util.cc:44
std::string fmt(const char *fmt, const Args &... args)
Definition: fmt.h:13