//======================================================================== // // CachedFile.cc // // This file is licensed under the GPLv2 or later // // Copyright 2009 Stefan Thomas // Copyright 2010, 2011 Hib Eris // Copyright 2010, 2018-2020 Albert Astals Cid // Copyright (C) 2013 Julien Nabet // //======================================================================== #include #include "CachedFile.h" //------------------------------------------------------------------------ // CachedFile //------------------------------------------------------------------------ CachedFile::CachedFile(CachedFileLoader *cacheLoader, GooString *uriA) { uri = uriA; loader = cacheLoader; streamPos = 0; chunks = new std::vector(); length = 0; length = loader->init(uri, this); refCnt = 1; if (length != ((size_t)-1)) { chunks->resize(length / CachedFileChunkSize + 1); } else { error(errInternal, -1, "Failed to initialize file cache for '{0:t}'.", uri); chunks->resize(0); } } CachedFile::~CachedFile() { delete uri; delete loader; delete chunks; } void CachedFile::incRefCnt() { refCnt++; } void CachedFile::decRefCnt() { if (--refCnt == 0) delete this; } long int CachedFile::tell() { return streamPos; } int CachedFile::seek(long int offset, int origin) { if (origin == SEEK_SET) { streamPos = offset; } else if (origin == SEEK_CUR) { streamPos += offset; } else { streamPos = length + offset; } if (streamPos > length) { streamPos = 0; return 1; } return 0; } int CachedFile::cache(const std::vector &origRanges) { std::vector loadChunks; int numChunks = length / CachedFileChunkSize + 1; std::vector chunkNeeded(numChunks); int startChunk, endChunk; std::vector chunk_ranges, all; ByteRange range; const std::vector *ranges = &origRanges; if (ranges->empty()) { range.offset = 0; range.length = length; all.push_back(range); ranges = &all; } for (int i = 0; i < numChunks; ++i) chunkNeeded[i] = false; for (const ByteRange &r : *ranges) { if (r.length == 0) continue; if (r.offset >= length) continue; const size_t start = r.offset; size_t end = start + r.length - 1; if (end >= length) end = length - 1; startChunk = start / CachedFileChunkSize; endChunk = end / CachedFileChunkSize; for (int chunk = startChunk; chunk <= endChunk; chunk++) { if ((*chunks)[chunk].state == chunkStateNew) { chunkNeeded[chunk] = true; } } } int chunk = 0; while (chunk < numChunks) { while (!chunkNeeded[chunk] && (++chunk != numChunks)) ; if (chunk == numChunks) break; startChunk = chunk; loadChunks.push_back(chunk); while ((++chunk != numChunks) && chunkNeeded[chunk]) { loadChunks.push_back(chunk); } endChunk = chunk - 1; range.offset = startChunk * CachedFileChunkSize; range.length = (endChunk - startChunk + 1) * CachedFileChunkSize; chunk_ranges.push_back(range); } if (chunk_ranges.size() > 0) { CachedFileWriter writer = CachedFileWriter(this, &loadChunks); return loader->load(chunk_ranges, &writer); } return 0; } size_t CachedFile::read(void *ptr, size_t unitsize, size_t count) { size_t bytes = unitsize * count; if (length < (streamPos + bytes)) { bytes = length - streamPos; } if (bytes == 0) return 0; // Load data if (cache(streamPos, bytes) != 0) return 0; // Copy data to buffer size_t toCopy = bytes; while (toCopy) { int chunk = streamPos / CachedFileChunkSize; int offset = streamPos % CachedFileChunkSize; size_t len = CachedFileChunkSize - offset; if (len > toCopy) len = toCopy; memcpy(ptr, (*chunks)[chunk].data + offset, len); streamPos += len; toCopy -= len; ptr = (char *)ptr + len; } return bytes; } int CachedFile::cache(size_t rangeOffset, size_t rangeLength) { std::vector r; ByteRange range; range.offset = rangeOffset; range.length = rangeLength; r.push_back(range); return cache(r); } //------------------------------------------------------------------------ // CachedFileWriter //------------------------------------------------------------------------ CachedFileWriter::CachedFileWriter(CachedFile *cachedFileA, std::vector *chunksA) { cachedFile = cachedFileA; chunks = chunksA; if (chunks) { offset = 0; it = (*chunks).begin(); } } CachedFileWriter::~CachedFileWriter() { } size_t CachedFileWriter::write(const char *ptr, size_t size) { const char *cp = ptr; size_t len = size; size_t nfree, ncopy; size_t written = 0; size_t chunk; if (!len) return 0; while (len) { if (chunks) { if (offset == CachedFileChunkSize) { ++it; if (it == (*chunks).end()) return written; offset = 0; } chunk = *it; } else { offset = cachedFile->length % CachedFileChunkSize; chunk = cachedFile->length / CachedFileChunkSize; } if (chunk >= cachedFile->chunks->size()) { cachedFile->chunks->resize(chunk + 1); } nfree = CachedFileChunkSize - offset; ncopy = (len >= nfree) ? nfree : len; memcpy(&((*cachedFile->chunks)[chunk].data[offset]), cp, ncopy); len -= ncopy; cp += ncopy; offset += ncopy; written += ncopy; if (!chunks) { cachedFile->length += ncopy; } if (offset == CachedFileChunkSize) { (*cachedFile->chunks)[chunk].state = CachedFile::chunkStateLoaded; } } if ((chunk == (cachedFile->length / CachedFileChunkSize)) && (offset == (cachedFile->length % CachedFileChunkSize))) { (*cachedFile->chunks)[chunk].state = CachedFile::chunkStateLoaded; } return written; } CachedFileLoader::~CachedFileLoader() = default; //------------------------------------------------------------------------