Browse Source
Merge pull request #403 from lioncash/common
Merge pull request #403 from lioncash/common
common: Remove chunk_file.h and linear_disk_cache.hnce_cpp
committed by
GitHub
3 changed files with 0 additions and 792 deletions
@ -1,623 +0,0 @@ |
|||||
// Copyright (C) 2003 Dolphin 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 SVN repository and contact information can be found at |
|
||||
// http://code.google.com/p/dolphin-emu/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
// Extremely simple serialization framework. |
|
||||
|
|
||||
// (mis)-features: |
|
||||
// + Super fast |
|
||||
// + Very simple |
|
||||
// + Same code is used for serialization and deserializaition (in most cases) |
|
||||
// - Zero backwards/forwards compatibility |
|
||||
// - Serialization code for anything complex has to be manually written. |
|
||||
|
|
||||
#include <cstring> |
|
||||
#include <deque> |
|
||||
#include <list> |
|
||||
#include <map> |
|
||||
#include <set> |
|
||||
#include <string> |
|
||||
#include <type_traits> |
|
||||
#include <utility> |
|
||||
#include <vector> |
|
||||
#include "common/assert.h" |
|
||||
#include "common/common_types.h" |
|
||||
#include "common/logging/log.h" |
|
||||
|
|
||||
template <class T> |
|
||||
struct LinkedListItem : public T { |
|
||||
LinkedListItem<T>* next; |
|
||||
}; |
|
||||
|
|
||||
class PointerWrap; |
|
||||
|
|
||||
class PointerWrapSection { |
|
||||
public: |
|
||||
PointerWrapSection(PointerWrap& p, int ver, const char* title) |
|
||||
: p_(p), ver_(ver), title_(title) {} |
|
||||
~PointerWrapSection(); |
|
||||
|
|
||||
bool operator==(const int& v) const { |
|
||||
return ver_ == v; |
|
||||
} |
|
||||
bool operator!=(const int& v) const { |
|
||||
return ver_ != v; |
|
||||
} |
|
||||
bool operator<=(const int& v) const { |
|
||||
return ver_ <= v; |
|
||||
} |
|
||||
bool operator>=(const int& v) const { |
|
||||
return ver_ >= v; |
|
||||
} |
|
||||
bool operator<(const int& v) const { |
|
||||
return ver_ < v; |
|
||||
} |
|
||||
bool operator>(const int& v) const { |
|
||||
return ver_ > v; |
|
||||
} |
|
||||
|
|
||||
operator bool() const { |
|
||||
return ver_ > 0; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
PointerWrap& p_; |
|
||||
int ver_; |
|
||||
const char* title_; |
|
||||
}; |
|
||||
|
|
||||
// Wrapper class |
|
||||
class PointerWrap { |
|
||||
// This makes it a compile error if you forget to define DoState() on non-POD. |
|
||||
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason... |
|
||||
#ifdef _MSC_VER |
|
||||
template <typename T, bool isPOD = std::is_pod<T>::value, |
|
||||
bool isPointer = std::is_pointer<T>::value> |
|
||||
#else |
|
||||
template <typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> |
|
||||
#endif |
|
||||
struct DoHelper { |
|
||||
static void DoArray(PointerWrap* p, T* x, int count) { |
|
||||
for (int i = 0; i < count; ++i) |
|
||||
p->Do(x[i]); |
|
||||
} |
|
||||
|
|
||||
static void Do(PointerWrap* p, T& x) { |
|
||||
p->DoClass(x); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
template <typename T> |
|
||||
struct DoHelper<T, true, false> { |
|
||||
static void DoArray(PointerWrap* p, T* x, int count) { |
|
||||
p->DoVoid((void*)x, sizeof(T) * count); |
|
||||
} |
|
||||
|
|
||||
static void Do(PointerWrap* p, T& x) { |
|
||||
p->DoVoid((void*)&x, sizeof(x)); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
public: |
|
||||
enum Mode { |
|
||||
MODE_READ = 1, // load |
|
||||
MODE_WRITE, // save |
|
||||
MODE_MEASURE, // calculate size |
|
||||
MODE_VERIFY, // compare |
|
||||
}; |
|
||||
|
|
||||
enum Error { |
|
||||
ERROR_NONE = 0, |
|
||||
ERROR_WARNING = 1, |
|
||||
ERROR_FAILURE = 2, |
|
||||
}; |
|
||||
|
|
||||
u8** ptr; |
|
||||
Mode mode; |
|
||||
Error error; |
|
||||
|
|
||||
public: |
|
||||
PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} |
|
||||
PointerWrap(unsigned char** ptr_, int mode_) |
|
||||
: ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} |
|
||||
|
|
||||
PointerWrapSection Section(const char* title, int ver) { |
|
||||
return Section(title, ver, ver); |
|
||||
} |
|
||||
|
|
||||
// The returned object can be compared against the version that was loaded. |
|
||||
// This can be used to support versions as old as minVer. |
|
||||
// Version = 0 means the section was not found. |
|
||||
PointerWrapSection Section(const char* title, int minVer, int ver) { |
|
||||
char marker[16] = {0}; |
|
||||
int foundVersion = ver; |
|
||||
|
|
||||
strncpy(marker, title, sizeof(marker)); |
|
||||
if (!ExpectVoid(marker, sizeof(marker))) { |
|
||||
// Might be before we added name markers for safety. |
|
||||
if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) |
|
||||
DoMarker(title); |
|
||||
// Wasn't found, but maybe we can still load the state. |
|
||||
else |
|
||||
foundVersion = 0; |
|
||||
} else |
|
||||
Do(foundVersion); |
|
||||
|
|
||||
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { |
|
||||
LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, |
|
||||
title); |
|
||||
SetError(ERROR_FAILURE); |
|
||||
return PointerWrapSection(*this, -1, title); |
|
||||
} |
|
||||
return PointerWrapSection(*this, foundVersion, title); |
|
||||
} |
|
||||
|
|
||||
void SetMode(Mode mode_) { |
|
||||
mode = mode_; |
|
||||
} |
|
||||
Mode GetMode() const { |
|
||||
return mode; |
|
||||
} |
|
||||
u8** GetPPtr() { |
|
||||
return ptr; |
|
||||
} |
|
||||
void SetError(Error error_) { |
|
||||
if (error < error_) |
|
||||
error = error_; |
|
||||
if (error > ERROR_WARNING) |
|
||||
mode = PointerWrap::MODE_MEASURE; |
|
||||
} |
|
||||
|
|
||||
bool ExpectVoid(void* data, int size) { |
|
||||
switch (mode) { |
|
||||
case MODE_READ: |
|
||||
if (memcmp(data, *ptr, size) != 0) |
|
||||
return false; |
|
||||
break; |
|
||||
case MODE_WRITE: |
|
||||
memcpy(*ptr, data, size); |
|
||||
break; |
|
||||
case MODE_MEASURE: |
|
||||
break; // MODE_MEASURE - don't need to do anything |
|
||||
case MODE_VERIFY: |
|
||||
for (int i = 0; i < size; i++) { |
|
||||
DEBUG_ASSERT_MSG( |
|
||||
((u8*)data)[i] == (*ptr)[i], |
|
||||
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", |
|
||||
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], |
|
||||
&(*ptr)[i]); |
|
||||
} |
|
||||
break; |
|
||||
default: |
|
||||
break; // throw an error? |
|
||||
} |
|
||||
(*ptr) += size; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
void DoVoid(void* data, int size) { |
|
||||
switch (mode) { |
|
||||
case MODE_READ: |
|
||||
memcpy(data, *ptr, size); |
|
||||
break; |
|
||||
case MODE_WRITE: |
|
||||
memcpy(*ptr, data, size); |
|
||||
break; |
|
||||
case MODE_MEASURE: |
|
||||
break; // MODE_MEASURE - don't need to do anything |
|
||||
case MODE_VERIFY: |
|
||||
for (int i = 0; i < size; i++) { |
|
||||
DEBUG_ASSERT_MSG( |
|
||||
((u8*)data)[i] == (*ptr)[i], |
|
||||
"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", |
|
||||
((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], |
|
||||
&(*ptr)[i]); |
|
||||
} |
|
||||
break; |
|
||||
default: |
|
||||
break; // throw an error? |
|
||||
} |
|
||||
(*ptr) += size; |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void Do(std::map<K, T*>& x) { |
|
||||
if (mode == MODE_READ) { |
|
||||
for (auto it = x.begin(), end = x.end(); it != end; ++it) { |
|
||||
if (it->second != nullptr) |
|
||||
delete it->second; |
|
||||
} |
|
||||
} |
|
||||
T* dv = nullptr; |
|
||||
DoMap(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void Do(std::map<K, T>& x) { |
|
||||
T dv = T(); |
|
||||
DoMap(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void DoMap(std::map<K, T>& x, T& default_val) { |
|
||||
unsigned int number = (unsigned int)x.size(); |
|
||||
Do(number); |
|
||||
switch (mode) { |
|
||||
case MODE_READ: { |
|
||||
x.clear(); |
|
||||
while (number > 0) { |
|
||||
K first = K(); |
|
||||
Do(first); |
|
||||
T second = default_val; |
|
||||
Do(second); |
|
||||
x[first] = second; |
|
||||
--number; |
|
||||
} |
|
||||
} break; |
|
||||
case MODE_WRITE: |
|
||||
case MODE_MEASURE: |
|
||||
case MODE_VERIFY: { |
|
||||
typename std::map<K, T>::iterator itr = x.begin(); |
|
||||
while (number > 0) { |
|
||||
K first = itr->first; |
|
||||
Do(first); |
|
||||
Do(itr->second); |
|
||||
--number; |
|
||||
++itr; |
|
||||
} |
|
||||
} break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void Do(std::multimap<K, T*>& x) { |
|
||||
if (mode == MODE_READ) { |
|
||||
for (auto it = x.begin(), end = x.end(); it != end; ++it) { |
|
||||
if (it->second != nullptr) |
|
||||
delete it->second; |
|
||||
} |
|
||||
} |
|
||||
T* dv = nullptr; |
|
||||
DoMultimap(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void Do(std::multimap<K, T>& x) { |
|
||||
T dv = T(); |
|
||||
DoMultimap(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class K, class T> |
|
||||
void DoMultimap(std::multimap<K, T>& x, T& default_val) { |
|
||||
unsigned int number = (unsigned int)x.size(); |
|
||||
Do(number); |
|
||||
switch (mode) { |
|
||||
case MODE_READ: { |
|
||||
x.clear(); |
|
||||
while (number > 0) { |
|
||||
K first = K(); |
|
||||
Do(first); |
|
||||
T second = default_val; |
|
||||
Do(second); |
|
||||
x.insert(std::make_pair(first, second)); |
|
||||
--number; |
|
||||
} |
|
||||
} break; |
|
||||
case MODE_WRITE: |
|
||||
case MODE_MEASURE: |
|
||||
case MODE_VERIFY: { |
|
||||
typename std::multimap<K, T>::iterator itr = x.begin(); |
|
||||
while (number > 0) { |
|
||||
Do(itr->first); |
|
||||
Do(itr->second); |
|
||||
--number; |
|
||||
++itr; |
|
||||
} |
|
||||
} break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Store vectors. |
|
||||
template <class T> |
|
||||
void Do(std::vector<T*>& x) { |
|
||||
T* dv = nullptr; |
|
||||
DoVector(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::vector<T>& x) { |
|
||||
T dv = T(); |
|
||||
DoVector(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoPOD(std::vector<T>& x) { |
|
||||
T dv = T(); |
|
||||
DoVectorPOD(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::vector<T>& x, T& default_val) { |
|
||||
DoVector(x, default_val); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoVector(std::vector<T>& x, T& default_val) { |
|
||||
u32 vec_size = (u32)x.size(); |
|
||||
Do(vec_size); |
|
||||
x.resize(vec_size, default_val); |
|
||||
if (vec_size > 0) |
|
||||
DoArray(&x[0], vec_size); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoVectorPOD(std::vector<T>& x, T& default_val) { |
|
||||
u32 vec_size = (u32)x.size(); |
|
||||
Do(vec_size); |
|
||||
x.resize(vec_size, default_val); |
|
||||
if (vec_size > 0) |
|
||||
DoArray(&x[0], vec_size); |
|
||||
} |
|
||||
|
|
||||
// Store deques. |
|
||||
template <class T> |
|
||||
void Do(std::deque<T*>& x) { |
|
||||
T* dv = nullptr; |
|
||||
DoDeque(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::deque<T>& x) { |
|
||||
T dv = T(); |
|
||||
DoDeque(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoDeque(std::deque<T>& x, T& default_val) { |
|
||||
u32 deq_size = (u32)x.size(); |
|
||||
Do(deq_size); |
|
||||
x.resize(deq_size, default_val); |
|
||||
u32 i; |
|
||||
for (i = 0; i < deq_size; i++) |
|
||||
Do(x[i]); |
|
||||
} |
|
||||
|
|
||||
// Store STL lists. |
|
||||
template <class T> |
|
||||
void Do(std::list<T*>& x) { |
|
||||
T* dv = nullptr; |
|
||||
Do(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::list<T>& x) { |
|
||||
T dv = T(); |
|
||||
DoList(x, dv); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::list<T>& x, T& default_val) { |
|
||||
DoList(x, default_val); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoList(std::list<T>& x, T& default_val) { |
|
||||
u32 list_size = (u32)x.size(); |
|
||||
Do(list_size); |
|
||||
x.resize(list_size, default_val); |
|
||||
|
|
||||
typename std::list<T>::iterator itr, end; |
|
||||
for (itr = x.begin(), end = x.end(); itr != end; ++itr) |
|
||||
Do(*itr); |
|
||||
} |
|
||||
|
|
||||
// Store STL sets. |
|
||||
template <class T> |
|
||||
void Do(std::set<T*>& x) { |
|
||||
if (mode == MODE_READ) { |
|
||||
for (auto it = x.begin(), end = x.end(); it != end; ++it) { |
|
||||
if (*it != nullptr) |
|
||||
delete *it; |
|
||||
} |
|
||||
} |
|
||||
DoSet(x); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(std::set<T>& x) { |
|
||||
DoSet(x); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoSet(std::set<T>& x) { |
|
||||
unsigned int number = (unsigned int)x.size(); |
|
||||
Do(number); |
|
||||
|
|
||||
switch (mode) { |
|
||||
case MODE_READ: { |
|
||||
x.clear(); |
|
||||
while (number-- > 0) { |
|
||||
T it = T(); |
|
||||
Do(it); |
|
||||
x.insert(it); |
|
||||
} |
|
||||
} break; |
|
||||
case MODE_WRITE: |
|
||||
case MODE_MEASURE: |
|
||||
case MODE_VERIFY: { |
|
||||
typename std::set<T>::iterator itr = x.begin(); |
|
||||
while (number-- > 0) |
|
||||
Do(*itr++); |
|
||||
} break; |
|
||||
|
|
||||
default: |
|
||||
LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Store strings. |
|
||||
void Do(std::string& x) { |
|
||||
int stringLen = (int)x.length() + 1; |
|
||||
Do(stringLen); |
|
||||
|
|
||||
switch (mode) { |
|
||||
case MODE_READ: |
|
||||
x = (char*)*ptr; |
|
||||
break; |
|
||||
case MODE_WRITE: |
|
||||
memcpy(*ptr, x.c_str(), stringLen); |
|
||||
break; |
|
||||
case MODE_MEASURE: |
|
||||
break; |
|
||||
case MODE_VERIFY: |
|
||||
DEBUG_ASSERT_MSG((x == (char*)*ptr), |
|
||||
"Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", |
|
||||
x.c_str(), (char*)*ptr, ptr); |
|
||||
break; |
|
||||
} |
|
||||
(*ptr) += stringLen; |
|
||||
} |
|
||||
|
|
||||
void Do(std::wstring& x) { |
|
||||
int stringLen = sizeof(wchar_t) * ((int)x.length() + 1); |
|
||||
Do(stringLen); |
|
||||
|
|
||||
switch (mode) { |
|
||||
case MODE_READ: |
|
||||
x = (wchar_t*)*ptr; |
|
||||
break; |
|
||||
case MODE_WRITE: |
|
||||
memcpy(*ptr, x.c_str(), stringLen); |
|
||||
break; |
|
||||
case MODE_MEASURE: |
|
||||
break; |
|
||||
case MODE_VERIFY: |
|
||||
DEBUG_ASSERT_MSG((x == (wchar_t*)*ptr), |
|
||||
"Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", |
|
||||
x.c_str(), (wchar_t*)*ptr, ptr); |
|
||||
break; |
|
||||
} |
|
||||
(*ptr) += stringLen; |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoClass(T& x) { |
|
||||
x.DoState(*this); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoClass(T*& x) { |
|
||||
if (mode == MODE_READ) { |
|
||||
if (x != nullptr) |
|
||||
delete x; |
|
||||
x = new T(); |
|
||||
} |
|
||||
x->DoState(*this); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoArray(T* x, int count) { |
|
||||
DoHelper<T>::DoArray(this, x, count); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void Do(T& x) { |
|
||||
DoHelper<T>::Do(this, x); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoPOD(T& x) { |
|
||||
DoHelper<T>::Do(this, x); |
|
||||
} |
|
||||
|
|
||||
template <class T> |
|
||||
void DoPointer(T*& x, T* const base) { |
|
||||
// pointers can be more than 2^31 apart, but you're using this function wrong if you need |
|
||||
// that much range |
|
||||
s32 offset = x - base; |
|
||||
Do(offset); |
|
||||
if (mode == MODE_READ) |
|
||||
x = base + offset; |
|
||||
} |
|
||||
|
|
||||
template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), |
|
||||
void (*TDo)(PointerWrap&, T*)> |
|
||||
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = nullptr) { |
|
||||
LinkedListItem<T>* list_cur = list_start; |
|
||||
LinkedListItem<T>* prev = nullptr; |
|
||||
|
|
||||
while (true) { |
|
||||
u8 shouldExist = (list_cur ? 1 : 0); |
|
||||
Do(shouldExist); |
|
||||
if (shouldExist == 1) { |
|
||||
LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); |
|
||||
TDo(*this, (T*)cur); |
|
||||
if (!list_cur) { |
|
||||
if (mode == MODE_READ) { |
|
||||
cur->next = nullptr; |
|
||||
list_cur = cur; |
|
||||
if (prev) |
|
||||
prev->next = cur; |
|
||||
else |
|
||||
list_start = cur; |
|
||||
} else { |
|
||||
TFree(cur); |
|
||||
continue; |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
if (mode == MODE_READ) { |
|
||||
if (prev) |
|
||||
prev->next = nullptr; |
|
||||
if (list_end) |
|
||||
*list_end = prev; |
|
||||
if (list_cur) { |
|
||||
if (list_start == list_cur) |
|
||||
list_start = nullptr; |
|
||||
do { |
|
||||
LinkedListItem<T>* next = list_cur->next; |
|
||||
TFree(list_cur); |
|
||||
list_cur = next; |
|
||||
} while (list_cur); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
prev = list_cur; |
|
||||
list_cur = list_cur->next; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) { |
|
||||
u32 cookie = arbitraryNumber; |
|
||||
Do(cookie); |
|
||||
if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) { |
|
||||
LOG_ERROR(Common, |
|
||||
"After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). " |
|
||||
"Aborting savestate load...", |
|
||||
prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); |
|
||||
SetError(ERROR_FAILURE); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
inline PointerWrapSection::~PointerWrapSection() { |
|
||||
if (ver_ > 0) { |
|
||||
p_.DoMarker(title_); |
|
||||
} |
|
||||
} |
|
||||
@ -1,167 +0,0 @@ |
|||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <fstream> |
|
||||
#include "common/common_types.h" |
|
||||
|
|
||||
// defined in Version.cpp |
|
||||
extern const char* scm_rev_git_str; |
|
||||
|
|
||||
// On disk format: |
|
||||
// header{ |
|
||||
// u32 'DCAC'; |
|
||||
// u32 version; // svn_rev |
|
||||
// u16 sizeof(key_type); |
|
||||
// u16 sizeof(value_type); |
|
||||
//} |
|
||||
|
|
||||
// key_value_pair{ |
|
||||
// u32 value_size; |
|
||||
// key_type key; |
|
||||
// value_type[value_size] value; |
|
||||
//} |
|
||||
|
|
||||
template <typename K, typename V> |
|
||||
class LinearDiskCacheReader { |
|
||||
public: |
|
||||
virtual void Read(const K& key, const V* value, u32 value_size) = 0; |
|
||||
}; |
|
||||
|
|
||||
// Dead simple unsorted key-value store with append functionality. |
|
||||
// No random read functionality, all reading is done in OpenAndRead. |
|
||||
// Keys and values can contain any characters, including \0. |
|
||||
// |
|
||||
// Suitable for caching generated shader bytecode between executions. |
|
||||
// Not tuned for extreme performance but should be reasonably fast. |
|
||||
// Does not support keys or values larger than 2GB, which should be reasonable. |
|
||||
// Keys must have non-zero length; values can have zero length. |
|
||||
|
|
||||
// K and V are some POD type |
|
||||
// K : the key type |
|
||||
// V : value array type |
|
||||
template <typename K, typename V> |
|
||||
class LinearDiskCache { |
|
||||
public: |
|
||||
// return number of read entries |
|
||||
u32 OpenAndRead(const char* filename, LinearDiskCacheReader<K, V>& reader) { |
|
||||
using std::ios_base; |
|
||||
|
|
||||
// close any currently opened file |
|
||||
Close(); |
|
||||
m_num_entries = 0; |
|
||||
|
|
||||
// try opening for reading/writing |
|
||||
OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary); |
|
||||
|
|
||||
m_file.seekg(0, std::ios::end); |
|
||||
std::fstream::pos_type end_pos = m_file.tellg(); |
|
||||
m_file.seekg(0, std::ios::beg); |
|
||||
std::fstream::pos_type start_pos = m_file.tellg(); |
|
||||
std::streamoff file_size = end_pos - start_pos; |
|
||||
|
|
||||
if (m_file.is_open() && ValidateHeader()) { |
|
||||
// good header, read some key/value pairs |
|
||||
K key; |
|
||||
|
|
||||
V* value = nullptr; |
|
||||
u32 value_size; |
|
||||
u32 entry_number; |
|
||||
|
|
||||
std::fstream::pos_type last_pos = m_file.tellg(); |
|
||||
|
|
||||
while (Read(&value_size)) { |
|
||||
std::streamoff next_extent = |
|
||||
(last_pos - start_pos) + sizeof(value_size) + value_size; |
|
||||
if (next_extent > file_size) |
|
||||
break; |
|
||||
|
|
||||
delete[] value; |
|
||||
value = new V[value_size]; |
|
||||
|
|
||||
// read key/value and pass to reader |
|
||||
if (Read(&key) && Read(value, value_size) && Read(&entry_number) && |
|
||||
entry_number == m_num_entries + 1) { |
|
||||
reader.Read(key, value, value_size); |
|
||||
} else { |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
m_num_entries++; |
|
||||
last_pos = m_file.tellg(); |
|
||||
} |
|
||||
m_file.seekp(last_pos); |
|
||||
m_file.clear(); |
|
||||
|
|
||||
delete[] value; |
|
||||
return m_num_entries; |
|
||||
} |
|
||||
|
|
||||
// failed to open file for reading or bad header |
|
||||
// close and recreate file |
|
||||
Close(); |
|
||||
m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary); |
|
||||
WriteHeader(); |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
void Sync() { |
|
||||
m_file.flush(); |
|
||||
} |
|
||||
|
|
||||
void Close() { |
|
||||
if (m_file.is_open()) |
|
||||
m_file.close(); |
|
||||
// clear any error flags |
|
||||
m_file.clear(); |
|
||||
} |
|
||||
|
|
||||
// Appends a key-value pair to the store. |
|
||||
void Append(const K& key, const V* value, u32 value_size) { |
|
||||
// TODO: Should do a check that we don't already have "key"? (I think each caller does that |
|
||||
// already.) |
|
||||
Write(&value_size); |
|
||||
Write(&key); |
|
||||
Write(value, value_size); |
|
||||
m_num_entries++; |
|
||||
Write(&m_num_entries); |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
void WriteHeader() { |
|
||||
Write(&m_header); |
|
||||
} |
|
||||
|
|
||||
bool ValidateHeader() { |
|
||||
char file_header[sizeof(Header)]; |
|
||||
|
|
||||
return (Read(file_header, sizeof(Header)) && |
|
||||
!memcmp((const char*)&m_header, file_header, sizeof(Header))); |
|
||||
} |
|
||||
|
|
||||
template <typename D> |
|
||||
bool Write(const D* data, u32 count = 1) { |
|
||||
return m_file.write((const char*)data, count * sizeof(D)).good(); |
|
||||
} |
|
||||
|
|
||||
template <typename D> |
|
||||
bool Read(const D* data, u32 count = 1) { |
|
||||
return m_file.read((char*)data, count * sizeof(D)).good(); |
|
||||
} |
|
||||
|
|
||||
struct Header { |
|
||||
Header() : id(*(u32*)"DCAC"), key_t_size(sizeof(K)), value_t_size(sizeof(V)) { |
|
||||
memcpy(ver, scm_rev_git_str, 40); |
|
||||
} |
|
||||
|
|
||||
const u32 id; |
|
||||
const u16 key_t_size, value_t_size; |
|
||||
char ver[40]; |
|
||||
|
|
||||
} m_header; |
|
||||
|
|
||||
std::fstream m_file; |
|
||||
u32 m_num_entries; |
|
||||
}; |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue