19ab99eecSDuncan P. N. Exon Smith //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===//
29ab99eecSDuncan P. N. Exon Smith //
39ab99eecSDuncan P. N. Exon Smith //                     The LLVM Compiler Infrastructure
49ab99eecSDuncan P. N. Exon Smith //
59ab99eecSDuncan P. N. Exon Smith // This file is distributed under the University of Illinois Open Source
69ab99eecSDuncan P. N. Exon Smith // License. See LICENSE.TXT for details.
79ab99eecSDuncan P. N. Exon Smith //
89ab99eecSDuncan P. N. Exon Smith //===--------------------------------------------------------------===//
99ab99eecSDuncan P. N. Exon Smith 
1008fd70faSDuncan P. N. Exon Smith #include "clang/Basic/CharInfo.h"
119ab99eecSDuncan P. N. Exon Smith #include "clang/Lex/HeaderMap.h"
129ab99eecSDuncan P. N. Exon Smith #include "clang/Lex/HeaderMapTypes.h"
1308fd70faSDuncan P. N. Exon Smith #include "llvm/ADT/SmallString.h"
149ab99eecSDuncan P. N. Exon Smith #include "llvm/Support/SwapByteOrder.h"
159ab99eecSDuncan P. N. Exon Smith #include "gtest/gtest.h"
1608fd70faSDuncan P. N. Exon Smith #include <cassert>
1796a01fa0SDuncan P. N. Exon Smith #include <type_traits>
189ab99eecSDuncan P. N. Exon Smith 
199ab99eecSDuncan P. N. Exon Smith using namespace clang;
209ab99eecSDuncan P. N. Exon Smith using namespace llvm;
219ab99eecSDuncan P. N. Exon Smith 
229ab99eecSDuncan P. N. Exon Smith namespace {
239ab99eecSDuncan P. N. Exon Smith 
249ab99eecSDuncan P. N. Exon Smith // Lay out a header file for testing.
259ab99eecSDuncan P. N. Exon Smith template <unsigned NumBuckets, unsigned NumBytes> struct MapFile {
269ab99eecSDuncan P. N. Exon Smith   HMapHeader Header;
279ab99eecSDuncan P. N. Exon Smith   HMapBucket Buckets[NumBuckets];
289ab99eecSDuncan P. N. Exon Smith   unsigned char Bytes[NumBytes];
299ab99eecSDuncan P. N. Exon Smith 
309ab99eecSDuncan P. N. Exon Smith   void init() {
319ab99eecSDuncan P. N. Exon Smith     memset(this, 0, sizeof(MapFile));
329ab99eecSDuncan P. N. Exon Smith     Header.Magic = HMAP_HeaderMagicNumber;
339ab99eecSDuncan P. N. Exon Smith     Header.Version = HMAP_HeaderVersion;
349ab99eecSDuncan P. N. Exon Smith     Header.NumBuckets = NumBuckets;
359ab99eecSDuncan P. N. Exon Smith     Header.StringsOffset = sizeof(Header) + sizeof(Buckets);
369ab99eecSDuncan P. N. Exon Smith   }
379ab99eecSDuncan P. N. Exon Smith 
389ab99eecSDuncan P. N. Exon Smith   void swapBytes() {
399ab99eecSDuncan P. N. Exon Smith     using llvm::sys::getSwappedBytes;
409ab99eecSDuncan P. N. Exon Smith     Header.Magic = getSwappedBytes(Header.Magic);
419ab99eecSDuncan P. N. Exon Smith     Header.Version = getSwappedBytes(Header.Version);
429ab99eecSDuncan P. N. Exon Smith     Header.NumBuckets = getSwappedBytes(Header.NumBuckets);
439ab99eecSDuncan P. N. Exon Smith     Header.StringsOffset = getSwappedBytes(Header.StringsOffset);
449ab99eecSDuncan P. N. Exon Smith   }
459ab99eecSDuncan P. N. Exon Smith 
469ab99eecSDuncan P. N. Exon Smith   std::unique_ptr<const MemoryBuffer> getBuffer() const {
479ab99eecSDuncan P. N. Exon Smith     return MemoryBuffer::getMemBuffer(
489ab99eecSDuncan P. N. Exon Smith         StringRef(reinterpret_cast<const char *>(this), sizeof(MapFile)),
499ab99eecSDuncan P. N. Exon Smith         "header",
509ab99eecSDuncan P. N. Exon Smith         /* RequresNullTerminator */ false);
519ab99eecSDuncan P. N. Exon Smith   }
529ab99eecSDuncan P. N. Exon Smith };
539ab99eecSDuncan P. N. Exon Smith 
5408fd70faSDuncan P. N. Exon Smith // The header map hash function.
5508fd70faSDuncan P. N. Exon Smith static inline unsigned getHash(StringRef Str) {
5608fd70faSDuncan P. N. Exon Smith   unsigned Result = 0;
5708fd70faSDuncan P. N. Exon Smith   for (char C : Str)
5808fd70faSDuncan P. N. Exon Smith     Result += toLowercase(C) * 13;
5908fd70faSDuncan P. N. Exon Smith   return Result;
6008fd70faSDuncan P. N. Exon Smith }
6108fd70faSDuncan P. N. Exon Smith 
6208fd70faSDuncan P. N. Exon Smith template <class FileTy> struct FileMaker {
6308fd70faSDuncan P. N. Exon Smith   FileTy &File;
6408fd70faSDuncan P. N. Exon Smith   unsigned SI = 1;
6508fd70faSDuncan P. N. Exon Smith   unsigned BI = 0;
6608fd70faSDuncan P. N. Exon Smith   FileMaker(FileTy &File) : File(File) {}
6708fd70faSDuncan P. N. Exon Smith 
6808fd70faSDuncan P. N. Exon Smith   unsigned addString(StringRef S) {
6908fd70faSDuncan P. N. Exon Smith     assert(SI + S.size() + 1 <= sizeof(File.Bytes));
7008fd70faSDuncan P. N. Exon Smith     std::copy(S.begin(), S.end(), File.Bytes + SI);
7108fd70faSDuncan P. N. Exon Smith     auto OldSI = SI;
7208fd70faSDuncan P. N. Exon Smith     SI += S.size() + 1;
7308fd70faSDuncan P. N. Exon Smith     return OldSI;
7408fd70faSDuncan P. N. Exon Smith   }
7508fd70faSDuncan P. N. Exon Smith   void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) {
7608fd70faSDuncan P. N. Exon Smith     assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1)));
7708fd70faSDuncan P. N. Exon Smith     unsigned I = Hash & (File.Header.NumBuckets - 1);
7808fd70faSDuncan P. N. Exon Smith     do {
7908fd70faSDuncan P. N. Exon Smith       if (!File.Buckets[I].Key) {
8008fd70faSDuncan P. N. Exon Smith         File.Buckets[I].Key = Key;
8108fd70faSDuncan P. N. Exon Smith         File.Buckets[I].Prefix = Prefix;
8208fd70faSDuncan P. N. Exon Smith         File.Buckets[I].Suffix = Suffix;
8308fd70faSDuncan P. N. Exon Smith         ++File.Header.NumEntries;
8408fd70faSDuncan P. N. Exon Smith         return;
8508fd70faSDuncan P. N. Exon Smith       }
8608fd70faSDuncan P. N. Exon Smith       ++I;
8708fd70faSDuncan P. N. Exon Smith       I &= File.Header.NumBuckets - 1;
8808fd70faSDuncan P. N. Exon Smith     } while (I != (Hash & (File.Header.NumBuckets - 1)));
8908fd70faSDuncan P. N. Exon Smith     llvm_unreachable("no empty buckets");
9008fd70faSDuncan P. N. Exon Smith   }
9108fd70faSDuncan P. N. Exon Smith };
9208fd70faSDuncan P. N. Exon Smith 
939ab99eecSDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderEmpty) {
949ab99eecSDuncan P. N. Exon Smith   bool NeedsSwap;
959ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(
969ab99eecSDuncan P. N. Exon Smith       *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
979ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(
989ab99eecSDuncan P. N. Exon Smith       *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
999ab99eecSDuncan P. N. Exon Smith }
1009ab99eecSDuncan P. N. Exon Smith 
1019ab99eecSDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderMagic) {
1029ab99eecSDuncan P. N. Exon Smith   MapFile<1, 1> File;
1039ab99eecSDuncan P. N. Exon Smith   File.init();
1049ab99eecSDuncan P. N. Exon Smith   File.Header.Magic = 0;
1059ab99eecSDuncan P. N. Exon Smith   bool NeedsSwap;
1069ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1079ab99eecSDuncan P. N. Exon Smith }
1089ab99eecSDuncan P. N. Exon Smith 
1099ab99eecSDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderReserved) {
1109ab99eecSDuncan P. N. Exon Smith   MapFile<1, 1> File;
1119ab99eecSDuncan P. N. Exon Smith   File.init();
1129ab99eecSDuncan P. N. Exon Smith   File.Header.Reserved = 1;
1139ab99eecSDuncan P. N. Exon Smith   bool NeedsSwap;
1149ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1159ab99eecSDuncan P. N. Exon Smith }
1169ab99eecSDuncan P. N. Exon Smith 
1179ab99eecSDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderVersion) {
1189ab99eecSDuncan P. N. Exon Smith   MapFile<1, 1> File;
1199ab99eecSDuncan P. N. Exon Smith   File.init();
1209ab99eecSDuncan P. N. Exon Smith   ++File.Header.Version;
1219ab99eecSDuncan P. N. Exon Smith   bool NeedsSwap;
1229ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1239ab99eecSDuncan P. N. Exon Smith }
1249ab99eecSDuncan P. N. Exon Smith 
1259ab99eecSDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderValidButEmpty) {
1269ab99eecSDuncan P. N. Exon Smith   MapFile<1, 1> File;
1279ab99eecSDuncan P. N. Exon Smith   File.init();
1289ab99eecSDuncan P. N. Exon Smith   bool NeedsSwap;
1299ab99eecSDuncan P. N. Exon Smith   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1309ab99eecSDuncan P. N. Exon Smith   ASSERT_FALSE(NeedsSwap);
1319ab99eecSDuncan P. N. Exon Smith 
1329ab99eecSDuncan P. N. Exon Smith   File.swapBytes();
1339ab99eecSDuncan P. N. Exon Smith   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1349ab99eecSDuncan P. N. Exon Smith   ASSERT_TRUE(NeedsSwap);
1359ab99eecSDuncan P. N. Exon Smith }
1369ab99eecSDuncan P. N. Exon Smith 
137dfe85305SDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeader3Buckets) {
138dfe85305SDuncan P. N. Exon Smith   MapFile<3, 1> File;
139dfe85305SDuncan P. N. Exon Smith   ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets));
140dfe85305SDuncan P. N. Exon Smith 
141dfe85305SDuncan P. N. Exon Smith   File.init();
142dfe85305SDuncan P. N. Exon Smith   bool NeedsSwap;
143dfe85305SDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
144dfe85305SDuncan P. N. Exon Smith }
145dfe85305SDuncan P. N. Exon Smith 
1463a7def09SDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeader0Buckets) {
1473a7def09SDuncan P. N. Exon Smith   // Create with 1 bucket to avoid 0-sized arrays.
1483a7def09SDuncan P. N. Exon Smith   MapFile<1, 1> File;
1493a7def09SDuncan P. N. Exon Smith   File.init();
1503a7def09SDuncan P. N. Exon Smith   File.Header.NumBuckets = 0;
1513a7def09SDuncan P. N. Exon Smith   bool NeedsSwap;
1523a7def09SDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1533a7def09SDuncan P. N. Exon Smith }
1543a7def09SDuncan P. N. Exon Smith 
1558d6a31c0SDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) {
1568d6a31c0SDuncan P. N. Exon Smith   MapFile<1, 1> File;
1578d6a31c0SDuncan P. N. Exon Smith   File.init();
1588d6a31c0SDuncan P. N. Exon Smith   File.Header.NumBuckets = 8;
1598d6a31c0SDuncan P. N. Exon Smith   bool NeedsSwap;
1608d6a31c0SDuncan P. N. Exon Smith   ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
1618d6a31c0SDuncan P. N. Exon Smith }
1628d6a31c0SDuncan P. N. Exon Smith 
16308fd70faSDuncan P. N. Exon Smith TEST(HeaderMapTest, lookupFilename) {
16408fd70faSDuncan P. N. Exon Smith   typedef MapFile<2, 7> FileTy;
16508fd70faSDuncan P. N. Exon Smith   FileTy File;
16608fd70faSDuncan P. N. Exon Smith   File.init();
16708fd70faSDuncan P. N. Exon Smith 
16808fd70faSDuncan P. N. Exon Smith   FileMaker<FileTy> Maker(File);
16908fd70faSDuncan P. N. Exon Smith   auto a = Maker.addString("a");
17008fd70faSDuncan P. N. Exon Smith   auto b = Maker.addString("b");
17108fd70faSDuncan P. N. Exon Smith   auto c = Maker.addString("c");
17208fd70faSDuncan P. N. Exon Smith   Maker.addBucket(getHash("a"), a, b, c);
17308fd70faSDuncan P. N. Exon Smith 
17408fd70faSDuncan P. N. Exon Smith   bool NeedsSwap;
17508fd70faSDuncan P. N. Exon Smith   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
17608fd70faSDuncan P. N. Exon Smith   ASSERT_FALSE(NeedsSwap);
17708fd70faSDuncan P. N. Exon Smith   HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
17808fd70faSDuncan P. N. Exon Smith 
17908fd70faSDuncan P. N. Exon Smith   SmallString<8> DestPath;
18008fd70faSDuncan P. N. Exon Smith   ASSERT_EQ("bc", Map.lookupFilename("a", DestPath));
18108fd70faSDuncan P. N. Exon Smith }
18208fd70faSDuncan P. N. Exon Smith 
18396a01fa0SDuncan P. N. Exon Smith template <class FileTy, class PaddingTy> struct PaddedFile {
18496a01fa0SDuncan P. N. Exon Smith   FileTy File;
18596a01fa0SDuncan P. N. Exon Smith   PaddingTy Padding;
18696a01fa0SDuncan P. N. Exon Smith };
18796a01fa0SDuncan P. N. Exon Smith 
188*b5ce11fcSDuncan P. N. Exon Smith TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) {
18996a01fa0SDuncan P. N. Exon Smith   typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
19096a01fa0SDuncan P. N. Exon Smith   static_assert(std::is_standard_layout<FileTy>::value,
19196a01fa0SDuncan P. N. Exon Smith                 "Expected standard layout");
19296a01fa0SDuncan P. N. Exon Smith   static_assert(sizeof(FileTy) == 64, "check the math");
19396a01fa0SDuncan P. N. Exon Smith   PaddedFile<FileTy, uint64_t> P;
19496a01fa0SDuncan P. N. Exon Smith   auto &File = P.File;
19596a01fa0SDuncan P. N. Exon Smith   auto &Padding = P.Padding;
19696a01fa0SDuncan P. N. Exon Smith   File.init();
19796a01fa0SDuncan P. N. Exon Smith 
19896a01fa0SDuncan P. N. Exon Smith   FileMaker<FileTy> Maker(File);
19996a01fa0SDuncan P. N. Exon Smith   auto a = Maker.addString("a");
20096a01fa0SDuncan P. N. Exon Smith   auto b = Maker.addString("b");
20196a01fa0SDuncan P. N. Exon Smith   auto c = Maker.addString("c");
20296a01fa0SDuncan P. N. Exon Smith   Maker.addBucket(getHash("a"), a, b, c);
20396a01fa0SDuncan P. N. Exon Smith 
20496a01fa0SDuncan P. N. Exon Smith   // Add 'x' characters to cause an overflow into Padding.
20596a01fa0SDuncan P. N. Exon Smith   ASSERT_EQ('c', File.Bytes[5]);
20696a01fa0SDuncan P. N. Exon Smith   for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
20796a01fa0SDuncan P. N. Exon Smith     ASSERT_EQ(0, File.Bytes[I]);
20896a01fa0SDuncan P. N. Exon Smith     File.Bytes[I] = 'x';
20996a01fa0SDuncan P. N. Exon Smith   }
21096a01fa0SDuncan P. N. Exon Smith   Padding = 0xffffffff; // Padding won't stop it either.
21196a01fa0SDuncan P. N. Exon Smith 
21296a01fa0SDuncan P. N. Exon Smith   bool NeedsSwap;
21396a01fa0SDuncan P. N. Exon Smith   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
21496a01fa0SDuncan P. N. Exon Smith   ASSERT_FALSE(NeedsSwap);
21596a01fa0SDuncan P. N. Exon Smith   HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
21696a01fa0SDuncan P. N. Exon Smith 
21796a01fa0SDuncan P. N. Exon Smith   // The string for "c" runs to the end of File.  Check that the suffix
218*b5ce11fcSDuncan P. N. Exon Smith   // ("cxxxx...") is detected as truncated, and an empty string is returned.
21996a01fa0SDuncan P. N. Exon Smith   SmallString<24> DestPath;
220*b5ce11fcSDuncan P. N. Exon Smith   ASSERT_EQ("", Map.lookupFilename("a", DestPath));
221*b5ce11fcSDuncan P. N. Exon Smith }
222*b5ce11fcSDuncan P. N. Exon Smith 
223*b5ce11fcSDuncan P. N. Exon Smith TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) {
224*b5ce11fcSDuncan P. N. Exon Smith   typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
225*b5ce11fcSDuncan P. N. Exon Smith   static_assert(std::is_standard_layout<FileTy>::value,
226*b5ce11fcSDuncan P. N. Exon Smith                 "Expected standard layout");
227*b5ce11fcSDuncan P. N. Exon Smith   static_assert(sizeof(FileTy) == 64, "check the math");
228*b5ce11fcSDuncan P. N. Exon Smith   PaddedFile<FileTy, uint64_t> P;
229*b5ce11fcSDuncan P. N. Exon Smith   auto &File = P.File;
230*b5ce11fcSDuncan P. N. Exon Smith   auto &Padding = P.Padding;
231*b5ce11fcSDuncan P. N. Exon Smith   File.init();
232*b5ce11fcSDuncan P. N. Exon Smith 
233*b5ce11fcSDuncan P. N. Exon Smith   FileMaker<FileTy> Maker(File);
234*b5ce11fcSDuncan P. N. Exon Smith   auto a = Maker.addString("a");
235*b5ce11fcSDuncan P. N. Exon Smith   auto c = Maker.addString("c");
236*b5ce11fcSDuncan P. N. Exon Smith   auto b = Maker.addString("b"); // Store the prefix last.
237*b5ce11fcSDuncan P. N. Exon Smith   Maker.addBucket(getHash("a"), a, b, c);
238*b5ce11fcSDuncan P. N. Exon Smith 
239*b5ce11fcSDuncan P. N. Exon Smith   // Add 'x' characters to cause an overflow into Padding.
240*b5ce11fcSDuncan P. N. Exon Smith   ASSERT_EQ('b', File.Bytes[5]);
241*b5ce11fcSDuncan P. N. Exon Smith   for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
242*b5ce11fcSDuncan P. N. Exon Smith     ASSERT_EQ(0, File.Bytes[I]);
243*b5ce11fcSDuncan P. N. Exon Smith     File.Bytes[I] = 'x';
244*b5ce11fcSDuncan P. N. Exon Smith   }
245*b5ce11fcSDuncan P. N. Exon Smith   Padding = 0xffffffff; // Padding won't stop it either.
246*b5ce11fcSDuncan P. N. Exon Smith 
247*b5ce11fcSDuncan P. N. Exon Smith   bool NeedsSwap;
248*b5ce11fcSDuncan P. N. Exon Smith   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
249*b5ce11fcSDuncan P. N. Exon Smith   ASSERT_FALSE(NeedsSwap);
250*b5ce11fcSDuncan P. N. Exon Smith   HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
251*b5ce11fcSDuncan P. N. Exon Smith 
252*b5ce11fcSDuncan P. N. Exon Smith   // The string for "b" runs to the end of File.  Check that the prefix
253*b5ce11fcSDuncan P. N. Exon Smith   // ("bxxxx...") is detected as truncated, and an empty string is returned.
254*b5ce11fcSDuncan P. N. Exon Smith   SmallString<24> DestPath;
255*b5ce11fcSDuncan P. N. Exon Smith   ASSERT_EQ("", Map.lookupFilename("a", DestPath));
25696a01fa0SDuncan P. N. Exon Smith }
25796a01fa0SDuncan P. N. Exon Smith 
2589ab99eecSDuncan P. N. Exon Smith } // end namespace
259