Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
David Sehr | c431b9d | 2018-03-02 12:01:51 -0800 | [diff] [blame] | 17 | #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_ |
| 18 | #define ART_LIBARTBASE_BASE_BIT_STRUCT_H_ |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 19 | |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 20 | #include <type_traits> |
| 21 | |
| 22 | #include "base/casts.h" |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 23 | #include "bit_struct_detail.h" |
David Sehr | 1979c64 | 2018-04-26 14:41:18 -0700 | [diff] [blame] | 24 | #include "bit_utils.h" |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 25 | |
| 26 | // |
| 27 | // Zero-cost, type-safe, well-defined "structs" of bit fields. |
| 28 | // |
| 29 | // --------------------------------------------- |
| 30 | // Usage example: |
| 31 | // --------------------------------------------- |
| 32 | // |
| 33 | // // Definition for type 'Example' |
| 34 | // BITSTRUCT_DEFINE_START(Example, 10) |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 35 | // BITSTRUCT_UINT(0, 2) u2; // Every field must be a BitStruct[*] with the same StorageType, |
| 36 | // BITSTRUCT_INT(2, 7) i7; // preferably using BITSTRUCT_{FIELD,UINT,INT} |
| 37 | // BITSTRUCT_UINT(9, 1) i1; // to fill in the StorageType parameter. |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 38 | // BITSTRUCT_DEFINE_END(Example); |
| 39 | // |
| 40 | // Would define a bit struct with this layout: |
| 41 | // <- 1 -> <-- 7 --> <- 2 -> |
| 42 | // +--------+---------------+-----+ |
| 43 | // | i1 | i7 | u2 + |
| 44 | // +--------+---------------+-----+ |
| 45 | // 10 9 2 0 |
| 46 | // |
| 47 | // // Read-write just like regular values. |
| 48 | // Example ex; |
| 49 | // ex.u2 = 3; |
| 50 | // ex.i7 = -25; |
| 51 | // ex.i1 = true; |
| 52 | // size_t u2 = ex.u2; |
| 53 | // int i7 = ex.i7; |
| 54 | // bool i1 = ex.i1; |
| 55 | // |
| 56 | // // It's packed down to the smallest # of machine words. |
| 57 | // assert(sizeof(Example) == 2); |
| 58 | // // The exact bit pattern is well-defined by the template parameters. |
| 59 | // uint16_t cast = *reinterpret_cast<uint16_t*>(ex); |
| 60 | // assert(cast == ((3) | (0b100111 << 2) | (true << 9); |
| 61 | // |
| 62 | // --------------------------------------------- |
| 63 | // Why not just use C++ bitfields? |
| 64 | // --------------------------------------------- |
| 65 | // |
| 66 | // The layout is implementation-defined. |
| 67 | // We do not know whether the fields are packed left-to-right or |
| 68 | // right-to-left, so it makes it useless when the memory layout needs to be |
| 69 | // precisely controlled. |
| 70 | // |
| 71 | // --------------------------------------------- |
| 72 | // More info: |
| 73 | // --------------------------------------------- |
| 74 | // Currently uintmax_t is the largest supported underlying storage type, |
| 75 | // all (kBitOffset + kBitWidth) must fit into BitSizeOf<uintmax_t>(); |
| 76 | // |
| 77 | // Using BitStruct[U]int will automatically select an underlying type |
| 78 | // that's the smallest to fit your (offset + bitwidth). |
| 79 | // |
| 80 | // BitStructNumber can be used to manually select an underlying type. |
| 81 | // |
| 82 | // BitStructField can be used with custom standard-layout structs, |
| 83 | // thus allowing for arbitrary nesting of bit structs. |
| 84 | // |
| 85 | namespace art { |
| 86 | // Zero-cost wrapper around a struct 'T', allowing it to be stored as a bitfield |
| 87 | // at offset 'kBitOffset' and width 'kBitWidth'. |
| 88 | // The storage is plain unsigned int, whose size is the smallest required to fit |
| 89 | // 'kBitOffset + kBitWidth'. All operations to this become BitFieldExtract/BitFieldInsert |
| 90 | // operations to the underlying uint. |
| 91 | // |
| 92 | // Field memory representation: |
| 93 | // |
| 94 | // MSB <-- width --> LSB |
| 95 | // +--------+------------+--------+ |
| 96 | // | ?????? | u bitfield | ?????? + |
| 97 | // +--------+------------+--------+ |
| 98 | // offset 0 |
| 99 | // |
| 100 | // Reading/writing the bitfield (un)packs it into a temporary T: |
| 101 | // |
| 102 | // MSB <-- width --> LSB |
| 103 | // +-----------------+------------+ |
| 104 | // | 0.............0 | T bitfield | |
| 105 | // +-----------------+------------+ |
| 106 | // 0 |
| 107 | // |
| 108 | // It's the responsibility of the StorageType to ensure the bit representation |
| 109 | // of T can be represented by kBitWidth. |
| 110 | template <typename T, |
| 111 | size_t kBitOffset, |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 112 | size_t kBitWidth, |
| 113 | typename StorageType> |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 114 | struct BitStructField { |
Vladimir Marko | 495311c | 2022-04-14 10:59:53 +0000 | [diff] [blame] | 115 | static_assert(std::is_standard_layout_v<T>, "T must be standard layout"); |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 116 | |
| 117 | operator T() const { |
| 118 | return Get(); |
| 119 | } |
| 120 | |
| 121 | // Exclude overload when T==StorageType. |
| 122 | template <typename _ = void, |
Vladimir Marko | 495311c | 2022-04-14 10:59:53 +0000 | [diff] [blame] | 123 | typename = std::enable_if_t<std::is_same_v<T, StorageType>, _>> |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 124 | explicit operator StorageType() const { |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 125 | return BitFieldExtract(storage_, kBitOffset, kBitWidth); |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | BitStructField& operator=(T value) { |
| 129 | return Assign(*this, value); |
| 130 | } |
| 131 | |
| 132 | static constexpr size_t BitStructSizeOf() { |
| 133 | return kBitWidth; |
| 134 | } |
| 135 | |
Igor Murashkin | dfabcc5 | 2017-10-26 14:51:52 -0700 | [diff] [blame] | 136 | BitStructField& operator=(const BitStructField& other) { |
| 137 | // Warning. The default operator= will overwrite the entire storage! |
| 138 | return *this = static_cast<T>(other); |
| 139 | } |
| 140 | |
| 141 | BitStructField(const BitStructField& other) { |
| 142 | Assign(*this, static_cast<T>(other)); |
| 143 | } |
| 144 | |
| 145 | BitStructField() = default; |
| 146 | ~BitStructField() = default; |
| 147 | |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 148 | protected: |
| 149 | template <typename T2> |
| 150 | T2& Assign(T2& what, T value) { |
| 151 | // Since C++ doesn't allow the type of operator= to change out |
| 152 | // in the subclass, reimplement operator= in each subclass |
| 153 | // manually and call this helper function. |
Vladimir Marko | 495311c | 2022-04-14 10:59:53 +0000 | [diff] [blame] | 154 | static_assert(std::is_base_of_v<BitStructField, T2>, "T2 must inherit BitStructField"); |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 155 | what.Set(value); |
| 156 | return what; |
| 157 | } |
| 158 | |
| 159 | T Get() const { |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 160 | ExtractionType storage = static_cast<ExtractionType>(storage_); |
| 161 | ExtractionType extracted = BitFieldExtract(storage, kBitOffset, kBitWidth); |
| 162 | ConversionType to_convert = dchecked_integral_cast<ConversionType>(extracted); |
| 163 | return ValueConverter::FromUnderlyingStorage(to_convert); |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | void Set(T value) { |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 167 | ConversionType converted = ValueConverter::ToUnderlyingStorage(value); |
| 168 | ExtractionType extracted = dchecked_integral_cast<ExtractionType>(converted); |
| 169 | storage_ = BitFieldInsert(storage_, extracted, kBitOffset, kBitWidth); |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | private: |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 173 | using ValueConverter = detail::ValueConverter<T>; |
| 174 | using ConversionType = typename ValueConverter::StorageType; |
Vladimir Marko | 495311c | 2022-04-14 10:59:53 +0000 | [diff] [blame] | 175 | using ExtractionType = std::conditional_t<std::is_signed_v<ConversionType>, |
| 176 | std::make_signed_t<StorageType>, |
| 177 | StorageType>; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 178 | |
Vladimir Marko | 299141a | 2020-01-23 14:50:12 +0000 | [diff] [blame] | 179 | StorageType storage_; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 180 | }; |
| 181 | |
| 182 | // Base class for number-like BitStruct fields. |
| 183 | // T is the type to store in as a bit field. |
| 184 | // kBitOffset, kBitWidth define the position and length of the bitfield. |
| 185 | // |
| 186 | // (Common usage should be BitStructInt, BitStructUint -- this |
| 187 | // intermediate template allows a user-defined integer to be used.) |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 188 | template <typename T, size_t kBitOffset, size_t kBitWidth, typename StorageType> |
| 189 | struct BitStructNumber : public BitStructField<T, kBitOffset, kBitWidth, StorageType> { |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 190 | BitStructNumber& operator=(T value) { |
| 191 | return BaseType::Assign(*this, value); |
| 192 | } |
| 193 | |
| 194 | /*implicit*/ operator T() const { |
| 195 | return Get(); |
| 196 | } |
| 197 | |
| 198 | explicit operator bool() const { |
| 199 | return static_cast<bool>(Get()); |
| 200 | } |
| 201 | |
| 202 | BitStructNumber& operator++() { |
| 203 | *this = Get() + 1u; |
| 204 | return *this; |
| 205 | } |
| 206 | |
| 207 | StorageType operator++(int) { |
| 208 | return Get() + 1u; |
| 209 | } |
| 210 | |
| 211 | BitStructNumber& operator--() { |
| 212 | *this = Get() - 1u; |
| 213 | return *this; |
| 214 | } |
| 215 | |
| 216 | StorageType operator--(int) { |
| 217 | return Get() - 1u; |
| 218 | } |
| 219 | |
| 220 | private: |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 221 | using BaseType = BitStructField<T, kBitOffset, kBitWidth, StorageType>; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 222 | using BaseType::Get; |
| 223 | }; |
| 224 | |
| 225 | // Create a BitStruct field which uses the smallest underlying int storage type, |
| 226 | // in order to be large enough to fit (kBitOffset + kBitWidth). |
| 227 | // |
| 228 | // Values are sign-extended when they are read out. |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 229 | template <size_t kBitOffset, size_t kBitWidth, typename StorageType> |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 230 | using BitStructInt = |
| 231 | BitStructNumber<typename detail::MinimumTypeHelper<int, kBitOffset + kBitWidth>::type, |
| 232 | kBitOffset, |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 233 | kBitWidth, |
| 234 | StorageType>; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 235 | |
| 236 | // Create a BitStruct field which uses the smallest underlying uint storage type, |
| 237 | // in order to be large enough to fit (kBitOffset + kBitWidth). |
| 238 | // |
| 239 | // Values are zero-extended when they are read out. |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 240 | template <size_t kBitOffset, size_t kBitWidth, typename StorageType> |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 241 | using BitStructUint = |
| 242 | BitStructNumber<typename detail::MinimumTypeHelper<unsigned int, kBitOffset + kBitWidth>::type, |
| 243 | kBitOffset, |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 244 | kBitWidth, |
| 245 | StorageType>; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 246 | |
| 247 | // Start a definition for a bitstruct. |
| 248 | // A bitstruct is defined to be a union with a common initial subsequence |
| 249 | // that we call 'DefineBitStructSize<bitwidth>'. |
| 250 | // |
| 251 | // See top of file for usage example. |
| 252 | // |
| 253 | // This marker is required by the C++ standard in order to |
| 254 | // have a "common initial sequence". |
| 255 | // |
| 256 | // See C++ 9.5.1 [class.union]: |
| 257 | // If a standard-layout union contains several standard-layout structs that share a common |
| 258 | // initial sequence ... it is permitted to inspect the common initial sequence of any of |
| 259 | // standard-layout struct members. |
Andreas Gampe | 584771b | 2018-10-18 13:22:23 -0700 | [diff] [blame] | 260 | #define BITSTRUCT_DEFINE_START(name, bitwidth) \ |
| 261 | union name { /* NOLINT */ \ |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 262 | using StorageType = \ |
| 263 | typename detail::MinimumTypeUnsignedHelper<(bitwidth)>::type; \ |
Andreas Gampe | 584771b | 2018-10-18 13:22:23 -0700 | [diff] [blame] | 264 | art::detail::DefineBitStructSize<(bitwidth)> _; \ |
| 265 | static constexpr size_t BitStructSizeOf() { return (bitwidth); } \ |
| 266 | name& operator=(const name& other) { _ = other._; return *this; } /* NOLINT */ \ |
| 267 | name(const name& other) : _(other._) {} \ |
| 268 | name() = default; \ |
Igor Murashkin | dfabcc5 | 2017-10-26 14:51:52 -0700 | [diff] [blame] | 269 | ~name() = default; |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 270 | |
Vladimir Marko | e4207a7 | 2020-01-24 14:42:03 +0000 | [diff] [blame] | 271 | // Define a field. See top of file for usage example. |
| 272 | #define BITSTRUCT_FIELD(type, bit_offset, bit_width) \ |
| 273 | BitStructField<type, (bit_offset), (bit_width), StorageType> |
| 274 | #define BITSTRUCT_INT(bit_offset, bit_width) \ |
| 275 | BitStructInt<(bit_offset), (bit_width), StorageType> |
| 276 | #define BITSTRUCT_UINT(bit_offset, bit_width) \ |
| 277 | BitStructUint<(bit_offset), (bit_width), StorageType> |
| 278 | |
Ian Pedowitz | 2d53643 | 2020-07-22 14:33:00 -0700 | [diff] [blame] | 279 | // End the definition of a bitstruct, and insert a check |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 280 | // to ensure that the bitstruct did not exceed the specified size. |
| 281 | // |
| 282 | // See top of file for usage example. |
| 283 | #define BITSTRUCT_DEFINE_END(name) \ |
Igor Murashkin | 5573c37 | 2017-11-16 13:34:30 -0800 | [diff] [blame] | 284 | }; \ |
Igor Murashkin | 1e77d27 | 2017-10-07 14:09:43 +0000 | [diff] [blame] | 285 | static_assert(art::detail::ValidateBitStructSize<name>(), \ |
| 286 | #name "bitsize incorrect: " \ |
| 287 | "did you insert extra fields that weren't BitStructX, " \ |
| 288 | "and does the size match the sum of the field widths?") |
| 289 | |
| 290 | // Determine the minimal bit size for a user-defined type T. |
| 291 | // Used by BitStructField to determine how small a custom type is. |
| 292 | template <typename T> |
| 293 | static constexpr size_t BitStructSizeOf() { |
| 294 | return T::BitStructSizeOf(); |
| 295 | } |
| 296 | |
| 297 | } // namespace art |
| 298 | |
David Sehr | c431b9d | 2018-03-02 12:01:51 -0800 | [diff] [blame] | 299 | #endif // ART_LIBARTBASE_BASE_BIT_STRUCT_H_ |