blob: 1e76d913b999f76ddd138eec5668a02a6ebb7720 [file] [log] [blame]
//========================================================================
//
// CachedFile.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2009 Stefan Thomas <thomas@eload24.com>
// Copyright 2010, 2011 Hib Eris <hib@hiberis.nl>
// Copyright 2010, 2018 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2013 Julien Nabet <serval2412@yahoo.fr>
//
//========================================================================
#include <config.h>
#include "CachedFile.h"
//------------------------------------------------------------------------
// CachedFile
//------------------------------------------------------------------------
CachedFile::CachedFile(CachedFileLoader *cachedFileLoaderA, GooString *uriA)
{
uri = uriA;
loader = cachedFileLoaderA;
streamPos = 0;
chunks = new std::vector<Chunk>();
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<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 (size_t i = 0; i < ranges->size(); i++) {
if ((*ranges)[i].length == 0) continue;
if ((*ranges)[i].offset >= length) continue;
size_t start = (*ranges)[i].offset;
size_t end = start + (*ranges)[i].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;
}
//------------------------------------------------------------------------