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