μHAL
util-bits.h
1#ifndef UTIL_BITS_H
2#define UTIL_BITS_H
3
4#include <bit>
5#include <cassert>
6#include <cstdint>
7#include <stdexcept>
8#include <string>
9#include <type_traits>
10
11static_assert(__cpp_lib_bitops >= 201907L);
12
13template <typename T>
14static inline void clear_and_insert(uint32_t &dest, T value, uint32_t mask)
15{
16 static_assert(std::is_integral_v<T>);
17
18 unsigned shift = std::countr_zero(mask);
19 uint32_t shifted_mask = mask >> shift;
20
21 T max, min;
22 uint32_t uvalue;
23 if constexpr (std::is_signed_v<T>) {
24 typedef typename std::make_unsigned<T>::type U;
25
26 uint32_t umax = shifted_mask >> 1;
27 /* in the case where mask == UINT32_MAX and value is signed, this would
28 * result in umin = INT32_MIN. Assigning that to an int32_t variable to
29 * then apply the unary minus operator below would be UB, which we avoid
30 * by using an int64_t intermediary value */
31 int64_t umin = shifted_mask ^ umax;
32
33 max = umax;
34 min = -umin;
35
36 uvalue = (U)value;
37 } else {
38 max = shifted_mask;
39 min = 0;
40 uvalue = value;
41 }
42
43 if (value < min)
44 throw std::runtime_error("value " + std::to_string(value)
45 + " less than min (" + std::to_string(min) + ")");
46 if (value > max)
47 throw std::runtime_error("value " + std::to_string(value)
48 + " greater than max (" + std::to_string(max) + ")");
49
50 dest &= UINT32_MAX & ~mask;
51 dest |= (uvalue << shift) & mask;
52}
53
54static inline void insert_bit(uint32_t &dest, bool value, uint32_t mask)
55{
56 assert(std::popcount(mask) == 1);
57 if (value)
58 dest |= mask;
59 else
60 dest &= ~mask;
61}
62
63static inline bool get_bit(uint32_t value, uint32_t mask)
64{
65 assert(std::popcount(mask) == 1);
66 return value & mask;
67}
68
69template <typename Signed> static inline int32_t sign_extend(uint32_t value)
70{
71 typedef typename std::make_unsigned<Signed>::type Unsigned;
72 return (Signed)(Unsigned)value;
73}
74
75static inline int32_t sign_extend(uint32_t value, unsigned width)
76{
77 switch (width) {
78 case 8:
79 return sign_extend<int8_t>(value);
80 case 16:
81 return sign_extend<int16_t>(value);
82 case 32:
83 return sign_extend<int32_t>(value);
84 default:
85 throw std::logic_error(
86 "invalid width should have been caught elsewhere");
87 }
88}
89
90static inline int32_t extract_value(
91 uint32_t value, uint32_t mask, bool is_signed = false)
92{
93 unsigned shift = std::countr_zero(mask);
94 unsigned popcount = std::popcount(mask);
95
96 /* should be using get_bit for single bit masks */
97 assert(popcount > 1);
98 /* the bit mask should be continuous */
99 assert(popcount == 32 - shift - std::countl_zero(mask));
100
101 uint32_t intermediary = (value & mask) >> shift;
102 if (is_signed) {
103 return sign_extend(intermediary, popcount);
104 } else {
105 return intermediary;
106 }
107}
108
109/* XXX: remove this when there are no more users */
110template <typename T>
111static inline T extract_value(uint32_t value, uint32_t mask)
112{
113 return extract_value(value, mask, std::is_signed_v<T>);
114}
115
116#endif