| 60 | }; |
| 61 | |
| 62 | struct PagedFileImpl final : FileReader::Impl { |
| 63 | std::string fn_; |
| 64 | FILE* fp_ = nullptr; |
| 65 | size_t file_size_ = 0; |
| 66 | size_t page_size_ = 4096; |
| 67 | |
| 68 | // LRU cache |
| 69 | size_t capacity_ = 8; |
| 70 | mutable std::list<size_t> lru_; |
| 71 | struct Entry { |
| 72 | FileReader::Page page; |
| 73 | std::list<size_t>::iterator it; |
| 74 | }; |
| 75 | mutable std::unordered_map<size_t, Entry> map_; |
| 76 | |
| 77 | PagedFileImpl(const std::string& fn, size_t page_size, size_t cap) |
| 78 | : fn_(fn), page_size_(std::max<size_t>(512, page_size)), capacity_(std::max<size_t>(2, cap)) |
| 79 | { |
| 80 | #ifdef _MSC_VER |
| 81 | std::wstring fn_ws = IfcUtil::path::from_utf8(fn); |
| 82 | const wchar_t* fn_wide = fn_ws.c_str(); |
| 83 | fp_ = _wfopen(fn_wide, L"rb"); |
| 84 | #else |
| 85 | fp_ = fopen(fn.c_str(), "rb"); |
| 86 | #endif |
| 87 | fseek(fp_, 0, SEEK_END); |
| 88 | file_size_ = (size_t)ftell(fp_); |
| 89 | rewind(fp_); |
| 90 | } |
| 91 | |
| 92 | ~PagedFileImpl() override { |
| 93 | if (fp_) std::fclose(fp_); |
| 94 | fp_ = nullptr; |
| 95 | } |
| 96 | |
| 97 | size_t size() const override { return file_size_; } |
| 98 | |
| 99 | char get(size_t pos) const override { |
| 100 | if (pos >= file_size_) throw std::out_of_range("get out of range"); |
| 101 | const size_t pidx = pos / page_size_; |
| 102 | const FileReader::Page& p = fetchPage_(pidx); |
| 103 | const size_t off = pos % page_size_; |
| 104 | if (off >= p.data.size()) throw std::out_of_range("offset beyond valid page bytes"); |
| 105 | return p.data[off]; |
| 106 | } |
| 107 | |
| 108 | private: |
| 109 | const FileReader::Page& fetchPage_(size_t idx) const { |
| 110 | auto it = map_.find(idx); |
| 111 | if (it != map_.end()) { |
| 112 | touch_(it); |
| 113 | return it->second.page; |
| 114 | } |
| 115 | // Load page from disk using persistent FILE* |
| 116 | FileReader::Page pg; |
| 117 | pg.data.resize(page_size_); |
| 118 | const size_t begin = idx * page_size_; |
| 119 | const size_t avail = std::min(page_size_, file_size_ - begin); |