/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_LIBARTBASE_BASE_BIT_STRUCT_H_ #define ART_LIBARTBASE_BASE_BIT_STRUCT_H_ #include #include "base/casts.h" #include "bit_struct_detail.h" #include "bit_utils.h" // // Zero-cost, type-safe, well-defined "structs" of bit fields. // // --------------------------------------------- // Usage example: // --------------------------------------------- // // // Definition for type 'Example' // BITSTRUCT_DEFINE_START(Example, 10) // BITSTRUCT_UINT(0, 2) u2; // Every field must be a BitStruct[*] with the same StorageType, // BITSTRUCT_INT(2, 7) i7; // preferably using BITSTRUCT_{FIELD,UINT,INT} // BITSTRUCT_UINT(9, 1) i1; // to fill in the StorageType parameter. // BITSTRUCT_DEFINE_END(Example); // // Would define a bit struct with this layout: // <- 1 -> <-- 7 --> <- 2 -> // +--------+---------------+-----+ // | i1 | i7 | u2 + // +--------+---------------+-----+ // 10 9 2 0 // // // Read-write just like regular values. // Example ex; // ex.u2 = 3; // ex.i7 = -25; // ex.i1 = true; // size_t u2 = ex.u2; // int i7 = ex.i7; // bool i1 = ex.i1; // // // It's packed down to the smallest # of machine words. // assert(sizeof(Example) == 2); // // The exact bit pattern is well-defined by the template parameters. // uint16_t cast = *reinterpret_cast(ex); // assert(cast == ((3) | (0b100111 << 2) | (true << 9); // // --------------------------------------------- // Why not just use C++ bitfields? // --------------------------------------------- // // The layout is implementation-defined. // We do not know whether the fields are packed left-to-right or // right-to-left, so it makes it useless when the memory layout needs to be // precisely controlled. // // --------------------------------------------- // More info: // --------------------------------------------- // Currently uintmax_t is the largest supported underlying storage type, // all (kBitOffset + kBitWidth) must fit into BitSizeOf(); // // Using BitStruct[U]int will automatically select an underlying type // that's the smallest to fit your (offset + bitwidth). // // BitStructNumber can be used to manually select an underlying type. // // BitStructField can be used with custom standard-layout structs, // thus allowing for arbitrary nesting of bit structs. // namespace art { // Zero-cost wrapper around a struct 'T', allowing it to be stored as a bitfield // at offset 'kBitOffset' and width 'kBitWidth'. // The storage is plain unsigned int, whose size is the smallest required to fit // 'kBitOffset + kBitWidth'. All operations to this become BitFieldExtract/BitFieldInsert // operations to the underlying uint. // // Field memory representation: // // MSB <-- width --> LSB // +--------+------------+--------+ // | ?????? | u bitfield | ?????? + // +--------+------------+--------+ // offset 0 // // Reading/writing the bitfield (un)packs it into a temporary T: // // MSB <-- width --> LSB // +-----------------+------------+ // | 0.............0 | T bitfield | // +-----------------+------------+ // 0 // // It's the responsibility of the StorageType to ensure the bit representation // of T can be represented by kBitWidth. template struct BitStructField { static_assert(std::is_standard_layout::value, "T must be standard layout"); operator T() const { return Get(); } // Exclude overload when T==StorageType. template ::value, _>> explicit operator StorageType() const { return BitFieldExtract(storage_, kBitOffset, kBitWidth); } BitStructField& operator=(T value) { return Assign(*this, value); } static constexpr size_t BitStructSizeOf() { return kBitWidth; } BitStructField& operator=(const BitStructField& other) { // Warning. The default operator= will overwrite the entire storage! return *this = static_cast(other); } BitStructField(const BitStructField& other) { Assign(*this, static_cast(other)); } BitStructField() = default; ~BitStructField() = default; protected: template T2& Assign(T2& what, T value) { // Since C++ doesn't allow the type of operator= to change out // in the subclass, reimplement operator= in each subclass // manually and call this helper function. static_assert(std::is_base_of::value, "T2 must inherit BitStructField"); what.Set(value); return what; } T Get() const { ExtractionType storage = static_cast(storage_); ExtractionType extracted = BitFieldExtract(storage, kBitOffset, kBitWidth); ConversionType to_convert = dchecked_integral_cast(extracted); return ValueConverter::FromUnderlyingStorage(to_convert); } void Set(T value) { ConversionType converted = ValueConverter::ToUnderlyingStorage(value); ExtractionType extracted = dchecked_integral_cast(converted); storage_ = BitFieldInsert(storage_, extracted, kBitOffset, kBitWidth); } private: using ValueConverter = detail::ValueConverter; using ConversionType = typename ValueConverter::StorageType; using ExtractionType = typename std::conditional, std::make_signed_t, StorageType>::type; StorageType storage_; }; // Base class for number-like BitStruct fields. // T is the type to store in as a bit field. // kBitOffset, kBitWidth define the position and length of the bitfield. // // (Common usage should be BitStructInt, BitStructUint -- this // intermediate template allows a user-defined integer to be used.) template struct BitStructNumber : public BitStructField { BitStructNumber& operator=(T value) { return BaseType::Assign(*this, value); } /*implicit*/ operator T() const { return Get(); } explicit operator bool() const { return static_cast(Get()); } BitStructNumber& operator++() { *this = Get() + 1u; return *this; } StorageType operator++(int) { return Get() + 1u; } BitStructNumber& operator--() { *this = Get() - 1u; return *this; } StorageType operator--(int) { return Get() - 1u; } private: using BaseType = BitStructField; using BaseType::Get; }; // Create a BitStruct field which uses the smallest underlying int storage type, // in order to be large enough to fit (kBitOffset + kBitWidth). // // Values are sign-extended when they are read out. template using BitStructInt = BitStructNumber::type, kBitOffset, kBitWidth, StorageType>; // Create a BitStruct field which uses the smallest underlying uint storage type, // in order to be large enough to fit (kBitOffset + kBitWidth). // // Values are zero-extended when they are read out. template using BitStructUint = BitStructNumber::type, kBitOffset, kBitWidth, StorageType>; // Start a definition for a bitstruct. // A bitstruct is defined to be a union with a common initial subsequence // that we call 'DefineBitStructSize'. // // See top of file for usage example. // // This marker is required by the C++ standard in order to // have a "common initial sequence". // // See C++ 9.5.1 [class.union]: // If a standard-layout union contains several standard-layout structs that share a common // initial sequence ... it is permitted to inspect the common initial sequence of any of // standard-layout struct members. #define BITSTRUCT_DEFINE_START(name, bitwidth) \ union name { /* NOLINT */ \ using StorageType = \ typename detail::MinimumTypeUnsignedHelper<(bitwidth)>::type; \ art::detail::DefineBitStructSize<(bitwidth)> _; \ static constexpr size_t BitStructSizeOf() { return (bitwidth); } \ name& operator=(const name& other) { _ = other._; return *this; } /* NOLINT */ \ name(const name& other) : _(other._) {} \ name() = default; \ ~name() = default; // Define a field. See top of file for usage example. #define BITSTRUCT_FIELD(type, bit_offset, bit_width) \ BitStructField #define BITSTRUCT_INT(bit_offset, bit_width) \ BitStructInt<(bit_offset), (bit_width), StorageType> #define BITSTRUCT_UINT(bit_offset, bit_width) \ BitStructUint<(bit_offset), (bit_width), StorageType> // End the definition of a bitstruct, and insert a check // to ensure that the bitstruct did not exceed the specified size. // // See top of file for usage example. #define BITSTRUCT_DEFINE_END(name) \ }; \ static_assert(art::detail::ValidateBitStructSize(), \ #name "bitsize incorrect: " \ "did you insert extra fields that weren't BitStructX, " \ "and does the size match the sum of the field widths?") // Determine the minimal bit size for a user-defined type T. // Used by BitStructField to determine how small a custom type is. template static constexpr size_t BitStructSizeOf() { return T::BitStructSizeOf(); } } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_STRUCT_H_