From e5d66b2a15323e31b418a43628ba4bcd31f193f9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 20 Jul 2020 16:55:17 +0200 Subject: qdoc: Cache source files when retrieving code snippets qdoc often retrieves the contents of a source file and matching headers via the clang cursors when parsing. Cache the last files except the dummy file to avoid reading the same file many times. This avoids roughly 43000 file reads in qtbase. Change-Id: I33fcc57039acfc667e095e782cdd6eb4f592028b Reviewed-by: Luca Di Sera (cherry picked from commit d25ee859693fdbcdd18e41a2411b5627ff6261b1) Reviewed-by: Qt Cherry-pick Bot --- src/qdoc/clangcodeparser.cpp | 54 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index c369b1c02..60ce615be 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -38,7 +38,7 @@ static CXTranslationUnit_Flags flags_ = static_cast(0); static CXIndex index_ = nullptr; QByteArray ClangCodeParser::s_fn; -constexpr const char *fnDummyFileName = "/fn_dummyfile.cpp"; +constexpr const char fnDummyFileName[] = "/fn_dummyfile.cpp"; #ifndef QT_NO_DEBUG_STREAM template @@ -166,6 +166,48 @@ static Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec) /*! Returns the spelling in the file for a source range */ + +struct FileCacheEntry +{ + QByteArray fileName; + QByteArray content; +}; + +static inline QString fromCache(const QByteArray &cache, + unsigned int offset1, unsigned int offset2) +{ + return QString::fromUtf8(cache.mid(offset1, offset2 - offset1)); +} + +static QString readFile(CXFile cxFile, unsigned int offset1, unsigned int offset2) +{ + using FileCache = QList; + static FileCache cache; + + CXString cxFileName = clang_getFileName(cxFile); + const QByteArray fileName = clang_getCString(cxFileName); + clang_disposeString(cxFileName); + + for (const auto &entry : std::as_const(cache)) { + if (fileName == entry.fileName) + return fromCache(entry.content, offset1, offset2); + } + + // "fn_dummyfile.cpp" comes with varying cxFile values + if (fileName == fnDummyFileName) + return fromCache(ClangCodeParser::fn(), offset1, offset2); + + QFile file(QString::fromUtf8(fileName)); + if (file.open(QIODeviceBase::ReadOnly)) { // binary to match clang offsets + FileCacheEntry entry{fileName, file.readAll()}; + cache.prepend(entry); + while (cache.size() > 5) + cache.removeLast(); + return fromCache(entry.content, offset1, offset2); + } + return {}; +} + static QString getSpelling(CXSourceRange range) { auto start = clang_getRangeStart(range); @@ -177,14 +219,8 @@ static QString getSpelling(CXSourceRange range) if (file1 != file2 || offset2 <= offset1) return QString(); - QFile file(fromCXString(clang_getFileName(file1))); - if (!file.open(QFile::ReadOnly)) { - if (file.fileName() == fnDummyFileName) - return QString::fromUtf8(ClangCodeParser::fn().mid(offset1, offset2 - offset1)); - return QString(); - } - file.seek(offset1); - return QString::fromUtf8(file.read(offset2 - offset1)); + + return readFile(file1, offset1, offset2); } /*! -- cgit v1.2.1