Reduce memory usage for parseSpineItem
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#include "ChapterXPathIndexer.h"
|
#include "ChapterXPathIndexer.h"
|
||||||
|
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
|
#include <Print.h>
|
||||||
#include <expat.h>
|
#include <expat.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -387,18 +388,10 @@ static std::optional<ParserState> parseSpineItem(const std::shared_ptr<Epub>& ep
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t chapterSize = 0;
|
|
||||||
uint8_t* chapterBytes = epub->readItemContentsToBytes(spineItem.href, &chapterSize, false);
|
|
||||||
if (!chapterBytes || chapterSize == 0) {
|
|
||||||
free(chapterBytes);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParserState state(spineIndex);
|
ParserState state(spineIndex);
|
||||||
|
|
||||||
XML_Parser parser = XML_ParserCreate(nullptr);
|
XML_Parser parser = XML_ParserCreate(nullptr);
|
||||||
if (!parser) {
|
if (!parser) {
|
||||||
free(chapterBytes);
|
|
||||||
LOG_ERR("KOX", "Failed to allocate XML parser for spine=%d", spineIndex);
|
LOG_ERR("KOX", "Failed to allocate XML parser for spine=%d", spineIndex);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -408,8 +401,30 @@ static std::optional<ParserState> parseSpineItem(const std::shared_ptr<Epub>& ep
|
|||||||
XML_SetCharacterDataHandler(parser, onCharacterData);
|
XML_SetCharacterDataHandler(parser, onCharacterData);
|
||||||
XML_SetDefaultHandlerExpand(parser, onDefaultHandlerExpand);
|
XML_SetDefaultHandlerExpand(parser, onDefaultHandlerExpand);
|
||||||
|
|
||||||
const bool parseOk = XML_Parse(parser, reinterpret_cast<const char*>(chapterBytes), static_cast<int>(chapterSize),
|
// Feed decompressed data to Expat incrementally to avoid a large contiguous allocation.
|
||||||
XML_TRUE) != XML_STATUS_ERROR;
|
class ExpatPrint : public Print {
|
||||||
|
public:
|
||||||
|
XML_Parser parser;
|
||||||
|
bool ok = true;
|
||||||
|
size_t write(uint8_t b) override { return write(&b, 1); }
|
||||||
|
size_t write(const uint8_t* buf, size_t size) override {
|
||||||
|
if (!ok) return 0;
|
||||||
|
if (XML_Parse(parser, reinterpret_cast<const char*>(buf), static_cast<int>(size), XML_FALSE) ==
|
||||||
|
XML_STATUS_ERROR) {
|
||||||
|
ok = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExpatPrint ep;
|
||||||
|
ep.parser = parser;
|
||||||
|
constexpr size_t kChunkSize = 512;
|
||||||
|
epub->readItemContentsToStream(spineItem.href, ep, kChunkSize);
|
||||||
|
|
||||||
|
// Finalise the parse regardless of streaming result
|
||||||
|
const bool parseOk = ep.ok && XML_Parse(parser, "", 0, XML_TRUE) != XML_STATUS_ERROR;
|
||||||
|
|
||||||
if (!parseOk) {
|
if (!parseOk) {
|
||||||
LOG_ERR("KOX", "XPath parse failed for spine=%d at line %lu: %s", spineIndex, XML_GetCurrentLineNumber(parser),
|
LOG_ERR("KOX", "XPath parse failed for spine=%d at line %lu: %s", spineIndex, XML_GetCurrentLineNumber(parser),
|
||||||
@@ -417,7 +432,6 @@ static std::optional<ParserState> parseSpineItem(const std::shared_ptr<Epub>& ep
|
|||||||
}
|
}
|
||||||
|
|
||||||
XML_ParserFree(parser);
|
XML_ParserFree(parser);
|
||||||
free(chapterBytes);
|
|
||||||
|
|
||||||
if (!parseOk) {
|
if (!parseOk) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
Reference in New Issue
Block a user