xref: /minix3/external/bsd/llvm/dist/clang/lib/Lex/HeaderMap.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1f4a2713aSLionel Sambuc //===--- HeaderMap.cpp - A file that acts like dir of symlinks ------------===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc //                     The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This file implements the HeaderMap interface.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc 
14f4a2713aSLionel Sambuc #include "clang/Lex/HeaderMap.h"
15f4a2713aSLionel Sambuc #include "clang/Basic/CharInfo.h"
16f4a2713aSLionel Sambuc #include "clang/Basic/FileManager.h"
17f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
18f4a2713aSLionel Sambuc #include "llvm/Support/DataTypes.h"
19f4a2713aSLionel Sambuc #include "llvm/Support/MathExtras.h"
20f4a2713aSLionel Sambuc #include "llvm/Support/MemoryBuffer.h"
21f4a2713aSLionel Sambuc #include <cstdio>
22*0a6a1f1dSLionel Sambuc #include <memory>
23f4a2713aSLionel Sambuc using namespace clang;
24f4a2713aSLionel Sambuc 
25f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
26f4a2713aSLionel Sambuc // Data Structures and Manifest Constants
27f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
28f4a2713aSLionel Sambuc 
29f4a2713aSLionel Sambuc enum {
30f4a2713aSLionel Sambuc   HMAP_HeaderMagicNumber = ('h' << 24) | ('m' << 16) | ('a' << 8) | 'p',
31f4a2713aSLionel Sambuc   HMAP_HeaderVersion = 1,
32f4a2713aSLionel Sambuc 
33f4a2713aSLionel Sambuc   HMAP_EmptyBucketKey = 0
34f4a2713aSLionel Sambuc };
35f4a2713aSLionel Sambuc 
36f4a2713aSLionel Sambuc namespace clang {
37f4a2713aSLionel Sambuc struct HMapBucket {
38f4a2713aSLionel Sambuc   uint32_t Key;          // Offset (into strings) of key.
39f4a2713aSLionel Sambuc 
40f4a2713aSLionel Sambuc   uint32_t Prefix;     // Offset (into strings) of value prefix.
41f4a2713aSLionel Sambuc   uint32_t Suffix;     // Offset (into strings) of value suffix.
42f4a2713aSLionel Sambuc };
43f4a2713aSLionel Sambuc 
44f4a2713aSLionel Sambuc struct HMapHeader {
45f4a2713aSLionel Sambuc   uint32_t Magic;           // Magic word, also indicates byte order.
46f4a2713aSLionel Sambuc   uint16_t Version;         // Version number -- currently 1.
47f4a2713aSLionel Sambuc   uint16_t Reserved;        // Reserved for future use - zero for now.
48f4a2713aSLionel Sambuc   uint32_t StringsOffset;   // Offset to start of string pool.
49f4a2713aSLionel Sambuc   uint32_t NumEntries;      // Number of entries in the string table.
50f4a2713aSLionel Sambuc   uint32_t NumBuckets;      // Number of buckets (always a power of 2).
51f4a2713aSLionel Sambuc   uint32_t MaxValueLength;  // Length of longest result path (excluding nul).
52f4a2713aSLionel Sambuc   // An array of 'NumBuckets' HMapBucket objects follows this header.
53f4a2713aSLionel Sambuc   // Strings follow the buckets, at StringsOffset.
54f4a2713aSLionel Sambuc };
55f4a2713aSLionel Sambuc } // end namespace clang.
56f4a2713aSLionel Sambuc 
57f4a2713aSLionel Sambuc /// HashHMapKey - This is the 'well known' hash function required by the file
58f4a2713aSLionel Sambuc /// format, used to look up keys in the hash table.  The hash table uses simple
59f4a2713aSLionel Sambuc /// linear probing based on this function.
HashHMapKey(StringRef Str)60f4a2713aSLionel Sambuc static inline unsigned HashHMapKey(StringRef Str) {
61f4a2713aSLionel Sambuc   unsigned Result = 0;
62f4a2713aSLionel Sambuc   const char *S = Str.begin(), *End = Str.end();
63f4a2713aSLionel Sambuc 
64f4a2713aSLionel Sambuc   for (; S != End; S++)
65f4a2713aSLionel Sambuc     Result += toLowercase(*S) * 13;
66f4a2713aSLionel Sambuc   return Result;
67f4a2713aSLionel Sambuc }
68f4a2713aSLionel Sambuc 
69f4a2713aSLionel Sambuc 
70f4a2713aSLionel Sambuc 
71f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
72f4a2713aSLionel Sambuc // Verification and Construction
73f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
74f4a2713aSLionel Sambuc 
75f4a2713aSLionel Sambuc /// HeaderMap::Create - This attempts to load the specified file as a header
76f4a2713aSLionel Sambuc /// map.  If it doesn't look like a HeaderMap, it gives up and returns null.
77f4a2713aSLionel Sambuc /// If it looks like a HeaderMap but is obviously corrupted, it puts a reason
78f4a2713aSLionel Sambuc /// into the string error argument and returns null.
Create(const FileEntry * FE,FileManager & FM)79f4a2713aSLionel Sambuc const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) {
80f4a2713aSLionel Sambuc   // If the file is too small to be a header map, ignore it.
81f4a2713aSLionel Sambuc   unsigned FileSize = FE->getSize();
82*0a6a1f1dSLionel Sambuc   if (FileSize <= sizeof(HMapHeader)) return nullptr;
83f4a2713aSLionel Sambuc 
84*0a6a1f1dSLionel Sambuc   auto FileBuffer = FM.getBufferForFile(FE);
85*0a6a1f1dSLionel Sambuc   if (!FileBuffer) return nullptr;  // Unreadable file?
86*0a6a1f1dSLionel Sambuc   const char *FileStart = (*FileBuffer)->getBufferStart();
87f4a2713aSLionel Sambuc 
88f4a2713aSLionel Sambuc   // We know the file is at least as big as the header, check it now.
89f4a2713aSLionel Sambuc   const HMapHeader *Header = reinterpret_cast<const HMapHeader*>(FileStart);
90f4a2713aSLionel Sambuc 
91f4a2713aSLionel Sambuc   // Sniff it to see if it's a headermap by checking the magic number and
92f4a2713aSLionel Sambuc   // version.
93f4a2713aSLionel Sambuc   bool NeedsByteSwap;
94f4a2713aSLionel Sambuc   if (Header->Magic == HMAP_HeaderMagicNumber &&
95f4a2713aSLionel Sambuc       Header->Version == HMAP_HeaderVersion)
96f4a2713aSLionel Sambuc     NeedsByteSwap = false;
97f4a2713aSLionel Sambuc   else if (Header->Magic == llvm::ByteSwap_32(HMAP_HeaderMagicNumber) &&
98f4a2713aSLionel Sambuc            Header->Version == llvm::ByteSwap_16(HMAP_HeaderVersion))
99f4a2713aSLionel Sambuc     NeedsByteSwap = true;  // Mixed endianness headermap.
100f4a2713aSLionel Sambuc   else
101*0a6a1f1dSLionel Sambuc     return nullptr;  // Not a header map.
102f4a2713aSLionel Sambuc 
103*0a6a1f1dSLionel Sambuc   if (Header->Reserved != 0) return nullptr;
104f4a2713aSLionel Sambuc 
105f4a2713aSLionel Sambuc   // Okay, everything looks good, create the header map.
106*0a6a1f1dSLionel Sambuc   return new HeaderMap(std::move(*FileBuffer), NeedsByteSwap);
107f4a2713aSLionel Sambuc }
108f4a2713aSLionel Sambuc 
109f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
110f4a2713aSLionel Sambuc //  Utility Methods
111f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
112f4a2713aSLionel Sambuc 
113f4a2713aSLionel Sambuc 
114f4a2713aSLionel Sambuc /// getFileName - Return the filename of the headermap.
getFileName() const115f4a2713aSLionel Sambuc const char *HeaderMap::getFileName() const {
116f4a2713aSLionel Sambuc   return FileBuffer->getBufferIdentifier();
117f4a2713aSLionel Sambuc }
118f4a2713aSLionel Sambuc 
getEndianAdjustedWord(unsigned X) const119f4a2713aSLionel Sambuc unsigned HeaderMap::getEndianAdjustedWord(unsigned X) const {
120f4a2713aSLionel Sambuc   if (!NeedsBSwap) return X;
121f4a2713aSLionel Sambuc   return llvm::ByteSwap_32(X);
122f4a2713aSLionel Sambuc }
123f4a2713aSLionel Sambuc 
124f4a2713aSLionel Sambuc /// getHeader - Return a reference to the file header, in unbyte-swapped form.
125f4a2713aSLionel Sambuc /// This method cannot fail.
getHeader() const126f4a2713aSLionel Sambuc const HMapHeader &HeaderMap::getHeader() const {
127f4a2713aSLionel Sambuc   // We know the file is at least as big as the header.  Return it.
128f4a2713aSLionel Sambuc   return *reinterpret_cast<const HMapHeader*>(FileBuffer->getBufferStart());
129f4a2713aSLionel Sambuc }
130f4a2713aSLionel Sambuc 
131f4a2713aSLionel Sambuc /// getBucket - Return the specified hash table bucket from the header map,
132f4a2713aSLionel Sambuc /// bswap'ing its fields as appropriate.  If the bucket number is not valid,
133f4a2713aSLionel Sambuc /// this return a bucket with an empty key (0).
getBucket(unsigned BucketNo) const134f4a2713aSLionel Sambuc HMapBucket HeaderMap::getBucket(unsigned BucketNo) const {
135f4a2713aSLionel Sambuc   HMapBucket Result;
136f4a2713aSLionel Sambuc   Result.Key = HMAP_EmptyBucketKey;
137f4a2713aSLionel Sambuc 
138f4a2713aSLionel Sambuc   const HMapBucket *BucketArray =
139f4a2713aSLionel Sambuc     reinterpret_cast<const HMapBucket*>(FileBuffer->getBufferStart() +
140f4a2713aSLionel Sambuc                                         sizeof(HMapHeader));
141f4a2713aSLionel Sambuc 
142f4a2713aSLionel Sambuc   const HMapBucket *BucketPtr = BucketArray+BucketNo;
143f4a2713aSLionel Sambuc   if ((const char*)(BucketPtr+1) > FileBuffer->getBufferEnd()) {
144f4a2713aSLionel Sambuc     Result.Prefix = 0;
145f4a2713aSLionel Sambuc     Result.Suffix = 0;
146f4a2713aSLionel Sambuc     return Result;  // Invalid buffer, corrupt hmap.
147f4a2713aSLionel Sambuc   }
148f4a2713aSLionel Sambuc 
149f4a2713aSLionel Sambuc   // Otherwise, the bucket is valid.  Load the values, bswapping as needed.
150f4a2713aSLionel Sambuc   Result.Key    = getEndianAdjustedWord(BucketPtr->Key);
151f4a2713aSLionel Sambuc   Result.Prefix = getEndianAdjustedWord(BucketPtr->Prefix);
152f4a2713aSLionel Sambuc   Result.Suffix = getEndianAdjustedWord(BucketPtr->Suffix);
153f4a2713aSLionel Sambuc   return Result;
154f4a2713aSLionel Sambuc }
155f4a2713aSLionel Sambuc 
156f4a2713aSLionel Sambuc /// getString - Look up the specified string in the string table.  If the string
157f4a2713aSLionel Sambuc /// index is not valid, it returns an empty string.
getString(unsigned StrTabIdx) const158f4a2713aSLionel Sambuc const char *HeaderMap::getString(unsigned StrTabIdx) const {
159f4a2713aSLionel Sambuc   // Add the start of the string table to the idx.
160f4a2713aSLionel Sambuc   StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset);
161f4a2713aSLionel Sambuc 
162f4a2713aSLionel Sambuc   // Check for invalid index.
163f4a2713aSLionel Sambuc   if (StrTabIdx >= FileBuffer->getBufferSize())
164*0a6a1f1dSLionel Sambuc     return nullptr;
165f4a2713aSLionel Sambuc 
166f4a2713aSLionel Sambuc   // Otherwise, we have a valid pointer into the file.  Just return it.  We know
167f4a2713aSLionel Sambuc   // that the "string" can not overrun the end of the file, because the buffer
168f4a2713aSLionel Sambuc   // is nul terminated by virtue of being a MemoryBuffer.
169f4a2713aSLionel Sambuc   return FileBuffer->getBufferStart()+StrTabIdx;
170f4a2713aSLionel Sambuc }
171f4a2713aSLionel Sambuc 
172f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
173f4a2713aSLionel Sambuc // The Main Drivers
174f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
175f4a2713aSLionel Sambuc 
176f4a2713aSLionel Sambuc /// dump - Print the contents of this headermap to stderr.
dump() const177f4a2713aSLionel Sambuc void HeaderMap::dump() const {
178f4a2713aSLionel Sambuc   const HMapHeader &Hdr = getHeader();
179f4a2713aSLionel Sambuc   unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);
180f4a2713aSLionel Sambuc 
181f4a2713aSLionel Sambuc   fprintf(stderr, "Header Map %s:\n  %d buckets, %d entries\n",
182f4a2713aSLionel Sambuc           getFileName(), NumBuckets,
183f4a2713aSLionel Sambuc           getEndianAdjustedWord(Hdr.NumEntries));
184f4a2713aSLionel Sambuc 
185f4a2713aSLionel Sambuc   for (unsigned i = 0; i != NumBuckets; ++i) {
186f4a2713aSLionel Sambuc     HMapBucket B = getBucket(i);
187f4a2713aSLionel Sambuc     if (B.Key == HMAP_EmptyBucketKey) continue;
188f4a2713aSLionel Sambuc 
189f4a2713aSLionel Sambuc     const char *Key    = getString(B.Key);
190f4a2713aSLionel Sambuc     const char *Prefix = getString(B.Prefix);
191f4a2713aSLionel Sambuc     const char *Suffix = getString(B.Suffix);
192f4a2713aSLionel Sambuc     fprintf(stderr, "  %d. %s -> '%s' '%s'\n", i, Key, Prefix, Suffix);
193f4a2713aSLionel Sambuc   }
194f4a2713aSLionel Sambuc }
195f4a2713aSLionel Sambuc 
196f4a2713aSLionel Sambuc /// LookupFile - Check to see if the specified relative filename is located in
197f4a2713aSLionel Sambuc /// this HeaderMap.  If so, open it and return its FileEntry.
LookupFile(StringRef Filename,FileManager & FM) const198f4a2713aSLionel Sambuc const FileEntry *HeaderMap::LookupFile(
199f4a2713aSLionel Sambuc     StringRef Filename, FileManager &FM) const {
200*0a6a1f1dSLionel Sambuc 
201*0a6a1f1dSLionel Sambuc   SmallString<1024> Path;
202*0a6a1f1dSLionel Sambuc   StringRef Dest = lookupFilename(Filename, Path);
203*0a6a1f1dSLionel Sambuc   if (Dest.empty())
204*0a6a1f1dSLionel Sambuc     return nullptr;
205*0a6a1f1dSLionel Sambuc 
206*0a6a1f1dSLionel Sambuc   return FM.getFile(Dest);
207*0a6a1f1dSLionel Sambuc }
208*0a6a1f1dSLionel Sambuc 
lookupFilename(StringRef Filename,SmallVectorImpl<char> & DestPath) const209*0a6a1f1dSLionel Sambuc StringRef HeaderMap::lookupFilename(StringRef Filename,
210*0a6a1f1dSLionel Sambuc                                     SmallVectorImpl<char> &DestPath) const {
211f4a2713aSLionel Sambuc   const HMapHeader &Hdr = getHeader();
212f4a2713aSLionel Sambuc   unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);
213f4a2713aSLionel Sambuc 
214f4a2713aSLionel Sambuc   // If the number of buckets is not a power of two, the headermap is corrupt.
215f4a2713aSLionel Sambuc   // Don't probe infinitely.
216f4a2713aSLionel Sambuc   if (NumBuckets & (NumBuckets-1))
217*0a6a1f1dSLionel Sambuc     return StringRef();
218f4a2713aSLionel Sambuc 
219f4a2713aSLionel Sambuc   // Linearly probe the hash table.
220f4a2713aSLionel Sambuc   for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) {
221f4a2713aSLionel Sambuc     HMapBucket B = getBucket(Bucket & (NumBuckets-1));
222*0a6a1f1dSLionel Sambuc     if (B.Key == HMAP_EmptyBucketKey) return StringRef(); // Hash miss.
223f4a2713aSLionel Sambuc 
224f4a2713aSLionel Sambuc     // See if the key matches.  If not, probe on.
225f4a2713aSLionel Sambuc     if (!Filename.equals_lower(getString(B.Key)))
226f4a2713aSLionel Sambuc       continue;
227f4a2713aSLionel Sambuc 
228f4a2713aSLionel Sambuc     // If so, we have a match in the hash table.  Construct the destination
229f4a2713aSLionel Sambuc     // path.
230*0a6a1f1dSLionel Sambuc     StringRef Prefix = getString(B.Prefix);
231*0a6a1f1dSLionel Sambuc     StringRef Suffix = getString(B.Suffix);
232*0a6a1f1dSLionel Sambuc     DestPath.clear();
233*0a6a1f1dSLionel Sambuc     DestPath.append(Prefix.begin(), Prefix.end());
234*0a6a1f1dSLionel Sambuc     DestPath.append(Suffix.begin(), Suffix.end());
235*0a6a1f1dSLionel Sambuc     return StringRef(DestPath.begin(), DestPath.size());
236f4a2713aSLionel Sambuc   }
237f4a2713aSLionel Sambuc }
238