danicoin/src/CryptoNoteCore/SwappedVector.h
2016-01-18 15:33:29 +00:00

397 lines
10 KiB
C++

// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <cstdint>
#include <cstddef>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <vector>
#include "Common/StdInputStream.h"
#include "Common/StdOutputStream.h"
#include "Serialization/BinaryInputStreamSerializer.h"
#include "Serialization/BinaryOutputStreamSerializer.h"
template<class T> class SwappedVector {
public:
typedef T value_type;
class const_iterator {
public:
typedef ptrdiff_t difference_type;
typedef std::random_access_iterator_tag iterator_category;
typedef const T* pointer;
typedef const T& reference;
typedef T value_type;
const_iterator() {
}
const_iterator(SwappedVector* swappedVector, size_t index) : m_swappedVector(swappedVector), m_index(index) {
}
bool operator!=(const const_iterator& other) const {
return m_index != other.m_index;
}
bool operator<(const const_iterator& other) const {
return m_index < other.m_index;
}
bool operator<=(const const_iterator& other) const {
return m_index <= other.m_index;
}
bool operator==(const const_iterator& other) const {
return m_index == other.m_index;
}
bool operator>(const const_iterator& other) const {
return m_index > other.m_index;
}
bool operator>=(const const_iterator& other) const {
return m_index >= other.m_index;
}
const_iterator& operator++() {
++m_index;
return *this;
}
const_iterator operator++(int) {
const_iterator i = *this;
++m_index;
return i;
}
const_iterator& operator--() {
--m_index;
return *this;
}
const_iterator operator--(int) {
const_iterator i = *this;
--m_index;
return i;
}
const_iterator& operator+=(difference_type n) {
m_index += n;
return *this;
}
const_iterator& operator-=(difference_type n) {
m_index -= n;
return *this;
}
const_iterator operator+(difference_type n) const {
return const_iterator(m_swappedVector, m_index + n);
}
friend const_iterator operator+(difference_type n, const const_iterator& i) {
return const_iterator(i.m_swappedVector, n + i.m_index);
}
difference_type operator-(const const_iterator& other) const {
return m_index - other.m_index;
}
const_iterator& operator-(difference_type n) const {
return const_iterator(m_swappedVector, m_index - n);
}
const T& operator*() const {
return (*m_swappedVector)[m_index];
}
const T* operator->() const {
return &(*m_swappedVector)[m_index];
}
const T& operator[](difference_type offset) const {
return (*m_swappedVector)[m_index + offset];
}
size_t index() const {
return m_index;
}
private:
SwappedVector* m_swappedVector;
size_t m_index;
};
SwappedVector();
//SwappedVector(const SwappedVector&) = delete;
~SwappedVector();
//SwappedVector& operator=(const SwappedVector&) = delete;
bool open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize);
void close();
bool empty() const;
uint64_t size() const;
const_iterator begin();
const_iterator end();
const T& operator[](uint64_t index);
const T& front();
const T& back();
void clear();
void pop_back();
void push_back(const T& item);
private:
struct ItemEntry;
struct CacheEntry;
struct ItemEntry {
public:
T item;
typename std::list<CacheEntry>::iterator cacheIter;
};
struct CacheEntry {
public:
typename std::map<uint64_t, ItemEntry>::iterator itemIter;
};
std::fstream m_itemsFile;
std::fstream m_indexesFile;
size_t m_poolSize;
std::vector<uint64_t> m_offsets;
uint64_t m_itemsFileSize;
std::map<uint64_t, ItemEntry> m_items;
std::list<CacheEntry> m_cache;
uint64_t m_cacheHits;
uint64_t m_cacheMisses;
T* prepare(uint64_t index);
};
template<class T> SwappedVector<T>::SwappedVector() {
}
template<class T> SwappedVector<T>::~SwappedVector() {
close();
}
template<class T> bool SwappedVector<T>::open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize) {
if (poolSize == 0) {
return false;
}
m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary);
m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary);
if (m_itemsFile && m_indexesFile) {
uint64_t count;
m_indexesFile.read(reinterpret_cast<char*>(&count), sizeof count);
if (!m_indexesFile) {
return false;
}
std::vector<uint64_t> offsets;
uint64_t itemsFileSize = 0;
for (uint64_t i = 0; i < count; ++i) {
uint32_t itemSize;
m_indexesFile.read(reinterpret_cast<char*>(&itemSize), sizeof itemSize);
if (!m_indexesFile) {
return false;
}
offsets.emplace_back(itemsFileSize);
itemsFileSize += itemSize;
}
m_offsets.swap(offsets);
m_itemsFileSize = itemsFileSize;
} else {
m_itemsFile.open(itemFileName, std::ios::out | std::ios::binary);
m_itemsFile.close();
m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary);
m_indexesFile.open(indexFileName, std::ios::out | std::ios::binary);
uint64_t count = 0;
m_indexesFile.write(reinterpret_cast<char*>(&count), sizeof count);
if (!m_indexesFile) {
return false;
}
m_indexesFile.close();
m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary);
m_offsets.clear();
m_itemsFileSize = 0;
}
m_poolSize = poolSize;
m_items.clear();
m_cache.clear();
m_cacheHits = 0;
m_cacheMisses = 0;
return true;
}
template<class T> void SwappedVector<T>::close() {
std::cout << "SwappedVector cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast<double>(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl;
}
template<class T> bool SwappedVector<T>::empty() const {
return m_offsets.empty();
}
template<class T> uint64_t SwappedVector<T>::size() const {
return m_offsets.size();
}
template<class T> typename SwappedVector<T>::const_iterator SwappedVector<T>::begin() {
return const_iterator(this, 0);
}
template<class T> typename SwappedVector<T>::const_iterator SwappedVector<T>::end() {
return const_iterator(this, m_offsets.size());
}
template<class T> const T& SwappedVector<T>::operator[](uint64_t index) {
auto itemIter = m_items.find(index);
if (itemIter != m_items.end()) {
if (itemIter->second.cacheIter != --m_cache.end()) {
m_cache.splice(m_cache.end(), m_cache, itemIter->second.cacheIter);
}
++m_cacheHits;
return itemIter->second.item;
}
if (index >= m_offsets.size()) {
throw std::runtime_error("SwappedVector::operator[]");
}
if (!m_itemsFile) {
throw std::runtime_error("SwappedVector::operator[]");
}
m_itemsFile.seekg(m_offsets[index]);
T tempItem;
Common::StdInputStream stream(m_itemsFile);
CryptoNote::BinaryInputStreamSerializer archive(stream);
serialize(tempItem, archive);
T* item = prepare(index);
std::swap(tempItem, *item);
++m_cacheMisses;
return *item;
}
template<class T> const T& SwappedVector<T>::front() {
return operator[](0);
}
template<class T> const T& SwappedVector<T>::back() {
return operator[](m_offsets.size() - 1);
}
template<class T> void SwappedVector<T>::clear() {
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::clear");
}
m_indexesFile.seekp(0);
uint64_t count = 0;
m_indexesFile.write(reinterpret_cast<char*>(&count), sizeof count);
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::clear");
}
m_offsets.clear();
m_itemsFileSize = 0;
m_items.clear();
m_cache.clear();
}
template<class T> void SwappedVector<T>::pop_back() {
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::pop_back");
}
m_indexesFile.seekp(0);
uint64_t count = m_offsets.size() - 1;
m_indexesFile.write(reinterpret_cast<char*>(&count), sizeof count);
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::pop_back");
}
m_itemsFileSize = m_offsets.back();
m_offsets.pop_back();
auto itemIter = m_items.find(m_offsets.size());
if (itemIter != m_items.end()) {
m_cache.erase(itemIter->second.cacheIter);
m_items.erase(itemIter);
}
}
template<class T> void SwappedVector<T>::push_back(const T& item) {
uint64_t itemsFileSize;
{
if (!m_itemsFile) {
throw std::runtime_error("SwappedVector::push_back");
}
m_itemsFile.seekp(m_itemsFileSize);
Common::StdOutputStream stream(m_itemsFile);
CryptoNote::BinaryOutputStreamSerializer archive(stream);
serialize(const_cast<T&>(item), archive);
itemsFileSize = m_itemsFile.tellp();
}
{
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::push_back");
}
m_indexesFile.seekp(sizeof(uint64_t) + sizeof(uint32_t) * m_offsets.size());
uint32_t itemSize = static_cast<uint32_t>(itemsFileSize - m_itemsFileSize);
m_indexesFile.write(reinterpret_cast<char*>(&itemSize), sizeof itemSize);
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::push_back");
}
m_indexesFile.seekp(0);
uint64_t count = m_offsets.size() + 1;
m_indexesFile.write(reinterpret_cast<char*>(&count), sizeof count);
if (!m_indexesFile) {
throw std::runtime_error("SwappedVector::push_back");
}
}
m_offsets.push_back(m_itemsFileSize);
m_itemsFileSize = itemsFileSize;
T* newItem = prepare(m_offsets.size() - 1);
*newItem = item;
}
template<class T> T* SwappedVector<T>::prepare(uint64_t index) {
if (m_items.size() == m_poolSize) {
auto cacheIter = m_cache.begin();
m_items.erase(cacheIter->itemIter);
m_cache.erase(cacheIter);
}
auto itemIter = m_items.insert(std::make_pair(index, ItemEntry()));
CacheEntry cacheEntry = { itemIter.first };
auto cacheIter = m_cache.insert(m_cache.end(), cacheEntry);
itemIter.first->second.cacheIter = cacheIter;
return &itemIter.first->second.item;
}