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
163 lines
6.5 KiB
C++
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(); }
|