1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
/*
* 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_DETAIL_H_
#define ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
#include "bit_utils.h"
#include "globals.h"
#include <type_traits>
// Implementation details for bit_struct.h
// Not intended to be used stand-alone.
namespace art {
template <typename T>
static constexpr size_t BitStructSizeOf();
namespace detail {
// Select the smallest uintX_t that will fit kBitSize bits.
template <size_t kBitSize>
struct MinimumTypeUnsignedHelper {
using type =
typename std::conditional<kBitSize == 0, void, // NOLINT [whitespace/operators] [3]
typename std::conditional<kBitSize <= 8, uint8_t, // NOLINT [whitespace/operators] [3]
typename std::conditional<kBitSize <= 16, uint16_t, // NOLINT [whitespace/operators] [3]
typename std::conditional<kBitSize <= 32, uint32_t,
typename std::conditional<kBitSize <= 64, uint64_t,
typename std::conditional<kBitSize <= BitSizeOf<uintmax_t>(), uintmax_t,
void>::type>::type>::type>::type>::type>::type;
};
// Select the smallest [u]intX_t that will fit kBitSize bits.
// Automatically picks intX_t or uintX_t based on the sign-ness of T.
template <typename T, size_t kBitSize>
struct MinimumTypeHelper {
using type_unsigned = typename MinimumTypeUnsignedHelper<kBitSize>::type;
using type =
typename std::conditional</* if */ std::is_signed<T>::value,
/* then */ typename std::make_signed<type_unsigned>::type,
/* else */ type_unsigned>::type;
};
// Helper for converting to and from T to an integral type.
template <typename T>
union ValueConverter {
using StorageType = typename MinimumTypeHelper<T, sizeof(T) * kBitsPerByte>::type;
static constexpr StorageType ToUnderlyingStorage(T value) {
ValueConverter converter;
converter.value_.val_ = value;
return converter.storage_.val_;
}
static constexpr T FromUnderlyingStorage(StorageType storage) {
ValueConverter converter;
converter.storage_.val_ = storage;
return converter.value_.val_;
}
// Underlying values must be wrapped in separate standard-layout structs.
// See below for more details.
struct StorageWrapper {
StorageType val_;
};
struct ValueWrapper {
T val_;
};
// Safely alias storage_ and value_ together.
//
// 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.
StorageWrapper storage_;
ValueWrapper value_;
#if __cplusplus >= 202000L
#error "When upgrading to C++20, remove this error and check that this is OK for all use cases."
static_assert(std::is_layout_compatible_v<StorageWrapper, ValueWrapper>);
#endif
// Future work: In theory almost non-standard layout can be supported here,
// assuming they don't rely on the address of (this).
// We just have to use memcpy since the union-aliasing would not work.
};
// Denotes the beginning of a bit struct.
//
// 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.
template <size_t kSize>
struct DefineBitStructSize {
private:
typename MinimumTypeUnsignedHelper<kSize>::type _;
};
// Check if type "T" has a member called _ in it.
template <typename T>
struct HasUnderscoreField {
private:
using TrueT = std::integral_constant<bool, true>::type;
using FalseT = std::integral_constant<bool, false>::type;
template <typename C>
static constexpr auto Test(void*) -> decltype(std::declval<C>()._, TrueT{});
template <typename>
static constexpr FalseT Test(...);
public:
static constexpr bool value = decltype(Test<T>(nullptr))::value;
};
// Infer the type of the member of &T::M.
template <typename T, typename M>
M GetMemberType(M T:: *);
// Ensure the minimal type storage for 'T' matches its declared BitStructSizeOf.
// Nominally used by the BITSTRUCT_DEFINE_END macro.
template <typename T>
static constexpr bool ValidateBitStructSize() {
static_assert(std::is_union<T>::value, "T must be union");
static_assert(std::is_standard_layout<T>::value, "T must be standard-layout");
static_assert(HasUnderscoreField<T>::value, "T must have the _ DefineBitStructSize");
const size_t kBitStructSizeOf = BitStructSizeOf<T>();
static_assert(std::is_same<decltype(GetMemberType(&T::_)),
DefineBitStructSize<kBitStructSizeOf>>::value,
"T::_ must be a DefineBitStructSize of the same size");
const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
? kBitsPerByte
: RoundUpToPowerOfTwo(kBitStructSizeOf);
// Ensure no extra fields were added in between START/END.
const size_t kActualSize = sizeof(T) * kBitsPerByte;
return kExpectedSize == kActualSize;
}
} // namespace detail
} // namespace art
#endif // ART_LIBARTBASE_BASE_BIT_STRUCT_DETAIL_H_
|