1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/utils/array_2d.h"
16
17 #include <cstdint>
18 #include <memory>
19 #include <new>
20 #include <type_traits>
21
22 #include "gtest/gtest.h"
23 #include "src/utils/compiler_attributes.h"
24
25 #if LIBGAV1_MSAN
26 #include <sanitizer/msan_interface.h>
27 #endif
28
29 namespace libgav1 {
30 namespace {
31
32 constexpr int kRows = 50;
33 constexpr int kColumns = 200;
34
TEST(Array2dViewTest,TestUint8)35 TEST(Array2dViewTest, TestUint8) {
36 uint8_t data[kRows * kColumns] = {};
37 Array2DView<uint8_t> data2d(kRows, kColumns, data);
38
39 // Verify data.
40 data[kColumns] = 100;
41 data[kColumns + 1] = 101;
42 data[kColumns * 2 + 10] = 210;
43 data[kColumns * 2 + 40] = 240;
44 EXPECT_EQ(data2d[1][0], 100);
45 EXPECT_EQ(data2d[1][1], 101);
46 EXPECT_EQ(data2d[2][10], 210);
47 EXPECT_EQ(data2d[2][40], 240);
48
49 // Verify pointers.
50 EXPECT_EQ(data2d[10], data + 10 * kColumns);
51 }
52
TEST(Array2dViewTest,TestUint16)53 TEST(Array2dViewTest, TestUint16) {
54 uint16_t data[kRows * kColumns] = {};
55 Array2DView<uint16_t> data2d(kRows, kColumns, data);
56
57 // Verify data.
58 data[kColumns] = 100;
59 data[kColumns + 1] = 101;
60 data[kColumns * 2 + 10] = 210;
61 data[kColumns * 2 + 40] = 240;
62 EXPECT_EQ(data2d[1][0], 100);
63 EXPECT_EQ(data2d[1][1], 101);
64 EXPECT_EQ(data2d[2][10], 210);
65 EXPECT_EQ(data2d[2][40], 240);
66
67 // Verify pointers.
68 EXPECT_EQ(data2d[10], data + 10 * kColumns);
69 }
70
TEST(Array2dViewTest,TestUint8Const)71 TEST(Array2dViewTest, TestUint8Const) {
72 uint8_t data[kRows * kColumns] = {};
73 // Declared as const to provide a read-only view of |data|.
74 const Array2DView<uint8_t> data2d(kRows, kColumns, data);
75
76 // Verify data.
77 data[kColumns] = 100;
78 data[kColumns + 1] = 101;
79 data[kColumns * 2 + 10] = 210;
80 data[kColumns * 2 + 40] = 240;
81 EXPECT_EQ(data2d[1][0], 100);
82 EXPECT_EQ(data2d[1][1], 101);
83 EXPECT_EQ(data2d[2][10], 210);
84 EXPECT_EQ(data2d[2][40], 240);
85
86 // Verify pointers.
87 EXPECT_EQ(data2d[10], data + 10 * kColumns);
88 }
89
TEST(Array2dTest,TestUint8)90 TEST(Array2dTest, TestUint8) {
91 Array2D<uint8_t> data2d;
92 ASSERT_TRUE(data2d.Reset(kRows, kColumns, true));
93
94 EXPECT_EQ(data2d.rows(), kRows);
95 EXPECT_EQ(data2d.columns(), kColumns);
96
97 // Verify pointers.
98 for (int i = 0; i < kRows; ++i) {
99 EXPECT_NE(data2d[i], nullptr);
100 }
101
102 // Verify data (must be zero initialized).
103 for (int i = 0; i < kRows; ++i) {
104 for (int j = 0; j < kColumns; ++j) {
105 EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]";
106 }
107 }
108
109 // Reset to a 2d array of smaller size with zero_initialize == false.
110 data2d[0][0] = 10;
111 ASSERT_TRUE(data2d.Reset(kRows - 1, kColumns - 1, false));
112
113 EXPECT_EQ(data2d.rows(), kRows - 1);
114 EXPECT_EQ(data2d.columns(), kColumns - 1);
115
116 // Verify pointers.
117 for (int i = 0; i < kRows - 1; ++i) {
118 EXPECT_NE(data2d[i], nullptr);
119 }
120
121 // Verify data (must be zero except for 0,0 because it was zero initialized in
122 // the previous call to Reset).
123 for (int i = 0; i < kRows - 1; ++i) {
124 for (int j = 0; j < kColumns - 1; ++j) {
125 if (i == 0 && j == 0) {
126 EXPECT_EQ(data2d[i][j], 10) << "Mismatch in [" << i << "][" << j << "]";
127 } else {
128 EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]";
129 }
130 }
131 }
132
133 // Reset to a 2d array of smaller size with zero_initialize == true.
134 ASSERT_TRUE(data2d.Reset(kRows - 2, kColumns - 2, true));
135
136 EXPECT_EQ(data2d.rows(), kRows - 2);
137 EXPECT_EQ(data2d.columns(), kColumns - 2);
138
139 // Verify pointers.
140 for (int i = 0; i < kRows - 2; ++i) {
141 EXPECT_NE(data2d[i], nullptr);
142 }
143
144 // Verify data (must be zero initialized).
145 for (int i = 0; i < kRows - 2; ++i) {
146 for (int j = 0; j < kColumns - 2; ++j) {
147 EXPECT_EQ(data2d[i][j], 0) << "Mismatch in [" << i << "][" << j << "]";
148 }
149 }
150 }
151
TEST(Array2dTest,TestUniquePtr1)152 TEST(Array2dTest, TestUniquePtr1) {
153 // A simple class that sets an int value to 0 in the destructor.
154 class Cleaner {
155 public:
156 explicit Cleaner(int* value) : value_(value) {}
157 ~Cleaner() { *value_ = 0; }
158
159 private:
160 int* value_;
161 };
162 int value = 100;
163 Array2D<std::unique_ptr<Cleaner>> data2d;
164 ASSERT_TRUE(data2d.Reset(4, 4, true));
165 data2d[0][0].reset(new (std::nothrow) Cleaner(&value));
166 EXPECT_EQ(value, 100);
167 // Reset to a smaller size. Depending on the implementation, the data_ buffer
168 // may or may not be reused.
169 ASSERT_TRUE(data2d.Reset(2, 2, true));
170 // Reset to a much larger size. The data_ buffer will be reallocated.
171 ASSERT_TRUE(data2d.Reset(32, 32, true));
172 // The destructors of all elements in the former data_ buffer should have
173 // been invoked.
174 EXPECT_EQ(value, 0);
175 }
176
TEST(Array2dTest,TestUniquePtr2)177 TEST(Array2dTest, TestUniquePtr2) {
178 // A simple class that sets an int value to 0 in the destructor.
179 class Cleaner {
180 public:
181 explicit Cleaner(int* value) : value_(value) {}
182 ~Cleaner() { *value_ = 0; }
183
184 private:
185 int* value_;
186 };
187 int value1 = 100;
188 int value2 = 200;
189 Array2D<std::unique_ptr<Cleaner>> data2d;
190 ASSERT_TRUE(data2d.Reset(4, 4, false));
191 data2d[0][0].reset(new (std::nothrow) Cleaner(&value1));
192 data2d[3][3].reset(new (std::nothrow) Cleaner(&value2));
193 EXPECT_EQ(value1, 100);
194 EXPECT_EQ(value2, 200);
195 // Reset to a smaller size. Whether or not the data_ buffer is reused, the
196 // destructors of all existing elements should be invoked.
197 ASSERT_TRUE(data2d.Reset(2, 2, false));
198 EXPECT_EQ(value1, 0);
199 EXPECT_EQ(value2, 0);
200 }
201
202 // Shows that std::is_standard_layout is not relevant to the default
203 // initialization vs. value initialization issue, but std::is_trivial is.
TEST(Array2dTest,TestStructInit)204 TEST(Array2dTest, TestStructInit) {
205 // Make one data member private so that this struct does not have a standard
206 // layout. This also makes the struct not a POD type.
207 struct Point {
208 int x;
209 int Y() const { return y; }
210
211 private:
212 int y;
213 };
214
215 EXPECT_TRUE(std::is_trivial<Point>::value);
216 EXPECT_FALSE(std::is_standard_layout<Point>::value);
217
218 // The Point structs in this array are default initialized.
219 Array2D<Point> data2d_default_init;
220 ASSERT_TRUE(data2d_default_init.Reset(kRows, kColumns, false));
221 // The Point structs in this array are value initialized (i.e., zero
222 // initialized).
223 Array2D<Point> data2d;
224 ASSERT_TRUE(data2d.Reset(kRows, kColumns, true));
225
226 #if LIBGAV1_MSAN
227 // Use MemorySanitizer to check Reset(rows, columns, false) does not
228 // initialize the memory while Reset(rows, columns, true) does.
229 //
230 // __msan_test_shadow(const void *x, uptr size) returns the offset of the
231 // first (at least partially) poisoned byte in the range, or -1 if the whole
232 // range is good.
233 for (int i = 0; i < kRows; ++i) {
234 EXPECT_EQ(__msan_test_shadow(data2d_default_init[i],
235 sizeof(data2d_default_init[0][0]) * kColumns),
236 0);
237 EXPECT_EQ(__msan_test_shadow(data2d[i], sizeof(data2d[0][0]) * kColumns),
238 -1);
239 for (int j = 0; j < kColumns; ++j) {
240 EXPECT_EQ(data2d[i][j].x, 0);
241 EXPECT_EQ(data2d[i][j].Y(), 0);
242 }
243 }
244 #endif
245 }
246
247 } // namespace
248 } // namespace libgav1
249