1*67e74705SXin Li //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===--------------------------------------------------------------===//
9*67e74705SXin Li
10*67e74705SXin Li #include "clang/Basic/CharInfo.h"
11*67e74705SXin Li #include "clang/Lex/HeaderMap.h"
12*67e74705SXin Li #include "clang/Lex/HeaderMapTypes.h"
13*67e74705SXin Li #include "llvm/ADT/SmallString.h"
14*67e74705SXin Li #include "llvm/Support/SwapByteOrder.h"
15*67e74705SXin Li #include "gtest/gtest.h"
16*67e74705SXin Li #include <cassert>
17*67e74705SXin Li #include <type_traits>
18*67e74705SXin Li
19*67e74705SXin Li using namespace clang;
20*67e74705SXin Li using namespace llvm;
21*67e74705SXin Li
22*67e74705SXin Li namespace {
23*67e74705SXin Li
24*67e74705SXin Li // Lay out a header file for testing.
25*67e74705SXin Li template <unsigned NumBuckets, unsigned NumBytes> struct MapFile {
26*67e74705SXin Li HMapHeader Header;
27*67e74705SXin Li HMapBucket Buckets[NumBuckets];
28*67e74705SXin Li unsigned char Bytes[NumBytes];
29*67e74705SXin Li
init__anonaa37d2cd0111::MapFile30*67e74705SXin Li void init() {
31*67e74705SXin Li memset(this, 0, sizeof(MapFile));
32*67e74705SXin Li Header.Magic = HMAP_HeaderMagicNumber;
33*67e74705SXin Li Header.Version = HMAP_HeaderVersion;
34*67e74705SXin Li Header.NumBuckets = NumBuckets;
35*67e74705SXin Li Header.StringsOffset = sizeof(Header) + sizeof(Buckets);
36*67e74705SXin Li }
37*67e74705SXin Li
swapBytes__anonaa37d2cd0111::MapFile38*67e74705SXin Li void swapBytes() {
39*67e74705SXin Li using llvm::sys::getSwappedBytes;
40*67e74705SXin Li Header.Magic = getSwappedBytes(Header.Magic);
41*67e74705SXin Li Header.Version = getSwappedBytes(Header.Version);
42*67e74705SXin Li Header.NumBuckets = getSwappedBytes(Header.NumBuckets);
43*67e74705SXin Li Header.StringsOffset = getSwappedBytes(Header.StringsOffset);
44*67e74705SXin Li }
45*67e74705SXin Li
getBuffer__anonaa37d2cd0111::MapFile46*67e74705SXin Li std::unique_ptr<const MemoryBuffer> getBuffer() const {
47*67e74705SXin Li return MemoryBuffer::getMemBuffer(
48*67e74705SXin Li StringRef(reinterpret_cast<const char *>(this), sizeof(MapFile)),
49*67e74705SXin Li "header",
50*67e74705SXin Li /* RequresNullTerminator */ false);
51*67e74705SXin Li }
52*67e74705SXin Li };
53*67e74705SXin Li
54*67e74705SXin Li // The header map hash function.
getHash(StringRef Str)55*67e74705SXin Li static inline unsigned getHash(StringRef Str) {
56*67e74705SXin Li unsigned Result = 0;
57*67e74705SXin Li for (char C : Str)
58*67e74705SXin Li Result += toLowercase(C) * 13;
59*67e74705SXin Li return Result;
60*67e74705SXin Li }
61*67e74705SXin Li
62*67e74705SXin Li template <class FileTy> struct FileMaker {
63*67e74705SXin Li FileTy &File;
64*67e74705SXin Li unsigned SI = 1;
65*67e74705SXin Li unsigned BI = 0;
FileMaker__anonaa37d2cd0111::FileMaker66*67e74705SXin Li FileMaker(FileTy &File) : File(File) {}
67*67e74705SXin Li
addString__anonaa37d2cd0111::FileMaker68*67e74705SXin Li unsigned addString(StringRef S) {
69*67e74705SXin Li assert(SI + S.size() + 1 <= sizeof(File.Bytes));
70*67e74705SXin Li std::copy(S.begin(), S.end(), File.Bytes + SI);
71*67e74705SXin Li auto OldSI = SI;
72*67e74705SXin Li SI += S.size() + 1;
73*67e74705SXin Li return OldSI;
74*67e74705SXin Li }
addBucket__anonaa37d2cd0111::FileMaker75*67e74705SXin Li void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) {
76*67e74705SXin Li assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1)));
77*67e74705SXin Li unsigned I = Hash & (File.Header.NumBuckets - 1);
78*67e74705SXin Li do {
79*67e74705SXin Li if (!File.Buckets[I].Key) {
80*67e74705SXin Li File.Buckets[I].Key = Key;
81*67e74705SXin Li File.Buckets[I].Prefix = Prefix;
82*67e74705SXin Li File.Buckets[I].Suffix = Suffix;
83*67e74705SXin Li ++File.Header.NumEntries;
84*67e74705SXin Li return;
85*67e74705SXin Li }
86*67e74705SXin Li ++I;
87*67e74705SXin Li I &= File.Header.NumBuckets - 1;
88*67e74705SXin Li } while (I != (Hash & (File.Header.NumBuckets - 1)));
89*67e74705SXin Li llvm_unreachable("no empty buckets");
90*67e74705SXin Li }
91*67e74705SXin Li };
92*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderEmpty)93*67e74705SXin Li TEST(HeaderMapTest, checkHeaderEmpty) {
94*67e74705SXin Li bool NeedsSwap;
95*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(
96*67e74705SXin Li *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
97*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(
98*67e74705SXin Li *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
99*67e74705SXin Li }
100*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderMagic)101*67e74705SXin Li TEST(HeaderMapTest, checkHeaderMagic) {
102*67e74705SXin Li MapFile<1, 1> File;
103*67e74705SXin Li File.init();
104*67e74705SXin Li File.Header.Magic = 0;
105*67e74705SXin Li bool NeedsSwap;
106*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
107*67e74705SXin Li }
108*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderReserved)109*67e74705SXin Li TEST(HeaderMapTest, checkHeaderReserved) {
110*67e74705SXin Li MapFile<1, 1> File;
111*67e74705SXin Li File.init();
112*67e74705SXin Li File.Header.Reserved = 1;
113*67e74705SXin Li bool NeedsSwap;
114*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
115*67e74705SXin Li }
116*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderVersion)117*67e74705SXin Li TEST(HeaderMapTest, checkHeaderVersion) {
118*67e74705SXin Li MapFile<1, 1> File;
119*67e74705SXin Li File.init();
120*67e74705SXin Li ++File.Header.Version;
121*67e74705SXin Li bool NeedsSwap;
122*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
123*67e74705SXin Li }
124*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderValidButEmpty)125*67e74705SXin Li TEST(HeaderMapTest, checkHeaderValidButEmpty) {
126*67e74705SXin Li MapFile<1, 1> File;
127*67e74705SXin Li File.init();
128*67e74705SXin Li bool NeedsSwap;
129*67e74705SXin Li ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
130*67e74705SXin Li ASSERT_FALSE(NeedsSwap);
131*67e74705SXin Li
132*67e74705SXin Li File.swapBytes();
133*67e74705SXin Li ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
134*67e74705SXin Li ASSERT_TRUE(NeedsSwap);
135*67e74705SXin Li }
136*67e74705SXin Li
TEST(HeaderMapTest,checkHeader3Buckets)137*67e74705SXin Li TEST(HeaderMapTest, checkHeader3Buckets) {
138*67e74705SXin Li MapFile<3, 1> File;
139*67e74705SXin Li ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets));
140*67e74705SXin Li
141*67e74705SXin Li File.init();
142*67e74705SXin Li bool NeedsSwap;
143*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
144*67e74705SXin Li }
145*67e74705SXin Li
TEST(HeaderMapTest,checkHeader0Buckets)146*67e74705SXin Li TEST(HeaderMapTest, checkHeader0Buckets) {
147*67e74705SXin Li // Create with 1 bucket to avoid 0-sized arrays.
148*67e74705SXin Li MapFile<1, 1> File;
149*67e74705SXin Li File.init();
150*67e74705SXin Li File.Header.NumBuckets = 0;
151*67e74705SXin Li bool NeedsSwap;
152*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
153*67e74705SXin Li }
154*67e74705SXin Li
TEST(HeaderMapTest,checkHeaderNotEnoughBuckets)155*67e74705SXin Li TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) {
156*67e74705SXin Li MapFile<1, 1> File;
157*67e74705SXin Li File.init();
158*67e74705SXin Li File.Header.NumBuckets = 8;
159*67e74705SXin Li bool NeedsSwap;
160*67e74705SXin Li ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
161*67e74705SXin Li }
162*67e74705SXin Li
TEST(HeaderMapTest,lookupFilename)163*67e74705SXin Li TEST(HeaderMapTest, lookupFilename) {
164*67e74705SXin Li typedef MapFile<2, 7> FileTy;
165*67e74705SXin Li FileTy File;
166*67e74705SXin Li File.init();
167*67e74705SXin Li
168*67e74705SXin Li FileMaker<FileTy> Maker(File);
169*67e74705SXin Li auto a = Maker.addString("a");
170*67e74705SXin Li auto b = Maker.addString("b");
171*67e74705SXin Li auto c = Maker.addString("c");
172*67e74705SXin Li Maker.addBucket(getHash("a"), a, b, c);
173*67e74705SXin Li
174*67e74705SXin Li bool NeedsSwap;
175*67e74705SXin Li ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
176*67e74705SXin Li ASSERT_FALSE(NeedsSwap);
177*67e74705SXin Li HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
178*67e74705SXin Li
179*67e74705SXin Li SmallString<8> DestPath;
180*67e74705SXin Li ASSERT_EQ("bc", Map.lookupFilename("a", DestPath));
181*67e74705SXin Li }
182*67e74705SXin Li
183*67e74705SXin Li template <class FileTy, class PaddingTy> struct PaddedFile {
184*67e74705SXin Li FileTy File;
185*67e74705SXin Li PaddingTy Padding;
186*67e74705SXin Li };
187*67e74705SXin Li
TEST(HeaderMapTest,lookupFilenameTruncatedSuffix)188*67e74705SXin Li TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) {
189*67e74705SXin Li typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
190*67e74705SXin Li static_assert(std::is_standard_layout<FileTy>::value,
191*67e74705SXin Li "Expected standard layout");
192*67e74705SXin Li static_assert(sizeof(FileTy) == 64, "check the math");
193*67e74705SXin Li PaddedFile<FileTy, uint64_t> P;
194*67e74705SXin Li auto &File = P.File;
195*67e74705SXin Li auto &Padding = P.Padding;
196*67e74705SXin Li File.init();
197*67e74705SXin Li
198*67e74705SXin Li FileMaker<FileTy> Maker(File);
199*67e74705SXin Li auto a = Maker.addString("a");
200*67e74705SXin Li auto b = Maker.addString("b");
201*67e74705SXin Li auto c = Maker.addString("c");
202*67e74705SXin Li Maker.addBucket(getHash("a"), a, b, c);
203*67e74705SXin Li
204*67e74705SXin Li // Add 'x' characters to cause an overflow into Padding.
205*67e74705SXin Li ASSERT_EQ('c', File.Bytes[5]);
206*67e74705SXin Li for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
207*67e74705SXin Li ASSERT_EQ(0, File.Bytes[I]);
208*67e74705SXin Li File.Bytes[I] = 'x';
209*67e74705SXin Li }
210*67e74705SXin Li Padding = 0xffffffff; // Padding won't stop it either.
211*67e74705SXin Li
212*67e74705SXin Li bool NeedsSwap;
213*67e74705SXin Li ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
214*67e74705SXin Li ASSERT_FALSE(NeedsSwap);
215*67e74705SXin Li HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
216*67e74705SXin Li
217*67e74705SXin Li // The string for "c" runs to the end of File. Check that the suffix
218*67e74705SXin Li // ("cxxxx...") is detected as truncated, and an empty string is returned.
219*67e74705SXin Li SmallString<24> DestPath;
220*67e74705SXin Li ASSERT_EQ("", Map.lookupFilename("a", DestPath));
221*67e74705SXin Li }
222*67e74705SXin Li
TEST(HeaderMapTest,lookupFilenameTruncatedPrefix)223*67e74705SXin Li TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) {
224*67e74705SXin Li typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
225*67e74705SXin Li static_assert(std::is_standard_layout<FileTy>::value,
226*67e74705SXin Li "Expected standard layout");
227*67e74705SXin Li static_assert(sizeof(FileTy) == 64, "check the math");
228*67e74705SXin Li PaddedFile<FileTy, uint64_t> P;
229*67e74705SXin Li auto &File = P.File;
230*67e74705SXin Li auto &Padding = P.Padding;
231*67e74705SXin Li File.init();
232*67e74705SXin Li
233*67e74705SXin Li FileMaker<FileTy> Maker(File);
234*67e74705SXin Li auto a = Maker.addString("a");
235*67e74705SXin Li auto c = Maker.addString("c");
236*67e74705SXin Li auto b = Maker.addString("b"); // Store the prefix last.
237*67e74705SXin Li Maker.addBucket(getHash("a"), a, b, c);
238*67e74705SXin Li
239*67e74705SXin Li // Add 'x' characters to cause an overflow into Padding.
240*67e74705SXin Li ASSERT_EQ('b', File.Bytes[5]);
241*67e74705SXin Li for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
242*67e74705SXin Li ASSERT_EQ(0, File.Bytes[I]);
243*67e74705SXin Li File.Bytes[I] = 'x';
244*67e74705SXin Li }
245*67e74705SXin Li Padding = 0xffffffff; // Padding won't stop it either.
246*67e74705SXin Li
247*67e74705SXin Li bool NeedsSwap;
248*67e74705SXin Li ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
249*67e74705SXin Li ASSERT_FALSE(NeedsSwap);
250*67e74705SXin Li HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
251*67e74705SXin Li
252*67e74705SXin Li // The string for "b" runs to the end of File. Check that the prefix
253*67e74705SXin Li // ("bxxxx...") is detected as truncated, and an empty string is returned.
254*67e74705SXin Li SmallString<24> DestPath;
255*67e74705SXin Li ASSERT_EQ("", Map.lookupFilename("a", DestPath));
256*67e74705SXin Li }
257*67e74705SXin Li
258*67e74705SXin Li } // end namespace
259