PTHLexer.cpp revision 6cb7c1a43b0c8f739d1f54b7fdae5ede86033496
1//===--- PTHLexer.cpp - Lex from a token stream ---------------------------===//
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// This file implements the PTHLexer interface.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Basic/TokenKinds.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Basic/IdentifierTable.h"
17#include "clang/Basic/OnDiskHashTable.h"
18#include "clang/Lex/PTHLexer.h"
19#include "clang/Lex/Preprocessor.h"
20#include "clang/Lex/PTHManager.h"
21#include "clang/Lex/Token.h"
22#include "clang/Lex/Preprocessor.h"
23#include "llvm/ADT/StringMap.h"
24#include "llvm/ADT/OwningPtr.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include <sys/stat.h>
27using namespace clang;
28using namespace clang::io;
29
30#define DISK_TOKEN_SIZE (1+1+2+4+4)
31
32//===----------------------------------------------------------------------===//
33// PTHLexer methods.
34//===----------------------------------------------------------------------===//
35
36PTHLexer::PTHLexer(Preprocessor &PP, FileID FID, const unsigned char *D,
37                   const unsigned char *ppcond, PTHManager &PM)
38  : PreprocessorLexer(&PP, FID), TokBuf(D), CurPtr(D), LastHashTokPtr(0),
39    PPCond(ppcond), CurPPCondPtr(ppcond), PTHMgr(PM) {
40
41  FileStartLoc = PP.getSourceManager().getLocForStartOfFile(FID);
42}
43
44void PTHLexer::Lex(Token& Tok) {
45LexNextToken:
46
47  //===--------------------------------------==//
48  // Read the raw token data.
49  //===--------------------------------------==//
50
51  // Shadow CurPtr into an automatic variable.
52  const unsigned char *CurPtrShadow = CurPtr;
53
54  // Read in the data for the token.
55  unsigned Word0 = ReadLE32(CurPtrShadow);
56  uint32_t IdentifierID = ReadLE32(CurPtrShadow);
57  uint32_t FileOffset = ReadLE32(CurPtrShadow);
58
59  tok::TokenKind TKind = (tok::TokenKind) (Word0 & 0xFF);
60  Token::TokenFlags TFlags = (Token::TokenFlags) ((Word0 >> 8) & 0xFF);
61  uint32_t Len = Word0 >> 16;
62
63  CurPtr = CurPtrShadow;
64
65  //===--------------------------------------==//
66  // Construct the token itself.
67  //===--------------------------------------==//
68
69  Tok.startToken();
70  Tok.setKind(TKind);
71  Tok.setFlag(TFlags);
72  assert(!LexingRawMode);
73  Tok.setLocation(FileStartLoc.getFileLocWithOffset(FileOffset));
74  Tok.setLength(Len);
75
76  // Handle identifiers.
77  if (Tok.isLiteral()) {
78    Tok.setLiteralData((const char*) (PTHMgr.SpellingBase + IdentifierID));
79  }
80  else if (IdentifierID) {
81    MIOpt.ReadToken();
82    IdentifierInfo *II = PTHMgr.GetIdentifierInfo(IdentifierID-1);
83
84    Tok.setIdentifierInfo(II);
85
86    // Change the kind of this identifier to the appropriate token kind, e.g.
87    // turning "for" into a keyword.
88    Tok.setKind(II->getTokenID());
89
90    if (II->isHandleIdentifierCase())
91      PP->HandleIdentifier(Tok);
92    return;
93  }
94
95  //===--------------------------------------==//
96  // Process the token.
97  //===--------------------------------------==//
98#if 0
99  SourceManager& SM = PP->getSourceManager();
100  llvm::errs() << SM.getFileEntryForID(FileID)->getName()
101    << ':' << SM.getLogicalLineNumber(Tok.getLocation())
102    << ':' << SM.getLogicalColumnNumber(Tok.getLocation())
103    << '\n';
104#endif
105
106  if (TKind == tok::eof) {
107    // Save the end-of-file token.
108    EofToken = Tok;
109
110    Preprocessor *PPCache = PP;
111
112    assert(!ParsingPreprocessorDirective);
113    assert(!LexingRawMode);
114
115    // FIXME: Issue diagnostics similar to Lexer.
116    if (PP->HandleEndOfFile(Tok, false))
117      return;
118
119    assert(PPCache && "Raw buffer::LexEndOfFile should return a token");
120    return PPCache->Lex(Tok);
121  }
122
123  if (TKind == tok::hash && Tok.isAtStartOfLine()) {
124    LastHashTokPtr = CurPtr - DISK_TOKEN_SIZE;
125    assert(!LexingRawMode);
126    PP->HandleDirective(Tok);
127
128    if (PP->isCurrentLexer(this))
129      goto LexNextToken;
130
131    return PP->Lex(Tok);
132  }
133
134  if (TKind == tok::eom) {
135    assert(ParsingPreprocessorDirective);
136    ParsingPreprocessorDirective = false;
137    return;
138  }
139
140  MIOpt.ReadToken();
141}
142
143// FIXME: We can just grab the last token instead of storing a copy
144// into EofToken.
145void PTHLexer::getEOF(Token& Tok) {
146  assert(EofToken.is(tok::eof));
147  Tok = EofToken;
148}
149
150void PTHLexer::DiscardToEndOfLine() {
151  assert(ParsingPreprocessorDirective && ParsingFilename == false &&
152         "Must be in a preprocessing directive!");
153
154  // We assume that if the preprocessor wishes to discard to the end of
155  // the line that it also means to end the current preprocessor directive.
156  ParsingPreprocessorDirective = false;
157
158  // Skip tokens by only peeking at their token kind and the flags.
159  // We don't need to actually reconstruct full tokens from the token buffer.
160  // This saves some copies and it also reduces IdentifierInfo* lookup.
161  const unsigned char* p = CurPtr;
162  while (1) {
163    // Read the token kind.  Are we at the end of the file?
164    tok::TokenKind x = (tok::TokenKind) (uint8_t) *p;
165    if (x == tok::eof) break;
166
167    // Read the token flags.  Are we at the start of the next line?
168    Token::TokenFlags y = (Token::TokenFlags) (uint8_t) p[1];
169    if (y & Token::StartOfLine) break;
170
171    // Skip to the next token.
172    p += DISK_TOKEN_SIZE;
173  }
174
175  CurPtr = p;
176}
177
178/// SkipBlock - Used by Preprocessor to skip the current conditional block.
179bool PTHLexer::SkipBlock() {
180  assert(CurPPCondPtr && "No cached PP conditional information.");
181  assert(LastHashTokPtr && "No known '#' token.");
182
183  const unsigned char* HashEntryI = 0;
184  uint32_t Offset;
185  uint32_t TableIdx;
186
187  do {
188    // Read the token offset from the side-table.
189    Offset = ReadLE32(CurPPCondPtr);
190
191    // Read the target table index from the side-table.
192    TableIdx = ReadLE32(CurPPCondPtr);
193
194    // Compute the actual memory address of the '#' token data for this entry.
195    HashEntryI = TokBuf + Offset;
196
197    // Optmization: "Sibling jumping".  #if...#else...#endif blocks can
198    //  contain nested blocks.  In the side-table we can jump over these
199    //  nested blocks instead of doing a linear search if the next "sibling"
200    //  entry is not at a location greater than LastHashTokPtr.
201    if (HashEntryI < LastHashTokPtr && TableIdx) {
202      // In the side-table we are still at an entry for a '#' token that
203      // is earlier than the last one we saw.  Check if the location we would
204      // stride gets us closer.
205      const unsigned char* NextPPCondPtr =
206        PPCond + TableIdx*(sizeof(uint32_t)*2);
207      assert(NextPPCondPtr >= CurPPCondPtr);
208      // Read where we should jump to.
209      uint32_t TmpOffset = ReadLE32(NextPPCondPtr);
210      const unsigned char* HashEntryJ = TokBuf + TmpOffset;
211
212      if (HashEntryJ <= LastHashTokPtr) {
213        // Jump directly to the next entry in the side table.
214        HashEntryI = HashEntryJ;
215        Offset = TmpOffset;
216        TableIdx = ReadLE32(NextPPCondPtr);
217        CurPPCondPtr = NextPPCondPtr;
218      }
219    }
220  }
221  while (HashEntryI < LastHashTokPtr);
222  assert(HashEntryI == LastHashTokPtr && "No PP-cond entry found for '#'");
223  assert(TableIdx && "No jumping from #endifs.");
224
225  // Update our side-table iterator.
226  const unsigned char* NextPPCondPtr = PPCond + TableIdx*(sizeof(uint32_t)*2);
227  assert(NextPPCondPtr >= CurPPCondPtr);
228  CurPPCondPtr = NextPPCondPtr;
229
230  // Read where we should jump to.
231  HashEntryI = TokBuf + ReadLE32(NextPPCondPtr);
232  uint32_t NextIdx = ReadLE32(NextPPCondPtr);
233
234  // By construction NextIdx will be zero if this is a #endif.  This is useful
235  // to know to obviate lexing another token.
236  bool isEndif = NextIdx == 0;
237
238  // This case can occur when we see something like this:
239  //
240  //  #if ...
241  //   /* a comment or nothing */
242  //  #elif
243  //
244  // If we are skipping the first #if block it will be the case that CurPtr
245  // already points 'elif'.  Just return.
246
247  if (CurPtr > HashEntryI) {
248    assert(CurPtr == HashEntryI + DISK_TOKEN_SIZE);
249    // Did we reach a #endif?  If so, go ahead and consume that token as well.
250    if (isEndif)
251      CurPtr += DISK_TOKEN_SIZE*2;
252    else
253      LastHashTokPtr = HashEntryI;
254
255    return isEndif;
256  }
257
258  // Otherwise, we need to advance.  Update CurPtr to point to the '#' token.
259  CurPtr = HashEntryI;
260
261  // Update the location of the last observed '#'.  This is useful if we
262  // are skipping multiple blocks.
263  LastHashTokPtr = CurPtr;
264
265  // Skip the '#' token.
266  assert(((tok::TokenKind)*CurPtr) == tok::hash);
267  CurPtr += DISK_TOKEN_SIZE;
268
269  // Did we reach a #endif?  If so, go ahead and consume that token as well.
270  if (isEndif) { CurPtr += DISK_TOKEN_SIZE*2; }
271
272  return isEndif;
273}
274
275SourceLocation PTHLexer::getSourceLocation() {
276  // getSourceLocation is not on the hot path.  It is used to get the location
277  // of the next token when transitioning back to this lexer when done
278  // handling a #included file.  Just read the necessary data from the token
279  // data buffer to construct the SourceLocation object.
280  // NOTE: This is a virtual function; hence it is defined out-of-line.
281  const unsigned char *OffsetPtr = CurPtr + (DISK_TOKEN_SIZE - 4);
282  uint32_t Offset = ReadLE32(OffsetPtr);
283  return FileStartLoc.getFileLocWithOffset(Offset);
284}
285
286//===----------------------------------------------------------------------===//
287// PTH file lookup: map from strings to file data.
288//===----------------------------------------------------------------------===//
289
290/// PTHFileLookup - This internal data structure is used by the PTHManager
291///  to map from FileEntry objects managed by FileManager to offsets within
292///  the PTH file.
293namespace {
294class VISIBILITY_HIDDEN PTHFileData {
295  const uint32_t TokenOff;
296  const uint32_t PPCondOff;
297public:
298  PTHFileData(uint32_t tokenOff, uint32_t ppCondOff)
299    : TokenOff(tokenOff), PPCondOff(ppCondOff) {}
300
301  uint32_t getTokenOffset() const { return TokenOff; }
302  uint32_t getPPCondOffset() const { return PPCondOff; }
303};
304
305
306class VISIBILITY_HIDDEN PTHFileLookupCommonTrait {
307public:
308  typedef std::pair<unsigned char, const char*> internal_key_type;
309
310  static unsigned ComputeHash(internal_key_type x) {
311    return BernsteinHash(x.second);
312  }
313
314  static std::pair<unsigned, unsigned>
315  ReadKeyDataLength(const unsigned char*& d) {
316    unsigned keyLen = (unsigned) ReadUnalignedLE16(d);
317    unsigned dataLen = (unsigned) *(d++);
318    return std::make_pair(keyLen, dataLen);
319  }
320
321  static internal_key_type ReadKey(const unsigned char* d, unsigned) {
322    unsigned char k = *(d++); // Read the entry kind.
323    return std::make_pair(k, (const char*) d);
324  }
325};
326
327class VISIBILITY_HIDDEN PTHFileLookupTrait : public PTHFileLookupCommonTrait {
328public:
329  typedef const FileEntry* external_key_type;
330  typedef PTHFileData      data_type;
331
332  static internal_key_type GetInternalKey(const FileEntry* FE) {
333    return std::make_pair((unsigned char) 0x1, FE->getName());
334  }
335
336  static bool EqualKey(internal_key_type a, internal_key_type b) {
337    return a.first == b.first && strcmp(a.second, b.second) == 0;
338  }
339
340  static PTHFileData ReadData(const internal_key_type& k,
341                              const unsigned char* d, unsigned) {
342    assert(k.first == 0x1 && "Only file lookups can match!");
343    uint32_t x = ::ReadUnalignedLE32(d);
344    uint32_t y = ::ReadUnalignedLE32(d);
345    return PTHFileData(x, y);
346  }
347};
348
349class VISIBILITY_HIDDEN PTHStringLookupTrait {
350public:
351  typedef uint32_t
352          data_type;
353
354  typedef const std::pair<const char*, unsigned>
355          external_key_type;
356
357  typedef external_key_type internal_key_type;
358
359  static bool EqualKey(const internal_key_type& a,
360                       const internal_key_type& b) {
361    return (a.second == b.second) ? memcmp(a.first, b.first, a.second) == 0
362                                  : false;
363  }
364
365  static unsigned ComputeHash(const internal_key_type& a) {
366    return BernsteinHash(a.first, a.second);
367  }
368
369  // This hopefully will just get inlined and removed by the optimizer.
370  static const internal_key_type&
371  GetInternalKey(const external_key_type& x) { return x; }
372
373  static std::pair<unsigned, unsigned>
374  ReadKeyDataLength(const unsigned char*& d) {
375    return std::make_pair((unsigned) ReadUnalignedLE16(d), sizeof(uint32_t));
376  }
377
378  static std::pair<const char*, unsigned>
379  ReadKey(const unsigned char* d, unsigned n) {
380      assert(n >= 2 && d[n-1] == '\0');
381      return std::make_pair((const char*) d, n-1);
382    }
383
384  static uint32_t ReadData(const internal_key_type& k, const unsigned char* d,
385                           unsigned) {
386    return ::ReadUnalignedLE32(d);
387  }
388};
389
390} // end anonymous namespace
391
392typedef OnDiskChainedHashTable<PTHFileLookupTrait>   PTHFileLookup;
393typedef OnDiskChainedHashTable<PTHStringLookupTrait> PTHStringIdLookup;
394
395//===----------------------------------------------------------------------===//
396// PTHManager methods.
397//===----------------------------------------------------------------------===//
398
399PTHManager::PTHManager(const llvm::MemoryBuffer* buf, void* fileLookup,
400                       const unsigned char* idDataTable,
401                       IdentifierInfo** perIDCache,
402                       void* stringIdLookup, unsigned numIds,
403                       const unsigned char* spellingBase,
404                       const char* originalSourceFile)
405: Buf(buf), PerIDCache(perIDCache), FileLookup(fileLookup),
406  IdDataTable(idDataTable), StringIdLookup(stringIdLookup),
407  NumIds(numIds), PP(0), SpellingBase(spellingBase),
408  OriginalSourceFile(originalSourceFile) {}
409
410PTHManager::~PTHManager() {
411  delete Buf;
412  delete (PTHFileLookup*) FileLookup;
413  delete (PTHStringIdLookup*) StringIdLookup;
414  free(PerIDCache);
415}
416
417static void InvalidPTH(Diagnostic *Diags, Diagnostic::Level level,
418                       const char* Msg = 0) {
419  if (!Diags) return;
420  if (!Msg) Msg = "Invalid or corrupted PTH file";
421  unsigned DiagID = Diags->getCustomDiagID(level, Msg);
422  Diags->Report(FullSourceLoc(), DiagID);
423}
424
425PTHManager* PTHManager::Create(const std::string& file, Diagnostic* Diags,
426                               Diagnostic::Level level) {
427  // Memory map the PTH file.
428  llvm::OwningPtr<llvm::MemoryBuffer>
429  File(llvm::MemoryBuffer::getFile(file.c_str()));
430
431  if (!File) {
432    if (Diags) {
433      unsigned DiagID = Diags->getCustomDiagID(level,
434                                               "PTH file %0 could not be read");
435      Diags->Report(FullSourceLoc(), DiagID) << file;
436    }
437
438    return 0;
439  }
440
441  // Get the buffer ranges and check if there are at least three 32-bit
442  // words at the end of the file.
443  const unsigned char* BufBeg = (unsigned char*)File->getBufferStart();
444  const unsigned char* BufEnd = (unsigned char*)File->getBufferEnd();
445
446  // Check the prologue of the file.
447  if ((BufEnd - BufBeg) < (signed) (sizeof("cfe-pth") + 3 + 4) ||
448      memcmp(BufBeg, "cfe-pth", sizeof("cfe-pth") - 1) != 0) {
449    InvalidPTH(Diags, level);
450    return 0;
451  }
452
453  // Read the PTH version.
454  const unsigned char *p = BufBeg + (sizeof("cfe-pth") - 1);
455  unsigned Version = ReadLE32(p);
456
457  if (Version != PTHManager::Version) {
458    InvalidPTH(Diags, level,
459        Version < PTHManager::Version
460        ? "PTH file uses an older PTH format that is no longer supported"
461        : "PTH file uses a newer PTH format that cannot be read");
462    return 0;
463  }
464
465  // Compute the address of the index table at the end of the PTH file.
466  const unsigned char *PrologueOffset = p;
467
468  if (PrologueOffset >= BufEnd) {
469    InvalidPTH(Diags, level);
470    return 0;
471  }
472
473  // Construct the file lookup table.  This will be used for mapping from
474  // FileEntry*'s to cached tokens.
475  const unsigned char* FileTableOffset = PrologueOffset + sizeof(uint32_t)*2;
476  const unsigned char* FileTable = BufBeg + ReadLE32(FileTableOffset);
477
478  if (!(FileTable > BufBeg && FileTable < BufEnd)) {
479    InvalidPTH(Diags, level);
480    return 0; // FIXME: Proper error diagnostic?
481  }
482
483  llvm::OwningPtr<PTHFileLookup> FL(PTHFileLookup::Create(FileTable, BufBeg));
484
485  // Warn if the PTH file is empty.  We still want to create a PTHManager
486  // as the PTH could be used with -include-pth.
487  if (FL->isEmpty())
488    InvalidPTH(Diags, level, "PTH file contains no cached source data");
489
490  // Get the location of the table mapping from persistent ids to the
491  // data needed to reconstruct identifiers.
492  const unsigned char* IDTableOffset = PrologueOffset + sizeof(uint32_t)*0;
493  const unsigned char* IData = BufBeg + ReadLE32(IDTableOffset);
494
495  if (!(IData >= BufBeg && IData < BufEnd)) {
496    InvalidPTH(Diags, level);
497    return 0;
498  }
499
500  // Get the location of the hashtable mapping between strings and
501  // persistent IDs.
502  const unsigned char* StringIdTableOffset = PrologueOffset + sizeof(uint32_t)*1;
503  const unsigned char* StringIdTable = BufBeg + ReadLE32(StringIdTableOffset);
504  if (!(StringIdTable >= BufBeg && StringIdTable < BufEnd)) {
505    InvalidPTH(Diags, level);
506    return 0;
507  }
508
509  llvm::OwningPtr<PTHStringIdLookup> SL(PTHStringIdLookup::Create(StringIdTable,
510                                                                  BufBeg));
511
512  // Get the location of the spelling cache.
513  const unsigned char* spellingBaseOffset = PrologueOffset + sizeof(uint32_t)*3;
514  const unsigned char* spellingBase = BufBeg + ReadLE32(spellingBaseOffset);
515  if (!(spellingBase >= BufBeg && spellingBase < BufEnd)) {
516    InvalidPTH(Diags, level);
517    return 0;
518  }
519
520  // Get the number of IdentifierInfos and pre-allocate the identifier cache.
521  uint32_t NumIds = ReadLE32(IData);
522
523  // Pre-allocate the peristent ID -> IdentifierInfo* cache.  We use calloc()
524  // so that we in the best case only zero out memory once when the OS returns
525  // us new pages.
526  IdentifierInfo** PerIDCache = 0;
527
528  if (NumIds) {
529    PerIDCache = (IdentifierInfo**)calloc(NumIds, sizeof(*PerIDCache));
530    if (!PerIDCache) {
531      InvalidPTH(Diags, level,
532                 "Could not allocate memory for processing PTH file");
533      return 0;
534    }
535  }
536
537  // Compute the address of the original source file.
538  const unsigned char* originalSourceBase = PrologueOffset + sizeof(uint32_t)*4;
539  unsigned len = ReadUnalignedLE16(originalSourceBase);
540  if (!len) originalSourceBase = 0;
541
542  // Create the new PTHManager.
543  return new PTHManager(File.take(), FL.take(), IData, PerIDCache,
544                        SL.take(), NumIds, spellingBase,
545                        (const char*) originalSourceBase);
546}
547
548IdentifierInfo* PTHManager::LazilyCreateIdentifierInfo(unsigned PersistentID) {
549  // Look in the PTH file for the string data for the IdentifierInfo object.
550  const unsigned char* TableEntry = IdDataTable + sizeof(uint32_t)*PersistentID;
551  const unsigned char* IDData =
552    (const unsigned char*)Buf->getBufferStart() + ReadLE32(TableEntry);
553  assert(IDData < (const unsigned char*)Buf->getBufferEnd());
554
555  // Allocate the object.
556  std::pair<IdentifierInfo,const unsigned char*> *Mem =
557    Alloc.Allocate<std::pair<IdentifierInfo,const unsigned char*> >();
558
559  Mem->second = IDData;
560  assert(IDData[0] != '\0');
561  IdentifierInfo *II = new ((void*) Mem) IdentifierInfo();
562
563  // Store the new IdentifierInfo in the cache.
564  PerIDCache[PersistentID] = II;
565  assert(II->getName() && II->getName()[0] != '\0');
566  return II;
567}
568
569IdentifierInfo* PTHManager::get(const char *NameStart, const char *NameEnd) {
570  PTHStringIdLookup& SL = *((PTHStringIdLookup*)StringIdLookup);
571  // Double check our assumption that the last character isn't '\0'.
572  assert(NameEnd==NameStart || NameStart[NameEnd-NameStart-1] != '\0');
573  PTHStringIdLookup::iterator I = SL.find(std::make_pair(NameStart,
574                                                         NameEnd - NameStart));
575  if (I == SL.end()) // No identifier found?
576    return 0;
577
578  // Match found.  Return the identifier!
579  assert(*I > 0);
580  return GetIdentifierInfo(*I-1);
581}
582
583PTHLexer *PTHManager::CreateLexer(FileID FID) {
584  const FileEntry *FE = PP->getSourceManager().getFileEntryForID(FID);
585  if (!FE)
586    return 0;
587
588  // Lookup the FileEntry object in our file lookup data structure.  It will
589  // return a variant that indicates whether or not there is an offset within
590  // the PTH file that contains cached tokens.
591  PTHFileLookup& PFL = *((PTHFileLookup*)FileLookup);
592  PTHFileLookup::iterator I = PFL.find(FE);
593
594  if (I == PFL.end()) // No tokens available?
595    return 0;
596
597  const PTHFileData& FileData = *I;
598
599  const unsigned char *BufStart = (const unsigned char *)Buf->getBufferStart();
600  // Compute the offset of the token data within the buffer.
601  const unsigned char* data = BufStart + FileData.getTokenOffset();
602
603  // Get the location of pp-conditional table.
604  const unsigned char* ppcond = BufStart + FileData.getPPCondOffset();
605  uint32_t Len = ReadLE32(ppcond);
606  if (Len == 0) ppcond = 0;
607
608  assert(PP && "No preprocessor set yet!");
609  return new PTHLexer(*PP, FID, data, ppcond, *this);
610}
611
612//===----------------------------------------------------------------------===//
613// 'stat' caching.
614//===----------------------------------------------------------------------===//
615
616namespace {
617class VISIBILITY_HIDDEN PTHStatData {
618public:
619  const bool hasStat;
620  const ino_t ino;
621  const dev_t dev;
622  const mode_t mode;
623  const time_t mtime;
624  const off_t size;
625
626  PTHStatData(ino_t i, dev_t d, mode_t mo, time_t m, off_t s)
627  : hasStat(true), ino(i), dev(d), mode(mo), mtime(m), size(s) {}
628
629  PTHStatData()
630    : hasStat(false), ino(0), dev(0), mode(0), mtime(0), size(0) {}
631};
632
633class VISIBILITY_HIDDEN PTHStatLookupTrait : public PTHFileLookupCommonTrait {
634public:
635  typedef const char* external_key_type;  // const char*
636  typedef PTHStatData data_type;
637
638  static internal_key_type GetInternalKey(const char *path) {
639    // The key 'kind' doesn't matter here because it is ignored in EqualKey.
640    return std::make_pair((unsigned char) 0x0, path);
641  }
642
643  static bool EqualKey(internal_key_type a, internal_key_type b) {
644    // When doing 'stat' lookups we don't care about the kind of 'a' and 'b',
645    // just the paths.
646    return strcmp(a.second, b.second) == 0;
647  }
648
649  static data_type ReadData(const internal_key_type& k, const unsigned char* d,
650                            unsigned) {
651
652    if (k.first /* File or Directory */) {
653      if (k.first == 0x1 /* File */) d += 4 * 2; // Skip the first 2 words.
654      ino_t ino = (ino_t) ReadUnalignedLE32(d);
655      dev_t dev = (dev_t) ReadUnalignedLE32(d);
656      mode_t mode = (mode_t) ReadUnalignedLE16(d);
657      time_t mtime = (time_t) ReadUnalignedLE64(d);
658      return data_type(ino, dev, mode, mtime, (off_t) ReadUnalignedLE64(d));
659    }
660
661    // Negative stat.  Don't read anything.
662    return data_type();
663  }
664};
665
666class VISIBILITY_HIDDEN PTHStatCache : public StatSysCallCache {
667  typedef OnDiskChainedHashTable<PTHStatLookupTrait> CacheTy;
668  CacheTy Cache;
669
670public:
671  PTHStatCache(PTHFileLookup &FL) :
672    Cache(FL.getNumBuckets(), FL.getNumEntries(), FL.getBuckets(),
673          FL.getBase()) {}
674
675  ~PTHStatCache() {}
676
677  int stat(const char *path, struct stat *buf) {
678    // Do the lookup for the file's data in the PTH file.
679    CacheTy::iterator I = Cache.find(path);
680
681    // If we don't get a hit in the PTH file just forward to 'stat'.
682    if (I == Cache.end()) return ::stat(path, buf);
683
684    const PTHStatData& Data = *I;
685
686    if (!Data.hasStat)
687      return 1;
688
689    buf->st_ino = Data.ino;
690    buf->st_dev = Data.dev;
691    buf->st_mtime = Data.mtime;
692    buf->st_mode = Data.mode;
693    buf->st_size = Data.size;
694    return 0;
695  }
696};
697} // end anonymous namespace
698
699StatSysCallCache *PTHManager::createStatCache() {
700  return new PTHStatCache(*((PTHFileLookup*) FileLookup));
701}
702