1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/base/small_vector.h"
18
19 #include <tuple>
20 #include <utility>
21
22 #include "test/gtest_and_gmock.h"
23
24 namespace perfetto {
25 namespace base {
26 namespace {
27
28 int g_instances = 0;
29
30 struct Obj {
Objperfetto::base::__anoncb840ffe0111::Obj31 explicit Obj(size_t v = 0) : value(v) {
32 EXPECT_FALSE(constructed);
33 constructed = true;
34 g_instances++;
35 }
36
~Objperfetto::base::__anoncb840ffe0111::Obj37 ~Obj() {
38 EXPECT_TRUE(constructed);
39 g_instances--;
40 }
41
42 // Move operators.
Objperfetto::base::__anoncb840ffe0111::Obj43 Obj(Obj&& other) noexcept {
44 g_instances++;
45 constructed = true;
46 moved_into = true;
47 value = other.value;
48 other.moved_from = true;
49 other.value = 0xffffffff - value;
50 }
51
operator =perfetto::base::__anoncb840ffe0111::Obj52 Obj& operator=(Obj&& other) noexcept {
53 this->~Obj();
54 new (this) Obj(std::move(other));
55 return *this;
56 }
57
58 // Copy operators.
Objperfetto::base::__anoncb840ffe0111::Obj59 Obj(const Obj& other) {
60 other.copied_from = true;
61 g_instances++;
62 constructed = true;
63 copied_into = true;
64 value = other.value;
65 }
66
operator =perfetto::base::__anoncb840ffe0111::Obj67 Obj& operator=(const Obj& other) {
68 this->~Obj();
69 new (this) Obj(other);
70 return *this;
71 }
72
73 uintptr_t addr = reinterpret_cast<uintptr_t>(this);
74 bool constructed = false;
75 size_t value = 0;
76 bool moved_from = false;
77 mutable bool copied_from = false;
78 bool moved_into = false;
79 bool copied_into = false;
80 };
81
TEST(SmallVectorTest,StaySmall)82 TEST(SmallVectorTest, StaySmall) {
83 SmallVector<Obj, 8> v;
84 EXPECT_EQ(g_instances, 0);
85 EXPECT_EQ(v.size(), 0u);
86 EXPECT_TRUE(v.empty());
87 EXPECT_EQ(v.begin(), v.end());
88
89 for (size_t i = 1; i <= 8; i++) {
90 v.emplace_back(i);
91 EXPECT_EQ(g_instances, static_cast<int>(i));
92 EXPECT_FALSE(v.empty());
93 EXPECT_EQ(v.end(), v.begin() + i);
94 EXPECT_EQ(v.back().value, i);
95 EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i);
96 EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i);
97 }
98
99 for (size_t i = 1; i <= 3; i++) {
100 v.pop_back();
101 EXPECT_EQ(g_instances, 8 - static_cast<int>(i));
102 }
103
104 v.clear();
105 EXPECT_EQ(g_instances, 0);
106 }
107
TEST(SmallVectorTest,GrowOnHeap)108 TEST(SmallVectorTest, GrowOnHeap) {
109 SmallVector<Obj, 4> v;
110 for (size_t i = 0; i < 10; i++) {
111 v.emplace_back(i);
112 EXPECT_EQ(g_instances, static_cast<int>(i + 1));
113 EXPECT_FALSE(v.empty());
114 EXPECT_EQ(v.end(), v.begin() + i + 1);
115 EXPECT_EQ(v[i].value, i);
116 }
117
118 // Do a second pass and check that the initial elements aren't corrupt.
119 for (size_t i = 0; i < 10; i++) {
120 EXPECT_EQ(v[i].value, i);
121 EXPECT_TRUE(v[i].constructed);
122 }
123
124 // The first 4 elements must have been moved into because of the heap growth.
125 for (size_t i = 0; i < 4; i++)
126 EXPECT_TRUE(v[i].moved_into);
127 EXPECT_FALSE(v.back().moved_into);
128 }
129
130 class SmallVectorTestP : public testing::TestWithParam<size_t> {};
131
TEST_P(SmallVectorTestP,MoveOperators)132 TEST_P(SmallVectorTestP, MoveOperators) {
133 size_t num_elements = GetParam();
134 static constexpr size_t kInlineCapacity = 4;
135 SmallVector<Obj, kInlineCapacity> v1;
136 for (size_t i = 0; i < num_elements; i++)
137 v1.emplace_back(i);
138
139 SmallVector<Obj, kInlineCapacity> v2(std::move(v1));
140 EXPECT_TRUE(v1.empty());
141 EXPECT_EQ(v2.size(), num_elements);
142
143 // Check that v2 (the moved into vector) is consistent.
144 for (size_t i = 0; i < num_elements; i++) {
145 EXPECT_EQ(v2[i].value, i);
146 EXPECT_TRUE(v2[i].constructed);
147 if (num_elements <= kInlineCapacity) {
148 EXPECT_TRUE(v2[i].moved_into);
149 }
150 }
151
152 // Check that v1 (the moved-from object) is still usable.
153 EXPECT_EQ(v1.size(), 0u);
154
155 for (size_t i = 0; i < num_elements; i++) {
156 v1.emplace_back(1000 + i);
157 EXPECT_EQ(v1.size(), i + 1);
158 }
159
160 EXPECT_NE(v1.data(), v2.data());
161
162 for (size_t i = 0; i < num_elements; i++) {
163 EXPECT_EQ(v1[i].value, 1000 + i);
164 EXPECT_EQ(v2[i].value, i);
165 EXPECT_TRUE(v1[i].constructed);
166 EXPECT_FALSE(v1[i].moved_from);
167 }
168
169 // Now swap again using the move-assignment.
170
171 v1 = std::move(v2);
172 EXPECT_EQ(v1.size(), num_elements);
173 EXPECT_TRUE(v2.empty());
174 for (size_t i = 0; i < num_elements; i++) {
175 EXPECT_EQ(v1[i].value, i);
176 EXPECT_TRUE(v1[i].constructed);
177 }
178
179 { auto destroy = std::move(v1); }
180
181 EXPECT_EQ(g_instances, 0);
182 }
183
TEST_P(SmallVectorTestP,CopyOperators)184 TEST_P(SmallVectorTestP, CopyOperators) {
185 size_t num_elements = GetParam();
186 static constexpr size_t kInlineCapacity = 4;
187 SmallVector<Obj, kInlineCapacity> v1;
188 for (size_t i = 0; i < num_elements; i++)
189 v1.emplace_back(i);
190
191 SmallVector<Obj, kInlineCapacity> v2(v1);
192 EXPECT_EQ(v1.size(), num_elements);
193 EXPECT_EQ(v2.size(), num_elements);
194 EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2));
195
196 for (size_t i = 0; i < num_elements; i++) {
197 EXPECT_EQ(v1[i].value, i);
198 EXPECT_TRUE(v1[i].copied_from);
199 EXPECT_EQ(v2[i].value, i);
200 EXPECT_TRUE(v2[i].copied_into);
201 }
202
203 // Now edit v2.
204 for (size_t i = 0; i < num_elements; i++)
205 v2[i].value = i + 100;
206 EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2));
207
208 // Append some extra elements.
209 for (size_t i = 0; i < num_elements; i++)
210 v2.emplace_back(i + 200);
211 EXPECT_EQ(g_instances, static_cast<int>(num_elements * 3));
212
213 for (size_t i = 0; i < num_elements * 2; i++) {
214 if (i < num_elements) {
215 EXPECT_EQ(v1[i].value, i);
216 EXPECT_EQ(v2[i].value, 100 + i);
217 } else {
218 EXPECT_EQ(v2[i].value, 200 + i - num_elements);
219 }
220 }
221
222 v2.clear();
223 EXPECT_EQ(g_instances, static_cast<int>(num_elements));
224 }
225
226 INSTANTIATE_TEST_SUITE_P(SmallVectorTest,
227 SmallVectorTestP,
228 testing::Values(2, 4, 7, 512));
229
230 } // namespace
231 } // namespace base
232 } // namespace perfetto
233