/* * 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 static constexpr bool ValidateBitStructSize(const char* name) { const size_t kBitStructSizeOf = BitStructSizeOf(); const size_t kExpectedSize = (BitStructSizeOf() < 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) 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 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(uint8_t data) : data(data) {} static constexpr size_t BitStructSizeOf() { return 4; } uint8_t data; }; TEST(BitStructs, Custom) { CustomBitStruct expected(0b1111u); BitStructField f{}; EXPECT_EQ(1u, sizeof(f)); f = CustomBitStruct(0b1111u); CustomBitStruct read_out = f; EXPECT_EQ(read_out.data, 0b1111u); EXPECT_EQ(AsUint(f), 0b11110000u); } BITSTRUCT_DEFINE_START(TestTwoCustom, /* size= */ 8) BITSTRUCT_FIELD(CustomBitStruct, /*lsb=*/0, /*width=*/4) f4_a; BITSTRUCT_FIELD(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(cst.f4_a).data; int8_t read_out_b = static_cast(cst.f4_b).data; EXPECT_EQ(0b0110, static_cast(read_out)); EXPECT_EQ(0b0101, static_cast(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(cst.f4_a).data; read_out_b = static_cast(cst.f4_b).data; EXPECT_EQ(0b0, static_cast(read_out)); EXPECT_EQ(0b0101, static_cast(read_out_b)); EXPECT_EQ(AsUint(cst), 0b01010000u); } TEST(BitStructs, Number) { BitStructNumber bsn{}; EXPECT_EQ(2u, sizeof(bsn)); bsn = 0b1111; uint32_t read_out = static_cast(bsn); uint32_t read_out_impl = bsn; EXPECT_EQ(read_out, read_out_impl); EXPECT_EQ(read_out, 0b1111u); EXPECT_EQ(AsUint(bsn), 0b11110000u); } TEST(BitStructs, NumberNarrowStorage) { BitStructNumber bsn{}; EXPECT_EQ(1u, sizeof(bsn)); bsn = 0b1111; uint32_t read_out = static_cast(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) BITSTRUCT_INT(/*lsb=*/0, /*width=*/3) i3; BITSTRUCT_UINT(/*lsb=*/3, /*width=*/4) u4; BITSTRUCT_UINT(/*lsb=*/0, /*width=*/7) alias_all; BITSTRUCT_DEFINE_END(TestBitStruct); TEST(BitStructs, Test1) { 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(-1), read_i3); EXPECT_EQ(0b1010, read_u4); // Ensure aliasing is working. EXPECT_EQ(0b1010111, static_cast(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(0b1011), static_cast(tst.u4)); --tst.u4; EXPECT_EQ(static_cast(0b1010), static_cast(tst.u4)); // Copy uint8_t read_and_convert = tst.u4++; EXPECT_EQ(static_cast(0b1011), read_and_convert); EXPECT_EQ(static_cast(0b1010), static_cast(tst.u4)); read_and_convert = tst.u4--; EXPECT_EQ(static_cast(0b1001), read_and_convert); EXPECT_EQ(static_cast(0b1010), static_cast(tst.u4)); // Check boolean operator conversion. tst.u4 = 0b1010; EXPECT_TRUE(static_cast(tst.u4)); bool succ = tst.u4 ? true : false; EXPECT_TRUE(succ); tst.u4 = 0; EXPECT_FALSE(static_cast(tst.u4)); /* // Disabled: Overflow is caught by the BitFieldInsert DCHECKs. // Check overflow for uint. tst.u4 = 0b1111; ++tst.u4; EXPECT_EQ(static_cast(0), static_cast(tst.u4)); */ } } BITSTRUCT_DEFINE_START(MixedSizeBitStruct, /* size= */ 32) BITSTRUCT_UINT(/*lsb=*/0, /*width=*/3) u3; BITSTRUCT_UINT(/*lsb=*/3, /*width=*/10) u10; BITSTRUCT_UINT(/*lsb=*/13, /*width=*/19) u19; BITSTRUCT_UINT(/*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) BITSTRUCT_INT(/*lsb=*/0, /*width=*/3) i3; BITSTRUCT_UINT(/*lsb=*/3, /*width=*/4) u4; BITSTRUCT_UINT(/*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= */ 2 * MixedSizeBitStruct::BitStructSizeOf()) BITSTRUCT_FIELD(MixedSizeBitStruct, /*lsb=*/0, /*width=*/MixedSizeBitStruct::BitStructSizeOf()) mixed_lower; BITSTRUCT_FIELD(MixedSizeBitStruct, /*lsb=*/MixedSizeBitStruct::BitStructSizeOf(), /*width=*/MixedSizeBitStruct::BitStructSizeOf()) mixed_upper; BITSTRUCT_UINT(/*lsb=*/0, /*width=*/ 2 * MixedSizeBitStruct::BitStructSizeOf()) 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