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