Files
ppsspp/Core/FileLoaders/DiskCachingFileLoader.h
T
Unknown W. Brackets ee5b68f1fc Loaders: Add cancelation to all file loaders.
Mainly, for HTTP which might be stalled trying to connect (especially if
you're not near your PC right now and it's in your recent, for example.)
2017-12-09 16:47:37 -08:00

186 lines
4.6 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <vector>
#include <map>
#include <mutex>
#include "Common/Common.h"
#include "Common/Swap.h"
#include "Core/Loaders.h"
class DiskCachingFileLoaderCache;
class DiskCachingFileLoader : public FileLoader {
public:
DiskCachingFileLoader(FileLoader *backend);
~DiskCachingFileLoader() override;
bool Exists() override;
bool ExistsFast() override;
bool IsDirectory() override;
s64 FileSize() override;
std::string Path() const override;
size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override {
return ReadAt(absolutePos, bytes * count, data, flags) / bytes;
}
size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override;
void Cancel() override;
static std::vector<std::string> GetCachedPathsInUse();
private:
void Prepare();
void InitCache();
void ShutdownCache();
std::once_flag preparedFlag_;
s64 filesize_ = 0;
FileLoader *backend_;
DiskCachingFileLoaderCache *cache_ = nullptr;
// We don't support concurrent disk cache access (we use memory cached indexes.)
// So we have to ensure there's only one of these per.
static std::map<std::string, DiskCachingFileLoaderCache *> caches_;
static std::mutex cachesMutex_;
};
class DiskCachingFileLoaderCache {
public:
DiskCachingFileLoaderCache(const std::string &path, u64 filesize);
~DiskCachingFileLoaderCache();
bool IsValid() {
return f_ != nullptr;
}
void AddRef() {
++refCount_;
}
bool Release() {
return --refCount_ == 0;
}
static void SetCacheDir(const std::string &path) {
cacheDir_ = path;
}
size_t ReadFromCache(s64 pos, size_t bytes, void *data);
// Guaranteed to read at least one block into the cache.
size_t SaveIntoCache(FileLoader *backend, s64 pos, size_t bytes, void *data, FileLoader::Flags flags);
bool HasData() const;
private:
void InitCache(const std::string &path);
void ShutdownCache();
bool MakeCacheSpaceFor(size_t blocks);
void RebalanceGenerations();
u32 AllocateBlock(u32 indexPos);
struct BlockInfo;
bool ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size);
void WriteBlockData(BlockInfo &info, u8 *src);
void WriteIndexData(u32 indexPos, BlockInfo &info);
s64 GetBlockOffset(u32 block);
std::string MakeCacheFilePath(const std::string &path);
std::string MakeCacheFilename(const std::string &path);
bool LoadCacheFile(const std::string &path);
void LoadCacheIndex();
void CreateCacheFile(const std::string &path);
bool LockCacheFile(bool lockStatus);
bool RemoveCacheFile(const std::string &path);
void CloseFileHandle();
u64 FreeDiskSpace();
u32 DetermineMaxBlocks();
u32 CountCachedFiles();
void GarbageCollectCacheFiles(u64 goalBytes);
// File format:
// 64 magic
// 32 version
// 32 blockSize
// 64 filesize
// 32 maxBlocks
// 32 flags
// index[filesize / blockSize] <-- ~500 KB for 4GB
// 32 (fileoffset - headersize) / blockSize -> -1=not present
// 16 generation?
// 16 hits?
// blocks[up to maxBlocks]
// 8 * blockSize
enum {
CACHE_VERSION = 3,
DEFAULT_BLOCK_SIZE = 65536,
MAX_BLOCKS_PER_READ = 16,
MAX_BLOCKS_LOWER_BOUND = 256, // 16 MB
MAX_BLOCKS_UPPER_BOUND = 8192, // 512 MB
INVALID_BLOCK = 0xFFFFFFFF,
INVALID_INDEX = 0xFFFFFFFF,
};
int refCount_ = 0;
s64 filesize_;
u32 blockSize_;
u16 generation_;
u16 oldestGeneration_;
u32 maxBlocks_;
u32 flags_;
size_t cacheSize_;
size_t indexCount_;
std::mutex lock_;
std::string origPath_;
struct FileHeader {
char magic[8];
u32_le version;
u32_le blockSize;
s64_le filesize;
u32_le maxBlocks;
u32_le flags;
};
enum FileFlags {
FLAG_LOCKED = 1 << 0,
};
struct BlockInfo {
u32 block;
u16 generation;
u16 hits;
BlockInfo() : block(-1), generation(0), hits(0) {
}
};
std::vector<BlockInfo> index_;
std::vector<u32> blockIndexLookup_;
FILE *f_ = nullptr;
int fd_ = 0;
static std::string cacheDir_;
};