Spaces:
Sleeping
Sleeping
//======================================================================== | |
// | |
// CachedFile.cc | |
// | |
// This file is licensed under the GPLv2 or later | |
// | |
// Copyright 2009 Stefan Thomas <[email protected]> | |
// Copyright 2010, 2011 Hib Eris <[email protected]> | |
// Copyright 2010, 2018-2020, 2022 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2013 Julien Nabet <[email protected]> | |
// | |
//======================================================================== | |
//------------------------------------------------------------------------ | |
// CachedFile | |
//------------------------------------------------------------------------ | |
CachedFile::CachedFile(CachedFileLoader *cacheLoader) | |
{ | |
loader = cacheLoader; | |
streamPos = 0; | |
chunks = new std::vector<Chunk>(); | |
length = 0; | |
length = loader->init(this); | |
refCnt = 1; | |
if (length != ((size_t)-1)) { | |
chunks->resize(length / CachedFileChunkSize + 1); | |
} else { | |
error(errInternal, -1, "Failed to initialize file cache."); | |
chunks->resize(0); | |
} | |
} | |
CachedFile::~CachedFile() | |
{ | |
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<ByteRange> &origRanges) | |
{ | |
std::vector<int> loadChunks; | |
int numChunks = length / CachedFileChunkSize + 1; | |
std::vector<bool> chunkNeeded(numChunks); | |
int startChunk, endChunk; | |
std::vector<ByteRange> chunk_ranges, all; | |
ByteRange range; | |
const std::vector<ByteRange> *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<ByteRange> r; | |
ByteRange range; | |
range.offset = rangeOffset; | |
range.length = rangeLength; | |
r.push_back(range); | |
return cache(r); | |
} | |
//------------------------------------------------------------------------ | |
// CachedFileWriter | |
//------------------------------------------------------------------------ | |
CachedFileWriter::CachedFileWriter(CachedFile *cachedFileA, std::vector<int> *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; | |
//------------------------------------------------------------------------ | |