Skip to content

Commit 357fe5d

Browse files
committed
WIP cache
1 parent 9bdbf8e commit 357fe5d

1 file changed

Lines changed: 50 additions & 4 deletions

File tree

‎lib/errorlogger.cpp‎

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@
3636
#include <cstring>
3737
#include <fstream>
3838
#include <iomanip>
39-
#include <numeric>
39+
#include <ios>
40+
#include <list>
4041
#include <sstream>
4142
#include <string>
4243
#include <unordered_map>
4344
#include <utility>
45+
#include <vector>
4446

4547
#include "xml.h"
4648

@@ -639,13 +641,57 @@ std::string ErrorMessage::toXML() const
639641
return printer.CStr();
640642
}
641643

644+
// Byte offset of the start of each line, indexed by 1-based line number.
645+
// Building this once per file keeps readCode() cheap. Without it every call
646+
// rescans the file from the start, which is quadratic for a file that produces
647+
// many findings - vendor/generated headers routinely produce tens of thousands.
648+
static const std::vector<std::streamoff>* getLineOffsets(const std::string &file)
649+
{
650+
// thread_local: the thread executor calls this concurrently
651+
static thread_local std::list<std::pair<std::string, std::vector<std::streamoff>>> cache;
652+
653+
for (auto it = cache.begin(); it != cache.end(); ++it) {
654+
if (it->first == file) {
655+
// most recently used goes first
656+
cache.splice(cache.begin(), cache, it);
657+
return &cache.front().second;
658+
}
659+
}
660+
661+
// Binary mode so the offsets match what the seek in readCode() expects.
662+
// A trailing '\r' is stripped by the caller.
663+
std::ifstream fin(file, std::ios::binary);
664+
if (!fin.is_open())
665+
return nullptr;
666+
667+
std::vector<std::streamoff> offsets{0, 0}; // index 0 is unused, line 1 starts at offset 0
668+
std::array<char, 64 * 1024> buf;
669+
std::streamoff pos = 0;
670+
while (fin.read(buf.data(), buf.size()) || fin.gcount() > 0) {
671+
const std::streamsize n = fin.gcount();
672+
for (std::streamsize i = 0; i < n; ++i) {
673+
if (buf[i] == '\n')
674+
offsets.push_back(pos + i + 1);
675+
}
676+
pos += n;
677+
}
678+
679+
constexpr std::size_t maxCachedFiles = 4;
680+
if (cache.size() >= maxCachedFiles)
681+
cache.pop_back();
682+
cache.emplace_front(file, std::move(offsets));
683+
return &cache.front().second;
684+
}
685+
642686
// TODO: read info from some shared resource instead?
643687
static std::string readCode(const std::string &file, int linenr, int column, const char endl[])
644688
{
645-
std::ifstream fin(file);
646689
std::string line;
647-
while (linenr > 0 && std::getline(fin,line)) {
648-
linenr--;
690+
const std::vector<std::streamoff>* const offsets = getLineOffsets(file);
691+
if (offsets && linenr > 0 && linenr < static_cast<int>(offsets->size())) {
692+
std::ifstream fin(file, std::ios::binary);
693+
fin.seekg((*offsets)[linenr]);
694+
std::getline(fin, line);
649695
}
650696
const std::string::size_type endPos = line.find_last_not_of("\r\n\t ");
651697
if (endPos + 1 < line.size())

0 commit comments

Comments
 (0)