xref: /llvm-project/clang/unittests/Lex/HeaderMapTest.cpp (revision 08fd70fae36a4146dd9b8bf8a73b0e84549b3924)
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