| /* |
| * 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_string.h" |
| |
| #include "gtest/gtest.h" |
| #include "android-base/logging.h" |
| |
| namespace art { |
| |
| constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity]; |
| constexpr size_t BitString::kCapacity; |
| |
| }; // namespace art |
| |
| using namespace art; // NOLINT [build/namespaces] [5] |
| |
| // These helper functions are only used by the test, |
| // so they are not in the main BitString class. |
| std::string Stringify(BitString bit_string) { |
| std::stringstream ss; |
| ss << bit_string; |
| return ss.str(); |
| } |
| |
| BitStringChar MakeBitStringChar(size_t idx, size_t val) { |
| return BitStringChar(val, BitString::MaybeGetBitLengthAtPosition(idx)); |
| } |
| |
| BitStringChar MakeBitStringChar(size_t val) { |
| return BitStringChar(val, MinimumBitsToStore(val)); |
| } |
| |
| BitString MakeBitString(std::initializer_list<size_t> values = {}) { |
| CHECK_GE(BitString::kCapacity, values.size()); |
| |
| BitString bs{}; |
| |
| size_t i = 0; |
| for (size_t val : values) { |
| bs.SetAt(i, MakeBitStringChar(i, val)); |
| ++i; |
| } |
| |
| return bs; |
| } |
| |
| template <typename T> |
| size_t AsUint(const T& value) { |
| size_t uint_value = 0; |
| memcpy(&uint_value, &value, sizeof(value)); |
| return uint_value; |
| } |
| |
| // Make max bitstring, e.g. BitString[4095,15,2047] for {12,4,11} |
| template <size_t kCount = BitString::kCapacity> |
| BitString MakeBitStringMax() { |
| BitString bs{}; |
| |
| for (size_t i = 0; i < kCount; ++i) { |
| bs.SetAt(i, |
| MakeBitStringChar(i, MaxInt<BitStringChar::StorageType>(BitString::kBitSizeAtPosition[i]))); |
| } |
| |
| return bs; |
| } |
| |
| BitString SetBitStringCharAt(BitString bit_string, size_t i, size_t val) { |
| BitString bs = bit_string; |
| bs.SetAt(i, MakeBitStringChar(i, val)); |
| return bs; |
| } |
| |
| #define EXPECT_BITSTRING_STR(expected_str, actual_value) \ |
| EXPECT_STREQ((expected_str), Stringify((actual_value)).c_str()) |
| |
| // TODO: Consider removing this test, it's kind of replicating the logic in GetLsbForPosition(). |
| TEST(InstanceOfBitString, GetLsbForPosition) { |
| ASSERT_LE(3u, BitString::kCapacity); |
| // Test will fail if kCapacity is not at least 3. Update it. |
| EXPECT_EQ(0u, BitString::GetLsbForPosition(0u)); |
| EXPECT_EQ(BitString::kBitSizeAtPosition[0u], BitString::GetLsbForPosition(1u)); |
| EXPECT_EQ(BitString::kBitSizeAtPosition[0u] + BitString::kBitSizeAtPosition[1u], |
| BitString::GetLsbForPosition(2u)); |
| } |
| |
| TEST(InstanceOfBitString, ToString) { |
| EXPECT_BITSTRING_STR("BitString[]", MakeBitString({0})); |
| EXPECT_BITSTRING_STR("BitString[1]", MakeBitString({1})); |
| EXPECT_BITSTRING_STR("BitString[1,2,3]", MakeBitString({1, 2, 3})); |
| } |
| |
| TEST(InstanceOfBitString, ReadWrite) { |
| BitString bs = MakeBitString(); |
| |
| // Update tests if changing the capacity. |
| ASSERT_EQ(BitString::kCapacity, 3u); |
| |
| EXPECT_BITSTRING_STR("BitString[]", bs); |
| bs = SetBitStringCharAt(bs, /*i=*/0, /*val=*/1u); |
| EXPECT_BITSTRING_STR("BitString[1]", bs); |
| bs = SetBitStringCharAt(bs, /*i=*/1, /*val=*/2u); |
| EXPECT_BITSTRING_STR("BitString[1,2]", bs); |
| bs = SetBitStringCharAt(bs, /*i=*/2, /*val=*/3u); |
| EXPECT_BITSTRING_STR("BitString[1,2,3]", bs); |
| |
| // There should be at least "kCapacity" # of checks here, 1 for each unique position. |
| EXPECT_EQ(MakeBitStringChar(/*idx=*/0, /*val=*/1u), bs[0]); |
| EXPECT_EQ(MakeBitStringChar(/*idx=*/1, /*val=*/2u), bs[1]); |
| EXPECT_EQ(MakeBitStringChar(/*idx=*/2, /*val=*/3u), bs[2]); |
| |
| // Each maximal value should be tested here for each position. |
| uint32_t max_bitstring_ints[] = { |
| MaxInt<uint32_t>(12), |
| MaxInt<uint32_t>(4), |
| MaxInt<uint32_t>(11), |
| }; |
| |
| // Update tests if changing the tuning values above. |
| for (size_t i = 0; i < arraysize(max_bitstring_ints); ++i) { |
| ASSERT_EQ(MinimumBitsToStore(max_bitstring_ints[i]), BitString::kBitSizeAtPosition[i]) << i; |
| } |
| |
| BitString bs_max = MakeBitStringMax(); |
| |
| for (size_t i = 0; i < arraysize(max_bitstring_ints); ++i) { |
| ASSERT_EQ(max_bitstring_ints[i], static_cast<uint32_t>(bs_max[i])) << i; |
| } |
| |
| EXPECT_EQ(MaskLeastSignificant(BitString::GetBitLengthTotalAtPosition(BitString::kCapacity)), |
| AsUint(MakeBitStringMax())); |
| } |
| |
| template <size_t kPos> |
| constexpr auto MaxForPos() { |
| return MaxInt<BitString::StorageType>(BitString::kBitSizeAtPosition[kPos]); |
| } |
| |
| TEST(InstanceOfBitString, MemoryRepresentation) { |
| // Verify that the lower positions are stored in less significant bits. |
| BitString bs = MakeBitString({MaxForPos<0>(), MaxForPos<1>()}); |
| BitString::StorageType as_int = static_cast<BitString::StorageType>(bs); |
| |
| // Below tests assumes the capacity is at least 3. |
| ASSERT_LE(3u, BitString::kCapacity); |
| EXPECT_EQ((MaxForPos<0>() << 0) | (MaxForPos<1>() << BitString::kBitSizeAtPosition[0]), |
| as_int); |
| } |
| |
| TEST(InstanceOfBitString, Truncate) { |
| EXPECT_BITSTRING_STR("BitString[]", MakeBitString({1, 2, 3}).Truncate(0)); |
| EXPECT_BITSTRING_STR("BitString[1]", MakeBitString({1, 2, 3}).Truncate(1)); |
| EXPECT_BITSTRING_STR("BitString[1,2]", MakeBitString({1, 2, 3}).Truncate(2)); |
| EXPECT_BITSTRING_STR("BitString[1,2,3]", MakeBitString({1, 2, 3}).Truncate(3)); |
| } |