1
0
Fork 0
forked from lthn/blockchain
blockchain/src/common/median_db_cache.h
2018-12-27 18:50:45 +03:00

133 lines
No EOL
4.7 KiB
C++

// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Copyright (c) 2012-2013 The Boolberry 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 "misc_language.h"
namespace tools
{
#define LOC_INT_ERR(cond, msg) CHECK_AND_ASSERT_THROW_MES(cond, "internal error in median_db_cache: " << msg << ENDL << *this)
template<typename key, typename associated_data>
class median_db_cache : public epee::misc_utils::median_helper<key, associated_data>
{
typedef epee::misc_utils::median_helper<key, associated_data> super_t;
struct db_entry_type
{
key k;
associated_data d;
};
//m_tx_median_helper;
typedef tools::db::cached_key_value_accessor<uint64_t, db_entry_type, false, true> median_db_container;
median_db_container m_median_container;
uint64_t m_counter_hi;
uint64_t m_counter_lo;
bool m_is_loading_from_db;
bool load_items_from_db()
{
std::map<uint64_t, db_entry_type> loaded_entries;
LOG_PRINT_L0("Loading median from db cache...");
m_median_container.enumerate_items([&](uint64_t i, uint64_t index, const db_entry_type& entry)
{
//adjust counters
if (index > m_counter_hi)
m_counter_hi = index;
if (index < m_counter_lo)
m_counter_lo = index;
loaded_entries[index] = entry;
return true;
});
LOG_PRINT_L0("Loaded " << loaded_entries.size() << " items, building median cache...");
m_is_loading_from_db = true;
for (auto i : loaded_entries)
{
this->push_item(i.second.k, i.second.d);
}
m_is_loading_from_db = false;
LOG_PRINT_L0("Median cache build OK");
return true;
}
virtual void handle_add_front_item(const key& k, const associated_data& ad)
{
if (m_is_loading_from_db)
return;
db_entry_type det = AUTO_VAL_INIT(det);
det.k = k;
det.d = ad;
m_median_container.set(++m_counter_hi, det);
//in case if it was totally empty at init(which is very doubtful)
if (m_counter_lo == std::numeric_limits<uint64_t>::max())
m_counter_lo = m_counter_hi;
}
virtual void handle_remove_front_item(const key& k)
{
LOC_INT_ERR(!m_is_loading_from_db, "m_is_loading_from_db is true");
LOC_INT_ERR(m_median_container.erase_validate(m_counter_hi), "erase_validate failed");
--m_counter_hi;
//TODO: check coretests and make a decision on what to do with the following check
//LOC_INT_ERR(m_counter_lo <= m_counter_hi, "handle_remove_front_item: m_counter_lo(" << m_counter_lo << ") > m_counter_hi(" << m_counter_hi << ")");
}
virtual void handle_purge_back_item()
{
LOC_INT_ERR(m_counter_lo < m_counter_hi, "handle_purge_back_item: m_counter_lo(" << m_counter_lo << ") >= m_counter_hi(" << m_counter_hi << ")");
m_median_container.erase(m_counter_lo++);
}
public:
median_db_cache(db::basic_db_accessor& db) :
m_median_container(db),
m_counter_hi(0),
m_counter_lo(std::numeric_limits<uint64_t>::max()),
m_is_loading_from_db(false)
{
}
bool init(const std::string& name)
{
bool r = m_median_container.init(name);
CHECK_AND_ASSERT_MES(r, false, "Failed to init m_median_container");
return load_items_from_db();
}
~median_db_cache()
{
}
void clear()
{
super_t::clear();
m_median_container.clear();
m_counter_hi = 0;
m_counter_lo = std::numeric_limits<uint64_t>::max();
}
template<typename key_t, typename associated_data_t>
friend std::ostream& operator<<(std::ostream& ss, const median_db_cache<key_t, associated_data_t> &mdbc);
}; // class median_db_cache
#undef LOC_INT_ERR
template<typename key_t, typename associated_data_t>
inline std::ostream& operator<<(std::ostream& s, const median_db_cache<key_t, associated_data_t> &mdbc)
{
s << "median_db_cache<" << typeid(key_t).name() << ", " << typeid(associated_data_t).name() << "> instance 0x" << &mdbc << ENDL
<< " m_counter_lo: " << mdbc.m_counter_lo << ENDL
<< " m_counter_hi: " << mdbc.m_counter_hi << ENDL
<< " m_is_loading_from_db: " << mdbc.m_is_loading_from_db << ENDL
<< " m_median_container.size: " << mdbc.m_median_container.size() << ENDL
<< " m_median_container.size_no_cache(): " << mdbc.m_median_container.size_no_cache() << ENDL
<< "parent " << static_cast<epee::misc_utils::median_helper<key_t, associated_data_t>>(mdbc);
return s;
}
}