μ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) + " less than min (" + std::to_string(min) + ")");
45 if (value > max)
46 throw std::runtime_error("value " + std::to_string(value) + " greater than max (" + std::to_string(max) + ")");
47
48 dest &= UINT32_MAX & ~mask;
49 dest |= (uvalue << shift) & mask;
50}
51
52static inline void insert_bit(uint32_t &dest, bool value, uint32_t mask)
53{
54 assert(std::popcount(mask) == 1);
55 if (value)
56 dest |= mask;
57 else
58 dest &= ~mask;
59}
60
61static inline bool get_bit(uint32_t value, uint32_t mask)
62{
63 assert(std::popcount(mask) == 1);
64 return value & mask;
65}
66
67template<typename Signed>
68static inline int32_t sign_extend(uint32_t value)
69{
70 typedef typename std::make_unsigned<Signed>::type Unsigned;
71 return (Signed)(Unsigned)value;
72}
73
74static inline int32_t sign_extend(uint32_t value, unsigned width)
75{
76 switch (width) {
77 case 8:
78 return sign_extend<int8_t>(value);
79 case 16:
80 return sign_extend<int16_t>(value);
81 case 32:
82 return sign_extend<int32_t>(value);
83 default:
84 throw std::logic_error("invalid width should have been caught elsewhere");
85 }
86}
87
88static inline int32_t extract_value(uint32_t value, uint32_t mask, bool is_signed=false)
89{
90 unsigned shift = std::countr_zero(mask);
91 unsigned popcount = std::popcount(mask);
92
93 /* should be using get_bit for single bit masks */
94 assert(popcount > 1);
95 /* the bit mask should be continuous */
96 assert(popcount == 32 - shift - std::countl_zero(mask));
97
98 uint32_t intermediary = (value & mask) >> shift;
99 if (is_signed) {
100 return sign_extend(intermediary, popcount);
101 } else {
102 return intermediary;
103 }
104}
105
106/* XXX: remove this when there are no more users */
107template<typename T>
108static inline T extract_value(uint32_t value, uint32_t mask)
109{
110 return extract_value(value, mask, std::is_signed_v<T>);
111}
112
113#endif