]>
Commit | Line | Data |
---|---|---|
7cac9316 XL |
1 | // RUN: %clangxx_unit -esan-instrument-loads-and-stores=0 -O0 %s -o %t 2>&1 |
2 | // RUN: %env_esan_opts="record_snapshots=0" %run %t 2>&1 | FileCheck %s | |
3 | ||
4 | #include "esan/esan_hashtable.h" | |
5 | #include <assert.h> | |
6 | #include <stdio.h> | |
7 | #include <stdlib.h> | |
8 | #include <string.h> | |
9 | ||
10 | class MyData { | |
11 | public: | |
12 | MyData(const char *Str) : RefCount(0) { Buf = strdup(Str); } | |
13 | ~MyData() { | |
14 | fprintf(stderr, " Destructor: %s.\n", Buf); | |
15 | free(Buf); | |
16 | } | |
17 | bool operator==(MyData &Cmp) { return strcmp(Buf, Cmp.Buf) == 0; } | |
18 | operator size_t() const { | |
19 | size_t Res = 0; | |
20 | for (int i = 0; i < strlen(Buf); ++i) | |
21 | Res ^= Buf[i]; | |
22 | return Res; | |
23 | } | |
24 | char *Buf; | |
25 | int RefCount; | |
26 | }; | |
27 | ||
28 | // We use a smart pointer wrapper to free the payload on hashtable removal. | |
29 | struct MyDataPayload { | |
30 | MyDataPayload() : Data(nullptr) {} | |
31 | explicit MyDataPayload(MyData *Data) : Data(Data) { ++Data->RefCount; } | |
32 | ~MyDataPayload() { | |
33 | if (Data && --Data->RefCount == 0) { | |
34 | fprintf(stderr, "Deleting %s.\n", Data->Buf); | |
35 | delete Data; | |
36 | } | |
37 | } | |
38 | MyDataPayload(const MyDataPayload &Copy) { | |
39 | Data = Copy.Data; | |
40 | ++Data->RefCount; | |
41 | } | |
42 | MyDataPayload & operator=(const MyDataPayload &Copy) { | |
43 | if (this != &Copy) { | |
44 | this->~MyDataPayload(); | |
45 | Data = Copy.Data; | |
46 | ++Data->RefCount; | |
47 | } | |
48 | return *this; | |
49 | } | |
50 | bool operator==(MyDataPayload &Cmp) { return *Data == *Cmp.Data; } | |
51 | operator size_t() const { return (size_t)*Data; } | |
52 | MyData *Data; | |
53 | }; | |
54 | ||
55 | int main() | |
56 | { | |
57 | __esan::HashTable<int, int> IntTable; | |
58 | assert(IntTable.size() == 0); | |
59 | ||
60 | // Test iteration on an empty table. | |
61 | int Count = 0; | |
62 | for (auto Iter = IntTable.begin(); Iter != IntTable.end(); | |
63 | ++Iter, ++Count) { | |
64 | // Empty. | |
65 | } | |
66 | assert(Count == 0); | |
67 | ||
68 | bool Added = IntTable.add(4, 42); | |
69 | assert(Added); | |
70 | assert(!IntTable.add(4, 42)); | |
71 | assert(IntTable.size() == 1); | |
72 | int Value; | |
73 | bool Found = IntTable.lookup(4, Value); | |
74 | assert(Found && Value == 42); | |
75 | ||
76 | // Test iterator. | |
77 | IntTable.lock(); | |
78 | for (auto Iter = IntTable.begin(); Iter != IntTable.end(); | |
79 | ++Iter, ++Count) { | |
80 | assert((*Iter).Key == 4); | |
81 | assert((*Iter).Data == 42); | |
82 | } | |
83 | IntTable.unlock(); | |
84 | assert(Count == 1); | |
85 | assert(Count == IntTable.size()); | |
86 | assert(!IntTable.remove(5)); | |
87 | assert(IntTable.remove(4)); | |
88 | ||
89 | // Test a more complex payload. | |
90 | __esan::HashTable<int, MyDataPayload> DataTable(4); | |
91 | MyDataPayload NewData(new MyData("mystring")); | |
92 | Added = DataTable.add(4, NewData); | |
93 | assert(Added); | |
94 | MyDataPayload FoundData; | |
95 | Found = DataTable.lookup(4, FoundData); | |
96 | assert(Found && strcmp(FoundData.Data->Buf, "mystring") == 0); | |
97 | assert(!DataTable.remove(5)); | |
98 | assert(DataTable.remove(4)); | |
99 | // Test resize. | |
100 | for (int i = 0; i < 4; ++i) { | |
101 | MyDataPayload MoreData(new MyData("delete-at-end")); | |
102 | Added = DataTable.add(i+1, MoreData); | |
103 | assert(Added); | |
104 | assert(!DataTable.add(i+1, MoreData)); | |
105 | } | |
106 | for (int i = 0; i < 4; ++i) { | |
107 | Found = DataTable.lookup(i+1, FoundData); | |
108 | assert(Found && strcmp(FoundData.Data->Buf, "delete-at-end") == 0); | |
109 | } | |
110 | DataTable.lock(); | |
111 | Count = 0; | |
112 | for (auto Iter = DataTable.begin(); Iter != DataTable.end(); | |
113 | ++Iter, ++Count) { | |
114 | int Key = (*Iter).Key; | |
115 | FoundData = (*Iter).Data; | |
116 | assert(Key >= 1 && Key <= 4); | |
117 | assert(strcmp(FoundData.Data->Buf, "delete-at-end") == 0); | |
118 | } | |
119 | DataTable.unlock(); | |
120 | assert(Count == 4); | |
121 | assert(Count == DataTable.size()); | |
122 | ||
123 | // Ensure the iterator supports a range-based for loop. | |
124 | DataTable.lock(); | |
125 | Count = 0; | |
126 | for (auto Pair : DataTable) { | |
127 | assert(Pair.Key >= 1 && Pair.Key <= 4); | |
128 | assert(strcmp(Pair.Data.Data->Buf, "delete-at-end") == 0); | |
129 | ++Count; | |
130 | } | |
131 | DataTable.unlock(); | |
132 | assert(Count == 4); | |
133 | assert(Count == DataTable.size()); | |
134 | ||
135 | // Test payload freeing via smart pointer wrapper. | |
136 | __esan::HashTable<MyDataPayload, MyDataPayload, true> DataKeyTable; | |
137 | MyDataPayload DataA(new MyData("string AB")); | |
138 | DataKeyTable.lock(); | |
139 | Added = DataKeyTable.add(DataA, DataA); | |
140 | assert(Added); | |
141 | Found = DataKeyTable.lookup(DataA, FoundData); | |
142 | assert(Found && strcmp(FoundData.Data->Buf, "string AB") == 0); | |
143 | MyDataPayload DataB(new MyData("string AB")); | |
144 | Added = DataKeyTable.add(DataB, DataB); | |
145 | assert(!Added); | |
146 | DataKeyTable.remove(DataB); // Should free the DataA payload. | |
147 | DataKeyTable.unlock(); | |
148 | ||
149 | // Test custom functors. | |
150 | struct CustomHash { | |
151 | size_t operator()(int Key) const { return Key % 4; } | |
152 | }; | |
153 | struct CustomEqual { | |
154 | bool operator()(int Key1, int Key2) const { return Key1 %4 == Key2 % 4; } | |
155 | }; | |
156 | __esan::HashTable<int, int, false, CustomHash, CustomEqual> ModTable; | |
157 | Added = ModTable.add(2, 42); | |
158 | assert(Added); | |
159 | Added = ModTable.add(6, 42); | |
160 | assert(!Added); | |
161 | ||
162 | fprintf(stderr, "All checks passed.\n"); | |
163 | return 0; | |
164 | } | |
165 | // CHECK: Deleting mystring. | |
166 | // CHECK-NEXT: Destructor: mystring. | |
167 | // CHECK-NEXT: All checks passed. | |
168 | // CHECK-NEXT: Deleting string AB. | |
169 | // CHECK-NEXT: Destructor: string AB. | |
170 | // CHECK-NEXT: Deleting string AB. | |
171 | // CHECK-NEXT: Destructor: string AB. | |
172 | // CHECK-NEXT: Deleting delete-at-end. | |
173 | // CHECK-NEXT: Destructor: delete-at-end. | |
174 | // CHECK-NEXT: Deleting delete-at-end. | |
175 | // CHECK-NEXT: Destructor: delete-at-end. | |
176 | // CHECK-NEXT: Deleting delete-at-end. | |
177 | // CHECK-NEXT: Destructor: delete-at-end. | |
178 | // CHECK-NEXT: Deleting delete-at-end. | |
179 | // CHECK-NEXT: Destructor: delete-at-end. |