Keep ZipFile open to speed up getting file stats. (#76)

Still a bit raw, but gets the time required to determine the size of
each chapter (for reading progress) down from ~25ms to 0-1ms.

This is done by keeping the zipArchive open (so simple ;)).

Probably we don't need to cache the spine sizes anymore then...

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
This commit is contained in:
Jonas Diemer 2025-12-21 04:38:51 +01:00 committed by GitHub
parent 299623927e
commit 926c786705
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 23 additions and 47 deletions

View File

@ -126,7 +126,6 @@ bool Epub::parseTocNcxFile() {
// load in the meta data for the epub file
bool Epub::load() {
Serial.printf("[%lu] [EBP] Loading ePub: %s\n", millis(), filepath.c_str());
ZipFile zip("/sd" + filepath);
std::string contentOpfFilePath;
if (!findContentOpfFile(&contentOpfFilePath)) {
@ -155,44 +154,20 @@ bool Epub::load() {
}
void Epub::initializeSpineItemSizes() {
setupCacheDir();
Serial.printf("[%lu] [EBP] Calculating book size\n", millis());
size_t spineItemsCount = getSpineItemsCount();
const size_t spineItemsCount = getSpineItemsCount();
size_t cumSpineItemSize = 0;
if (SD.exists((getCachePath() + "/spine_size.bin").c_str())) {
File f = SD.open((getCachePath() + "/spine_size.bin").c_str());
uint8_t data[4];
for (size_t i = 0; i < spineItemsCount; i++) {
f.read(data, 4);
cumSpineItemSize = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
cumulativeSpineItemSize.emplace_back(cumSpineItemSize);
// Serial.printf("[%lu] [EBP] Loading item %d size %u to %u %u\n", millis(),
// i, cumSpineItemSize, data[1], data[0]);
}
f.close();
} else {
File f = SD.open((getCachePath() + "/spine_size.bin").c_str(), FILE_WRITE);
uint8_t data[4];
// determine size of spine items
for (size_t i = 0; i < spineItemsCount; i++) {
std::string spineItem = getSpineItem(i);
size_t s = 0;
getItemSize(spineItem, &s);
cumSpineItemSize += s;
cumulativeSpineItemSize.emplace_back(cumSpineItemSize);
const ZipFile zip("/sd" + filepath);
// and persist to cache
data[0] = cumSpineItemSize & 0xFF;
data[1] = (cumSpineItemSize >> 8) & 0xFF;
data[2] = (cumSpineItemSize >> 16) & 0xFF;
data[3] = (cumSpineItemSize >> 24) & 0xFF;
// Serial.printf("[%lu] [EBP] Persisting item %d size %u to %u %u\n", millis(),
// i, cumSpineItemSize, data[1], data[0]);
f.write(data, 4);
}
f.close();
for (size_t i = 0; i < spineItemsCount; i++) {
std::string spineItem = getSpineItem(i);
size_t s = 0;
getItemSize(zip, spineItem, &s);
cumSpineItemSize += s;
cumulativeSpineItemSize.emplace_back(cumSpineItemSize);
}
Serial.printf("[%lu] [EBP] Book size: %lu\n", millis(), cumSpineItemSize);
}
@ -291,8 +266,11 @@ bool Epub::readItemContentsToStream(const std::string& itemHref, Print& out, con
bool Epub::getItemSize(const std::string& itemHref, size_t* size) const {
const ZipFile zip("/sd" + filepath);
const std::string path = normalisePath(itemHref);
return getItemSize(zip, itemHref, size);
}
bool Epub::getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size) {
const std::string path = normalisePath(itemHref);
return zip.getInflatedFileSize(path.c_str(), size);
}

View File

@ -33,6 +33,7 @@ class Epub {
bool parseContentOpf(const std::string& contentOpfFilePath);
bool parseTocNcxFile();
void initializeSpineItemSizes();
static bool getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size);
public:
explicit Epub(std::string filepath, const std::string& cacheDir) : filepath(std::move(filepath)) {

View File

@ -27,31 +27,28 @@ bool inflateOneShot(const uint8_t* inputBuf, const size_t deflatedSize, uint8_t*
return true;
}
bool ZipFile::loadFileStat(const char* filename, mz_zip_archive_file_stat* fileStat) const {
mz_zip_archive zipArchive = {};
const bool status = mz_zip_reader_init_file(&zipArchive, filePath.c_str(), 0);
ZipFile::ZipFile(std::string filePath) : filePath(std::move(filePath)) {
const bool status = mz_zip_reader_init_file(&zipArchive, this->filePath.c_str(), 0);
if (!status) {
Serial.printf("[%lu] [ZIP] mz_zip_reader_init_file() failed! Error: %s\n", millis(),
Serial.printf("[%lu] [ZIP] mz_zip_reader_init_file() failed for %s! Error: %s\n", millis(), this->filePath.c_str(),
mz_zip_get_error_string(zipArchive.m_last_error));
return false;
}
}
bool ZipFile::loadFileStat(const char* filename, mz_zip_archive_file_stat* fileStat) const {
// find the file
mz_uint32 fileIndex = 0;
if (!mz_zip_reader_locate_file_v2(&zipArchive, filename, nullptr, 0, &fileIndex)) {
Serial.printf("[%lu] [ZIP] Could not find file %s\n", millis(), filename);
mz_zip_reader_end(&zipArchive);
return false;
}
if (!mz_zip_reader_file_stat(&zipArchive, fileIndex, fileStat)) {
Serial.printf("[%lu] [ZIP] mz_zip_reader_file_stat() failed! Error: %s\n", millis(),
mz_zip_get_error_string(zipArchive.m_last_error));
mz_zip_reader_end(&zipArchive);
return false;
}
mz_zip_reader_end(&zipArchive);
return true;
}

View File

@ -1,19 +1,19 @@
#pragma once
#include <Print.h>
#include <functional>
#include <string>
#include "miniz.h"
class ZipFile {
std::string filePath;
mutable mz_zip_archive zipArchive = {};
bool loadFileStat(const char* filename, mz_zip_archive_file_stat* fileStat) const;
long getDataOffset(const mz_zip_archive_file_stat& fileStat) const;
public:
explicit ZipFile(std::string filePath) : filePath(std::move(filePath)) {}
~ZipFile() = default;
explicit ZipFile(std::string filePath);
~ZipFile() { mz_zip_reader_end(&zipArchive); }
bool getInflatedFileSize(const char* filename, size_t* size) const;
uint8_t* readFileToMemory(const char* filename, size_t* size = nullptr, bool trailingNullByte = false) const;
bool readFileToStream(const char* filename, Print& out, size_t chunkSize) const;