ZipFile.h
1 #pragma once 2 #include <SdFat.h> 3 4 #include <string> 5 #include <unordered_map> 6 #include <vector> 7 8 class ZipFile { 9 public: 10 struct FileStatSlim { 11 uint16_t method; // Compression method 12 uint32_t compressedSize; // Compressed size 13 uint32_t uncompressedSize; // Uncompressed size 14 uint32_t localHeaderOffset; // Offset of local file header 15 }; 16 17 struct ZipDetails { 18 uint32_t centralDirOffset; 19 uint16_t totalEntries; 20 bool isSet; 21 }; 22 23 struct SizeTarget { 24 uint64_t hash; // FNV-1a 64-bit hash of normalized path 25 uint16_t len; // Length for collision reduction 26 uint16_t index; // Caller's index (e.g. spine index) 27 28 bool operator<(const SizeTarget& other) const { 29 return hash < other.hash || (hash == other.hash && len < other.len); 30 } 31 }; 32 33 // FNV-1a 64-bit hash (no std::string allocation) 34 // Combined with 16-bit length provides ~80 bits of entropy; 35 // collision probability negligible for typical EPUB file counts 36 static uint64_t fnvHash64(const char* s, size_t len) { 37 uint64_t hash = 14695981039346656037ull; 38 for (size_t i = 0; i < len; i++) { 39 hash ^= static_cast<uint8_t>(s[i]); 40 hash *= 1099511628211ull; 41 } 42 return hash; 43 } 44 45 private: 46 const std::string& filePath; 47 FsFile file; 48 ZipDetails zipDetails = {0, 0, false}; 49 std::unordered_map<std::string, FileStatSlim> fileStatSlimCache; 50 51 bool loadFileStatSlim(const char* filename, FileStatSlim* fileStat); 52 long getDataOffset(const FileStatSlim& fileStat); 53 bool loadZipDetails(); 54 55 public: 56 explicit ZipFile(const std::string& filePath) : filePath(filePath) {} 57 ~ZipFile() = default; 58 // Zip file can be opened and closed by hand in order to allow for quick calculation of inflated file size 59 // It is NOT recommended to pre-open it for any kind of inflation due to memory constraints 60 bool isOpen() const { return !!file; } 61 bool open(); 62 bool close(); 63 bool loadAllFileStatSlims(); 64 uint16_t getTotalEntries(); 65 bool getInflatedFileSize(const char* filename, size_t* size); 66 // Batch lookup: scan ZIP central dir once and fill sizes for matching targets. 67 // targets must be sorted by (hash, len). sizes[target.index] receives uncompressedSize. 68 // Returns number of targets matched. 69 int fillUncompressedSizes(std::vector<SizeTarget>& targets, std::vector<uint32_t>& sizes); 70 // Find first existing file from a list of paths. Returns index into paths array, or -1 if none found. 71 // More efficient than calling getInflatedFileSize() for each path individually. 72 int findFirstExisting(const char* const* paths, int pathCount); 73 // Due to the memory required to run each of these, it is recommended to not preopen the zip file for multiple 74 // These functions will open and close the zip as needed 75 uint8_t* readFileToMemory(const char* filename, size_t* size = nullptr, bool trailingNullByte = false); 76 bool readFileToStream(const char* filename, Print& out, size_t chunkSize); 77 };