[SCCP] Avoid modifying AdditionalUsers while iterating over it
[llvm-project.git] / clang / unittests / Lex / HeaderMapTest.cpp
blobc18ce79ef58f0dd7517810f8a0ba43f402b9fad0
1 //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===--------------------------------------------------------------===//
9 #include "clang/Basic/CharInfo.h"
10 #include "clang/Lex/HeaderMap.h"
11 #include "clang/Lex/HeaderMapTypes.h"
12 #include "llvm/ADT/SmallString.h"
13 #include "llvm/Support/SwapByteOrder.h"
14 #include "gtest/gtest.h"
15 #include <cassert>
16 #include <type_traits>
18 using namespace clang;
19 using namespace llvm;
21 namespace {
23 // Lay out a header file for testing.
24 template <unsigned NumBuckets, unsigned NumBytes> struct MapFile {
25 HMapHeader Header;
26 HMapBucket Buckets[NumBuckets];
27 unsigned char Bytes[NumBytes];
29 void init() {
30 memset(this, 0, sizeof(MapFile));
31 Header.Magic = HMAP_HeaderMagicNumber;
32 Header.Version = HMAP_HeaderVersion;
33 Header.NumBuckets = NumBuckets;
34 Header.StringsOffset = sizeof(Header) + sizeof(Buckets);
37 void swapBytes() {
38 using llvm::sys::getSwappedBytes;
39 Header.Magic = getSwappedBytes(Header.Magic);
40 Header.Version = getSwappedBytes(Header.Version);
41 Header.NumBuckets = getSwappedBytes(Header.NumBuckets);
42 Header.StringsOffset = getSwappedBytes(Header.StringsOffset);
45 std::unique_ptr<const MemoryBuffer> getBuffer() const {
46 return MemoryBuffer::getMemBuffer(
47 StringRef(reinterpret_cast<const char *>(this), sizeof(MapFile)),
48 "header",
49 /* RequresNullTerminator */ false);
53 // The header map hash function.
54 static inline unsigned getHash(StringRef Str) {
55 unsigned Result = 0;
56 for (char C : Str)
57 Result += toLowercase(C) * 13;
58 return Result;
61 template <class FileTy> struct FileMaker {
62 FileTy &File;
63 unsigned SI = 1;
64 unsigned BI = 0;
65 FileMaker(FileTy &File) : File(File) {}
67 unsigned addString(StringRef S) {
68 assert(SI + S.size() + 1 <= sizeof(File.Bytes));
69 std::copy(S.begin(), S.end(), File.Bytes + SI);
70 auto OldSI = SI;
71 SI += S.size() + 1;
72 return OldSI;
74 void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) {
75 assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1)));
76 unsigned I = Hash & (File.Header.NumBuckets - 1);
77 do {
78 if (!File.Buckets[I].Key) {
79 File.Buckets[I].Key = Key;
80 File.Buckets[I].Prefix = Prefix;
81 File.Buckets[I].Suffix = Suffix;
82 ++File.Header.NumEntries;
83 return;
85 ++I;
86 I &= File.Header.NumBuckets - 1;
87 } while (I != (Hash & (File.Header.NumBuckets - 1)));
88 llvm_unreachable("no empty buckets");
92 TEST(HeaderMapTest, checkHeaderEmpty) {
93 bool NeedsSwap;
94 ASSERT_FALSE(HeaderMapImpl::checkHeader(
95 *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
96 ASSERT_FALSE(HeaderMapImpl::checkHeader(
97 *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
100 TEST(HeaderMapTest, checkHeaderMagic) {
101 MapFile<1, 1> File;
102 File.init();
103 File.Header.Magic = 0;
104 bool NeedsSwap;
105 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
108 TEST(HeaderMapTest, checkHeaderReserved) {
109 MapFile<1, 1> File;
110 File.init();
111 File.Header.Reserved = 1;
112 bool NeedsSwap;
113 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
116 TEST(HeaderMapTest, checkHeaderVersion) {
117 MapFile<1, 1> File;
118 File.init();
119 ++File.Header.Version;
120 bool NeedsSwap;
121 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
124 TEST(HeaderMapTest, checkHeaderValidButEmpty) {
125 MapFile<1, 1> File;
126 File.init();
127 bool NeedsSwap;
128 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
129 ASSERT_FALSE(NeedsSwap);
131 File.swapBytes();
132 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
133 ASSERT_TRUE(NeedsSwap);
136 TEST(HeaderMapTest, checkHeader3Buckets) {
137 MapFile<3, 1> File;
138 ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets));
140 File.init();
141 bool NeedsSwap;
142 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
145 TEST(HeaderMapTest, checkHeader0Buckets) {
146 // Create with 1 bucket to avoid 0-sized arrays.
147 MapFile<1, 1> File;
148 File.init();
149 File.Header.NumBuckets = 0;
150 bool NeedsSwap;
151 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
154 TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) {
155 MapFile<1, 1> File;
156 File.init();
157 File.Header.NumBuckets = 8;
158 bool NeedsSwap;
159 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
162 TEST(HeaderMapTest, lookupFilename) {
163 typedef MapFile<2, 7> FileTy;
164 FileTy File;
165 File.init();
167 FileMaker<FileTy> Maker(File);
168 auto a = Maker.addString("a");
169 auto b = Maker.addString("b");
170 auto c = Maker.addString("c");
171 Maker.addBucket(getHash("a"), a, b, c);
173 bool NeedsSwap;
174 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
175 ASSERT_FALSE(NeedsSwap);
176 HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
178 SmallString<8> DestPath;
179 ASSERT_EQ("bc", Map.lookupFilename("a", DestPath));
182 template <class FileTy, class PaddingTy> struct PaddedFile {
183 FileTy File;
184 PaddingTy Padding;
187 TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) {
188 typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
189 static_assert(std::is_standard_layout<FileTy>::value,
190 "Expected standard layout");
191 static_assert(sizeof(FileTy) == 64, "check the math");
192 PaddedFile<FileTy, uint64_t> P;
193 auto &File = P.File;
194 auto &Padding = P.Padding;
195 File.init();
197 FileMaker<FileTy> Maker(File);
198 auto a = Maker.addString("a");
199 auto b = Maker.addString("b");
200 auto c = Maker.addString("c");
201 Maker.addBucket(getHash("a"), a, b, c);
203 // Add 'x' characters to cause an overflow into Padding.
204 ASSERT_EQ('c', File.Bytes[5]);
205 for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
206 ASSERT_EQ(0, File.Bytes[I]);
207 File.Bytes[I] = 'x';
209 Padding = 0xffffffff; // Padding won't stop it either.
211 bool NeedsSwap;
212 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
213 ASSERT_FALSE(NeedsSwap);
214 HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
216 // The string for "c" runs to the end of File. Check that the suffix
217 // ("cxxxx...") is detected as truncated, and an empty string is returned.
218 SmallString<24> DestPath;
219 ASSERT_EQ("", Map.lookupFilename("a", DestPath));
222 TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) {
223 typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
224 static_assert(std::is_standard_layout<FileTy>::value,
225 "Expected standard layout");
226 static_assert(sizeof(FileTy) == 64, "check the math");
227 PaddedFile<FileTy, uint64_t> P;
228 auto &File = P.File;
229 auto &Padding = P.Padding;
230 File.init();
232 FileMaker<FileTy> Maker(File);
233 auto a = Maker.addString("a");
234 auto c = Maker.addString("c");
235 auto b = Maker.addString("b"); // Store the prefix last.
236 Maker.addBucket(getHash("a"), a, b, c);
238 // Add 'x' characters to cause an overflow into Padding.
239 ASSERT_EQ('b', File.Bytes[5]);
240 for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
241 ASSERT_EQ(0, File.Bytes[I]);
242 File.Bytes[I] = 'x';
244 Padding = 0xffffffff; // Padding won't stop it either.
246 bool NeedsSwap;
247 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
248 ASSERT_FALSE(NeedsSwap);
249 HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
251 // The string for "b" runs to the end of File. Check that the prefix
252 // ("bxxxx...") is detected as truncated, and an empty string is returned.
253 SmallString<24> DestPath;
254 ASSERT_EQ("", Map.lookupFilename("a", DestPath));
257 } // end namespace