Files
crosspoint-reader-mod/lib/hal/HalStorage.cpp
cottongin 60a3e21c0e mod: Phase 3 — Re-port unmerged upstream PRs
Re-applied upstream PRs not yet merged to upstream/master:

- #1055: Byte-level framebuffer writes (fillPhysicalHSpan*,
  optimized fillRect/drawLine/fillRectDither/fillPolygon)
- #1027: Word-width cache (FNV-1a, 128-entry) and hyphenation
  early exit in ParsedText for 7-9% layout speedup
- #1068: Already present in upstream — URL hyphenation fix
- #1019: Already present in upstream — file extensions in browser
- #1090/#1185/#1217: KOReader sync improvements — binary credential
  store, document hash caching, ChapterXPathIndexer integration
- #1209: OPDS multi-server — OpdsBookBrowserActivity accepts
  OpdsServer, directory picker for downloads, download-complete
  prompt with open/back options
- #857: Dictionary activities already ported in Phase 1/2
- #1003: Placeholder cover already integrated in Phase 2

Also fixed: STR_OFF i18n string, include paths, replaced
Epub::isValidThumbnailBmp with Storage.exists, replaced
StringUtils::checkFileExtension with FsHelpers equivalents.

Made-with: Cursor
2026-03-07 16:15:42 -05:00

163 lines
6.5 KiB
C++

#define HAL_STORAGE_IMPL
#include "HalStorage.h"
#include <FS.h> // need to be included before SdFat.h for compatibility with FS.h's File class
#include <Logging.h>
#include <SDCardManager.h>
#include <cassert>
#define SDCard SDCardManager::getInstance()
HalStorage HalStorage::instance;
HalStorage::HalStorage() {
storageMutex = xSemaphoreCreateMutex();
assert(storageMutex != nullptr);
}
// begin() and ready() are only called from setup, no need to acquire mutex for them
bool HalStorage::begin() { return SDCard.begin(); }
bool HalStorage::ready() const { return SDCard.ready(); }
// For the rest of the methods, we acquire the mutex to ensure thread safety
class HalStorage::StorageLock {
public:
StorageLock() { xSemaphoreTake(HalStorage::getInstance().storageMutex, portMAX_DELAY); }
~StorageLock() { xSemaphoreGive(HalStorage::getInstance().storageMutex); }
};
#define HAL_STORAGE_WRAPPED_CALL(method, ...) \
HalStorage::StorageLock lock; \
return SDCard.method(__VA_ARGS__);
std::vector<String> HalStorage::listFiles(const char* path, int maxFiles) {
HAL_STORAGE_WRAPPED_CALL(listFiles, path, maxFiles);
}
String HalStorage::readFile(const char* path) { HAL_STORAGE_WRAPPED_CALL(readFile, path); }
bool HalStorage::readFileToStream(const char* path, Print& out, size_t chunkSize) {
HAL_STORAGE_WRAPPED_CALL(readFileToStream, path, out, chunkSize);
}
size_t HalStorage::readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes) {
HAL_STORAGE_WRAPPED_CALL(readFileToBuffer, path, buffer, bufferSize, maxBytes);
}
bool HalStorage::writeFile(const char* path, const String& content) {
HAL_STORAGE_WRAPPED_CALL(writeFile, path, content);
}
bool HalStorage::ensureDirectoryExists(const char* path) { HAL_STORAGE_WRAPPED_CALL(ensureDirectoryExists, path); }
class HalFile::Impl {
public:
Impl(FsFile&& fsFile) : file(std::move(fsFile)) {}
FsFile file;
};
HalFile::HalFile() = default;
HalFile::HalFile(std::unique_ptr<Impl> impl) : impl(std::move(impl)) {}
HalFile::~HalFile() = default;
HalFile::HalFile(HalFile&&) = default;
HalFile& HalFile::operator=(HalFile&&) = default;
HalFile HalStorage::open(const char* path, const oflag_t oflag) {
StorageLock lock; // ensure thread safety for the duration of this function
return HalFile(std::make_unique<HalFile::Impl>(SDCard.open(path, oflag)));
}
bool HalStorage::mkdir(const char* path, const bool pFlag) { HAL_STORAGE_WRAPPED_CALL(mkdir, path, pFlag); }
bool HalStorage::exists(const char* path) { HAL_STORAGE_WRAPPED_CALL(exists, path); }
bool HalStorage::remove(const char* path) { HAL_STORAGE_WRAPPED_CALL(remove, path); }
bool HalStorage::rename(const char* oldPath, const char* newPath) {
HAL_STORAGE_WRAPPED_CALL(rename, oldPath, newPath);
}
bool HalStorage::rmdir(const char* path) { HAL_STORAGE_WRAPPED_CALL(rmdir, path); }
bool HalStorage::openFileForRead(const char* moduleName, const char* path, HalFile& file) {
StorageLock lock; // ensure thread safety for the duration of this function
FsFile fsFile;
bool ok = SDCard.openFileForRead(moduleName, path, fsFile);
file = HalFile(std::make_unique<HalFile::Impl>(std::move(fsFile)));
return ok;
}
bool HalStorage::openFileForRead(const char* moduleName, const std::string& path, HalFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForRead(const char* moduleName, const String& path, HalFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForWrite(const char* moduleName, const char* path, HalFile& file) {
StorageLock lock; // ensure thread safety for the duration of this function
FsFile fsFile;
bool ok = SDCard.openFileForWrite(moduleName, path, fsFile);
file = HalFile(std::make_unique<HalFile::Impl>(std::move(fsFile)));
return ok;
}
bool HalStorage::openFileForWrite(const char* moduleName, const std::string& path, HalFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForWrite(const char* moduleName, const String& path, HalFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool HalStorage::removeDir(const char* path) { HAL_STORAGE_WRAPPED_CALL(removeDir, path); }
// HalFile implementation
// Allow doing file operations while ensuring thread safety via HalStorage's mutex.
// Please keep the list below in sync with the HalFile.h header
#define HAL_FILE_WRAPPED_CALL(method, ...) \
HalStorage::StorageLock lock; \
assert(impl != nullptr); \
return impl->file.method(__VA_ARGS__);
#define HAL_FILE_FORWARD_CALL(method, ...) \
assert(impl != nullptr); \
return impl->file.method(__VA_ARGS__);
void HalFile::flush() { HAL_FILE_WRAPPED_CALL(flush, ); }
size_t HalFile::getName(char* name, size_t len) { HAL_FILE_WRAPPED_CALL(getName, name, len); }
size_t HalFile::size() { HAL_FILE_FORWARD_CALL(size, ); } // already thread-safe, no need to wrap
size_t HalFile::fileSize() { HAL_FILE_FORWARD_CALL(fileSize, ); } // already thread-safe, no need to wrap
bool HalFile::getModifyDateTime(uint16_t* pdate, uint16_t* ptime) {
HAL_FILE_WRAPPED_CALL(getModifyDateTime, pdate, ptime);
}
bool HalFile::seek(size_t pos) { HAL_FILE_WRAPPED_CALL(seekSet, pos); }
bool HalFile::seekCur(int64_t offset) { HAL_FILE_WRAPPED_CALL(seekCur, offset); }
bool HalFile::seekSet(size_t offset) { HAL_FILE_WRAPPED_CALL(seekSet, offset); }
int HalFile::available() const { HAL_FILE_WRAPPED_CALL(available, ); }
size_t HalFile::position() const { HAL_FILE_WRAPPED_CALL(position, ); }
int HalFile::read(void* buf, size_t count) { HAL_FILE_WRAPPED_CALL(read, buf, count); }
int HalFile::read() { HAL_FILE_WRAPPED_CALL(read, ); }
size_t HalFile::write(const void* buf, size_t count) { HAL_FILE_WRAPPED_CALL(write, buf, count); }
size_t HalFile::write(uint8_t b) { HAL_FILE_WRAPPED_CALL(write, b); }
bool HalFile::rename(const char* newPath) { HAL_FILE_WRAPPED_CALL(rename, newPath); }
bool HalFile::isDirectory() const { HAL_FILE_FORWARD_CALL(isDirectory, ); } // already thread-safe, no need to wrap
void HalFile::rewindDirectory() { HAL_FILE_WRAPPED_CALL(rewindDirectory, ); }
bool HalFile::close() { HAL_FILE_WRAPPED_CALL(close, ); }
HalFile HalFile::openNextFile() {
HalStorage::StorageLock lock;
assert(impl != nullptr);
return HalFile(std::make_unique<Impl>(impl->file.openNextFile()));
}
bool HalFile::isOpen() const { return impl != nullptr && impl->file.isOpen(); } // already thread-safe, no need to wrap
HalFile::operator bool() const { return isOpen(); }