1bc0d697dSNico Weber #include "test_helpers.h"
2bc0d697dSNico Weber #include "xray_segmented_array.h"
3bc0d697dSNico Weber #include "gmock/gmock.h"
4bc0d697dSNico Weber #include "gtest/gtest.h"
5bc0d697dSNico Weber #include <algorithm>
6bc0d697dSNico Weber #include <numeric>
7bc0d697dSNico Weber #include <vector>
8bc0d697dSNico Weber
9bc0d697dSNico Weber namespace __xray {
10bc0d697dSNico Weber namespace {
11bc0d697dSNico Weber
12bc0d697dSNico Weber using ::testing::SizeIs;
13bc0d697dSNico Weber
14bc0d697dSNico Weber struct TestData {
15bc0d697dSNico Weber s64 First;
16bc0d697dSNico Weber s64 Second;
17bc0d697dSNico Weber
18bc0d697dSNico Weber // Need a constructor for emplace operations.
TestData__xray::__anon467c9eb30111::TestData19bc0d697dSNico Weber TestData(s64 F, s64 S) : First(F), Second(S) {}
20bc0d697dSNico Weber };
21bc0d697dSNico Weber
PrintTo(const TestData & D,std::ostream * OS)22bc0d697dSNico Weber void PrintTo(const TestData &D, std::ostream *OS) {
23bc0d697dSNico Weber *OS << "{ " << D.First << ", " << D.Second << " }";
24bc0d697dSNico Weber }
25bc0d697dSNico Weber
TEST(SegmentedArrayTest,ConstructWithAllocators)26bc0d697dSNico Weber TEST(SegmentedArrayTest, ConstructWithAllocators) {
27bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
28bc0d697dSNico Weber AllocatorType A(1 << 4);
29bc0d697dSNico Weber Array<TestData> Data(A);
30bc0d697dSNico Weber (void)Data;
31bc0d697dSNico Weber }
32bc0d697dSNico Weber
TEST(SegmentedArrayTest,ConstructAndPopulate)33bc0d697dSNico Weber TEST(SegmentedArrayTest, ConstructAndPopulate) {
34bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
35bc0d697dSNico Weber AllocatorType A(1 << 4);
36bc0d697dSNico Weber Array<TestData> data(A);
37bc0d697dSNico Weber ASSERT_NE(data.Append(TestData{0, 0}), nullptr);
38bc0d697dSNico Weber ASSERT_NE(data.Append(TestData{1, 1}), nullptr);
39bc0d697dSNico Weber ASSERT_EQ(data.size(), 2u);
40bc0d697dSNico Weber }
41bc0d697dSNico Weber
TEST(SegmentedArrayTest,ConstructPopulateAndLookup)42bc0d697dSNico Weber TEST(SegmentedArrayTest, ConstructPopulateAndLookup) {
43bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
44bc0d697dSNico Weber AllocatorType A(1 << 4);
45bc0d697dSNico Weber Array<TestData> data(A);
46bc0d697dSNico Weber ASSERT_NE(data.Append(TestData{0, 1}), nullptr);
47bc0d697dSNico Weber ASSERT_EQ(data.size(), 1u);
48bc0d697dSNico Weber ASSERT_EQ(data[0].First, 0);
49bc0d697dSNico Weber ASSERT_EQ(data[0].Second, 1);
50bc0d697dSNico Weber }
51bc0d697dSNico Weber
TEST(SegmentedArrayTest,PopulateWithMoreElements)52bc0d697dSNico Weber TEST(SegmentedArrayTest, PopulateWithMoreElements) {
53bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
54bc0d697dSNico Weber AllocatorType A(1 << 24);
55bc0d697dSNico Weber Array<TestData> data(A);
56bc0d697dSNico Weber static const auto kMaxElements = 100u;
57bc0d697dSNico Weber for (auto I = 0u; I < kMaxElements; ++I) {
58bc0d697dSNico Weber ASSERT_NE(data.Append(TestData{I, I + 1}), nullptr);
59bc0d697dSNico Weber }
60bc0d697dSNico Weber ASSERT_EQ(data.size(), kMaxElements);
61bc0d697dSNico Weber for (auto I = 0u; I < kMaxElements; ++I) {
62bc0d697dSNico Weber ASSERT_EQ(data[I].First, I);
63bc0d697dSNico Weber ASSERT_EQ(data[I].Second, I + 1);
64bc0d697dSNico Weber }
65bc0d697dSNico Weber }
66bc0d697dSNico Weber
TEST(SegmentedArrayTest,AppendEmplace)67bc0d697dSNico Weber TEST(SegmentedArrayTest, AppendEmplace) {
68bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
69bc0d697dSNico Weber AllocatorType A(1 << 4);
70bc0d697dSNico Weber Array<TestData> data(A);
71bc0d697dSNico Weber ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
72bc0d697dSNico Weber ASSERT_EQ(data[0].First, 1);
73bc0d697dSNico Weber ASSERT_EQ(data[0].Second, 1);
74bc0d697dSNico Weber }
75bc0d697dSNico Weber
TEST(SegmentedArrayTest,AppendAndTrim)76bc0d697dSNico Weber TEST(SegmentedArrayTest, AppendAndTrim) {
77bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
78bc0d697dSNico Weber AllocatorType A(1 << 4);
79bc0d697dSNico Weber Array<TestData> data(A);
80bc0d697dSNico Weber ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
81bc0d697dSNico Weber ASSERT_EQ(data.size(), 1u);
82bc0d697dSNico Weber data.trim(1);
83bc0d697dSNico Weber ASSERT_EQ(data.size(), 0u);
84bc0d697dSNico Weber ASSERT_TRUE(data.empty());
85bc0d697dSNico Weber }
86bc0d697dSNico Weber
TEST(SegmentedArrayTest,IteratorAdvance)87bc0d697dSNico Weber TEST(SegmentedArrayTest, IteratorAdvance) {
88bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
89bc0d697dSNico Weber AllocatorType A(1 << 4);
90bc0d697dSNico Weber Array<TestData> data(A);
91bc0d697dSNico Weber ASSERT_TRUE(data.empty());
92bc0d697dSNico Weber ASSERT_EQ(data.begin(), data.end());
93bc0d697dSNico Weber auto I0 = data.begin();
94bc0d697dSNico Weber ASSERT_EQ(I0++, data.begin());
95bc0d697dSNico Weber ASSERT_NE(I0, data.begin());
96bc0d697dSNico Weber for (const auto &D : data) {
97bc0d697dSNico Weber (void)D;
98bc0d697dSNico Weber FAIL();
99bc0d697dSNico Weber }
100bc0d697dSNico Weber ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
101bc0d697dSNico Weber ASSERT_EQ(data.size(), 1u);
102bc0d697dSNico Weber ASSERT_NE(data.begin(), data.end());
103bc0d697dSNico Weber auto &D0 = *data.begin();
104bc0d697dSNico Weber ASSERT_EQ(D0.First, 1);
105bc0d697dSNico Weber ASSERT_EQ(D0.Second, 1);
106bc0d697dSNico Weber }
107bc0d697dSNico Weber
TEST(SegmentedArrayTest,IteratorRetreat)108bc0d697dSNico Weber TEST(SegmentedArrayTest, IteratorRetreat) {
109bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
110bc0d697dSNico Weber AllocatorType A(1 << 4);
111bc0d697dSNico Weber Array<TestData> data(A);
112bc0d697dSNico Weber ASSERT_TRUE(data.empty());
113bc0d697dSNico Weber ASSERT_EQ(data.begin(), data.end());
114bc0d697dSNico Weber ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
115bc0d697dSNico Weber ASSERT_EQ(data.size(), 1u);
116bc0d697dSNico Weber ASSERT_NE(data.begin(), data.end());
117bc0d697dSNico Weber auto &D0 = *data.begin();
118bc0d697dSNico Weber ASSERT_EQ(D0.First, 1);
119bc0d697dSNico Weber ASSERT_EQ(D0.Second, 1);
120bc0d697dSNico Weber
121bc0d697dSNico Weber auto I0 = data.end();
122bc0d697dSNico Weber ASSERT_EQ(I0--, data.end());
123bc0d697dSNico Weber ASSERT_NE(I0, data.end());
124bc0d697dSNico Weber ASSERT_EQ(I0, data.begin());
125bc0d697dSNico Weber ASSERT_EQ(I0->First, 1);
126bc0d697dSNico Weber ASSERT_EQ(I0->Second, 1);
127bc0d697dSNico Weber }
128bc0d697dSNico Weber
TEST(SegmentedArrayTest,IteratorTrimBehaviour)129bc0d697dSNico Weber TEST(SegmentedArrayTest, IteratorTrimBehaviour) {
130bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
131bc0d697dSNico Weber AllocatorType A(1 << 20);
132bc0d697dSNico Weber Array<TestData> Data(A);
133bc0d697dSNico Weber ASSERT_TRUE(Data.empty());
134bc0d697dSNico Weber auto I0Begin = Data.begin(), I0End = Data.end();
135bc0d697dSNico Weber // Add enough elements in Data to have more than one chunk.
136bc0d697dSNico Weber constexpr auto Segment = Array<TestData>::SegmentSize;
137bc0d697dSNico Weber constexpr auto SegmentX2 = Segment * 2;
138bc0d697dSNico Weber for (auto i = SegmentX2; i > 0u; --i) {
139bc0d697dSNico Weber Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i));
140bc0d697dSNico Weber }
141bc0d697dSNico Weber ASSERT_EQ(Data.size(), SegmentX2);
142bc0d697dSNico Weber {
143bc0d697dSNico Weber auto &Back = Data.back();
144bc0d697dSNico Weber ASSERT_EQ(Back.First, 1);
145bc0d697dSNico Weber ASSERT_EQ(Back.Second, 1);
146bc0d697dSNico Weber }
147bc0d697dSNico Weber
148bc0d697dSNico Weber // Trim one chunk's elements worth.
149bc0d697dSNico Weber Data.trim(Segment);
150bc0d697dSNico Weber ASSERT_EQ(Data.size(), Segment);
151bc0d697dSNico Weber
152bc0d697dSNico Weber // Check that we are still able to access 'back' properly.
153bc0d697dSNico Weber {
154bc0d697dSNico Weber auto &Back = Data.back();
155bc0d697dSNico Weber ASSERT_EQ(Back.First, static_cast<s64>(Segment + 1));
156bc0d697dSNico Weber ASSERT_EQ(Back.Second, static_cast<s64>(Segment + 1));
157bc0d697dSNico Weber }
158bc0d697dSNico Weber
159bc0d697dSNico Weber // Then trim until it's empty.
160bc0d697dSNico Weber Data.trim(Segment);
161bc0d697dSNico Weber ASSERT_TRUE(Data.empty());
162bc0d697dSNico Weber
163bc0d697dSNico Weber // Here our iterators should be the same.
164bc0d697dSNico Weber auto I1Begin = Data.begin(), I1End = Data.end();
165bc0d697dSNico Weber EXPECT_EQ(I0Begin, I1Begin);
166bc0d697dSNico Weber EXPECT_EQ(I0End, I1End);
167bc0d697dSNico Weber
168bc0d697dSNico Weber // Then we ensure that adding elements back works just fine.
169bc0d697dSNico Weber for (auto i = SegmentX2; i > 0u; --i) {
170bc0d697dSNico Weber Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i));
171bc0d697dSNico Weber }
172bc0d697dSNico Weber EXPECT_EQ(Data.size(), SegmentX2);
173bc0d697dSNico Weber }
174bc0d697dSNico Weber
TEST(SegmentedArrayTest,HandleExhaustedAllocator)175bc0d697dSNico Weber TEST(SegmentedArrayTest, HandleExhaustedAllocator) {
176bc0d697dSNico Weber using AllocatorType = typename Array<TestData>::AllocatorType;
177bc0d697dSNico Weber constexpr auto Segment = Array<TestData>::SegmentSize;
178bc0d697dSNico Weber constexpr auto MaxElements = Array<TestData>::ElementsPerSegment;
179bc0d697dSNico Weber AllocatorType A(Segment);
180bc0d697dSNico Weber Array<TestData> Data(A);
181bc0d697dSNico Weber for (auto i = MaxElements; i > 0u; --i)
182bc0d697dSNico Weber EXPECT_NE(Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i)),
183bc0d697dSNico Weber nullptr);
184bc0d697dSNico Weber EXPECT_EQ(Data.AppendEmplace(0, 0), nullptr);
185bc0d697dSNico Weber EXPECT_THAT(Data, SizeIs(MaxElements));
186bc0d697dSNico Weber
187bc0d697dSNico Weber // Trimming more elements than there are in the container should be fine.
188bc0d697dSNico Weber Data.trim(MaxElements + 1);
189bc0d697dSNico Weber EXPECT_THAT(Data, SizeIs(0u));
190bc0d697dSNico Weber }
191bc0d697dSNico Weber
192bc0d697dSNico Weber struct ShadowStackEntry {
193bc0d697dSNico Weber uint64_t EntryTSC = 0;
194bc0d697dSNico Weber uint64_t *NodePtr = nullptr;
ShadowStackEntry__xray::__anon467c9eb30111::ShadowStackEntry195bc0d697dSNico Weber ShadowStackEntry(uint64_t T, uint64_t *N) : EntryTSC(T), NodePtr(N) {}
196bc0d697dSNico Weber };
197bc0d697dSNico Weber
TEST(SegmentedArrayTest,SimulateStackBehaviour)198bc0d697dSNico Weber TEST(SegmentedArrayTest, SimulateStackBehaviour) {
199bc0d697dSNico Weber using AllocatorType = typename Array<ShadowStackEntry>::AllocatorType;
200bc0d697dSNico Weber AllocatorType A(1 << 10);
201bc0d697dSNico Weber Array<ShadowStackEntry> Data(A);
202bc0d697dSNico Weber static uint64_t Dummy = 0;
203bc0d697dSNico Weber constexpr uint64_t Max = 9;
204bc0d697dSNico Weber
205bc0d697dSNico Weber for (uint64_t i = 0; i < Max; ++i) {
206bc0d697dSNico Weber auto P = Data.Append({i, &Dummy});
207bc0d697dSNico Weber ASSERT_NE(P, nullptr);
208bc0d697dSNico Weber ASSERT_EQ(P->NodePtr, &Dummy);
209bc0d697dSNico Weber auto &Back = Data.back();
210bc0d697dSNico Weber ASSERT_EQ(Back.NodePtr, &Dummy);
211bc0d697dSNico Weber ASSERT_EQ(Back.EntryTSC, i);
212bc0d697dSNico Weber }
213bc0d697dSNico Weber
214bc0d697dSNico Weber // Simulate a stack by checking the data from the end as we're trimming.
215bc0d697dSNico Weber auto Counter = Max;
216bc0d697dSNico Weber ASSERT_EQ(Data.size(), size_t(Max));
217bc0d697dSNico Weber while (!Data.empty()) {
218bc0d697dSNico Weber const auto &Top = Data.back();
219bc0d697dSNico Weber uint64_t *TopNode = Top.NodePtr;
220bc0d697dSNico Weber EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter;
221bc0d697dSNico Weber Data.trim(1);
222bc0d697dSNico Weber --Counter;
223bc0d697dSNico Weber ASSERT_EQ(Data.size(), size_t(Counter));
224bc0d697dSNico Weber }
225bc0d697dSNico Weber }
226bc0d697dSNico Weber
TEST(SegmentedArrayTest,PlacementNewOnAlignedStorage)227bc0d697dSNico Weber TEST(SegmentedArrayTest, PlacementNewOnAlignedStorage) {
228bc0d697dSNico Weber using AllocatorType = typename Array<ShadowStackEntry>::AllocatorType;
229*cac78214SMarc Auberer alignas(AllocatorType) std::byte AllocatorStorage[sizeof(AllocatorType)];
230bc0d697dSNico Weber new (&AllocatorStorage) AllocatorType(1 << 10);
231bc0d697dSNico Weber auto *A = reinterpret_cast<AllocatorType *>(&AllocatorStorage);
232*cac78214SMarc Auberer alignas(Array<ShadowStackEntry>)
233*cac78214SMarc Auberer std::byte ArrayStorage[sizeof(Array<ShadowStackEntry>)];
234bc0d697dSNico Weber new (&ArrayStorage) Array<ShadowStackEntry>(*A);
235bc0d697dSNico Weber auto *Data = reinterpret_cast<Array<ShadowStackEntry> *>(&ArrayStorage);
236bc0d697dSNico Weber
237bc0d697dSNico Weber static uint64_t Dummy = 0;
238bc0d697dSNico Weber constexpr uint64_t Max = 9;
239bc0d697dSNico Weber
240bc0d697dSNico Weber for (uint64_t i = 0; i < Max; ++i) {
241bc0d697dSNico Weber auto P = Data->Append({i, &Dummy});
242bc0d697dSNico Weber ASSERT_NE(P, nullptr);
243bc0d697dSNico Weber ASSERT_EQ(P->NodePtr, &Dummy);
244bc0d697dSNico Weber auto &Back = Data->back();
245bc0d697dSNico Weber ASSERT_EQ(Back.NodePtr, &Dummy);
246bc0d697dSNico Weber ASSERT_EQ(Back.EntryTSC, i);
247bc0d697dSNico Weber }
248bc0d697dSNico Weber
249bc0d697dSNico Weber // Simulate a stack by checking the data from the end as we're trimming.
250bc0d697dSNico Weber auto Counter = Max;
251bc0d697dSNico Weber ASSERT_EQ(Data->size(), size_t(Max));
252bc0d697dSNico Weber while (!Data->empty()) {
253bc0d697dSNico Weber const auto &Top = Data->back();
254bc0d697dSNico Weber uint64_t *TopNode = Top.NodePtr;
255bc0d697dSNico Weber EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter;
256bc0d697dSNico Weber Data->trim(1);
257bc0d697dSNico Weber --Counter;
258bc0d697dSNico Weber ASSERT_EQ(Data->size(), size_t(Counter));
259bc0d697dSNico Weber }
260bc0d697dSNico Weber
261bc0d697dSNico Weber // Once the stack is exhausted, we re-use the storage.
262bc0d697dSNico Weber for (uint64_t i = 0; i < Max; ++i) {
263bc0d697dSNico Weber auto P = Data->Append({i, &Dummy});
264bc0d697dSNico Weber ASSERT_NE(P, nullptr);
265bc0d697dSNico Weber ASSERT_EQ(P->NodePtr, &Dummy);
266bc0d697dSNico Weber auto &Back = Data->back();
267bc0d697dSNico Weber ASSERT_EQ(Back.NodePtr, &Dummy);
268bc0d697dSNico Weber ASSERT_EQ(Back.EntryTSC, i);
269bc0d697dSNico Weber }
270bc0d697dSNico Weber
271bc0d697dSNico Weber // We re-initialize the storage, by calling the destructor and
272bc0d697dSNico Weber // placement-new'ing again.
273bc0d697dSNico Weber Data->~Array();
274bc0d697dSNico Weber A->~AllocatorType();
275bc0d697dSNico Weber new (A) AllocatorType(1 << 10);
276bc0d697dSNico Weber new (Data) Array<ShadowStackEntry>(*A);
277bc0d697dSNico Weber
278bc0d697dSNico Weber // Then re-do the test.
279bc0d697dSNico Weber for (uint64_t i = 0; i < Max; ++i) {
280bc0d697dSNico Weber auto P = Data->Append({i, &Dummy});
281bc0d697dSNico Weber ASSERT_NE(P, nullptr);
282bc0d697dSNico Weber ASSERT_EQ(P->NodePtr, &Dummy);
283bc0d697dSNico Weber auto &Back = Data->back();
284bc0d697dSNico Weber ASSERT_EQ(Back.NodePtr, &Dummy);
285bc0d697dSNico Weber ASSERT_EQ(Back.EntryTSC, i);
286bc0d697dSNico Weber }
287bc0d697dSNico Weber
288bc0d697dSNico Weber // Simulate a stack by checking the data from the end as we're trimming.
289bc0d697dSNico Weber Counter = Max;
290bc0d697dSNico Weber ASSERT_EQ(Data->size(), size_t(Max));
291bc0d697dSNico Weber while (!Data->empty()) {
292bc0d697dSNico Weber const auto &Top = Data->back();
293bc0d697dSNico Weber uint64_t *TopNode = Top.NodePtr;
294bc0d697dSNico Weber EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter;
295bc0d697dSNico Weber Data->trim(1);
296bc0d697dSNico Weber --Counter;
297bc0d697dSNico Weber ASSERT_EQ(Data->size(), size_t(Counter));
298bc0d697dSNico Weber }
299bc0d697dSNico Weber
300bc0d697dSNico Weber // Once the stack is exhausted, we re-use the storage.
301bc0d697dSNico Weber for (uint64_t i = 0; i < Max; ++i) {
302bc0d697dSNico Weber auto P = Data->Append({i, &Dummy});
303bc0d697dSNico Weber ASSERT_NE(P, nullptr);
304bc0d697dSNico Weber ASSERT_EQ(P->NodePtr, &Dummy);
305bc0d697dSNico Weber auto &Back = Data->back();
306bc0d697dSNico Weber ASSERT_EQ(Back.NodePtr, &Dummy);
307bc0d697dSNico Weber ASSERT_EQ(Back.EntryTSC, i);
308bc0d697dSNico Weber }
309bc0d697dSNico Weber }
310bc0d697dSNico Weber
TEST(SegmentedArrayTest,ArrayOfPointersIteratorAccess)311bc0d697dSNico Weber TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccess) {
312bc0d697dSNico Weber using PtrArray = Array<int *>;
313bc0d697dSNico Weber PtrArray::AllocatorType Alloc(16384);
314bc0d697dSNico Weber Array<int *> A(Alloc);
315bc0d697dSNico Weber static constexpr size_t Count = 100;
316bc0d697dSNico Weber std::vector<int> Integers(Count);
317bc0d697dSNico Weber std::iota(Integers.begin(), Integers.end(), 0);
318bc0d697dSNico Weber for (auto &I : Integers)
319bc0d697dSNico Weber ASSERT_NE(A.Append(&I), nullptr);
320bc0d697dSNico Weber int V = 0;
321bc0d697dSNico Weber ASSERT_EQ(A.size(), Count);
322bc0d697dSNico Weber for (auto P : A) {
323bc0d697dSNico Weber ASSERT_NE(P, nullptr);
324bc0d697dSNico Weber ASSERT_EQ(*P, V++);
325bc0d697dSNico Weber }
326bc0d697dSNico Weber }
327bc0d697dSNico Weber
TEST(SegmentedArrayTest,ArrayOfPointersIteratorAccessExhaustion)328bc0d697dSNico Weber TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccessExhaustion) {
329bc0d697dSNico Weber using PtrArray = Array<int *>;
330bc0d697dSNico Weber PtrArray::AllocatorType Alloc(4096);
331bc0d697dSNico Weber Array<int *> A(Alloc);
332bc0d697dSNico Weber static constexpr size_t Count = 1000;
333bc0d697dSNico Weber std::vector<int> Integers(Count);
334bc0d697dSNico Weber std::iota(Integers.begin(), Integers.end(), 0);
335bc0d697dSNico Weber for (auto &I : Integers)
336bc0d697dSNico Weber if (A.Append(&I) == nullptr)
337bc0d697dSNico Weber break;
338bc0d697dSNico Weber int V = 0;
339bc0d697dSNico Weber ASSERT_LT(A.size(), Count);
340bc0d697dSNico Weber for (auto P : A) {
341bc0d697dSNico Weber ASSERT_NE(P, nullptr);
342bc0d697dSNico Weber ASSERT_EQ(*P, V++);
343bc0d697dSNico Weber }
344bc0d697dSNico Weber }
345bc0d697dSNico Weber
346bc0d697dSNico Weber } // namespace
347bc0d697dSNico Weber } // namespace __xray
348