2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2015-05-18 01:08:10 +02:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 05:09:55 +02:00
|
|
|
// Refer to the license.txt file included.
|
2010-02-19 18:05:26 +01:00
|
|
|
|
2014-02-10 19:54:46 +01:00
|
|
|
#pragma once
|
2010-02-19 18:05:26 +01:00
|
|
|
|
2014-02-20 04:11:52 +01:00
|
|
|
#include <cstring>
|
2010-11-15 06:22:03 +01:00
|
|
|
#include <fstream>
|
2014-03-12 20:33:41 +01:00
|
|
|
#include <string>
|
2010-02-19 18:05:26 +01:00
|
|
|
|
2014-09-08 03:06:58 +02:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-02-19 12:33:13 +01:00
|
|
|
#include "Common/FileUtil.h"
|
2014-02-17 11:18:15 +01:00
|
|
|
|
2010-02-19 18:05:26 +01:00
|
|
|
// On disk format:
|
2010-11-15 06:22:03 +01:00
|
|
|
//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;
|
|
|
|
//}
|
|
|
|
|
2011-06-11 21:37:21 +02:00
|
|
|
template <typename K, typename V>
|
|
|
|
class LinearDiskCacheReader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void Read(const K &key, const V *value, u32 value_size) = 0;
|
|
|
|
};
|
2010-02-19 18:05:26 +01:00
|
|
|
|
|
|
|
// 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.
|
2010-11-15 06:22:03 +01:00
|
|
|
|
|
|
|
// K and V are some POD type
|
|
|
|
// K : the key type
|
|
|
|
// V : value array type
|
|
|
|
template <typename K, typename V>
|
|
|
|
class LinearDiskCache
|
|
|
|
{
|
2010-02-19 18:05:26 +01:00
|
|
|
public:
|
2010-11-15 06:22:03 +01:00
|
|
|
// return number of read entries
|
2014-03-12 20:33:41 +01:00
|
|
|
u32 OpenAndRead(const std::string& filename, LinearDiskCacheReader<K, V> &reader)
|
2010-11-15 06:22:03 +01:00
|
|
|
{
|
2011-06-11 21:37:21 +02:00
|
|
|
using std::ios_base;
|
2010-11-15 06:22:03 +01:00
|
|
|
|
|
|
|
// close any currently opened file
|
|
|
|
Close();
|
2011-12-31 05:37:46 +01:00
|
|
|
m_num_entries = 0;
|
2010-11-15 06:22:03 +01:00
|
|
|
|
|
|
|
// try opening for reading/writing
|
2013-03-01 02:33:39 +01:00
|
|
|
OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary);
|
2011-12-31 05:37:46 +01:00
|
|
|
|
|
|
|
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;
|
2013-10-29 06:23:17 +01:00
|
|
|
|
2010-11-15 06:22:03 +01:00
|
|
|
if (m_file.is_open() && ValidateHeader())
|
|
|
|
{
|
|
|
|
// good header, read some key/value pairs
|
2011-06-11 21:37:21 +02:00
|
|
|
K key;
|
2010-11-15 06:22:03 +01:00
|
|
|
|
2014-03-09 21:14:26 +01:00
|
|
|
V *value = nullptr;
|
2015-09-29 04:27:27 +02:00
|
|
|
u32 value_size = 0;
|
|
|
|
u32 entry_number = 0;
|
2011-12-31 05:37:46 +01:00
|
|
|
|
|
|
|
std::fstream::pos_type last_pos = m_file.tellg();
|
2011-03-30 12:46:27 +02:00
|
|
|
|
2011-06-11 21:37:21 +02:00
|
|
|
while (Read(&value_size))
|
|
|
|
{
|
2011-12-31 05:37:46 +01:00
|
|
|
std::streamoff next_extent = (last_pos - start_pos) + sizeof(value_size) + value_size;
|
|
|
|
if (next_extent > file_size)
|
|
|
|
break;
|
|
|
|
|
2011-06-11 21:37:21 +02:00
|
|
|
delete[] value;
|
|
|
|
value = new V[value_size];
|
2010-11-15 06:22:03 +01:00
|
|
|
|
2011-06-11 21:37:21 +02:00
|
|
|
// read key/value and pass to reader
|
2011-12-31 05:37:46 +01:00
|
|
|
if (Read(&key) &&
|
2013-10-29 06:23:17 +01:00
|
|
|
Read(value, value_size) &&
|
2011-12-31 05:37:46 +01:00
|
|
|
Read(&entry_number) &&
|
|
|
|
entry_number == m_num_entries+1)
|
2014-02-17 05:51:41 +01:00
|
|
|
{
|
2011-06-11 21:37:21 +02:00
|
|
|
reader.Read(key, value, value_size);
|
2011-12-31 05:37:46 +01:00
|
|
|
}
|
2011-06-11 21:37:21 +02:00
|
|
|
else
|
2011-12-31 05:37:46 +01:00
|
|
|
{
|
2011-06-11 21:37:21 +02:00
|
|
|
break;
|
2011-12-31 05:37:46 +01:00
|
|
|
}
|
2010-11-15 06:22:03 +01:00
|
|
|
|
2011-12-31 05:37:46 +01:00
|
|
|
m_num_entries++;
|
|
|
|
last_pos = m_file.tellg();
|
2010-11-15 06:22:03 +01:00
|
|
|
}
|
2011-12-31 05:37:46 +01:00
|
|
|
m_file.seekp(last_pos);
|
2010-12-30 08:32:16 +01:00
|
|
|
m_file.clear();
|
2010-02-19 18:05:26 +01:00
|
|
|
|
2011-06-11 21:37:21 +02:00
|
|
|
delete[] value;
|
2011-12-31 05:37:46 +01:00
|
|
|
return m_num_entries;
|
2010-11-15 06:22:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// failed to open file for reading or bad header
|
|
|
|
// close and recreate file
|
|
|
|
Close();
|
2011-06-11 21:37:21 +02:00
|
|
|
m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary);
|
2010-11-15 06:22:03 +01:00
|
|
|
WriteHeader();
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-29 06:23:17 +01:00
|
|
|
|
2010-11-15 06:22:03 +01:00
|
|
|
void Sync()
|
|
|
|
{
|
|
|
|
m_file.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Close()
|
|
|
|
{
|
|
|
|
if (m_file.is_open())
|
|
|
|
m_file.close();
|
|
|
|
// clear any error flags
|
|
|
|
m_file.clear();
|
|
|
|
}
|
2010-02-19 18:05:26 +01:00
|
|
|
|
|
|
|
// Appends a key-value pair to the store.
|
2010-11-15 06:22:03 +01:00
|
|
|
void Append(const K &key, const V *value, u32 value_size)
|
|
|
|
{
|
2011-12-31 05:37:46 +01:00
|
|
|
// TODO: Should do a check that we don't already have "key"? (I think each caller does that already.)
|
2010-11-15 06:22:03 +01:00
|
|
|
Write(&value_size);
|
|
|
|
Write(&key);
|
|
|
|
Write(value, value_size);
|
2011-12-31 05:37:46 +01:00
|
|
|
m_num_entries++;
|
|
|
|
Write(&m_num_entries);
|
2010-11-15 06:22:03 +01:00
|
|
|
}
|
2010-02-19 18:05:26 +01:00
|
|
|
|
|
|
|
private:
|
2010-11-15 06:22:03 +01:00
|
|
|
void WriteHeader()
|
|
|
|
{
|
|
|
|
Write(&m_header);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ValidateHeader()
|
|
|
|
{
|
|
|
|
char file_header[sizeof(Header)];
|
|
|
|
|
2014-03-10 12:30:55 +01:00
|
|
|
return (Read(file_header, sizeof(Header)) &&
|
|
|
|
!memcmp((const char*)&m_header, file_header, sizeof(Header)));
|
2010-11-15 06:22:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2015-09-15 18:13:13 +02:00
|
|
|
: key_t_size(sizeof(K))
|
2010-11-15 06:22:03 +01:00
|
|
|
, value_t_size(sizeof(V))
|
2013-02-26 10:48:19 +01:00
|
|
|
{
|
2015-09-15 18:13:13 +02:00
|
|
|
// Null-terminator is intentionally not copied.
|
|
|
|
std::memcpy(&id, "DCAC", sizeof(u32));
|
|
|
|
std::memcpy(ver, scm_rev_git_str, 40);
|
2013-02-26 10:48:19 +01:00
|
|
|
}
|
2010-11-15 06:22:03 +01:00
|
|
|
|
2015-09-15 18:13:13 +02:00
|
|
|
u32 id;
|
2010-11-15 06:22:03 +01:00
|
|
|
const u16 key_t_size, value_t_size;
|
2013-02-26 10:48:19 +01:00
|
|
|
char ver[40];
|
2010-11-15 06:22:03 +01:00
|
|
|
|
|
|
|
} m_header;
|
2010-02-19 18:05:26 +01:00
|
|
|
|
2010-11-15 06:22:03 +01:00
|
|
|
std::fstream m_file;
|
2011-12-31 05:37:46 +01:00
|
|
|
u32 m_num_entries;
|
2010-02-19 18:05:26 +01:00
|
|
|
};
|