1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // Low-level bit operations including std::endian from C++20.
16 #pragma once
17
18 #include <climits>
19
20 #include "lib/stdcompat/bit.h"
21
22 namespace pw {
23
24 using ::cpp20::endian;
25
26 namespace bytes {
27
28 /// Queries size of the object or type in bits.
29 #define SIZE_OF_IN_BIT(...) (sizeof(__VA_ARGS__) * CHAR_BIT)
30
31 /// Extends the nth bit to the left. Useful for expanding singed values into
32 /// larger integer types.
33 template <std::size_t kBitWidth, typename T>
SignExtend(T nbit_value)34 constexpr T SignExtend(T nbit_value) {
35 static_assert(std::is_integral_v<T>);
36 static_assert(kBitWidth < SIZE_OF_IN_BIT(T));
37
38 using SignedT = std::make_signed_t<T>;
39
40 constexpr std::size_t extension_bits = SIZE_OF_IN_BIT(SignedT) - kBitWidth;
41
42 SignedT nbit_temp = static_cast<SignedT>(nbit_value);
43 return ((nbit_temp << extension_bits) >> extension_bits);
44 }
45
46 /// Extracts bits between msb and lsb from a value.
47 ///
48 /// @tparam OutType The type of output number to be extracted from input
49 /// number.
50 /// @tparam kMsb The left bit (included) that extraction starts at.
51 /// @tparam kLsb The right bit (included) that extraction ends at.
52 /// @tparam InType The type of input number.
53 /// @param[in] value The input number.
54 ///
55 /// Example (extrat bits between 10 and 5 from a uint32_t and return as a
56 /// uint8_t):
57 ///
58 /// @code
59 /// constexpr uint32_t number = 0xA0A0A0A0;
60 /// constexpr uint8_t extracted_number = ExtractBits<uint8_t, 10, 5>(number);
61 /// @endcode
62 template <typename OutType, std::size_t kMsb, std::size_t kLsb, typename InType>
ExtractBits(InType value)63 constexpr OutType ExtractBits(InType value) {
64 static_assert(kMsb >= kLsb);
65 static_assert(kMsb < SIZE_OF_IN_BIT(InType));
66
67 constexpr std::size_t kBitWidth = kMsb - kLsb + 1;
68 static_assert(kBitWidth <= SIZE_OF_IN_BIT(OutType));
69
70 if constexpr (kBitWidth == SIZE_OF_IN_BIT(InType)) {
71 return OutType(value);
72 } else {
73 constexpr OutType mask = OutType((OutType(1) << kBitWidth) - 1);
74 return OutType((value >> kLsb) & mask);
75 }
76 }
77
78 } // namespace bytes
79 } // namespace pw
80