Backup: Stable state with EPUB reader fixes (Freeze, OOM, Speed, State, Tooling)
This commit is contained in:
@@ -319,11 +319,16 @@ bool Epub::clearCache() const {
|
||||
}
|
||||
|
||||
void Epub::setupCacheDir() const {
|
||||
if (SdMan.exists(cachePath.c_str())) {
|
||||
return;
|
||||
// Always try to create, just in case.
|
||||
if (!SdMan.mkdir(cachePath.c_str())) {
|
||||
// If mkdir failed, it might already exist. Check if it's a directory.
|
||||
// SdMan doesn't allow checking type easily without opening.
|
||||
// But let's log the detailed failure state.
|
||||
bool exists = SdMan.exists(cachePath.c_str());
|
||||
Serial.printf("[%lu] [EBP] mkdir failed for %s. Exists? %s\n", millis(), cachePath.c_str(), exists ? "YES" : "NO");
|
||||
} else {
|
||||
// Serial.printf("[%lu] [EBP] Created cache directory: %s\n", millis(), cachePath.c_str());
|
||||
}
|
||||
|
||||
SdMan.mkdir(cachePath.c_str());
|
||||
}
|
||||
|
||||
const std::string& Epub::getCachePath() const { return cachePath; }
|
||||
|
||||
@@ -52,6 +52,11 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||
uint16_t count;
|
||||
serialization::readPod(file, count);
|
||||
|
||||
if (count > 1000) {
|
||||
Serial.printf("[%lu] [PGE] WARNING: Suspicious element count %d\n", millis(), count);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
uint8_t tag;
|
||||
serialization::readPod(file, tag);
|
||||
@@ -60,7 +65,7 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||
auto pl = PageLine::deserialize(file);
|
||||
page->elements.push_back(std::move(pl));
|
||||
} else {
|
||||
Serial.printf("[%lu] [PGE] Deserialization failed: Unknown tag %u\n", millis(), tag);
|
||||
Serial.printf("[%lu] [PGE] Deserialization failed: Unknown tag %u at index %d\n", millis(), tag, i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
constexpr int MAX_COST = std::numeric_limits<int>::max();
|
||||
|
||||
void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle) {
|
||||
// Serial.printf("addWord: %s\n", word.c_str());
|
||||
if (word.empty()) return;
|
||||
|
||||
words.push_back(std::move(word));
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
#include "parsers/ChapterHtmlSlimParser.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t SECTION_FILE_VERSION = 9;
|
||||
constexpr uint8_t SECTION_FILE_VERSION = 10;
|
||||
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
|
||||
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t);
|
||||
} // namespace
|
||||
|
||||
uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
|
||||
SDLock lock;
|
||||
if (!file) {
|
||||
Serial.printf("[%lu] [SCT] File not open for writing page %d\n", millis(), pageCount);
|
||||
return 0;
|
||||
@@ -23,7 +24,6 @@ uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
|
||||
Serial.printf("[%lu] [SCT] Failed to serialize page %d\n", millis(), pageCount);
|
||||
return 0;
|
||||
}
|
||||
Serial.printf("[%lu] [SCT] Page %d processed\n", millis(), pageCount);
|
||||
|
||||
pageCount++;
|
||||
return position;
|
||||
@@ -54,6 +54,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
|
||||
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||
const uint16_t viewportHeight) {
|
||||
SDLock lock;
|
||||
if (!SdMan.openFileForRead("SCT", filePath, file)) {
|
||||
return false;
|
||||
}
|
||||
@@ -93,14 +94,14 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
|
||||
|
||||
serialization::readPod(file, pageCount);
|
||||
file.close();
|
||||
Serial.printf("[%lu] [SCT] Deserialization succeeded: %d pages\n", millis(), pageCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Your updated class method (assuming you are using the 'SD' object, which is a wrapper for a specific filesystem)
|
||||
bool Section::clearCache() const {
|
||||
SDLock lock;
|
||||
if (!SdMan.exists(filePath.c_str())) {
|
||||
Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -109,7 +110,6 @@ bool Section::clearCache() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [SCT] Cache cleared successfully\n", millis());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
|
||||
const uint16_t viewportHeight, const std::function<void()>& progressSetupFn,
|
||||
const std::function<void(int)>& progressFn) {
|
||||
SDLock lock;
|
||||
constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
||||
const auto localPath = epub->getSpineItem(spineIndex).href;
|
||||
const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html";
|
||||
@@ -161,8 +162,6 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [SCT] Streamed temp HTML to %s (%d bytes)\n", millis(), tmpHtmlPath.c_str(), fileSize);
|
||||
|
||||
// Only show progress bar for larger chapters where rendering overhead is worth it
|
||||
if (progressSetupFn && fileSize >= MIN_SIZE_FOR_PROGRESS) {
|
||||
progressSetupFn();
|
||||
@@ -217,6 +216,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
|
||||
}
|
||||
|
||||
std::unique_ptr<Page> Section::loadPageFromSectionFile() {
|
||||
SDLock lock;
|
||||
if (!SdMan.openFileForRead("SCT", filePath, file)) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -224,9 +224,23 @@ std::unique_ptr<Page> Section::loadPageFromSectionFile() {
|
||||
file.seek(HEADER_SIZE - sizeof(uint32_t));
|
||||
uint32_t lutOffset;
|
||||
serialization::readPod(file, lutOffset);
|
||||
|
||||
if (lutOffset > file.size() || lutOffset < HEADER_SIZE) {
|
||||
Serial.printf("[%lu] [SCT] Invalid LUT offset %u (file size %u)\n", millis(), lutOffset, file.size());
|
||||
file.close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seek(lutOffset + sizeof(uint32_t) * currentPage);
|
||||
uint32_t pagePos;
|
||||
serialization::readPod(file, pagePos);
|
||||
|
||||
if (pagePos > file.size()) {
|
||||
Serial.printf("[%lu] [SCT] Invalid page pos %u for page %d\n", millis(), pagePos, currentPage);
|
||||
file.close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seek(pagePos);
|
||||
|
||||
auto page = Page::deserialize(file);
|
||||
|
||||
@@ -16,6 +16,7 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int
|
||||
auto wordXposIt = wordXpos.begin();
|
||||
|
||||
for (size_t i = 0; i < words.size(); i++) {
|
||||
Serial.printf("[%lu] [TXB] Rendering word: %s\n", millis(), wordIt->c_str());
|
||||
renderer.drawText(fontId, *wordXposIt + x, y, wordIt->c_str(), true, *wordStylesIt);
|
||||
|
||||
std::advance(wordIt, 1);
|
||||
@@ -52,6 +53,7 @@ std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
|
||||
|
||||
// Word count
|
||||
serialization::readPod(file, wc);
|
||||
Serial.printf("[%lu] [TXB] Deserializing TextBlock: %u words\n", millis(), wc);
|
||||
|
||||
// Sanity check: prevent allocation of unreasonably large lists (max 10000 words per block)
|
||||
if (wc > 10000) {
|
||||
@@ -63,7 +65,13 @@ std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
|
||||
words.resize(wc);
|
||||
wordXpos.resize(wc);
|
||||
wordStyles.resize(wc);
|
||||
for (auto& w : words) serialization::readString(file, w);
|
||||
wordStyles.resize(wc);
|
||||
int i = 0;
|
||||
for (auto& w : words) {
|
||||
if (i % 100 == 0 && i > 0) Serial.printf("[%lu] [TXB] Reading word %d/%d\n", millis(), i, wc);
|
||||
serialization::readString(file, w);
|
||||
i++;
|
||||
}
|
||||
for (auto& x : wordXpos) serialization::readPod(file, x);
|
||||
for (auto& s : wordStyles) serialization::readPod(file, s);
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ void ChapterHtmlSlimParser::startNewTextBlock(const TextBlock::Style style) {
|
||||
}
|
||||
|
||||
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
|
||||
// Serial.printf("startElement: %s\n", name);
|
||||
auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
|
||||
|
||||
// Middle of skip
|
||||
@@ -268,9 +269,17 @@ void XMLCALL ChapterHtmlSlimParser::endElement(void* userData, const XML_Char* n
|
||||
}
|
||||
|
||||
bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
startNewTextBlock((TextBlock::Style)this->paragraphAlignment);
|
||||
SDLock lock;
|
||||
Serial.printf("[%lu] [EHP] parseAndBuildPages start. Heap: %u\n", millis(), ESP.getFreeHeap());
|
||||
|
||||
Serial.printf("[%lu] [EHP] Calling startNewTextBlock\n", millis());
|
||||
startNewTextBlock((TextBlock::Style)this->paragraphAlignment);
|
||||
Serial.printf("[%lu] [EHP] startNewTextBlock returned\n", millis());
|
||||
|
||||
Serial.printf("[%lu] [EHP] Creating XML parser\n", millis());
|
||||
const XML_Parser parser = XML_ParserCreate(nullptr);
|
||||
if (parser) Serial.printf("[%lu] [EHP] Parser created\n", millis());
|
||||
|
||||
int done;
|
||||
|
||||
if (!parser) {
|
||||
@@ -306,6 +315,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
}
|
||||
|
||||
const size_t len = file.read(buf, 1024);
|
||||
// Serial.printf("[%lu] [EHP] Read %d bytes\n", millis(), len);
|
||||
|
||||
if (len == 0 && file.available() > 0) {
|
||||
Serial.printf("[%lu] [EHP] File read error\n", millis());
|
||||
@@ -331,6 +341,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
done = file.available() == 0;
|
||||
|
||||
if (XML_ParseBuffer(parser, static_cast<int>(len), done) == XML_STATUS_ERROR) {
|
||||
Serial.printf("[%lu] [EHP] XML_ParseBuffer returned error\n", millis());
|
||||
Serial.printf("[%lu] [EHP] Parse error at line %lu:\n%s\n", millis(), XML_GetCurrentLineNumber(parser),
|
||||
XML_ErrorString(XML_GetErrorCode(parser)));
|
||||
XML_StopParser(parser, XML_FALSE); // Stop any pending processing
|
||||
@@ -340,6 +351,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(1);
|
||||
} while (!done);
|
||||
|
||||
XML_StopParser(parser, XML_FALSE); // Stop any pending processing
|
||||
|
||||
Reference in New Issue
Block a user