| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/zucchini/buffer_source.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <iterator> |
| #include <string> |
| #include <tuple> |
| #include <vector> |
| |
| #include "components/zucchini/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace zucchini { |
| |
| using vec = std::vector<uint8_t>; |
| |
| class BufferSourceTest : public testing::Test { |
| protected: |
| std::vector<uint8_t> bytes_ = ParseHexString("10 32 54 76 98 BA DC FE 10 00"); |
| |
| BufferSource source_ = {bytes_.data(), bytes_.size()}; |
| }; |
| |
| TEST_F(BufferSourceTest, Skip) { |
| EXPECT_EQ(bytes_.size(), source_.Remaining()); |
| source_.Skip(2); |
| EXPECT_EQ(bytes_.size() - 2, source_.Remaining()); |
| source_.Skip(10); // Skipping past end just moves cursor to end. |
| EXPECT_EQ(size_t(0), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, CheckNextBytes) { |
| EXPECT_TRUE(source_.CheckNextBytes({0x10, 0x32, 0x54, 0x76})); |
| source_.Skip(4); |
| EXPECT_TRUE(source_.CheckNextBytes({0x98, 0xBA, 0xDC, 0xFE})); |
| |
| // Cursor has not advanced, so check fails. |
| EXPECT_FALSE(source_.CheckNextBytes({0x10, 0x00})); |
| |
| source_.Skip(4); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| |
| // Goes beyond end by 2 bytes. |
| EXPECT_FALSE(source_.CheckNextBytes({0x10, 0x00, 0x00, 0x00})); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, ConsumeBytes) { |
| EXPECT_FALSE(source_.ConsumeBytes({0x10, 0x00})); |
| EXPECT_EQ(bytes_.size(), source_.Remaining()); |
| EXPECT_TRUE(source_.ConsumeBytes({0x10, 0x32, 0x54, 0x76})); |
| EXPECT_EQ(size_t(6), source_.Remaining()); |
| EXPECT_TRUE(source_.ConsumeBytes({0x98, 0xBA, 0xDC, 0xFE})); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| |
| // Goes beyond end by 2 bytes. |
| EXPECT_FALSE(source_.ConsumeBytes({0x10, 0x00, 0x00, 0x00})); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, CheckNextValue) { |
| EXPECT_TRUE(source_.CheckNextValue(uint32_t(0x76543210))); |
| EXPECT_FALSE(source_.CheckNextValue(uint32_t(0x0))); |
| EXPECT_TRUE(source_.CheckNextValue(uint64_t(0xFEDCBA9876543210))); |
| EXPECT_FALSE(source_.CheckNextValue(uint64_t(0x0))); |
| |
| source_.Skip(8); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| |
| // Goes beyond end by 2 bytes. |
| EXPECT_FALSE(source_.CheckNextValue(uint32_t(0x1000))); |
| } |
| |
| // Supported by MSVC, g++, and clang++. |
| // Ensures no gaps in packing. |
| #pragma pack(push, 1) |
| struct ValueType { |
| uint32_t a; |
| uint16_t b; |
| }; |
| #pragma pack(pop) |
| |
| TEST_F(BufferSourceTest, GetValueIntegral) { |
| uint32_t value = 0; |
| EXPECT_TRUE(source_.GetValue(&value)); |
| EXPECT_EQ(uint32_t(0x76543210), value); |
| EXPECT_EQ(size_t(6), source_.Remaining()); |
| |
| EXPECT_TRUE(source_.GetValue(&value)); |
| EXPECT_EQ(uint32_t(0xFEDCBA98), value); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| |
| EXPECT_FALSE(source_.GetValue(&value)); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetValueAggregate) { |
| ValueType value = {}; |
| EXPECT_TRUE(source_.GetValue(&value)); |
| EXPECT_EQ(uint32_t(0x76543210), value.a); |
| EXPECT_EQ(uint32_t(0xBA98), value.b); |
| EXPECT_EQ(size_t(4), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetRegion) { |
| ConstBufferView region; |
| EXPECT_TRUE(source_.GetRegion(0, ®ion)); |
| EXPECT_EQ(bytes_.size(), source_.Remaining()); |
| EXPECT_TRUE(region.empty()); |
| |
| EXPECT_TRUE(source_.GetRegion(2, ®ion)); |
| EXPECT_EQ(size_t(2), region.size()); |
| EXPECT_EQ(vec({0x10, 0x32}), vec(region.begin(), region.end())); |
| EXPECT_EQ(size_t(8), source_.Remaining()); |
| |
| EXPECT_FALSE(source_.GetRegion(bytes_.size(), ®ion)); |
| EXPECT_EQ(size_t(8), source_.Remaining()); |
| // |region| is left untouched. |
| EXPECT_EQ(vec({0x10, 0x32}), vec(region.begin(), region.end())); |
| EXPECT_EQ(size_t(2), region.size()); |
| } |
| |
| TEST_F(BufferSourceTest, GetPointerIntegral) { |
| const uint32_t* ptr = source_.GetPointer<uint32_t>(); |
| EXPECT_NE(nullptr, ptr); |
| EXPECT_EQ(uint32_t(0x76543210), *ptr); |
| EXPECT_EQ(size_t(6), source_.Remaining()); |
| |
| ptr = source_.GetPointer<uint32_t>(); |
| EXPECT_NE(nullptr, ptr); |
| EXPECT_EQ(uint32_t(0xFEDCBA98), *ptr); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| |
| EXPECT_EQ(nullptr, source_.GetPointer<uint32_t>()); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetPointerAggregate) { |
| const ValueType* ptr = source_.GetPointer<ValueType>(); |
| EXPECT_NE(nullptr, ptr); |
| EXPECT_EQ(uint32_t(0x76543210), ptr->a); |
| EXPECT_EQ(uint32_t(0xBA98), ptr->b); |
| EXPECT_EQ(size_t(4), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetArrayIntegral) { |
| EXPECT_EQ(nullptr, source_.GetArray<uint32_t>(3)); |
| |
| const uint32_t* ptr = source_.GetArray<uint32_t>(2); |
| EXPECT_NE(nullptr, ptr); |
| EXPECT_EQ(uint32_t(0x76543210), ptr[0]); |
| EXPECT_EQ(uint32_t(0xFEDCBA98), ptr[1]); |
| EXPECT_EQ(size_t(2), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetArrayAggregate) { |
| const ValueType* ptr = source_.GetArray<ValueType>(2); |
| EXPECT_EQ(nullptr, ptr); |
| |
| ptr = source_.GetArray<ValueType>(1); |
| |
| EXPECT_NE(nullptr, ptr); |
| EXPECT_EQ(uint32_t(0x76543210), ptr[0].a); |
| EXPECT_EQ(uint32_t(0xBA98), ptr[0].b); |
| EXPECT_EQ(size_t(4), source_.Remaining()); |
| } |
| |
| TEST_F(BufferSourceTest, GetUleb128) { |
| using size_type = BufferSource::size_type; |
| // Result = {success, value, bytes_consumed}. |
| using Result = std::tuple<bool, uint32_t, size_type>; |
| |
| constexpr uint32_t kUnInit = 0xCCCCCCCC; // Arbitrary value. |
| constexpr Result kBad{false, kUnInit, 0U}; |
| |
| auto run = [](const std::string hex_string) -> Result { |
| std::vector<uint8_t> bytes = ParseHexString(hex_string); |
| BufferSource source(ConstBufferView{bytes.data(), bytes.size()}); |
| BufferSource::iterator base = source.begin(); |
| // Initialize |value| to |kUnInit| to ensure no write on failure. |
| uint32_t value = kUnInit; |
| bool success = source.GetUleb128(&value); |
| return {success, value, source.begin() - base}; |
| }; |
| |
| auto good = [](uint32_t value, size_type bytes_consumed) -> Result { |
| return Result{true, value, bytes_consumed}; |
| }; |
| |
| EXPECT_EQ(good(0x0U, 1U), run("00")); |
| EXPECT_EQ(good(0x20U, 1U), run("20")); |
| EXPECT_EQ(good(0x42U, 1U), run("42")); |
| EXPECT_EQ(good(0x7FU, 1U), run("7F")); |
| EXPECT_EQ(kBad, run("80")); // Out of data. |
| EXPECT_EQ(good(0x0U, 2U), run("80 00")); // Redundant code. |
| EXPECT_EQ(good(0x80U, 2U), run("80 01")); |
| EXPECT_EQ(good(0x7FU, 2U), run("FF 00")); // Redundant (unsigned). |
| EXPECT_EQ(good(0x3FFFU, 2U), run("FF 7F")); |
| EXPECT_EQ(good(0x0U, 1U), run("00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("F1 88")); // Out of data. |
| EXPECT_EQ(good(0x0U, 3U), run("80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x4000U, 3U), run("80 80 01")); |
| EXPECT_EQ(good(0x00100000U, 3U), run("80 80 40")); |
| EXPECT_EQ(good(0x001FFFFFU, 3U), run("FF FF 7F")); |
| EXPECT_EQ(good(0x0U, 1U), run("00 00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("AB CD EF")); // Out of data. |
| EXPECT_EQ(good(0x0U, 4U), run("80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x00100000U, 4U), run("80 80 C0 00")); |
| EXPECT_EQ(good(0x00200000U, 4U), run("80 80 80 01")); |
| EXPECT_EQ(good(0x08000000U, 4U), run("80 80 80 40")); |
| EXPECT_EQ(good(0x001FC07FU, 4U), run("FF 80 FF 00")); |
| EXPECT_EQ(good(0x0U, 5U), run("80 80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x10000000U, 5U), run("80 80 80 80 01")); |
| EXPECT_EQ(good(0x10204081U, 5U), run("81 81 81 81 01")); |
| EXPECT_EQ(good(0x7FFFFFFFU, 5U), run("FF FF FF FF 07")); |
| EXPECT_EQ(good(0x80000000U, 5U), run("80 80 80 80 08")); |
| EXPECT_EQ(good(0xFFFFFFFFU, 5U), run("FF FF FF FF 0F")); |
| EXPECT_EQ(kBad, run("FF FF FF FF 80")); // Too long / out of data. |
| EXPECT_EQ(good(0x0FFFFFFFU, 5U), run("FF FF FF FF 10")); // "1" discarded. |
| EXPECT_EQ(good(0x00000000U, 5U), run("80 80 80 80 20")); // "2" discarded. |
| EXPECT_EQ(good(0xA54A952AU, 5U), run("AA AA AA AA 7A")); // "7" discarded. |
| EXPECT_EQ(kBad, run("FF FF FF FF FF 00")); // Too long. |
| } |
| |
| TEST_F(BufferSourceTest, GetSleb128) { |
| using size_type = BufferSource::size_type; |
| // Result = {success, value, bytes_consumed}. |
| using Result = std::tuple<bool, int32_t, size_type>; |
| |
| constexpr int32_t kUnInit = 0xCCCCCCCC; // Arbitrary value. |
| constexpr Result kBad{false, kUnInit, 0U}; |
| |
| auto run = [](const std::string hex_string) -> Result { |
| std::vector<uint8_t> bytes = ParseHexString(hex_string); |
| BufferSource source(ConstBufferView{bytes.data(), bytes.size()}); |
| BufferSource::iterator base = source.begin(); |
| // Initialize |value| to |kUnInit| to ensure no write on failure. |
| int32_t value = kUnInit; |
| bool success = source.GetSleb128(&value); |
| return {success, value, source.begin() - base}; |
| }; |
| |
| auto good = [](int32_t value, size_type bytes_consumed) -> Result { |
| return Result{true, value, bytes_consumed}; |
| }; |
| |
| EXPECT_EQ(good(0x0, 1U), run("00")); |
| EXPECT_EQ(good(0x20U, 1U), run("20")); |
| EXPECT_EQ(good(-0x3E, 1U), run("42")); |
| EXPECT_EQ(good(-0x1, 1U), run("7F")); |
| EXPECT_EQ(kBad, run("80")); // Out of data. |
| EXPECT_EQ(good(0x0, 2U), run("80 00")); // Redundant code. |
| EXPECT_EQ(good(0x80, 2U), run("80 01")); |
| EXPECT_EQ(good(0x7F, 2U), run("FF 00")); // Not redudnant. |
| EXPECT_EQ(good(-0x1, 2U), run("FF 7F")); // Redundant code. |
| EXPECT_EQ(good(0x0, 1U), run("00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("F1 88")); // Out of data. |
| EXPECT_EQ(good(0x0, 3U), run("80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x4000, 3U), run("80 80 01")); |
| EXPECT_EQ(good(-0x100000, 3U), run("80 80 40")); |
| EXPECT_EQ(good(-0x1, 3U), run("FF FF 7F")); // Redundant code. |
| EXPECT_EQ(good(0x0, 1U), run("00 00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("AB CD EF")); // Out of data. |
| EXPECT_EQ(good(0x0, 4U), run("80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x00100000, 4U), run("80 80 C0 00")); |
| EXPECT_EQ(good(0x00200000, 4U), run("80 80 80 01")); |
| EXPECT_EQ(good(-static_cast<int32_t>(0x08000000), 4U), run("80 80 80 40")); |
| EXPECT_EQ(good(0x001FC07F, 4U), run("FF 80 FF 00")); |
| EXPECT_EQ(good(0x0, 5U), run("80 80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(0x10000000, 5U), run("80 80 80 80 01")); |
| EXPECT_EQ(good(0x10204081, 5U), run("81 81 81 81 01")); |
| EXPECT_EQ(good(0x7FFFFFFF, 5U), run("FF FF FF FF 07")); |
| EXPECT_EQ(good(-static_cast<int32_t>(0x80000000), 5U), run("80 80 80 80 08")); |
| EXPECT_EQ(good(-0x1, 5U), run("FF FF FF FF 0F")); // Redundant code. |
| EXPECT_EQ(kBad, run("FF FF FF FF 80")); // Too long / out of data. |
| EXPECT_EQ(good(0x0FFFFFFF, 5U), run("FF FF FF FF 10")); // "1" discarded. |
| EXPECT_EQ(good(0x00000000, 5U), run("80 80 80 80 20")); // "2" discarded. |
| EXPECT_EQ(good(-0x5AB56AD6, 5U), run("AA AA AA AA 7A")); // "7" discarded. |
| EXPECT_EQ(kBad, run("FF FF FF FF FF 00")); // Too long. |
| } |
| |
| TEST_F(BufferSourceTest, SkipLeb128) { |
| using size_type = BufferSource::size_type; |
| // Result = {success, value, bytes_consumed}. |
| using Result = std::tuple<bool, size_type>; |
| |
| constexpr Result kBad{false, 0U}; |
| |
| auto run = [](const std::string hex_string) -> Result { |
| std::vector<uint8_t> bytes = ParseHexString(hex_string); |
| BufferSource source(ConstBufferView{bytes.data(), bytes.size()}); |
| BufferSource::iterator base = source.begin(); |
| bool success = source.SkipLeb128(); |
| return {success, source.begin() - base}; |
| }; |
| |
| auto good = [](size_type bytes_consumed) -> Result { |
| return Result{true, bytes_consumed}; |
| }; |
| |
| EXPECT_EQ(good(1U), run("00")); |
| EXPECT_EQ(good(1U), run("20")); |
| EXPECT_EQ(good(1U), run("42")); |
| EXPECT_EQ(good(1U), run("7F")); |
| EXPECT_EQ(kBad, run("80")); // Out of data. |
| EXPECT_EQ(good(2U), run("80 00")); // Redundant code. |
| EXPECT_EQ(good(2U), run("80 01")); |
| EXPECT_EQ(good(2U), run("FF 00")); // Redundant (unsigned). |
| EXPECT_EQ(good(2U), run("FF 7F")); |
| EXPECT_EQ(good(1U), run("00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("F1 88")); // Out of data. |
| EXPECT_EQ(good(3U), run("80 80 00")); // Redundant code. |
| EXPECT_EQ(good(3U), run("80 80 01")); |
| EXPECT_EQ(good(3U), run("80 80 40")); |
| EXPECT_EQ(good(3U), run("FF FF 7F")); |
| EXPECT_EQ(good(1U), run("00 00 80")); // Only reads byte 0. |
| EXPECT_EQ(kBad, run("80 80 80")); // Out of data. |
| EXPECT_EQ(kBad, run("AB CD EF")); // Out of data. |
| EXPECT_EQ(good(4U), run("80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(4U), run("80 80 C0 00")); |
| EXPECT_EQ(good(4U), run("80 80 80 01")); |
| EXPECT_EQ(good(4U), run("80 80 80 40")); |
| EXPECT_EQ(good(4U), run("FF 80 FF 00")); |
| EXPECT_EQ(good(5U), run("80 80 80 80 00")); // Redundant code. |
| EXPECT_EQ(good(5U), run("80 80 80 80 01")); |
| EXPECT_EQ(good(5U), run("81 81 81 81 01")); |
| EXPECT_EQ(good(5U), run("FF FF FF FF 07")); |
| EXPECT_EQ(good(5U), run("80 80 80 80 08")); |
| EXPECT_EQ(good(5U), run("FF FF FF FF 0F")); |
| EXPECT_EQ(kBad, run("FF FF FF FF 80")); // Too long / out of data. |
| EXPECT_EQ(good(5U), run("FF FF FF FF 10")); // "1" discarded. |
| EXPECT_EQ(good(5U), run("80 80 80 80 20")); // "2" discarded. |
| EXPECT_EQ(good(5U), run("AA AA AA AA 7A")); // "7" discarded. |
| EXPECT_EQ(kBad, run("FF FF FF FF FF 00")); // Too long. |
| } |
| |
| } // namespace zucchini |