From 14d1c14137c08209d60c4d156bc279daf8048d02 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Fri, 20 Mar 2026 11:12:19 +0100 Subject: [PATCH 1/2] Fix xpath case issue --- lib/KOReaderSync/ChapterXPathIndexer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/KOReaderSync/ChapterXPathIndexer.cpp b/lib/KOReaderSync/ChapterXPathIndexer.cpp index 96325fd2..e0098e93 100644 --- a/lib/KOReaderSync/ChapterXPathIndexer.cpp +++ b/lib/KOReaderSync/ChapterXPathIndexer.cpp @@ -45,7 +45,7 @@ struct ParserState { std::vector> siblingCounters; std::vector anchors; - std::string baseXPath() const { return "/body/docfragment[" + std::to_string(spineIndex + 1) + "]/body"; } + std::string baseXPath() const { return "/body/DocFragment[" + std::to_string(spineIndex + 1) + "]/body"; } // Canonicalize incoming KOReader XPath before matching: // - remove all whitespace From beeea3872190ef18488cdc40d1a7eb681fe8847c Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sat, 21 Mar 2026 09:04:03 +0100 Subject: [PATCH 2/2] Remove cache - caused OOM in certain scenarios --- lib/KOReaderSync/ChapterXPathIndexer.cpp | 56 +----------------------- 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/lib/KOReaderSync/ChapterXPathIndexer.cpp b/lib/KOReaderSync/ChapterXPathIndexer.cpp index e0098e93..4ddcdd3c 100644 --- a/lib/KOReaderSync/ChapterXPathIndexer.cpp +++ b/lib/KOReaderSync/ChapterXPathIndexer.cpp @@ -463,63 +463,11 @@ static std::optional parseSpineItem(const std::shared_ptr& ep return state; } -// Keep a single parsed spine in memory to avoid repeated decompress+parse work -// in the KO sync flow. -// -// Why this helps here: -// - Sync result screen calls both mapping directions (remote->local for compare, -// local->KOReader for display). -// - If the user chooses Upload, local->KOReader mapping is performed again. -// - Without caching, the same chapter can be decompressed and reparsed multiple -// times during one activity session. -// -// Why the cache is exactly one entry: -// - We only need to accelerate immediate repeat lookups for the current chapter. -// - A single-entry cache bounds RAM use and avoids long-lived heap growth on -// ESP32-C3. -static const ParserState* getParsedStateCached(const std::shared_ptr& epub, const int spineIndex) { - static const Epub* cachedEpub = nullptr; - static int cachedSpineIndex = -1; - static std::string cachedHref; - static std::optional cachedState; - - if (!epub || spineIndex < 0 || spineIndex >= epub->getSpineItemsCount()) { - return nullptr; - } - - const auto spineItem = epub->getSpineItem(spineIndex); - if (spineItem.href.empty()) { - return nullptr; - } - - const bool cacheHit = cachedState.has_value() && cachedEpub == epub.get() && cachedSpineIndex == spineIndex && - cachedHref == spineItem.href; - if (cacheHit) { - LOG_DBG("KOX", "Cache hit: spine=%d anchors=%zu textBytes=%zu", spineIndex, cachedState->anchors.size(), - cachedState->totalTextBytes); - return &cachedState.value(); - } - - auto parsed = parseSpineItem(epub, spineIndex); - if (!parsed) { - return nullptr; - } - - cachedEpub = epub.get(); - cachedSpineIndex = spineIndex; - cachedHref = spineItem.href; - cachedState = std::move(parsed); - - LOG_DBG("KOX", "Cache store: spine=%d anchors=%zu textBytes=%zu", spineIndex, cachedState->anchors.size(), - cachedState->totalTextBytes); - return &cachedState.value(); -} - } // namespace std::string ChapterXPathIndexer::findXPathForProgress(const std::shared_ptr& epub, const int spineIndex, const float intraSpineProgress) { - const auto* state = getParsedStateCached(epub, spineIndex); + auto state = parseSpineItem(epub, spineIndex); if (!state) { return ""; } @@ -540,7 +488,7 @@ bool ChapterXPathIndexer::findProgressForXPath(const std::shared_ptr& epub return false; } - const auto* state = getParsedStateCached(epub, spineIndex); + auto state = parseSpineItem(epub, spineIndex); if (!state) { return false; }