blob: 577682ccceaaabbe58fbcc460767d0b55fb1919e [file] [log] [blame]
/*
* 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.
*/
#include "bit_struct.h"
#include "gtest/gtest.h"
namespace art {
// A copy of detail::ValidateBitStructSize that uses EXPECT for a more
// human-readable message.
template <typename T>
static constexpr bool ValidateBitStructSize(const char* name) {
const size_t kBitStructSizeOf = BitStructSizeOf<T>();
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;
EXPECT_EQ(kExpectedSize, kActualSize) << name;
return true;
}
#define VALIDATE_BITSTRUCT_SIZE(type) ValidateBitStructSize<type>(#type)
TEST(BitStructs, MinimumType) {
EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<1>::type));
EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<2>::type));
EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<3>::type));
EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<8>::type));
EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<9>::type));
EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<10>::type));
EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<15>::type));
EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<16>::type));
EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<17>::type));
EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<32>::type));
EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<33>::type));
EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<64>::type));
}
template <typename T>
size_t AsUint(const T& value) {
size_t uint_value = 0;
memcpy(&uint_value, &value, sizeof(value));
return uint_value;
}
struct CustomBitStruct {
CustomBitStruct() = default;
explicit CustomBitStruct(int8_t data) : data(data) {}
static constexpr size_t BitStructSizeOf() {
return 4;
}
int8_t data;
};
TEST(BitStructs, Custom) {
CustomBitStruct expected(0b1111);
BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f{};
EXPECT_EQ(1u, sizeof(f));
f = CustomBitStruct(0b1111);
CustomBitStruct read_out = f;
EXPECT_EQ(read_out.data, 0b1111);
EXPECT_EQ(AsUint(f), 0b11110000u);
}
BITSTRUCT_DEFINE_START(TestTwoCustom, /* size */ 8)
BitStructField<CustomBitStruct, /*lsb*/0, /*width*/4> f4_a;
BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f4_b;
BITSTRUCT_DEFINE_END(TestTwoCustom);
TEST(BitStructs, TwoCustom) {
EXPECT_EQ(sizeof(TestTwoCustom), 1u);
VALIDATE_BITSTRUCT_SIZE(TestTwoCustom);
TestTwoCustom cst{};
// Test the write to most-significant field doesn't clobber least-significant.
cst.f4_a = CustomBitStruct(0b0110);
cst.f4_b = CustomBitStruct(0b0101);
int8_t read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
int8_t read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
EXPECT_EQ(0b0110, static_cast<int>(read_out));
EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
EXPECT_EQ(AsUint(cst), 0b01010110u);
// Test write to least-significant field doesn't clobber most-significant.
cst.f4_a = CustomBitStruct(0);
read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
EXPECT_EQ(0b0, static_cast<int>(read_out));
EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
EXPECT_EQ(AsUint(cst), 0b01010000u);
}
TEST(BitStructs, Number) {
BitStructNumber<uint16_t, /*lsb*/4, /*width*/4> bsn{};
EXPECT_EQ(2u, sizeof(bsn));
bsn = 0b1111;
uint32_t read_out = static_cast<uint32_t>(bsn);
uint32_t read_out_impl = bsn;
EXPECT_EQ(read_out, read_out_impl);
EXPECT_EQ(read_out, 0b1111u);
EXPECT_EQ(AsUint(bsn), 0b11110000u);
}
BITSTRUCT_DEFINE_START(TestBitStruct, /* size */ 8)
BitStructInt</*lsb*/0, /*width*/3> i3;
BitStructUint</*lsb*/3, /*width*/4> u4;
BitStructUint</*lsb*/0, /*width*/7> alias_all;
BITSTRUCT_DEFINE_END(TestBitStruct);
TEST(BitStructs, Test1) {
{
// Check minimal size selection is correct.
BitStructInt</*lsb*/0, /*width*/3> i3;
BitStructUint</*lsb*/3, /*width*/4> u4;
BitStructUint</*lsb*/0, /*width*/7> alias_all;
EXPECT_EQ(1u, sizeof(i3));
EXPECT_EQ(1u, sizeof(u4));
EXPECT_EQ(1u, sizeof(alias_all));
}
TestBitStruct tst{};
// Check minimal size selection is correct.
EXPECT_EQ(1u, sizeof(TestBitStruct));
EXPECT_EQ(1u, sizeof(tst._));
EXPECT_EQ(1u, sizeof(tst.i3));
EXPECT_EQ(1u, sizeof(tst.u4));
EXPECT_EQ(1u, sizeof(tst.alias_all));
// Check operator assignment.
tst.i3 = -1;
tst.u4 = 0b1010;
// Check implicit operator conversion.
int8_t read_i3 = tst.i3;
uint8_t read_u4 = tst.u4;
// Ensure read-out values were correct.
EXPECT_EQ(static_cast<int8_t>(-1), read_i3);
EXPECT_EQ(0b1010, read_u4);
// Ensure aliasing is working.
EXPECT_EQ(0b1010111, static_cast<uint8_t>(tst.alias_all));
// Ensure the bit pattern is correct.
EXPECT_EQ(0b1010111u, AsUint(tst));
// Math operator checks
{
// In-place
++tst.u4;
EXPECT_EQ(static_cast<uint8_t>(0b1011), static_cast<uint8_t>(tst.u4));
--tst.u4;
EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
// Copy
uint8_t read_and_convert = tst.u4++;
EXPECT_EQ(static_cast<uint8_t>(0b1011), read_and_convert);
EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
read_and_convert = tst.u4--;
EXPECT_EQ(static_cast<uint8_t>(0b1001), read_and_convert);
EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
// Check boolean operator conversion.
tst.u4 = 0b1010;
EXPECT_TRUE(static_cast<bool>(tst.u4));
bool succ = tst.u4 ? true : false;
EXPECT_TRUE(succ);
tst.u4 = 0;
EXPECT_FALSE(static_cast<bool>(tst.u4));
/*
// Disabled: Overflow is caught by the BitFieldInsert DCHECKs.
// Check overflow for uint.
tst.u4 = 0b1111;
++tst.u4;
EXPECT_EQ(static_cast<uint8_t>(0), static_cast<uint8_t>(tst.u4));
*/
}
}
BITSTRUCT_DEFINE_START(MixedSizeBitStruct, /* size */ 32)
BitStructUint</*lsb*/0, /*width*/3> u3;
BitStructUint</*lsb*/3, /*width*/10> u10;
BitStructUint</*lsb*/13, /*width*/19> u19;
BitStructUint</*lsb*/0, /*width*/32> alias_all;
BITSTRUCT_DEFINE_END(MixedSizeBitStruct);
// static_assert(sizeof(MixedSizeBitStruct) == sizeof(uint32_t), "TestBitStructs#MixedSize");
TEST(BitStructs, Mixed) {
EXPECT_EQ(4u, sizeof(MixedSizeBitStruct));
MixedSizeBitStruct tst{};
// Check operator assignment.
tst.u3 = 0b111u;
tst.u10 = 0b1111010100u;
tst.u19 = 0b1010101010101010101u;
// Check implicit operator conversion.
uint8_t read_u3 = tst.u3;
uint16_t read_u10 = tst.u10;
uint32_t read_u19 = tst.u19;
// Ensure read-out values were correct.
EXPECT_EQ(0b111u, read_u3);
EXPECT_EQ(0b1111010100u, read_u10);
EXPECT_EQ(0b1010101010101010101u, read_u19);
uint32_t read_all = tst.alias_all;
// Ensure aliasing is working.
EXPECT_EQ(0b10101010101010101011111010100111u, read_all);
// Ensure the bit pattern is correct.
EXPECT_EQ(0b10101010101010101011111010100111u, AsUint(tst));
}
BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size */ 8)
BitStructInt</*lsb*/0, /*width*/3> i3;
BitStructUint</*lsb*/3, /*width*/4> u4;
BitStructUint</*lsb*/0, /*width*/8> alias_all;
BITSTRUCT_DEFINE_END(TestBitStruct_u8);
TEST(BitStructs, FieldAssignment) {
TestBitStruct_u8 all_1s{};
all_1s.alias_all = 0xffu;
{
TestBitStruct_u8 tst{};
tst.i3 = all_1s.i3;
// Copying a single bitfield does not copy all bitfields.
EXPECT_EQ(0b111, tst.alias_all);
}
{
TestBitStruct_u8 tst{};
tst.u4 = all_1s.u4;
// Copying a single bitfield does not copy all bitfields.
EXPECT_EQ(0b1111000, tst.alias_all);
}
}
BITSTRUCT_DEFINE_START(NestedStruct, /* size */ 64)
BitStructField<MixedSizeBitStruct, /*lsb*/0> mixed_lower;
BitStructField<MixedSizeBitStruct, /*lsb*/32> mixed_upper;
BitStructUint</*lsb*/0, /*width*/64> alias_all;
BITSTRUCT_DEFINE_END(NestedStruct);
TEST(BitStructs, NestedFieldAssignment) {
MixedSizeBitStruct mixed_all_1s{};
mixed_all_1s.alias_all = 0xFFFFFFFFu;
{
NestedStruct xyz{};
NestedStruct other{};
other.mixed_upper = mixed_all_1s;
other.mixed_lower = mixed_all_1s;
// Copying a single bitfield does not copy all bitfields.
xyz.mixed_lower = other.mixed_lower;
EXPECT_EQ(0xFFFFFFFFu, xyz.alias_all);
}
{
NestedStruct xyz{};
NestedStruct other{};
other.mixed_upper = mixed_all_1s;
other.mixed_lower = mixed_all_1s;
// Copying a single bitfield does not copy all bitfields.
xyz.mixed_upper = other.mixed_upper;
EXPECT_EQ(0xFFFFFFFF00000000u, xyz.alias_all);
}
}
} // namespace art