// Copyright 2021 The libgav1 Authors // // 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 "src/utils/array_2d.h" #include #include #include #include #include "gtest/gtest.h" #include "src/utils/compiler_attributes.h" #if LIBGAV1_MSAN #include #endif namespace libgav1 { namespace { constexpr int kRows = 50; constexpr int kColumns = 200; TEST(Array2dViewTest, TestUint8) { uint8_t data[kRows * kColumns] = {}; Array2DView data2d(kRows, kColumns, data); // Verify data. data[kColumns] = 100; data[kColumns + 1] = 101; data[kColumns * 2 + 10] = 210; data[kColumns * 2 + 40] = 240; EXPECT_EQ(data2d[1][0], 100); EXPECT_EQ(data2d[1][1], 101); EXPECT_EQ(data2d[2][10], 210); EXPECT_EQ(data2d[2][40], 240); // Verify pointers. EXPECT_EQ(data2d[10], data + 10 * kColumns); } TEST(Array2dViewTest, TestUint16) { uint16_t data[kRows * kColumns] = {}; Array2DView data2d(kRows, kColumns, data); // Verify data. data[kColumns] = 100; data[kColumns + 1] = 101; data[kColumns * 2 + 10] = 210; data[kColumns * 2 + 40] = 240; EXPECT_EQ(data2d[1][0], 100); EXPECT_EQ(data2d[1][1], 101); EXPECT_EQ(data2d[2][10], 210); EXPECT_EQ(data2d[2][40], 240); // Verify pointers. EXPECT_EQ(data2d[10], data + 10 * kColumns); } TEST(Array2dViewTest, TestUint8Const) { uint8_t data[kRows * kColumns] = {}; // Declared as const to provide a read-only view of |data|. const Array2DView data2d(kRows, kColumns, data); // Verify data. data[kColumns] = 100; data[kColumns + 1] = 101; data[kColumns * 2 + 10] = 210; data[kColumns * 2 + 40] = 240; EXPECT_EQ(data2d[1][0], 100); EXPECT_EQ(data2d[1][1], 101); EXPECT_EQ(data2d[2][10], 210); EXPECT_EQ(data2d[2][40], 240); // Verify pointers. EXPECT_EQ(data2d[10], data + 10 * kColumns); } TEST(Array2dTest, TestUint8) { Array2D data2d; ASSERT_TRUE(data2d.Reset(kRows, kColumns, true)); EXPECT_EQ(data2d.rows(), kRows); EXPECT_EQ(data2d.columns(), kColumns); // Verify pointers. for (int i = 0; i < kRows; ++i) { EXPECT_NE(data2d[i], nullptr); } // Verify data (must be zero initialized). for (int i = 0; i < kRows; ++i) { for (int j = 0; j < kColumns; ++j) { EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; } } // Reset to a 2d array of smaller size with zero_initialize == false. data2d[0][0] = 10; ASSERT_TRUE(data2d.Reset(kRows - 1, kColumns - 1, false)); EXPECT_EQ(data2d.rows(), kRows - 1); EXPECT_EQ(data2d.columns(), kColumns - 1); // Verify pointers. for (int i = 0; i < kRows - 1; ++i) { EXPECT_NE(data2d[i], nullptr); } // Verify data (must be zero except for 0,0 because it was zero initialized in // the previous call to Reset). for (int i = 0; i < kRows - 1; ++i) { for (int j = 0; j < kColumns - 1; ++j) { if (i == 0 && j == 0) { EXPECT_EQ(data2d[i][j], 10) << "Mismatch in [" << i << "][" << j << "]"; } else { EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; } } } // Reset to a 2d array of smaller size with zero_initialize == true. ASSERT_TRUE(data2d.Reset(kRows - 2, kColumns - 2, true)); EXPECT_EQ(data2d.rows(), kRows - 2); EXPECT_EQ(data2d.columns(), kColumns - 2); // Verify pointers. for (int i = 0; i < kRows - 2; ++i) { EXPECT_NE(data2d[i], nullptr); } // Verify data (must be zero initialized). for (int i = 0; i < kRows - 2; ++i) { for (int j = 0; j < kColumns - 2; ++j) { EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]"; } } } TEST(Array2dTest, TestUniquePtr1) { // A simple class that sets an int value to 0 in the destructor. class Cleaner { public: explicit Cleaner(int* value) : value_(value) {} ~Cleaner() { *value_ = 0; } private: int* value_; }; int value = 100; Array2D> data2d; ASSERT_TRUE(data2d.Reset(4, 4, true)); data2d[0][0].reset(new (std::nothrow) Cleaner(&value)); EXPECT_EQ(value, 100); // Reset to a smaller size. Depending on the implementation, the data_ buffer // may or may not be reused. ASSERT_TRUE(data2d.Reset(2, 2, true)); // Reset to a much larger size. The data_ buffer will be reallocated. ASSERT_TRUE(data2d.Reset(32, 32, true)); // The destructors of all elements in the former data_ buffer should have // been invoked. EXPECT_EQ(value, 0); } TEST(Array2dTest, TestUniquePtr2) { // A simple class that sets an int value to 0 in the destructor. class Cleaner { public: explicit Cleaner(int* value) : value_(value) {} ~Cleaner() { *value_ = 0; } private: int* value_; }; int value1 = 100; int value2 = 200; Array2D> data2d; ASSERT_TRUE(data2d.Reset(4, 4, false)); data2d[0][0].reset(new (std::nothrow) Cleaner(&value1)); data2d[3][3].reset(new (std::nothrow) Cleaner(&value2)); EXPECT_EQ(value1, 100); EXPECT_EQ(value2, 200); // Reset to a smaller size. Whether or not the data_ buffer is reused, the // destructors of all existing elements should be invoked. ASSERT_TRUE(data2d.Reset(2, 2, false)); EXPECT_EQ(value1, 0); EXPECT_EQ(value2, 0); } // Shows that std::is_standard_layout is not relevant to the default // initialization vs. value initialization issue, but std::is_trivial is. TEST(Array2dTest, TestStructInit) { // Make one data member private so that this struct does not have a standard // layout. This also makes the struct not a POD type. struct Point { int x; int Y() const { return y; } private: int y; }; EXPECT_TRUE(std::is_trivial::value); EXPECT_FALSE(std::is_standard_layout::value); // The Point structs in this array are default initialized. Array2D data2d_default_init; ASSERT_TRUE(data2d_default_init.Reset(kRows, kColumns, false)); // The Point structs in this array are value initialized (i.e., zero // initialized). Array2D data2d; ASSERT_TRUE(data2d.Reset(kRows, kColumns, true)); #if LIBGAV1_MSAN // Use MemorySanitizer to check Reset(rows, columns, false) does not // initialize the memory while Reset(rows, columns, true) does. // // __msan_test_shadow(const void *x, uptr size) returns the offset of the // first (at least partially) poisoned byte in the range, or -1 if the whole // range is good. for (int i = 0; i < kRows; ++i) { EXPECT_EQ(__msan_test_shadow(data2d_default_init[i], sizeof(data2d_default_init[0][0]) * kColumns), 0); EXPECT_EQ(__msan_test_shadow(data2d[i], sizeof(data2d[0][0]) * kColumns), -1); for (int j = 0; j < kColumns; ++j) { EXPECT_EQ(data2d[i][j].x, 0); EXPECT_EQ(data2d[i][j].Y(), 0); } } #endif } } // namespace } // namespace libgav1