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 146*3a7def09SDuncan P. N. Exon Smith TEST(HeaderMapTest, checkHeader0Buckets) { 147*3a7def09SDuncan P. N. Exon Smith // Create with 1 bucket to avoid 0-sized arrays. 148*3a7def09SDuncan P. N. Exon Smith MapFile<1, 1> File; 149*3a7def09SDuncan P. N. Exon Smith File.init(); 150*3a7def09SDuncan P. N. Exon Smith File.Header.NumBuckets = 0; 151*3a7def09SDuncan P. N. Exon Smith bool NeedsSwap; 152*3a7def09SDuncan P. N. Exon Smith ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); 153*3a7def09SDuncan P. N. Exon Smith } 154*3a7def09SDuncan 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 18896a01fa0SDuncan P. N. Exon Smith TEST(HeaderMapTest, lookupFilenameTruncated) { 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 21896a01fa0SDuncan P. N. Exon Smith // ("cxxxx...") is ignored. Another option would be to return an empty 21996a01fa0SDuncan P. N. Exon Smith // filename altogether. 22096a01fa0SDuncan P. N. Exon Smith SmallString<24> DestPath; 22196a01fa0SDuncan P. N. Exon Smith ASSERT_EQ("b", Map.lookupFilename("a", DestPath)); 22296a01fa0SDuncan P. N. Exon Smith } 22396a01fa0SDuncan P. N. Exon Smith 2249ab99eecSDuncan P. N. Exon Smith } // end namespace 225