1 //===--- PTHLexer.cpp - Lex from a token stream ---------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file implements the PTHLexer interface.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Basic/TokenKinds.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Basic/FileSystemStatCache.h"
17 #include "clang/Basic/IdentifierTable.h"
18 #include "clang/Basic/OnDiskHashTable.h"
19 #include "clang/Lex/LexDiagnostic.h"
20 #include "clang/Lex/PTHLexer.h"
21 #include "clang/Lex/Preprocessor.h"
22 #include "clang/Lex/PTHManager.h"
23 #include "clang/Lex/Token.h"
24 #include "clang/Lex/Preprocessor.h"
25 #include "llvm/ADT/OwningPtr.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/ADT/StringMap.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/system_error.h"
30 using namespace clang
;
31 using namespace clang::io
;
33 #define DISK_TOKEN_SIZE (1+1+2+4+4)
35 //===----------------------------------------------------------------------===//
37 //===----------------------------------------------------------------------===//
39 PTHLexer::PTHLexer(Preprocessor
&PP
, FileID FID
, const unsigned char *D
,
40 const unsigned char *ppcond
, PTHManager
&PM
)
41 : PreprocessorLexer(&PP
, FID
), TokBuf(D
), CurPtr(D
), LastHashTokPtr(0),
42 PPCond(ppcond
), CurPPCondPtr(ppcond
), PTHMgr(PM
) {
44 FileStartLoc
= PP
.getSourceManager().getLocForStartOfFile(FID
);
47 void PTHLexer::Lex(Token
& Tok
) {
50 //===--------------------------------------==//
51 // Read the raw token data.
52 //===--------------------------------------==//
54 // Shadow CurPtr into an automatic variable.
55 const unsigned char *CurPtrShadow
= CurPtr
;
57 // Read in the data for the token.
58 unsigned Word0
= ReadLE32(CurPtrShadow
);
59 uint32_t IdentifierID
= ReadLE32(CurPtrShadow
);
60 uint32_t FileOffset
= ReadLE32(CurPtrShadow
);
62 tok::TokenKind TKind
= (tok::TokenKind
) (Word0
& 0xFF);
63 Token::TokenFlags TFlags
= (Token::TokenFlags
) ((Word0
>> 8) & 0xFF);
64 uint32_t Len
= Word0
>> 16;
66 CurPtr
= CurPtrShadow
;
68 //===--------------------------------------==//
69 // Construct the token itself.
70 //===--------------------------------------==//
75 assert(!LexingRawMode
);
76 Tok
.setLocation(FileStartLoc
.getFileLocWithOffset(FileOffset
));
79 // Handle identifiers.
80 if (Tok
.isLiteral()) {
81 Tok
.setLiteralData((const char*) (PTHMgr
.SpellingBase
+ IdentifierID
));
83 else if (IdentifierID
) {
85 IdentifierInfo
*II
= PTHMgr
.GetIdentifierInfo(IdentifierID
-1);
87 Tok
.setIdentifierInfo(II
);
89 // Change the kind of this identifier to the appropriate token kind, e.g.
90 // turning "for" into a keyword.
91 Tok
.setKind(II
->getTokenID());
93 if (II
->isHandleIdentifierCase())
94 PP
->HandleIdentifier(Tok
);
98 //===--------------------------------------==//
100 //===--------------------------------------==//
101 if (TKind
== tok::eof
) {
102 // Save the end-of-file token.
105 // Save 'PP' to 'PPCache' as LexEndOfFile can delete 'this'.
106 Preprocessor
*PPCache
= PP
;
108 assert(!ParsingPreprocessorDirective
);
109 assert(!LexingRawMode
);
111 if (LexEndOfFile(Tok
))
114 return PPCache
->Lex(Tok
);
117 if (TKind
== tok::hash
&& Tok
.isAtStartOfLine()) {
118 LastHashTokPtr
= CurPtr
- DISK_TOKEN_SIZE
;
119 assert(!LexingRawMode
);
120 PP
->HandleDirective(Tok
);
122 if (PP
->isCurrentLexer(this))
128 if (TKind
== tok::eom
) {
129 assert(ParsingPreprocessorDirective
);
130 ParsingPreprocessorDirective
= false;
137 bool PTHLexer::LexEndOfFile(Token
&Result
) {
138 // If we hit the end of the file while parsing a preprocessor directive,
139 // end the preprocessor directive first. The next token returned will
140 // then be the end of file.
141 if (ParsingPreprocessorDirective
) {
142 ParsingPreprocessorDirective
= false; // Done parsing the "line".
143 return true; // Have a token.
146 assert(!LexingRawMode
);
148 // If we are in a #if directive, emit an error.
149 while (!ConditionalStack
.empty()) {
150 if (!PP
->isCodeCompletionFile(FileStartLoc
))
151 PP
->Diag(ConditionalStack
.back().IfLoc
,
152 diag::err_pp_unterminated_conditional
);
153 ConditionalStack
.pop_back();
156 // Finally, let the preprocessor handle this.
157 return PP
->HandleEndOfFile(Result
);
160 // FIXME: We can just grab the last token instead of storing a copy
162 void PTHLexer::getEOF(Token
& Tok
) {
163 assert(EofToken
.is(tok::eof
));
167 void PTHLexer::DiscardToEndOfLine() {
168 assert(ParsingPreprocessorDirective
&& ParsingFilename
== false &&
169 "Must be in a preprocessing directive!");
171 // We assume that if the preprocessor wishes to discard to the end of
172 // the line that it also means to end the current preprocessor directive.
173 ParsingPreprocessorDirective
= false;
175 // Skip tokens by only peeking at their token kind and the flags.
176 // We don't need to actually reconstruct full tokens from the token buffer.
177 // This saves some copies and it also reduces IdentifierInfo* lookup.
178 const unsigned char* p
= CurPtr
;
180 // Read the token kind. Are we at the end of the file?
181 tok::TokenKind x
= (tok::TokenKind
) (uint8_t) *p
;
182 if (x
== tok::eof
) break;
184 // Read the token flags. Are we at the start of the next line?
185 Token::TokenFlags y
= (Token::TokenFlags
) (uint8_t) p
[1];
186 if (y
& Token::StartOfLine
) break;
188 // Skip to the next token.
189 p
+= DISK_TOKEN_SIZE
;
195 /// SkipBlock - Used by Preprocessor to skip the current conditional block.
196 bool PTHLexer::SkipBlock() {
197 assert(CurPPCondPtr
&& "No cached PP conditional information.");
198 assert(LastHashTokPtr
&& "No known '#' token.");
200 const unsigned char* HashEntryI
= 0;
205 // Read the token offset from the side-table.
206 Offset
= ReadLE32(CurPPCondPtr
);
208 // Read the target table index from the side-table.
209 TableIdx
= ReadLE32(CurPPCondPtr
);
211 // Compute the actual memory address of the '#' token data for this entry.
212 HashEntryI
= TokBuf
+ Offset
;
214 // Optmization: "Sibling jumping". #if...#else...#endif blocks can
215 // contain nested blocks. In the side-table we can jump over these
216 // nested blocks instead of doing a linear search if the next "sibling"
217 // entry is not at a location greater than LastHashTokPtr.
218 if (HashEntryI
< LastHashTokPtr
&& TableIdx
) {
219 // In the side-table we are still at an entry for a '#' token that
220 // is earlier than the last one we saw. Check if the location we would
221 // stride gets us closer.
222 const unsigned char* NextPPCondPtr
=
223 PPCond
+ TableIdx
*(sizeof(uint32_t)*2);
224 assert(NextPPCondPtr
>= CurPPCondPtr
);
225 // Read where we should jump to.
226 uint32_t TmpOffset
= ReadLE32(NextPPCondPtr
);
227 const unsigned char* HashEntryJ
= TokBuf
+ TmpOffset
;
229 if (HashEntryJ
<= LastHashTokPtr
) {
230 // Jump directly to the next entry in the side table.
231 HashEntryI
= HashEntryJ
;
233 TableIdx
= ReadLE32(NextPPCondPtr
);
234 CurPPCondPtr
= NextPPCondPtr
;
238 while (HashEntryI
< LastHashTokPtr
);
239 assert(HashEntryI
== LastHashTokPtr
&& "No PP-cond entry found for '#'");
240 assert(TableIdx
&& "No jumping from #endifs.");
242 // Update our side-table iterator.
243 const unsigned char* NextPPCondPtr
= PPCond
+ TableIdx
*(sizeof(uint32_t)*2);
244 assert(NextPPCondPtr
>= CurPPCondPtr
);
245 CurPPCondPtr
= NextPPCondPtr
;
247 // Read where we should jump to.
248 HashEntryI
= TokBuf
+ ReadLE32(NextPPCondPtr
);
249 uint32_t NextIdx
= ReadLE32(NextPPCondPtr
);
251 // By construction NextIdx will be zero if this is a #endif. This is useful
252 // to know to obviate lexing another token.
253 bool isEndif
= NextIdx
== 0;
255 // This case can occur when we see something like this:
258 // /* a comment or nothing */
261 // If we are skipping the first #if block it will be the case that CurPtr
262 // already points 'elif'. Just return.
264 if (CurPtr
> HashEntryI
) {
265 assert(CurPtr
== HashEntryI
+ DISK_TOKEN_SIZE
);
266 // Did we reach a #endif? If so, go ahead and consume that token as well.
268 CurPtr
+= DISK_TOKEN_SIZE
*2;
270 LastHashTokPtr
= HashEntryI
;
275 // Otherwise, we need to advance. Update CurPtr to point to the '#' token.
278 // Update the location of the last observed '#'. This is useful if we
279 // are skipping multiple blocks.
280 LastHashTokPtr
= CurPtr
;
282 // Skip the '#' token.
283 assert(((tok::TokenKind
)*CurPtr
) == tok::hash
);
284 CurPtr
+= DISK_TOKEN_SIZE
;
286 // Did we reach a #endif? If so, go ahead and consume that token as well.
287 if (isEndif
) { CurPtr
+= DISK_TOKEN_SIZE
*2; }
292 SourceLocation
PTHLexer::getSourceLocation() {
293 // getSourceLocation is not on the hot path. It is used to get the location
294 // of the next token when transitioning back to this lexer when done
295 // handling a #included file. Just read the necessary data from the token
296 // data buffer to construct the SourceLocation object.
297 // NOTE: This is a virtual function; hence it is defined out-of-line.
298 const unsigned char *OffsetPtr
= CurPtr
+ (DISK_TOKEN_SIZE
- 4);
299 uint32_t Offset
= ReadLE32(OffsetPtr
);
300 return FileStartLoc
.getFileLocWithOffset(Offset
);
303 //===----------------------------------------------------------------------===//
304 // PTH file lookup: map from strings to file data.
305 //===----------------------------------------------------------------------===//
307 /// PTHFileLookup - This internal data structure is used by the PTHManager
308 /// to map from FileEntry objects managed by FileManager to offsets within
312 const uint32_t TokenOff
;
313 const uint32_t PPCondOff
;
315 PTHFileData(uint32_t tokenOff
, uint32_t ppCondOff
)
316 : TokenOff(tokenOff
), PPCondOff(ppCondOff
) {}
318 uint32_t getTokenOffset() const { return TokenOff
; }
319 uint32_t getPPCondOffset() const { return PPCondOff
; }
323 class PTHFileLookupCommonTrait
{
325 typedef std::pair
<unsigned char, const char*> internal_key_type
;
327 static unsigned ComputeHash(internal_key_type x
) {
328 return llvm::HashString(x
.second
);
331 static std::pair
<unsigned, unsigned>
332 ReadKeyDataLength(const unsigned char*& d
) {
333 unsigned keyLen
= (unsigned) ReadUnalignedLE16(d
);
334 unsigned dataLen
= (unsigned) *(d
++);
335 return std::make_pair(keyLen
, dataLen
);
338 static internal_key_type
ReadKey(const unsigned char* d
, unsigned) {
339 unsigned char k
= *(d
++); // Read the entry kind.
340 return std::make_pair(k
, (const char*) d
);
344 class PTHFileLookupTrait
: public PTHFileLookupCommonTrait
{
346 typedef const FileEntry
* external_key_type
;
347 typedef PTHFileData data_type
;
349 static internal_key_type
GetInternalKey(const FileEntry
* FE
) {
350 return std::make_pair((unsigned char) 0x1, FE
->getName());
353 static bool EqualKey(internal_key_type a
, internal_key_type b
) {
354 return a
.first
== b
.first
&& strcmp(a
.second
, b
.second
) == 0;
357 static PTHFileData
ReadData(const internal_key_type
& k
,
358 const unsigned char* d
, unsigned) {
359 assert(k
.first
== 0x1 && "Only file lookups can match!");
360 uint32_t x
= ::ReadUnalignedLE32(d
);
361 uint32_t y
= ::ReadUnalignedLE32(d
);
362 return PTHFileData(x
, y
);
366 class PTHStringLookupTrait
{
371 typedef const std::pair
<const char*, unsigned>
374 typedef external_key_type internal_key_type
;
376 static bool EqualKey(const internal_key_type
& a
,
377 const internal_key_type
& b
) {
378 return (a
.second
== b
.second
) ? memcmp(a
.first
, b
.first
, a
.second
) == 0
382 static unsigned ComputeHash(const internal_key_type
& a
) {
383 return llvm::HashString(llvm::StringRef(a
.first
, a
.second
));
386 // This hopefully will just get inlined and removed by the optimizer.
387 static const internal_key_type
&
388 GetInternalKey(const external_key_type
& x
) { return x
; }
390 static std::pair
<unsigned, unsigned>
391 ReadKeyDataLength(const unsigned char*& d
) {
392 return std::make_pair((unsigned) ReadUnalignedLE16(d
), sizeof(uint32_t));
395 static std::pair
<const char*, unsigned>
396 ReadKey(const unsigned char* d
, unsigned n
) {
397 assert(n
>= 2 && d
[n
-1] == '\0');
398 return std::make_pair((const char*) d
, n
-1);
401 static uint32_t ReadData(const internal_key_type
& k
, const unsigned char* d
,
403 return ::ReadUnalignedLE32(d
);
407 } // end anonymous namespace
409 typedef OnDiskChainedHashTable
<PTHFileLookupTrait
> PTHFileLookup
;
410 typedef OnDiskChainedHashTable
<PTHStringLookupTrait
> PTHStringIdLookup
;
412 //===----------------------------------------------------------------------===//
413 // PTHManager methods.
414 //===----------------------------------------------------------------------===//
416 PTHManager::PTHManager(const llvm::MemoryBuffer
* buf
, void* fileLookup
,
417 const unsigned char* idDataTable
,
418 IdentifierInfo
** perIDCache
,
419 void* stringIdLookup
, unsigned numIds
,
420 const unsigned char* spellingBase
,
421 const char* originalSourceFile
)
422 : Buf(buf
), PerIDCache(perIDCache
), FileLookup(fileLookup
),
423 IdDataTable(idDataTable
), StringIdLookup(stringIdLookup
),
424 NumIds(numIds
), PP(0), SpellingBase(spellingBase
),
425 OriginalSourceFile(originalSourceFile
) {}
427 PTHManager::~PTHManager() {
429 delete (PTHFileLookup
*) FileLookup
;
430 delete (PTHStringIdLookup
*) StringIdLookup
;
434 static void InvalidPTH(Diagnostic
&Diags
, const char *Msg
) {
435 Diags
.Report(Diags
.getCustomDiagID(Diagnostic::Error
, Msg
));
438 PTHManager
*PTHManager::Create(const std::string
&file
, Diagnostic
&Diags
) {
439 // Memory map the PTH file.
440 llvm::OwningPtr
<llvm::MemoryBuffer
> File
;
442 if (llvm::MemoryBuffer::getFile(file
, File
)) {
443 // FIXME: Add ec.message() to this diag.
444 Diags
.Report(diag::err_invalid_pth_file
) << file
;
448 // Get the buffer ranges and check if there are at least three 32-bit
449 // words at the end of the file.
450 const unsigned char *BufBeg
= (unsigned char*)File
->getBufferStart();
451 const unsigned char *BufEnd
= (unsigned char*)File
->getBufferEnd();
453 // Check the prologue of the file.
454 if ((BufEnd
- BufBeg
) < (signed)(sizeof("cfe-pth") + 3 + 4) ||
455 memcmp(BufBeg
, "cfe-pth", sizeof("cfe-pth") - 1) != 0) {
456 Diags
.Report(diag::err_invalid_pth_file
) << file
;
460 // Read the PTH version.
461 const unsigned char *p
= BufBeg
+ (sizeof("cfe-pth") - 1);
462 unsigned Version
= ReadLE32(p
);
464 if (Version
< PTHManager::Version
) {
466 Version
< PTHManager::Version
467 ? "PTH file uses an older PTH format that is no longer supported"
468 : "PTH file uses a newer PTH format that cannot be read");
472 // Compute the address of the index table at the end of the PTH file.
473 const unsigned char *PrologueOffset
= p
;
475 if (PrologueOffset
>= BufEnd
) {
476 Diags
.Report(diag::err_invalid_pth_file
) << file
;
480 // Construct the file lookup table. This will be used for mapping from
481 // FileEntry*'s to cached tokens.
482 const unsigned char* FileTableOffset
= PrologueOffset
+ sizeof(uint32_t)*2;
483 const unsigned char* FileTable
= BufBeg
+ ReadLE32(FileTableOffset
);
485 if (!(FileTable
> BufBeg
&& FileTable
< BufEnd
)) {
486 Diags
.Report(diag::err_invalid_pth_file
) << file
;
487 return 0; // FIXME: Proper error diagnostic?
490 llvm::OwningPtr
<PTHFileLookup
> FL(PTHFileLookup::Create(FileTable
, BufBeg
));
492 // Warn if the PTH file is empty. We still want to create a PTHManager
493 // as the PTH could be used with -include-pth.
495 InvalidPTH(Diags
, "PTH file contains no cached source data");
497 // Get the location of the table mapping from persistent ids to the
498 // data needed to reconstruct identifiers.
499 const unsigned char* IDTableOffset
= PrologueOffset
+ sizeof(uint32_t)*0;
500 const unsigned char* IData
= BufBeg
+ ReadLE32(IDTableOffset
);
502 if (!(IData
>= BufBeg
&& IData
< BufEnd
)) {
503 Diags
.Report(diag::err_invalid_pth_file
) << file
;
507 // Get the location of the hashtable mapping between strings and
509 const unsigned char* StringIdTableOffset
= PrologueOffset
+ sizeof(uint32_t)*1;
510 const unsigned char* StringIdTable
= BufBeg
+ ReadLE32(StringIdTableOffset
);
511 if (!(StringIdTable
>= BufBeg
&& StringIdTable
< BufEnd
)) {
512 Diags
.Report(diag::err_invalid_pth_file
) << file
;
516 llvm::OwningPtr
<PTHStringIdLookup
> SL(PTHStringIdLookup::Create(StringIdTable
,
519 // Get the location of the spelling cache.
520 const unsigned char* spellingBaseOffset
= PrologueOffset
+ sizeof(uint32_t)*3;
521 const unsigned char* spellingBase
= BufBeg
+ ReadLE32(spellingBaseOffset
);
522 if (!(spellingBase
>= BufBeg
&& spellingBase
< BufEnd
)) {
523 Diags
.Report(diag::err_invalid_pth_file
) << file
;
527 // Get the number of IdentifierInfos and pre-allocate the identifier cache.
528 uint32_t NumIds
= ReadLE32(IData
);
530 // Pre-allocate the peristent ID -> IdentifierInfo* cache. We use calloc()
531 // so that we in the best case only zero out memory once when the OS returns
533 IdentifierInfo
** PerIDCache
= 0;
536 PerIDCache
= (IdentifierInfo
**)calloc(NumIds
, sizeof(*PerIDCache
));
538 InvalidPTH(Diags
, "Could not allocate memory for processing PTH file");
543 // Compute the address of the original source file.
544 const unsigned char* originalSourceBase
= PrologueOffset
+ sizeof(uint32_t)*4;
545 unsigned len
= ReadUnalignedLE16(originalSourceBase
);
546 if (!len
) originalSourceBase
= 0;
548 // Create the new PTHManager.
549 return new PTHManager(File
.take(), FL
.take(), IData
, PerIDCache
,
550 SL
.take(), NumIds
, spellingBase
,
551 (const char*) originalSourceBase
);
554 IdentifierInfo
* PTHManager::LazilyCreateIdentifierInfo(unsigned PersistentID
) {
555 // Look in the PTH file for the string data for the IdentifierInfo object.
556 const unsigned char* TableEntry
= IdDataTable
+ sizeof(uint32_t)*PersistentID
;
557 const unsigned char* IDData
=
558 (const unsigned char*)Buf
->getBufferStart() + ReadLE32(TableEntry
);
559 assert(IDData
< (const unsigned char*)Buf
->getBufferEnd());
561 // Allocate the object.
562 std::pair
<IdentifierInfo
,const unsigned char*> *Mem
=
563 Alloc
.Allocate
<std::pair
<IdentifierInfo
,const unsigned char*> >();
565 Mem
->second
= IDData
;
566 assert(IDData
[0] != '\0');
567 IdentifierInfo
*II
= new ((void*) Mem
) IdentifierInfo();
569 // Store the new IdentifierInfo in the cache.
570 PerIDCache
[PersistentID
] = II
;
571 assert(II
->getNameStart() && II
->getNameStart()[0] != '\0');
575 IdentifierInfo
* PTHManager::get(llvm::StringRef Name
) {
576 PTHStringIdLookup
& SL
= *((PTHStringIdLookup
*)StringIdLookup
);
577 // Double check our assumption that the last character isn't '\0'.
578 assert(Name
.empty() || Name
.data()[Name
.size()-1] != '\0');
579 PTHStringIdLookup::iterator I
= SL
.find(std::make_pair(Name
.data(),
581 if (I
== SL
.end()) // No identifier found?
584 // Match found. Return the identifier!
586 return GetIdentifierInfo(*I
-1);
589 PTHLexer
*PTHManager::CreateLexer(FileID FID
) {
590 const FileEntry
*FE
= PP
->getSourceManager().getFileEntryForID(FID
);
594 // Lookup the FileEntry object in our file lookup data structure. It will
595 // return a variant that indicates whether or not there is an offset within
596 // the PTH file that contains cached tokens.
597 PTHFileLookup
& PFL
= *((PTHFileLookup
*)FileLookup
);
598 PTHFileLookup::iterator I
= PFL
.find(FE
);
600 if (I
== PFL
.end()) // No tokens available?
603 const PTHFileData
& FileData
= *I
;
605 const unsigned char *BufStart
= (const unsigned char *)Buf
->getBufferStart();
606 // Compute the offset of the token data within the buffer.
607 const unsigned char* data
= BufStart
+ FileData
.getTokenOffset();
609 // Get the location of pp-conditional table.
610 const unsigned char* ppcond
= BufStart
+ FileData
.getPPCondOffset();
611 uint32_t Len
= ReadLE32(ppcond
);
612 if (Len
== 0) ppcond
= 0;
614 assert(PP
&& "No preprocessor set yet!");
615 return new PTHLexer(*PP
, FID
, data
, ppcond
, *this);
618 //===----------------------------------------------------------------------===//
620 //===----------------------------------------------------------------------===//
632 PTHStatData(ino_t i
, dev_t d
, mode_t mo
, time_t m
, off_t s
)
633 : hasStat(true), ino(i
), dev(d
), mode(mo
), mtime(m
), size(s
) {}
636 : hasStat(false), ino(0), dev(0), mode(0), mtime(0), size(0) {}
639 class PTHStatLookupTrait
: public PTHFileLookupCommonTrait
{
641 typedef const char* external_key_type
; // const char*
642 typedef PTHStatData data_type
;
644 static internal_key_type
GetInternalKey(const char *path
) {
645 // The key 'kind' doesn't matter here because it is ignored in EqualKey.
646 return std::make_pair((unsigned char) 0x0, path
);
649 static bool EqualKey(internal_key_type a
, internal_key_type b
) {
650 // When doing 'stat' lookups we don't care about the kind of 'a' and 'b',
652 return strcmp(a
.second
, b
.second
) == 0;
655 static data_type
ReadData(const internal_key_type
& k
, const unsigned char* d
,
658 if (k
.first
/* File or Directory */) {
659 if (k
.first
== 0x1 /* File */) d
+= 4 * 2; // Skip the first 2 words.
660 ino_t ino
= (ino_t
) ReadUnalignedLE32(d
);
661 dev_t dev
= (dev_t
) ReadUnalignedLE32(d
);
662 mode_t mode
= (mode_t
) ReadUnalignedLE16(d
);
663 time_t mtime
= (time_t) ReadUnalignedLE64(d
);
664 return data_type(ino
, dev
, mode
, mtime
, (off_t
) ReadUnalignedLE64(d
));
667 // Negative stat. Don't read anything.
672 class PTHStatCache
: public FileSystemStatCache
{
673 typedef OnDiskChainedHashTable
<PTHStatLookupTrait
> CacheTy
;
677 PTHStatCache(PTHFileLookup
&FL
) :
678 Cache(FL
.getNumBuckets(), FL
.getNumEntries(), FL
.getBuckets(),
683 LookupResult
getStat(const char *Path
, struct stat
&StatBuf
,
684 int *FileDescriptor
) {
685 // Do the lookup for the file's data in the PTH file.
686 CacheTy::iterator I
= Cache
.find(Path
);
688 // If we don't get a hit in the PTH file just forward to 'stat'.
689 if (I
== Cache
.end())
690 return statChained(Path
, StatBuf
, FileDescriptor
);
692 const PTHStatData
&Data
= *I
;
697 StatBuf
.st_ino
= Data
.ino
;
698 StatBuf
.st_dev
= Data
.dev
;
699 StatBuf
.st_mtime
= Data
.mtime
;
700 StatBuf
.st_mode
= Data
.mode
;
701 StatBuf
.st_size
= Data
.size
;
705 } // end anonymous namespace
707 FileSystemStatCache
*PTHManager::createStatCache() {
708 return new PTHStatCache(*((PTHFileLookup
*) FileLookup
));