Cleanup SpineTocCache

This commit is contained in:
Dave Allie 2025-12-22 21:43:01 +11:00
parent dc3869ac1c
commit 05fce6b818
No known key found for this signature in database
GPG Key ID: F2FDDB3AD8D0276F
5 changed files with 79 additions and 91 deletions

View File

@ -1,6 +1,7 @@
#include "FsHelpers.h" #include "FsHelpers.h"
#include <SD.h> #include <SD.h>
#include <vector> #include <vector>
bool FsHelpers::removeDir(const char* path) { bool FsHelpers::removeDir(const char* path) {

View File

@ -4,5 +4,5 @@
class FsHelpers { class FsHelpers {
public: public:
static bool removeDir(const char* path); static bool removeDir(const char* path);
static std::string normalisePath(const std::string &path); static std::string normalisePath(const std::string& path);
}; };

View File

@ -14,6 +14,24 @@ constexpr uint8_t SPINE_TOC_CACHE_VERSION = 1;
constexpr char spineTocMetaBinFile[] = "/spine_toc_meta.bin"; constexpr char spineTocMetaBinFile[] = "/spine_toc_meta.bin";
constexpr char spineBinFile[] = "/spine.bin"; constexpr char spineBinFile[] = "/spine.bin";
constexpr char tocBinFile[] = "/toc.bin"; constexpr char tocBinFile[] = "/toc.bin";
bool openFileForRead(const std::string& path, File& file) {
file = SD.open(path.c_str(), FILE_READ);
if (!file) {
Serial.printf("[%lu] [STC] Failed to open file for reading: %s\n", millis(), path.c_str());
return false;
}
return true;
}
bool openFileForWrite(const std::string& path, File& file) {
file = SD.open(path.c_str(), FILE_WRITE, true);
if (!file) {
Serial.printf("[%lu] [STC] Failed to open spine file for writing: %s\n", millis(), path.c_str());
return false;
}
return true;
}
} // namespace } // namespace
bool SpineTocCache::beginWrite() { bool SpineTocCache::beginWrite() {
@ -24,20 +42,12 @@ bool SpineTocCache::beginWrite() {
Serial.printf("[%lu] [STC] Beginning write to cache path: %s\n", millis(), cachePath.c_str()); Serial.printf("[%lu] [STC] Beginning write to cache path: %s\n", millis(), cachePath.c_str());
// Open spine file for writing // Open spine file for writing
const std::string spineFilePath = cachePath + spineBinFile; if (!openFileForWrite(cachePath + spineBinFile, spineFile)) {
Serial.printf("[%lu] [STC] Opening spine file: %s\n", millis(), spineFilePath.c_str());
spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
if (!spineFile) {
Serial.printf("[%lu] [STC] Failed to open spine file for writing: %s\n", millis(), spineFilePath.c_str());
return false; return false;
} }
// Open TOC file for writing // Open TOC file for writing
const std::string tocFilePath = cachePath + tocBinFile; if (!openFileForWrite(cachePath + tocBinFile, tocFile)) {
Serial.printf("[%lu] [STC] Opening toc file: %s\n", millis(), tocFilePath.c_str());
tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
if (!tocFile) {
Serial.printf("[%lu] [STC] Failed to open toc file for writing: %s\n", millis(), tocFilePath.c_str());
spineFile.close(); spineFile.close();
return false; return false;
} }
@ -93,10 +103,8 @@ bool SpineTocCache::endWrite() {
tocFile.close(); tocFile.close();
// Write metadata files with counts // Write metadata files with counts
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile; File metaFile;
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_WRITE, true); if (!openFileForWrite(cachePath + spineTocMetaBinFile, metaFile)) {
if (!metaFile) {
Serial.printf("[%lu] [STC] Failed to write spine metadata\n", millis());
return false; return false;
} }
serialization::writePod(metaFile, SPINE_TOC_CACHE_VERSION); serialization::writePod(metaFile, SPINE_TOC_CACHE_VERSION);
@ -109,25 +117,25 @@ bool SpineTocCache::endWrite() {
return true; return true;
} }
SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(std::ifstream& is) const { SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(File& file) const {
SpineEntry entry; SpineEntry entry;
serialization::readString(is, entry.href); serialization::readString(file, entry.href);
serialization::readPod(is, entry.cumulativeSize); serialization::readPod(file, entry.cumulativeSize);
serialization::readPod(is, entry.tocIndex); serialization::readPod(file, entry.tocIndex);
return entry; return entry;
} }
SpineTocCache::TocEntry SpineTocCache::readTocEntry(std::ifstream& is) const { SpineTocCache::TocEntry SpineTocCache::readTocEntry(File& file) const {
TocEntry entry; TocEntry entry;
serialization::readString(is, entry.title); serialization::readString(file, entry.title);
serialization::readString(is, entry.href); serialization::readString(file, entry.href);
serialization::readString(is, entry.anchor); serialization::readString(file, entry.anchor);
serialization::readPod(is, entry.level); serialization::readPod(file, entry.level);
serialization::readPod(is, entry.spineIndex); serialization::readPod(file, entry.spineIndex);
return entry; return entry;
} }
bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const { bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) {
Serial.printf("[%lu] [STC] Computing mappings and sizes for %d spine, %d TOC entries\n", millis(), spineCount, Serial.printf("[%lu] [STC] Computing mappings and sizes for %d spine, %d TOC entries\n", millis(), spineCount,
tocCount); tocCount);
@ -141,32 +149,24 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
// Read spine entries // Read spine entries
{ {
const auto spineFilePath = "/sd" + cachePath + spineBinFile; if (!openFileForRead(cachePath + spineBinFile, spineFile)) {
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
if (!spineStream) {
Serial.printf("[%lu] [STC] Failed to open spine file for reading\n", millis());
return false; return false;
} }
for (int i = 0; i < spineCount; i++) { for (int i = 0; i < spineCount; i++) {
spineEntries.push_back(readSpineEntry(spineStream)); spineEntries.push_back(readSpineEntry(spineFile));
} }
spineStream.close(); spineFile.close();
} }
// Read TOC entries // Read TOC entries
{ {
const auto tocFilePath = "/sd" + cachePath + tocBinFile; if (!openFileForRead(cachePath + tocBinFile, tocFile)) {
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
if (!tocStream) {
Serial.printf("[%lu] [STC] Failed to open toc file for reading\n", millis());
return false; return false;
} }
for (int i = 0; i < tocCount; i++) { for (int i = 0; i < tocCount; i++) {
tocEntries.push_back(readTocEntry(tocStream)); tocEntries.push_back(readTocEntry(tocFile));
} }
tocStream.close(); tocFile.close();
} }
// Compute cumulative sizes // Compute cumulative sizes
@ -199,13 +199,9 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
// Rewrite spine file with updated data // Rewrite spine file with updated data
{ {
const auto spineFilePath = cachePath + spineBinFile; if (!openFileForWrite(cachePath + spineBinFile, spineFile)) {
File spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
if (!spineFile) {
Serial.printf("[%lu] [STC] Failed to reopen spine file for writing\n", millis());
return false; return false;
} }
for (const auto& entry : spineEntries) { for (const auto& entry : spineEntries) {
writeSpineEntry(spineFile, entry); writeSpineEntry(spineFile, entry);
} }
@ -214,13 +210,9 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
// Rewrite TOC file with updated data // Rewrite TOC file with updated data
{ {
const auto tocFilePath = cachePath + tocBinFile; if (!openFileForWrite(cachePath + tocBinFile, tocFile)) {
File tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
if (!tocFile) {
Serial.printf("[%lu] [STC] Failed to reopen toc file for writing\n", millis());
return false; return false;
} }
for (const auto& entry : tocEntries) { for (const auto& entry : tocEntries) {
writeTocEntry(tocFile, entry); writeTocEntry(tocFile, entry);
} }
@ -239,15 +231,8 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
bool SpineTocCache::load() { bool SpineTocCache::load() {
// Load metadata // Load metadata
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile; File metaFile;
if (!SD.exists(spineTocMetaPath.c_str())) { if (!openFileForRead(cachePath + spineTocMetaBinFile, metaFile)) {
Serial.printf("[%lu] [STC] Cache metadata does not exist: %s\n", millis(), spineTocMetaPath.c_str());
return false;
}
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_READ);
if (!metaFile) {
Serial.printf("[%lu] [STC] Failed to open cache metadata\n", millis());
return false; return false;
} }
@ -270,61 +255,55 @@ bool SpineTocCache::load() {
return true; return true;
} }
SpineTocCache::SpineEntry SpineTocCache::getSpineEntry(const int index) const { SpineTocCache::SpineEntry SpineTocCache::getSpineEntry(const int index) {
if (!loaded) { if (!loaded) {
Serial.printf("[%lu] [STC] getSpineEntry called but cache not loaded\n", millis()); Serial.printf("[%lu] [STC] getSpineEntry called but cache not loaded\n", millis());
return SpineEntry(); return {};
} }
if (index < 0 || index >= static_cast<int>(spineCount)) { if (index < 0 || index >= static_cast<int>(spineCount)) {
Serial.printf("[%lu] [STC] getSpineEntry index %d out of range\n", millis(), index); Serial.printf("[%lu] [STC] getSpineEntry index %d out of range\n", millis(), index);
return SpineEntry(); return {};
} }
const auto spineFilePath = "/sd" + cachePath + spineBinFile; if (!openFileForRead(cachePath + spineBinFile, spineFile)) {
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary); return {};
if (!spineStream) {
Serial.printf("[%lu] [STC] Failed to open spine file for reading entry\n", millis());
return SpineEntry();
} }
// Seek to the correct entry - need to read entries sequentially until we reach the index // Seek to the correct entry - need to read entries sequentially until we reach the index
// TODO: This could/should be based on a look up table/fixed sizes // TODO: This could/should be based on a look up table/fixed sizes
for (int i = 0; i < index; i++) { for (int i = 0; i < index; i++) {
readSpineEntry(spineStream); // Skip entries readSpineEntry(spineFile); // Skip entries
} }
auto entry = readSpineEntry(spineStream); auto entry = readSpineEntry(spineFile);
spineStream.close(); spineFile.close();
return entry; return entry;
} }
SpineTocCache::TocEntry SpineTocCache::getTocEntry(const int index) const { SpineTocCache::TocEntry SpineTocCache::getTocEntry(const int index) {
if (!loaded) { if (!loaded) {
Serial.printf("[%lu] [STC] getTocEntry called but cache not loaded\n", millis()); Serial.printf("[%lu] [STC] getTocEntry called but cache not loaded\n", millis());
return TocEntry(); return {};
} }
if (index < 0 || index >= static_cast<int>(tocCount)) { if (index < 0 || index >= static_cast<int>(tocCount)) {
Serial.printf("[%lu] [STC] getTocEntry index %d out of range\n", millis(), index); Serial.printf("[%lu] [STC] getTocEntry index %d out of range\n", millis(), index);
return TocEntry(); return {};
} }
const auto tocFilePath = "/sd" + cachePath + tocBinFile; if (!openFileForRead(cachePath + tocBinFile, tocFile)) {
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary); return {};
if (!tocStream) {
Serial.printf("[%lu] [STC] Failed to open toc file for reading entry\n", millis());
return TocEntry();
} }
// Seek to the correct entry - need to read entries sequentially until we reach the index // Seek to the correct entry - need to read entries sequentially until we reach the index
// TODO: This could/should be based on a look up table/fixed sizes // TODO: This could/should be based on a look up table/fixed sizes
for (int i = 0; i < index; i++) { for (int i = 0; i < index; i++) {
readTocEntry(tocStream); // Skip entries readTocEntry(tocFile); // Skip entries
} }
auto entry = readTocEntry(tocStream); auto entry = readTocEntry(tocFile);
tocStream.close(); tocFile.close();
return entry; return entry;
} }

View File

@ -2,7 +2,6 @@
#include <SD.h> #include <SD.h>
#include <fstream>
#include <string> #include <string>
class SpineTocCache { class SpineTocCache {
@ -13,7 +12,7 @@ class SpineTocCache {
int16_t tocIndex; int16_t tocIndex;
SpineEntry() : cumulativeSize(0), tocIndex(-1) {} SpineEntry() : cumulativeSize(0), tocIndex(-1) {}
SpineEntry(std::string href, size_t cumulativeSize, int16_t tocIndex) SpineEntry(std::string href, const size_t cumulativeSize, const int16_t tocIndex)
: href(std::move(href)), cumulativeSize(cumulativeSize), tocIndex(tocIndex) {} : href(std::move(href)), cumulativeSize(cumulativeSize), tocIndex(tocIndex) {}
}; };
@ -25,7 +24,7 @@ class SpineTocCache {
int16_t spineIndex; int16_t spineIndex;
TocEntry() : level(0), spineIndex(-1) {} TocEntry() : level(0), spineIndex(-1) {}
TocEntry(std::string title, std::string href, std::string anchor, uint8_t level, int16_t spineIndex) TocEntry(std::string title, std::string href, std::string anchor, const uint8_t level, const int16_t spineIndex)
: title(std::move(title)), : title(std::move(title)),
href(std::move(href)), href(std::move(href)),
anchor(std::move(anchor)), anchor(std::move(anchor)),
@ -46,8 +45,8 @@ class SpineTocCache {
void writeSpineEntry(File& file, const SpineEntry& entry) const; void writeSpineEntry(File& file, const SpineEntry& entry) const;
void writeTocEntry(File& file, const TocEntry& entry) const; void writeTocEntry(File& file, const TocEntry& entry) const;
SpineEntry readSpineEntry(std::ifstream& is) const; SpineEntry readSpineEntry(File& file) const;
TocEntry readTocEntry(std::ifstream& is) const; TocEntry readTocEntry(File& file) const;
public: public:
explicit SpineTocCache(std::string cachePath) explicit SpineTocCache(std::string cachePath)
@ -61,12 +60,12 @@ class SpineTocCache {
bool endWrite(); bool endWrite();
// Post-processing to update mappings and sizes // Post-processing to update mappings and sizes
bool updateMappingsAndSizes(const std::string& epubPath) const; bool updateMappingsAndSizes(const std::string& epubPath);
// Reading phase (read mode) // Reading phase (read mode)
bool load(); bool load();
SpineEntry getSpineEntry(int index) const; SpineEntry getSpineEntry(int index);
TocEntry getTocEntry(int index) const; TocEntry getTocEntry(int index);
int getSpineCount() const; int getSpineCount() const;
int getTocCount() const; int getTocCount() const;
bool isLoaded() const; bool isLoaded() const;

View File

@ -1,4 +1,6 @@
#pragma once #pragma once
#include <FS.h>
#include <iostream> #include <iostream>
namespace serialization { namespace serialization {
@ -17,7 +19,7 @@ static void readPod(std::istream& is, T& value) {
is.read(reinterpret_cast<char*>(&value), sizeof(T)); is.read(reinterpret_cast<char*>(&value), sizeof(T));
} }
template <typename T> template <typename T>
static void readPod(File& file, T& value) { static void readPod(File& file, T& value) {
file.read(reinterpret_cast<uint8_t*>(&value), sizeof(T)); file.read(reinterpret_cast<uint8_t*>(&value), sizeof(T));
} }
@ -31,7 +33,7 @@ static void writeString(std::ostream& os, const std::string& s) {
static void writeString(File& file, const std::string& s) { static void writeString(File& file, const std::string& s) {
const uint32_t len = s.size(); const uint32_t len = s.size();
writePod(file, len); writePod(file, len);
file.write(reinterpret_cast<const uint8_t *>(s.data()), len); file.write(reinterpret_cast<const uint8_t*>(s.data()), len);
} }
static void readString(std::istream& is, std::string& s) { static void readString(std::istream& is, std::string& s) {
@ -40,4 +42,11 @@ static void readString(std::istream& is, std::string& s) {
s.resize(len); s.resize(len);
is.read(&s[0], len); is.read(&s[0], len);
} }
static void readString(File& file, std::string& s) {
uint32_t len;
readPod(file, len);
s.resize(len);
file.read(reinterpret_cast<uint8_t*>(&s[0]), len);
}
} // namespace serialization } // namespace serialization