1 //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===--------------------------------------------------------------===// 9 10 #include "clang/Basic/CharInfo.h" 11 #include "clang/Lex/HeaderMap.h" 12 #include "clang/Lex/HeaderMapTypes.h" 13 #include "llvm/ADT/SmallString.h" 14 #include "llvm/Support/SwapByteOrder.h" 15 #include "gtest/gtest.h" 16 #include <cassert> 17 18 using namespace clang; 19 using namespace llvm; 20 21 namespace { 22 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]; 28 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); 35 } 36 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); 43 } 44 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); 50 } 51 }; 52 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; 59 } 60 61 template <class FileTy> struct FileMaker { 62 FileTy &File; 63 unsigned SI = 1; 64 unsigned BI = 0; 65 FileMaker(FileTy &File) : File(File) {} 66 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; 73 } 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; 84 } 85 ++I; 86 I &= File.Header.NumBuckets - 1; 87 } while (I != (Hash & (File.Header.NumBuckets - 1))); 88 llvm_unreachable("no empty buckets"); 89 } 90 }; 91 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)); 98 } 99 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)); 106 } 107 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)); 114 } 115 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)); 122 } 123 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); 130 131 File.swapBytes(); 132 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); 133 ASSERT_TRUE(NeedsSwap); 134 } 135 136 TEST(HeaderMapTest, checkHeader3Buckets) { 137 MapFile<3, 1> File; 138 ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); 139 140 File.init(); 141 bool NeedsSwap; 142 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); 143 } 144 145 TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { 146 MapFile<1, 1> File; 147 File.init(); 148 File.Header.NumBuckets = 8; 149 bool NeedsSwap; 150 ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); 151 } 152 153 TEST(HeaderMapTest, lookupFilename) { 154 typedef MapFile<2, 7> FileTy; 155 FileTy File; 156 File.init(); 157 158 FileMaker<FileTy> Maker(File); 159 auto a = Maker.addString("a"); 160 auto b = Maker.addString("b"); 161 auto c = Maker.addString("c"); 162 Maker.addBucket(getHash("a"), a, b, c); 163 164 bool NeedsSwap; 165 ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); 166 ASSERT_FALSE(NeedsSwap); 167 HeaderMapImpl Map(File.getBuffer(), NeedsSwap); 168 169 SmallString<8> DestPath; 170 ASSERT_EQ("bc", Map.lookupFilename("a", DestPath)); 171 } 172 173 } // end namespace 174