diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc b/third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc new file mode 100644 index 0000000000..997abdf573 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/bitstream_reader_unittest.cc @@ -0,0 +1,345 @@ +/* + * Copyright 2021 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/bitstream_reader.h" + +#include <stddef.h> +#include <stdint.h> + +#include <array> +#include <limits> + +#include "absl/numeric/bits.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(BitstreamReaderTest, InDebugModeRequiresToCheckOkStatusBeforeDestruction) { + const uint8_t bytes[32] = {}; + absl::optional<BitstreamReader> reader(absl::in_place, bytes); + + EXPECT_GE(reader->ReadBits(7), 0u); +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(OS_ANDROID) + EXPECT_DEATH(reader = absl::nullopt, ""); +#endif + EXPECT_TRUE(reader->Ok()); + reader = absl::nullopt; +} + +TEST(BitstreamReaderTest, InDebugModeMayCheckRemainingBitsInsteadOfOkStatus) { + const uint8_t bytes[32] = {}; + absl::optional<BitstreamReader> reader(absl::in_place, bytes); + + EXPECT_GE(reader->ReadBit(), 0); +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(OS_ANDROID) + EXPECT_DEATH(reader = absl::nullopt, ""); +#endif + EXPECT_GE(reader->RemainingBitCount(), 0); + reader = absl::nullopt; +} + +TEST(BitstreamReaderTest, ConsumeBits) { + const uint8_t bytes[32] = {}; + BitstreamReader reader(bytes); + + int total_bits = 32 * 8; + EXPECT_EQ(reader.RemainingBitCount(), total_bits); + reader.ConsumeBits(3); + total_bits -= 3; + EXPECT_EQ(reader.RemainingBitCount(), total_bits); + reader.ConsumeBits(3); + total_bits -= 3; + EXPECT_EQ(reader.RemainingBitCount(), total_bits); + reader.ConsumeBits(15); + total_bits -= 15; + EXPECT_EQ(reader.RemainingBitCount(), total_bits); + reader.ConsumeBits(67); + total_bits -= 67; + EXPECT_EQ(reader.RemainingBitCount(), total_bits); + EXPECT_TRUE(reader.Ok()); + + reader.ConsumeBits(32 * 8); + EXPECT_FALSE(reader.Ok()); + EXPECT_LT(reader.RemainingBitCount(), 0); +} + +TEST(BitstreamReaderTest, ConsumeLotsOfBits) { + const uint8_t bytes[1] = {}; + BitstreamReader reader(bytes); + + reader.ConsumeBits(std::numeric_limits<int>::max()); + reader.ConsumeBits(std::numeric_limits<int>::max()); + EXPECT_GE(reader.ReadBit(), 0); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBit) { + const uint8_t bytes[] = {0b0100'0001, 0b1011'0001}; + BitstreamReader reader(bytes); + // First byte. + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 1); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 0); + + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 1); + + // Second byte. + EXPECT_EQ(reader.ReadBit(), 1); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 1); + EXPECT_EQ(reader.ReadBit(), 1); + + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_EQ(reader.ReadBit(), 1); + + EXPECT_TRUE(reader.Ok()); + // Try to read beyound the buffer. + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBoolConsumesSingleBit) { + const uint8_t bytes[] = {0b1010'1010}; + BitstreamReader reader(bytes); + ASSERT_EQ(reader.RemainingBitCount(), 8); + EXPECT_TRUE(reader.Read<bool>()); + EXPECT_EQ(reader.RemainingBitCount(), 7); +} + +TEST(BitstreamReaderTest, ReadBytesAligned) { + const uint8_t bytes[] = {0x0A, // + 0xBC, // + 0xDE, 0xF1, // + 0x23, 0x45, 0x67, 0x89}; + BitstreamReader reader(bytes); + EXPECT_EQ(reader.Read<uint8_t>(), 0x0Au); + EXPECT_EQ(reader.Read<uint8_t>(), 0xBCu); + EXPECT_EQ(reader.Read<uint16_t>(), 0xDEF1u); + EXPECT_EQ(reader.Read<uint32_t>(), 0x23456789u); + EXPECT_TRUE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBytesOffset4) { + const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, + 0x45, 0x67, 0x89, 0x0A}; + BitstreamReader reader(bytes); + reader.ConsumeBits(4); + + EXPECT_EQ(reader.Read<uint8_t>(), 0xABu); + EXPECT_EQ(reader.Read<uint8_t>(), 0xCDu); + EXPECT_EQ(reader.Read<uint16_t>(), 0xEF12u); + EXPECT_EQ(reader.Read<uint32_t>(), 0x34567890u); + EXPECT_TRUE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBytesOffset3) { + // The pattern we'll check against is counting down from 0b1111. It looks + // weird here because it's all offset by 3. + // Byte pattern is: + // 56701234 + // 0b00011111, + // 0b11011011, + // 0b10010111, + // 0b01010011, + // 0b00001110, + // 0b11001010, + // 0b10000110, + // 0b01000010 + // xxxxx <-- last 5 bits unused. + + // The bytes. It almost looks like counting down by two at a time, except the + // jump at 5->3->0, since that's when the high bit is turned off. + const uint8_t bytes[] = {0x1F, 0xDB, 0x97, 0x53, 0x0E, 0xCA, 0x86, 0x42}; + + BitstreamReader reader(bytes); + reader.ConsumeBits(3); + EXPECT_EQ(reader.Read<uint8_t>(), 0xFEu); + EXPECT_EQ(reader.Read<uint16_t>(), 0xDCBAu); + EXPECT_EQ(reader.Read<uint32_t>(), 0x98765432u); + EXPECT_TRUE(reader.Ok()); + + // 5 bits left unread. Not enough to read a uint8_t. + EXPECT_EQ(reader.RemainingBitCount(), 5); + EXPECT_EQ(reader.Read<uint8_t>(), 0); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBits) { + const uint8_t bytes[] = {0b010'01'101, 0b0011'00'1'0}; + BitstreamReader reader(bytes); + EXPECT_EQ(reader.ReadBits(3), 0b010u); + EXPECT_EQ(reader.ReadBits(2), 0b01u); + EXPECT_EQ(reader.ReadBits(7), 0b101'0011u); + EXPECT_EQ(reader.ReadBits(2), 0b00u); + EXPECT_EQ(reader.ReadBits(1), 0b1u); + EXPECT_EQ(reader.ReadBits(1), 0b0u); + EXPECT_TRUE(reader.Ok()); + + EXPECT_EQ(reader.ReadBits(1), 0u); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadZeroBits) { + BitstreamReader reader(rtc::ArrayView<const uint8_t>(nullptr, 0)); + + EXPECT_EQ(reader.ReadBits(0), 0u); + EXPECT_TRUE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBitFromEmptyArray) { + BitstreamReader reader(rtc::ArrayView<const uint8_t>(nullptr, 0)); + + // Trying to read from the empty array shouldn't dereference the pointer, + // i.e. shouldn't crash. + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBitsFromEmptyArray) { + BitstreamReader reader(rtc::ArrayView<const uint8_t>(nullptr, 0)); + + // Trying to read from the empty array shouldn't dereference the pointer, + // i.e. shouldn't crash. + EXPECT_EQ(reader.ReadBits(1), 0u); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadBits64) { + const uint8_t bytes[] = {0x4D, 0x32, 0xAB, 0x54, 0x00, 0xFF, 0xFE, 0x01, + 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89}; + BitstreamReader reader(bytes); + + EXPECT_EQ(reader.ReadBits(33), 0x4D32AB5400FFFE01u >> (64 - 33)); + + constexpr uint64_t kMask31Bits = (1ull << 32) - 1; + EXPECT_EQ(reader.ReadBits(31), 0x4D32AB5400FFFE01ull & kMask31Bits); + + EXPECT_EQ(reader.ReadBits(64), 0xABCDEF0123456789ull); + EXPECT_TRUE(reader.Ok()); + + // Nothing more to read. + EXPECT_EQ(reader.ReadBit(), 0); + EXPECT_FALSE(reader.Ok()); +} + +TEST(BitstreamReaderTest, CanPeekBitsUsingCopyConstructor) { + // BitstreamReader doesn't have peek function. To simulate it, user may use + // cheap BitstreamReader copy constructor. + const uint8_t bytes[] = {0x0A, 0xBC}; + BitstreamReader reader(bytes); + reader.ConsumeBits(4); + ASSERT_EQ(reader.RemainingBitCount(), 12); + + BitstreamReader peeker = reader; + EXPECT_EQ(peeker.ReadBits(8), 0xABu); + EXPECT_EQ(peeker.RemainingBitCount(), 4); + + EXPECT_EQ(reader.RemainingBitCount(), 12); + // Can resume reading from before peeker was created. + EXPECT_EQ(reader.ReadBits(4), 0xAu); + EXPECT_EQ(reader.RemainingBitCount(), 8); +} + +TEST(BitstreamReaderTest, + ReadNonSymmetricSameNumberOfBitsWhenNumValuesPowerOf2) { + const uint8_t bytes[2] = {0xf3, 0xa0}; + BitstreamReader reader(bytes); + + ASSERT_EQ(reader.RemainingBitCount(), 16); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/1 << 4), 0xfu); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/1 << 4), 0x3u); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/1 << 4), 0xau); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/1 << 4), 0x0u); + EXPECT_EQ(reader.RemainingBitCount(), 0); + EXPECT_TRUE(reader.Ok()); +} + +TEST(BitstreamReaderTest, ReadNonSymmetricOnlyValueConsumesZeroBits) { + const uint8_t bytes[2] = {}; + BitstreamReader reader(bytes); + + ASSERT_EQ(reader.RemainingBitCount(), 16); + EXPECT_EQ(reader.ReadNonSymmetric(/*num_values=*/1), 0u); + EXPECT_EQ(reader.RemainingBitCount(), 16); +} + +std::array<uint8_t, 8> GolombEncoded(uint32_t val) { + int val_width = absl::bit_width(val + 1); + int total_width = 2 * val_width - 1; + uint64_t representation = (uint64_t{val} + 1) << (64 - total_width); + std::array<uint8_t, 8> result; + for (int i = 0; i < 8; ++i) { + result[i] = representation >> (7 - i) * 8; + } + return result; +} + +TEST(BitstreamReaderTest, GolombUint32Values) { + // Test over the uint32_t range with a large enough step that the test doesn't + // take forever. Around 20,000 iterations should do. + const int kStep = std::numeric_limits<uint32_t>::max() / 20000; + for (uint32_t i = 0; i < std::numeric_limits<uint32_t>::max() - kStep; + i += kStep) { + std::array<uint8_t, 8> buffer = GolombEncoded(i); + BitstreamReader reader(buffer); + // Use assert instead of EXPECT to avoid spamming thousands of failed + // expectation when this test fails. + ASSERT_EQ(reader.ReadExponentialGolomb(), i); + EXPECT_TRUE(reader.Ok()); + } +} + +TEST(BitstreamReaderTest, SignedGolombValues) { + uint8_t golomb_bits[][1] = { + {0b1'0000000}, {0b010'00000}, {0b011'00000}, {0b00100'000}, {0b00111'000}, + }; + int expected[] = {0, 1, -1, 2, -3}; + for (size_t i = 0; i < sizeof(golomb_bits); ++i) { + BitstreamReader reader(golomb_bits[i]); + EXPECT_EQ(reader.ReadSignedExponentialGolomb(), expected[i]) + << "Mismatch in expected/decoded value for golomb_bits[" << i + << "]: " << static_cast<int>(golomb_bits[i][0]); + EXPECT_TRUE(reader.Ok()); + } +} + +TEST(BitstreamReaderTest, NoGolombOverread) { + const uint8_t bytes[] = {0x00, 0xFF, 0xFF}; + // Make sure the bit buffer correctly enforces byte length on golomb reads. + // If it didn't, the above buffer would be valid at 3 bytes. + BitstreamReader reader1(rtc::MakeArrayView(bytes, 1)); + // When parse fails, `ReadExponentialGolomb` may return any number. + reader1.ReadExponentialGolomb(); + EXPECT_FALSE(reader1.Ok()); + + BitstreamReader reader2(rtc::MakeArrayView(bytes, 2)); + reader2.ReadExponentialGolomb(); + EXPECT_FALSE(reader2.Ok()); + + BitstreamReader reader3(bytes); + // Golomb should have read 9 bits, so 0x01FF, and since it is golomb, the + // result is 0x01FF - 1 = 0x01FE. + EXPECT_EQ(reader3.ReadExponentialGolomb(), 0x01FEu); + EXPECT_TRUE(reader3.Ok()); +} + +} // namespace +} // namespace webrtc |