xref: /llvm-project/llvm/unittests/ADT/TrieRawHashMapTest.cpp (revision b510cdb895b9188e5819c4c85a6dab22a4d14385)
1*b510cdb8SSteven Wu //===- TrieRawHashMapTest.cpp ---------------------------------------------===//
2*b510cdb8SSteven Wu //
3*b510cdb8SSteven Wu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*b510cdb8SSteven Wu // See https://llvm.org/LICENSE.txt for license information.
5*b510cdb8SSteven Wu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*b510cdb8SSteven Wu //
7*b510cdb8SSteven Wu //===----------------------------------------------------------------------===//
8*b510cdb8SSteven Wu 
9*b510cdb8SSteven Wu #include "llvm/ADT/TrieRawHashMap.h"
10*b510cdb8SSteven Wu #include "llvm/ADT/Twine.h"
11*b510cdb8SSteven Wu #include "llvm/Support/Endian.h"
12*b510cdb8SSteven Wu #include "llvm/Support/SHA1.h"
13*b510cdb8SSteven Wu #include "gtest/gtest.h"
14*b510cdb8SSteven Wu 
15*b510cdb8SSteven Wu using namespace llvm;
16*b510cdb8SSteven Wu 
17*b510cdb8SSteven Wu namespace llvm {
18*b510cdb8SSteven Wu class TrieRawHashMapTestHelper {
19*b510cdb8SSteven Wu public:
20*b510cdb8SSteven Wu   TrieRawHashMapTestHelper() = default;
21*b510cdb8SSteven Wu 
22*b510cdb8SSteven Wu   void setTrie(ThreadSafeTrieRawHashMapBase *T) { Trie = T; }
23*b510cdb8SSteven Wu 
24*b510cdb8SSteven Wu   ThreadSafeTrieRawHashMapBase::PointerBase getRoot() const {
25*b510cdb8SSteven Wu     return Trie->getRoot();
26*b510cdb8SSteven Wu   }
27*b510cdb8SSteven Wu   unsigned getStartBit(ThreadSafeTrieRawHashMapBase::PointerBase P) const {
28*b510cdb8SSteven Wu     return Trie->getStartBit(P);
29*b510cdb8SSteven Wu   }
30*b510cdb8SSteven Wu   unsigned getNumBits(ThreadSafeTrieRawHashMapBase::PointerBase P) const {
31*b510cdb8SSteven Wu     return Trie->getNumBits(P);
32*b510cdb8SSteven Wu   }
33*b510cdb8SSteven Wu   unsigned getNumSlotUsed(ThreadSafeTrieRawHashMapBase::PointerBase P) const {
34*b510cdb8SSteven Wu     return Trie->getNumSlotUsed(P);
35*b510cdb8SSteven Wu   }
36*b510cdb8SSteven Wu   unsigned getNumTries() const { return Trie->getNumTries(); }
37*b510cdb8SSteven Wu   std::string
38*b510cdb8SSteven Wu   getTriePrefixAsString(ThreadSafeTrieRawHashMapBase::PointerBase P) const {
39*b510cdb8SSteven Wu     return Trie->getTriePrefixAsString(P);
40*b510cdb8SSteven Wu   }
41*b510cdb8SSteven Wu   ThreadSafeTrieRawHashMapBase::PointerBase
42*b510cdb8SSteven Wu   getNextTrie(ThreadSafeTrieRawHashMapBase::PointerBase P) const {
43*b510cdb8SSteven Wu     return Trie->getNextTrie(P);
44*b510cdb8SSteven Wu   }
45*b510cdb8SSteven Wu 
46*b510cdb8SSteven Wu private:
47*b510cdb8SSteven Wu   ThreadSafeTrieRawHashMapBase *Trie = nullptr;
48*b510cdb8SSteven Wu };
49*b510cdb8SSteven Wu } // namespace llvm
50*b510cdb8SSteven Wu 
51*b510cdb8SSteven Wu namespace {
52*b510cdb8SSteven Wu template <typename DataType, size_t HashSize = sizeof(uint64_t)>
53*b510cdb8SSteven Wu class SimpleTrieHashMapTest : public TrieRawHashMapTestHelper,
54*b510cdb8SSteven Wu                               public ::testing::Test {
55*b510cdb8SSteven Wu public:
56*b510cdb8SSteven Wu   using NumType = DataType;
57*b510cdb8SSteven Wu   using HashType = std::array<uint8_t, HashSize>;
58*b510cdb8SSteven Wu   using TrieType = ThreadSafeTrieRawHashMap<DataType, sizeof(HashType)>;
59*b510cdb8SSteven Wu 
60*b510cdb8SSteven Wu   TrieType &createTrie(size_t RootBits, size_t SubtrieBits) {
61*b510cdb8SSteven Wu     auto &Ret = Trie.emplace(RootBits, SubtrieBits);
62*b510cdb8SSteven Wu     TrieRawHashMapTestHelper::setTrie(&Ret);
63*b510cdb8SSteven Wu     return Ret;
64*b510cdb8SSteven Wu   }
65*b510cdb8SSteven Wu 
66*b510cdb8SSteven Wu   void destroyTrie() { Trie.reset(); }
67*b510cdb8SSteven Wu   ~SimpleTrieHashMapTest() { destroyTrie(); }
68*b510cdb8SSteven Wu 
69*b510cdb8SSteven Wu   // Use the number itself as hash to test the pathological case.
70*b510cdb8SSteven Wu   static HashType hash(uint64_t Num) {
71*b510cdb8SSteven Wu     uint64_t HashN =
72*b510cdb8SSteven Wu         llvm::support::endian::byte_swap(Num, llvm::endianness::big);
73*b510cdb8SSteven Wu     HashType Hash;
74*b510cdb8SSteven Wu     memcpy(&Hash[0], &HashN, sizeof(HashType));
75*b510cdb8SSteven Wu     return Hash;
76*b510cdb8SSteven Wu   };
77*b510cdb8SSteven Wu 
78*b510cdb8SSteven Wu private:
79*b510cdb8SSteven Wu   std::optional<TrieType> Trie;
80*b510cdb8SSteven Wu };
81*b510cdb8SSteven Wu 
82*b510cdb8SSteven Wu using SmallNodeTrieTest = SimpleTrieHashMapTest<uint64_t>;
83*b510cdb8SSteven Wu 
84*b510cdb8SSteven Wu TEST_F(SmallNodeTrieTest, TrieAllocation) {
85*b510cdb8SSteven Wu   NumType Numbers[] = {
86*b510cdb8SSteven Wu       0x0, std::numeric_limits<NumType>::max(),      0x1, 0x2,
87*b510cdb8SSteven Wu       0x3, std::numeric_limits<NumType>::max() - 1u,
88*b510cdb8SSteven Wu   };
89*b510cdb8SSteven Wu 
90*b510cdb8SSteven Wu   unsigned ExpectedTries[] = {
91*b510cdb8SSteven Wu       1,       // Allocate Root.
92*b510cdb8SSteven Wu       1,       // Both on the root.
93*b510cdb8SSteven Wu       64,      // 0 and 1 sinks all the way down.
94*b510cdb8SSteven Wu       64,      // no new allocation needed.
95*b510cdb8SSteven Wu       65,      // need a new node between 2 and 3.
96*b510cdb8SSteven Wu       65 + 63, // 63 new allocation to sink two big numbers all the way.
97*b510cdb8SSteven Wu   };
98*b510cdb8SSteven Wu 
99*b510cdb8SSteven Wu   const char *ExpectedPrefix[] = {
100*b510cdb8SSteven Wu       "", // Root.
101*b510cdb8SSteven Wu       "", // Root.
102*b510cdb8SSteven Wu       "00000000000000[0000000]",
103*b510cdb8SSteven Wu       "00000000000000[0000000]",
104*b510cdb8SSteven Wu       "00000000000000[0000001]",
105*b510cdb8SSteven Wu       "ffffffffffffff[1111111]",
106*b510cdb8SSteven Wu   };
107*b510cdb8SSteven Wu 
108*b510cdb8SSteven Wu   // Use root and subtrie sizes of 1 so this gets sunk quite deep.
109*b510cdb8SSteven Wu   auto &Trie = createTrie(/*RootBits=*/1, /*SubtrieBits=*/1);
110*b510cdb8SSteven Wu 
111*b510cdb8SSteven Wu   for (unsigned I = 0; I < 6; ++I) {
112*b510cdb8SSteven Wu     // Lookup first to exercise hint code for deep tries.
113*b510cdb8SSteven Wu     TrieType::pointer Lookup = Trie.find(hash(Numbers[I]));
114*b510cdb8SSteven Wu     EXPECT_FALSE(Lookup);
115*b510cdb8SSteven Wu 
116*b510cdb8SSteven Wu     Trie.insert(Lookup, TrieType::value_type(hash(Numbers[I]), Numbers[I]));
117*b510cdb8SSteven Wu     EXPECT_EQ(getNumTries(), ExpectedTries[I]);
118*b510cdb8SSteven Wu     EXPECT_EQ(getTriePrefixAsString(getNextTrie(getRoot())), ExpectedPrefix[I]);
119*b510cdb8SSteven Wu   }
120*b510cdb8SSteven Wu }
121*b510cdb8SSteven Wu 
122*b510cdb8SSteven Wu TEST_F(SmallNodeTrieTest, TrieStructure) {
123*b510cdb8SSteven Wu   NumType Numbers[] = {
124*b510cdb8SSteven Wu       // Three numbers that will nest deeply to test (1) sinking subtries and
125*b510cdb8SSteven Wu       // (2) deep, non-trivial hints.
126*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max(),
127*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max() - 2u,
128*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max() - 3u,
129*b510cdb8SSteven Wu       // One number to stay at the top-level.
130*b510cdb8SSteven Wu       0x37,
131*b510cdb8SSteven Wu   };
132*b510cdb8SSteven Wu 
133*b510cdb8SSteven Wu   // Use root and subtrie sizes of 1 so this gets sunk quite deep.
134*b510cdb8SSteven Wu   auto &Trie = createTrie(/*RootBits=*/1, /*SubtrieBits=*/1);
135*b510cdb8SSteven Wu 
136*b510cdb8SSteven Wu   for (NumType N : Numbers) {
137*b510cdb8SSteven Wu     // Lookup first to exercise hint code for deep tries.
138*b510cdb8SSteven Wu     TrieType::pointer Lookup = Trie.find(hash(N));
139*b510cdb8SSteven Wu     EXPECT_FALSE(Lookup);
140*b510cdb8SSteven Wu 
141*b510cdb8SSteven Wu     Trie.insert(Lookup, TrieType::value_type(hash(N), N));
142*b510cdb8SSteven Wu   }
143*b510cdb8SSteven Wu   for (NumType N : Numbers) {
144*b510cdb8SSteven Wu     TrieType::pointer Lookup = Trie.find(hash(N));
145*b510cdb8SSteven Wu     EXPECT_TRUE(Lookup);
146*b510cdb8SSteven Wu     if (!Lookup)
147*b510cdb8SSteven Wu       continue;
148*b510cdb8SSteven Wu     EXPECT_EQ(hash(N), Lookup->Hash);
149*b510cdb8SSteven Wu     EXPECT_EQ(N, Lookup->Data);
150*b510cdb8SSteven Wu 
151*b510cdb8SSteven Wu     // Confirm a subsequent insertion fails to overwrite by trying to insert a
152*b510cdb8SSteven Wu     // bad value.
153*b510cdb8SSteven Wu     auto Result = Trie.insert(Lookup, TrieType::value_type(hash(N), N - 1));
154*b510cdb8SSteven Wu     EXPECT_EQ(N, Result->Data);
155*b510cdb8SSteven Wu   }
156*b510cdb8SSteven Wu 
157*b510cdb8SSteven Wu   // Check the trie so we can confirm the structure is correct. Each subtrie
158*b510cdb8SSteven Wu   // should have 2 slots. The root's index=0 should have the content for
159*b510cdb8SSteven Wu   // 0x37 directly, and index=1 should be a linked-list of subtries, finally
160*b510cdb8SSteven Wu   // ending with content for (max-2) and (max-3).
161*b510cdb8SSteven Wu   //
162*b510cdb8SSteven Wu   // Note: This structure is not exhaustive (too expensive to update tests),
163*b510cdb8SSteven Wu   // but it does test that the dump format is somewhat readable and that the
164*b510cdb8SSteven Wu   // basic structure is correct.
165*b510cdb8SSteven Wu   //
166*b510cdb8SSteven Wu   // Note: This test requires that the trie reads bytes starting from index 0
167*b510cdb8SSteven Wu   // of the array of uint8_t, and then reads each byte's bits from high to low.
168*b510cdb8SSteven Wu 
169*b510cdb8SSteven Wu   // Check the Trie.
170*b510cdb8SSteven Wu   // We should allocated a total of 64 SubTries for 64 bit hash.
171*b510cdb8SSteven Wu   ASSERT_EQ(getNumTries(), 64u);
172*b510cdb8SSteven Wu   // Check the root trie. Two slots and both are used.
173*b510cdb8SSteven Wu   ASSERT_EQ(getNumSlotUsed(getRoot()), 2u);
174*b510cdb8SSteven Wu   // Check last subtrie.
175*b510cdb8SSteven Wu   // Last allocated trie is the next node in the allocation chain.
176*b510cdb8SSteven Wu   auto LastAlloctedSubTrie = getNextTrie(getRoot());
177*b510cdb8SSteven Wu   ASSERT_EQ(getTriePrefixAsString(LastAlloctedSubTrie),
178*b510cdb8SSteven Wu             "ffffffffffffff[1111110]");
179*b510cdb8SSteven Wu   ASSERT_EQ(getStartBit(LastAlloctedSubTrie), 63u);
180*b510cdb8SSteven Wu   ASSERT_EQ(getNumBits(LastAlloctedSubTrie), 1u);
181*b510cdb8SSteven Wu   ASSERT_EQ(getNumSlotUsed(LastAlloctedSubTrie), 2u);
182*b510cdb8SSteven Wu }
183*b510cdb8SSteven Wu 
184*b510cdb8SSteven Wu TEST_F(SmallNodeTrieTest, TrieStructureSmallFinalSubtrie) {
185*b510cdb8SSteven Wu   NumType Numbers[] = {
186*b510cdb8SSteven Wu       // Three numbers that will nest deeply to test (1) sinking subtries and
187*b510cdb8SSteven Wu       // (2) deep, non-trivial hints.
188*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max(),
189*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max() - 2u,
190*b510cdb8SSteven Wu       std::numeric_limits<NumType>::max() - 3u,
191*b510cdb8SSteven Wu       // One number to stay at the top-level.
192*b510cdb8SSteven Wu       0x37,
193*b510cdb8SSteven Wu   };
194*b510cdb8SSteven Wu 
195*b510cdb8SSteven Wu   // Use subtrie size of 5 to avoid hitting 64 evenly, making the final subtrie
196*b510cdb8SSteven Wu   // small.
197*b510cdb8SSteven Wu   auto &Trie = createTrie(/*RootBits=*/8, /*SubtrieBits=*/5);
198*b510cdb8SSteven Wu 
199*b510cdb8SSteven Wu   for (NumType N : Numbers) {
200*b510cdb8SSteven Wu     // Lookup first to exercise hint code for deep tries.
201*b510cdb8SSteven Wu     TrieType::pointer Lookup = Trie.find(hash(N));
202*b510cdb8SSteven Wu     EXPECT_FALSE(Lookup);
203*b510cdb8SSteven Wu 
204*b510cdb8SSteven Wu     Trie.insert(Lookup, TrieType::value_type(hash(N), N));
205*b510cdb8SSteven Wu   }
206*b510cdb8SSteven Wu   for (NumType N : Numbers) {
207*b510cdb8SSteven Wu     TrieType::pointer Lookup = Trie.find(hash(N));
208*b510cdb8SSteven Wu     ASSERT_TRUE(Lookup);
209*b510cdb8SSteven Wu     EXPECT_EQ(hash(N), Lookup->Hash);
210*b510cdb8SSteven Wu     EXPECT_EQ(N, Lookup->Data);
211*b510cdb8SSteven Wu 
212*b510cdb8SSteven Wu     // Confirm a subsequent insertion fails to overwrite by trying to insert a
213*b510cdb8SSteven Wu     // bad value.
214*b510cdb8SSteven Wu     auto Result = Trie.insert(Lookup, TrieType::value_type(hash(N), N - 1));
215*b510cdb8SSteven Wu     EXPECT_EQ(N, Result->Data);
216*b510cdb8SSteven Wu   }
217*b510cdb8SSteven Wu 
218*b510cdb8SSteven Wu   // Check the trie so we can confirm the structure is correct. The root
219*b510cdb8SSteven Wu   // should have 2^8=256 slots, most subtries should have 2^5=32 slots, and the
220*b510cdb8SSteven Wu   // deepest subtrie should have 2^1=2 slots (since (64-8)mod(5)=1).
221*b510cdb8SSteven Wu   // should have 2 slots. The root's index=0 should have the content for
222*b510cdb8SSteven Wu   // 0x37 directly, and index=1 should be a linked-list of subtries, finally
223*b510cdb8SSteven Wu   // ending with content for (max-2) and (max-3).
224*b510cdb8SSteven Wu   //
225*b510cdb8SSteven Wu   // Note: This structure is not exhaustive (too expensive to update tests),
226*b510cdb8SSteven Wu   // but it does test that the dump format is somewhat readable and that the
227*b510cdb8SSteven Wu   // basic structure is correct.
228*b510cdb8SSteven Wu   //
229*b510cdb8SSteven Wu   // Note: This test requires that the trie reads bytes starting from index 0
230*b510cdb8SSteven Wu   // of the array of uint8_t, and then reads each byte's bits from high to low.
231*b510cdb8SSteven Wu 
232*b510cdb8SSteven Wu   // Check the Trie.
233*b510cdb8SSteven Wu   // 64 bit hash = 8 + 5 * 11 + 1, so 1 root, 11 8bit subtrie and 1 last level
234*b510cdb8SSteven Wu   // subtrie, 13 total.
235*b510cdb8SSteven Wu   ASSERT_EQ(getNumTries(), 13u);
236*b510cdb8SSteven Wu   // Check the root trie. Two slots and both are used.
237*b510cdb8SSteven Wu   ASSERT_EQ(getNumSlotUsed(getRoot()), 2u);
238*b510cdb8SSteven Wu   // Check last subtrie.
239*b510cdb8SSteven Wu   // Last allocated trie is the next node in the allocation chain.
240*b510cdb8SSteven Wu   auto LastAlloctedSubTrie = getNextTrie(getRoot());
241*b510cdb8SSteven Wu   ASSERT_EQ(getTriePrefixAsString(LastAlloctedSubTrie),
242*b510cdb8SSteven Wu             "ffffffffffffff[1111110]");
243*b510cdb8SSteven Wu   ASSERT_EQ(getStartBit(LastAlloctedSubTrie), 63u);
244*b510cdb8SSteven Wu   ASSERT_EQ(getNumBits(LastAlloctedSubTrie), 1u);
245*b510cdb8SSteven Wu   ASSERT_EQ(getNumSlotUsed(LastAlloctedSubTrie), 2u);
246*b510cdb8SSteven Wu }
247*b510cdb8SSteven Wu 
248*b510cdb8SSteven Wu TEST_F(SmallNodeTrieTest, TrieDestructionLoop) {
249*b510cdb8SSteven Wu   // Test destroying large Trie. Make sure there is no recursion that can
250*b510cdb8SSteven Wu   // overflow the stack.
251*b510cdb8SSteven Wu 
252*b510cdb8SSteven Wu   // Limit the tries to 2 slots (1 bit) to generate subtries at a higher rate.
253*b510cdb8SSteven Wu   auto &Trie = createTrie(/*NumRootBits=*/1, /*NumSubtrieBits=*/1);
254*b510cdb8SSteven Wu 
255*b510cdb8SSteven Wu   // Fill them up. Pick a MaxN high enough to cause a stack overflow in debug
256*b510cdb8SSteven Wu   // builds.
257*b510cdb8SSteven Wu   static constexpr uint64_t MaxN = 100000;
258*b510cdb8SSteven Wu   for (uint64_t N = 0; N != MaxN; ++N) {
259*b510cdb8SSteven Wu     HashType Hash = hash(N);
260*b510cdb8SSteven Wu     Trie.insert(TrieType::pointer(), TrieType::value_type(Hash, NumType{N}));
261*b510cdb8SSteven Wu   }
262*b510cdb8SSteven Wu 
263*b510cdb8SSteven Wu   // Destroy tries. If destruction is recursive and MaxN is high enough, these
264*b510cdb8SSteven Wu   // will both fail.
265*b510cdb8SSteven Wu   destroyTrie();
266*b510cdb8SSteven Wu }
267*b510cdb8SSteven Wu 
268*b510cdb8SSteven Wu struct NumWithDestructorT {
269*b510cdb8SSteven Wu   uint64_t Num;
270*b510cdb8SSteven Wu   llvm::function_ref<void()> DestructorCallback;
271*b510cdb8SSteven Wu   ~NumWithDestructorT() { DestructorCallback(); }
272*b510cdb8SSteven Wu };
273*b510cdb8SSteven Wu 
274*b510cdb8SSteven Wu using NodeWithDestructorTrieTest = SimpleTrieHashMapTest<NumWithDestructorT>;
275*b510cdb8SSteven Wu 
276*b510cdb8SSteven Wu TEST_F(NodeWithDestructorTrieTest, TrieDestructionLoop) {
277*b510cdb8SSteven Wu   // Test destroying large Trie. Make sure there is no recursion that can
278*b510cdb8SSteven Wu   // overflow the stack.
279*b510cdb8SSteven Wu 
280*b510cdb8SSteven Wu   // Limit the tries to 2 slots (1 bit) to generate subtries at a higher rate.
281*b510cdb8SSteven Wu   auto &Trie = createTrie(/*NumRootBits=*/1, /*NumSubtrieBits=*/1);
282*b510cdb8SSteven Wu 
283*b510cdb8SSteven Wu   // Fill them up. Pick a MaxN high enough to cause a stack overflow in debug
284*b510cdb8SSteven Wu   // builds.
285*b510cdb8SSteven Wu   static constexpr uint64_t MaxN = 100000;
286*b510cdb8SSteven Wu 
287*b510cdb8SSteven Wu   uint64_t DestructorCalled = 0;
288*b510cdb8SSteven Wu   auto DtorCallback = [&DestructorCalled]() { ++DestructorCalled; };
289*b510cdb8SSteven Wu   for (uint64_t N = 0; N != MaxN; ++N) {
290*b510cdb8SSteven Wu     HashType Hash = hash(N);
291*b510cdb8SSteven Wu     Trie.insert(TrieType::pointer(),
292*b510cdb8SSteven Wu                 TrieType::value_type(Hash, NumType{N, DtorCallback}));
293*b510cdb8SSteven Wu   }
294*b510cdb8SSteven Wu   // Reset the count after all the temporaries get destroyed.
295*b510cdb8SSteven Wu   DestructorCalled = 0;
296*b510cdb8SSteven Wu 
297*b510cdb8SSteven Wu   // Destroy tries. If destruction is recursive and MaxN is high enough, these
298*b510cdb8SSteven Wu   // will both fail.
299*b510cdb8SSteven Wu   destroyTrie();
300*b510cdb8SSteven Wu 
301*b510cdb8SSteven Wu   // Count the number of destructor calls during `destroyTrie()`.
302*b510cdb8SSteven Wu   ASSERT_EQ(DestructorCalled, MaxN);
303*b510cdb8SSteven Wu }
304*b510cdb8SSteven Wu 
305*b510cdb8SSteven Wu using NumStrNodeTrieTest = SimpleTrieHashMapTest<std::string>;
306*b510cdb8SSteven Wu 
307*b510cdb8SSteven Wu TEST_F(NumStrNodeTrieTest, TrieInsertLazy) {
308*b510cdb8SSteven Wu   for (unsigned RootBits : {2, 3, 6, 10}) {
309*b510cdb8SSteven Wu     for (unsigned SubtrieBits : {2, 3, 4}) {
310*b510cdb8SSteven Wu       auto &Trie = createTrie(RootBits, SubtrieBits);
311*b510cdb8SSteven Wu       for (int I = 0, E = 1000; I != E; ++I) {
312*b510cdb8SSteven Wu         TrieType::pointer Lookup;
313*b510cdb8SSteven Wu         HashType H = hash(I);
314*b510cdb8SSteven Wu         if (I & 1)
315*b510cdb8SSteven Wu           Lookup = Trie.find(H);
316*b510cdb8SSteven Wu 
317*b510cdb8SSteven Wu         auto insertNum = [&](uint64_t Num) {
318*b510cdb8SSteven Wu           std::string S = Twine(I).str();
319*b510cdb8SSteven Wu           auto Hash = hash(Num);
320*b510cdb8SSteven Wu           return Trie.insertLazy(
321*b510cdb8SSteven Wu               Hash, [&](TrieType::LazyValueConstructor C) { C(std::move(S)); });
322*b510cdb8SSteven Wu         };
323*b510cdb8SSteven Wu         auto S1 = insertNum(I);
324*b510cdb8SSteven Wu         // The address of the Data should be the same.
325*b510cdb8SSteven Wu         EXPECT_EQ(&S1->Data, &insertNum(I)->Data);
326*b510cdb8SSteven Wu 
327*b510cdb8SSteven Wu         auto insertStr = [&](std::string S) {
328*b510cdb8SSteven Wu           int Num = std::stoi(S);
329*b510cdb8SSteven Wu           return insertNum(Num);
330*b510cdb8SSteven Wu         };
331*b510cdb8SSteven Wu         std::string S2 = S1->Data;
332*b510cdb8SSteven Wu         // The address of the Data should be the same.
333*b510cdb8SSteven Wu         EXPECT_EQ(&S1->Data, &insertStr(S2)->Data);
334*b510cdb8SSteven Wu       }
335*b510cdb8SSteven Wu       for (int I = 0, E = 1000; I != E; ++I) {
336*b510cdb8SSteven Wu         std::string S = Twine(I).str();
337*b510cdb8SSteven Wu         TrieType::pointer Lookup = Trie.find(hash(I));
338*b510cdb8SSteven Wu         EXPECT_TRUE(Lookup);
339*b510cdb8SSteven Wu         if (!Lookup)
340*b510cdb8SSteven Wu           continue;
341*b510cdb8SSteven Wu         EXPECT_EQ(S, Lookup->Data);
342*b510cdb8SSteven Wu       }
343*b510cdb8SSteven Wu     }
344*b510cdb8SSteven Wu   }
345*b510cdb8SSteven Wu }
346*b510cdb8SSteven Wu } // end anonymous namespace
347