diff options
Diffstat (limited to 'src/common/linear_disk_cache.h')
-rw-r--r-- | src/common/linear_disk_cache.h | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h new file mode 100644 index 000000000..da5d6b9b4 --- /dev/null +++ b/src/common/linear_disk_cache.h @@ -0,0 +1,191 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _LINEAR_DISKCACHE +#define _LINEAR_DISKCACHE + +#include "common.h" +#include <fstream> + +// 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 = NULL; + 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; +}; + +#endif // _LINEAR_DISKCACHE |