forked from lthn/blockchain
582 lines
16 KiB
C++
582 lines
16 KiB
C++
// Copyright (c) 2019, anonimal, <anonimal@zano.org>
|
|
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of the Andrey N. Sabelnikov nor the
|
|
// names of its contributors may be used to endorse or promote products
|
|
// derived from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
|
|
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
|
|
|
|
|
|
#pragma once
|
|
#include <limits>
|
|
#include <set>
|
|
#include <iterator>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <boost/thread.hpp>
|
|
#include <boost/any.hpp>
|
|
#include "include_base_utils.h"
|
|
#include "auto_val_init.h"
|
|
|
|
|
|
#define MARK_AS_POD_C11(type) \
|
|
namespace std \
|
|
{ \
|
|
template<> \
|
|
struct is_pod< type > \
|
|
{ \
|
|
static const bool value = true; \
|
|
}; \
|
|
}
|
|
|
|
|
|
namespace epee
|
|
{
|
|
#define STD_TRY_BEGIN() try {
|
|
|
|
#define STD_TRY_CATCH(where_, ret_val) \
|
|
} \
|
|
catch (const std::exception &e) \
|
|
{ \
|
|
LOG_ERROR("EXCEPTION: " << where_ << ", mes: "<< e.what()); \
|
|
return ret_val; \
|
|
} \
|
|
catch (...) \
|
|
{ \
|
|
LOG_ERROR("EXCEPTION: " << where_ ); \
|
|
return ret_val; \
|
|
}
|
|
|
|
|
|
#define STD_TRY_CATCH_LOCATION(return_val) STD_TRY_CATCH(LOCATION_SS, return_val)
|
|
|
|
/* helper class, to make able get namespace via decltype()::*/
|
|
template<class base_class>
|
|
class namespace_accessor: public base_class{};
|
|
|
|
|
|
#define STRINGIFY_EXPAND(s) STRINGIFY(s)
|
|
#define STRINGIFY(s) #s
|
|
|
|
|
|
|
|
namespace misc_utils
|
|
{
|
|
template<class _Ty1,
|
|
class _Ty2,
|
|
class _Ty3>
|
|
struct triple
|
|
{ // store a pair of values
|
|
typedef _Ty1 first_type;
|
|
typedef _Ty2 second_type;
|
|
typedef _Ty3 third_type;
|
|
|
|
triple()
|
|
: first(), second(), third()
|
|
{ // default construct
|
|
}
|
|
|
|
triple(const _Ty1& _Val1, const _Ty2& _Val2, const _Ty3& _Val3)
|
|
: first(_Val1), second(_Val2), third(_Val3)
|
|
{ // construct from specified values
|
|
}
|
|
|
|
_Ty1 first; // the first stored value
|
|
_Ty2 second; // the second stored value
|
|
_Ty3 third; // the second stored value
|
|
};
|
|
|
|
|
|
|
|
template<typename t_type>
|
|
t_type get_max_t_val(t_type t)
|
|
{
|
|
return (std::numeric_limits<t_type>::max)();
|
|
}
|
|
|
|
template<class _Ty>
|
|
bool is_less_as_pod(const _Ty& _Left, const _Ty& _Right)
|
|
{ // apply operator< to operands
|
|
return memcmp(&_Left, &_Right, sizeof(_Left)) < 0;
|
|
}
|
|
|
|
|
|
inline
|
|
bool sleep_no_w(long ms )
|
|
{
|
|
boost::this_thread::sleep(
|
|
boost::get_system_time() +
|
|
boost::posix_time::milliseconds( std::max<long>(ms,0) ) );
|
|
|
|
return true;
|
|
}
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
template<typename key, typename associated_data>
|
|
class median_helper
|
|
{
|
|
typedef std::multiset<key> ordered_items_container;
|
|
typedef std::pair<typename ordered_items_container::iterator, associated_data> queued_item_type;
|
|
typedef std::list<queued_item_type> queued_items_container;
|
|
typedef std::list<std::pair<key, associated_data> > recent_items_container;
|
|
ordered_items_container ordered_items;
|
|
queued_items_container queued_items;
|
|
recent_items_container recent_items; // this needed to have chance to roll back at least for some depth, so we keep elements removed from median window for a while
|
|
bool had_been_in_recent_items;
|
|
mutable critical_section m_lock;
|
|
|
|
typename ordered_items_container::iterator m_current_median_it;
|
|
uint64_t current_index;
|
|
|
|
virtual void handle_purge_back_item() {}
|
|
virtual void handle_add_front_item(const key& k, const associated_data& ad) {}
|
|
virtual void handle_remove_front_item(const key& k) {}
|
|
void push_item_as_recent(const key& k, const associated_data& ad)
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
auto it = ordered_items.insert(k);
|
|
queued_items.push_front(queued_item_type(it, ad));
|
|
}
|
|
public:
|
|
median_helper() :had_been_in_recent_items(false)
|
|
{
|
|
m_current_median_it = ordered_items.end();
|
|
current_index = 0;
|
|
}
|
|
void clear()
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
ordered_items.clear();
|
|
queued_items.clear();
|
|
recent_items.clear();
|
|
had_been_in_recent_items = false;
|
|
m_current_median_it = ordered_items.end();
|
|
current_index = 0;
|
|
}
|
|
|
|
void push_item(const key& k, const associated_data& ad)
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
auto it = ordered_items.insert(k);
|
|
queued_items.push_back(queued_item_type(it, ad));
|
|
handle_add_front_item(k, ad);
|
|
}
|
|
|
|
void pop_item()
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
if (!queued_items.empty())
|
|
{
|
|
auto it = queued_items.back().first;
|
|
handle_remove_front_item(*it);
|
|
ordered_items.erase(it);
|
|
queued_items.pop_back();
|
|
}
|
|
}
|
|
|
|
/*
|
|
cb(key, associated_data) - enumerating while result is false
|
|
when returned true - clean items behind and move it to recent_items
|
|
|
|
cb_final_remove(key, associated_data) - for every element in recent_items if returned false - element removed forever
|
|
*/
|
|
template<class cb_t, class cb_t2>
|
|
bool scan_items(cb_t cb, cb_t2 cb_final_remove)
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
//process median window items
|
|
for (auto it = queued_items.begin(); it != queued_items.end(); it++)
|
|
{
|
|
if (cb(*it->first, it->second))
|
|
{
|
|
//stop element, remove all items before this
|
|
for (auto clean_it = queued_items.begin(); clean_it != it; clean_it++)
|
|
{
|
|
//std::pair<key, associated_data> p(*it->first, it->second);
|
|
recent_items.push_back(std::make_pair(*clean_it->first, clean_it->second));
|
|
had_been_in_recent_items = true;
|
|
ordered_items.erase(clean_it->first);
|
|
}
|
|
queued_items.erase(queued_items.begin(), it);
|
|
break;
|
|
}
|
|
}
|
|
//process recent items front part (put back to median window set if needed)
|
|
for (auto it = recent_items.rbegin(); it != recent_items.rend();)
|
|
{
|
|
if (!cb(it->first, it->second))
|
|
break;//element should stay in recent
|
|
|
|
//element should be back to median window
|
|
this->push_item_as_recent(it->first, it->second);
|
|
it = typename recent_items_container::reverse_iterator(recent_items.erase((++it).base())); // remove *it and advance 'it' by 1
|
|
}
|
|
|
|
|
|
//process recent items back part (remove outdated elements)
|
|
while (recent_items.size() && !cb_final_remove(recent_items.begin()->first, recent_items.begin()->second))
|
|
{
|
|
recent_items.erase(recent_items.begin());
|
|
handle_purge_back_item();
|
|
}
|
|
|
|
//detect if recent_items exhausted
|
|
if (!recent_items.size() && had_been_in_recent_items)
|
|
{
|
|
LOG_PRINT_RED_L0("[MEDIAN_HELPER]: recent_items exhausted, need to rebuild median from scratch");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint64_t get_median() const
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
if (!ordered_items.size())
|
|
return 0;
|
|
|
|
size_t n = ordered_items.size() / 2;
|
|
//std::sort(v.begin(), v.end());
|
|
|
|
auto n_it = std::next(ordered_items.begin(), n);
|
|
|
|
//nth_element(v.begin(), v.begin()+n-1, v.end());
|
|
if (ordered_items.size() % 2)
|
|
{//1, 3, 5...
|
|
return *n_it;
|
|
}
|
|
else
|
|
{//2, 4, 6...
|
|
auto n_it_sub = n_it;
|
|
--n_it_sub;
|
|
return (*n_it_sub + *n_it) / 2;
|
|
}
|
|
}
|
|
|
|
uint64_t get_avg() const
|
|
{
|
|
CRITICAL_REGION_LOCAL(m_lock);
|
|
if (!queued_items.size())
|
|
return 0;
|
|
|
|
uint64_t summ = 0;
|
|
for (const auto& item : queued_items)
|
|
{
|
|
summ += *item.first;
|
|
}
|
|
|
|
return summ / queued_items.size();
|
|
}
|
|
|
|
template<typename key_t, typename associated_data_t>
|
|
friend std::ostream & operator<< (std::ostream &out, median_helper<key_t, associated_data_t> const &mh);
|
|
}; // class median_helper
|
|
|
|
template<typename key_t, typename associated_data_t>
|
|
std::ostream & operator<< (std::ostream &s, median_helper<key_t, associated_data_t> const &mh)
|
|
{
|
|
s << "median_helper<" << typeid(key_t).name() << ", " << typeid(associated_data_t).name() << "> instance 0x" << &mh << ENDL
|
|
<< " ordered_items: " << mh.ordered_items.size() << ENDL
|
|
<< " queued_items: " << mh.queued_items.size() << ENDL
|
|
<< " recent_items: " << mh.recent_items.size() << ENDL
|
|
<< " had_been_in_recent_items: " << mh.had_been_in_recent_items << ENDL;
|
|
return s;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
template<typename container_t>
|
|
typename container_t::value_type median(container_t &v)
|
|
{
|
|
typename container_t::value_type median{};
|
|
if(v.empty())
|
|
return median;
|
|
if(v.size() == 1)
|
|
return v[0];
|
|
|
|
auto median_it = v.begin() + v.size() / 2;
|
|
std::nth_element(v.begin(), median_it, v.end());
|
|
median = *median_it;
|
|
if (v.size() % 2 == 0)
|
|
{
|
|
auto max_it = std::max_element(v.begin(), median_it); // it's garanteed that after nth_element() the necessary element is in this interval
|
|
median = (median + *max_it) / 2; // average of [size/2-1] and [size/2] elements
|
|
}
|
|
|
|
return median;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
struct call_befor_die_base
|
|
{
|
|
virtual ~call_befor_die_base(){}
|
|
};
|
|
|
|
typedef boost::shared_ptr<call_befor_die_base> auto_scope_leave_caller;
|
|
|
|
|
|
template<class t_scope_leave_handler>
|
|
struct call_befor_die: public call_befor_die_base
|
|
{
|
|
t_scope_leave_handler m_func;
|
|
call_befor_die(t_scope_leave_handler f):m_func(f)
|
|
{}
|
|
~call_befor_die()
|
|
{
|
|
NESTED_TRY_ENTRY();
|
|
|
|
m_func();
|
|
|
|
NESTED_CATCH_ENTRY(__func__);
|
|
}
|
|
};
|
|
|
|
template<class t_scope_leave_handler>
|
|
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
|
|
{
|
|
auto_scope_leave_caller slc(new call_befor_die<t_scope_leave_handler>(f));
|
|
return slc;
|
|
}
|
|
|
|
|
|
#define ON_EXIT misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler
|
|
|
|
|
|
template< typename t_contaner, typename t_redicate>
|
|
void erase_if( t_contaner& items, const t_redicate& predicate )
|
|
{
|
|
for(auto it = items.begin(); it != items.end(); )
|
|
{
|
|
if( predicate(*it) )
|
|
it = items.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
struct call_basic
|
|
{
|
|
virtual void do_call(){};
|
|
};
|
|
|
|
template<typename param_t>
|
|
struct call_basic_param
|
|
{
|
|
virtual void do_call(param_t& p) {};
|
|
};
|
|
|
|
template<typename t_callback>
|
|
struct call_specific: public call_basic
|
|
{
|
|
call_specific(t_callback cb):m_cb(cb)
|
|
{}
|
|
virtual void do_call()
|
|
{
|
|
m_cb();
|
|
}
|
|
private:
|
|
t_callback m_cb;
|
|
};
|
|
|
|
template<typename param_t, typename t_callback>
|
|
struct call_specific_param : public call_basic_param<param_t>
|
|
{
|
|
call_specific_param(t_callback cb) :m_cb(cb)
|
|
{}
|
|
virtual void do_call(const param_t& p)
|
|
{
|
|
m_cb(p);
|
|
}
|
|
private:
|
|
t_callback m_cb;
|
|
};
|
|
|
|
|
|
|
|
template<typename t_callback>
|
|
auto build_abstract_callback(t_callback cb) -> std::shared_ptr<call_basic>
|
|
{
|
|
return std::shared_ptr<call_basic>(new call_specific<t_callback>(cb));
|
|
}
|
|
|
|
|
|
template<typename param_t, typename t_callback>
|
|
auto build_abstract_callback_param(t_callback cb) -> std::shared_ptr<call_basic_param<param_t>>
|
|
{
|
|
return std::shared_ptr<call_basic_param<param_t>>(new call_specific_param<param_t, t_callback>(cb));
|
|
}
|
|
|
|
|
|
|
|
template<class callback_type>
|
|
bool static_initializer(callback_type cb)
|
|
{
|
|
return cb();
|
|
};
|
|
|
|
|
|
template<class t_container_type>
|
|
typename t_container_type::mapped_type& get_or_insert_value_initialized(t_container_type& container, const typename t_container_type::key_type& key)
|
|
{
|
|
auto it = container.find(key);
|
|
if (it != container.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
auto res = container.insert(typename t_container_type::value_type(key, AUTO_VAL_INIT(typename t_container_type::mapped_type())));
|
|
return res.first->second;
|
|
}
|
|
|
|
template<class t_container_type>
|
|
typename t_container_type::iterator it_get_or_insert_value_initialized(t_container_type& container, const typename t_container_type::key_type& key)
|
|
{
|
|
auto it = container.find(key);
|
|
if (it != container.end())
|
|
{
|
|
return it;
|
|
}
|
|
|
|
auto res = container.insert(typename t_container_type::value_type(key, AUTO_VAL_INIT(typename t_container_type::mapped_type())));
|
|
return res.first;
|
|
}
|
|
|
|
|
|
class events_dispatcher
|
|
{
|
|
|
|
public:
|
|
|
|
template<typename param_t>
|
|
struct callback_entry
|
|
{
|
|
std::shared_ptr<epee::misc_utils::call_basic_param<const param_t> > m_cb;
|
|
};
|
|
|
|
std::map<std::type_index, boost::any> m_callbacks;
|
|
|
|
template<typename param_t, typename callback_t>
|
|
void SUBSCIRBE_DEBUG_EVENT(callback_t cb)
|
|
{
|
|
std::type_index ti = typeid(param_t);
|
|
auto it = m_callbacks.find(ti);
|
|
if (it != m_callbacks.end())
|
|
{
|
|
throw std::runtime_error("Handler for this type already registered");
|
|
}
|
|
|
|
callback_entry<const param_t> cb_entry = { epee::misc_utils::build_abstract_callback_param<const param_t>(cb) };
|
|
|
|
m_callbacks[ti] = cb_entry;
|
|
}
|
|
|
|
template<typename param_t>
|
|
void UNSUBSCRIBE_DEBUG_EVENT()
|
|
{
|
|
std::type_index ti = typeid(param_t);
|
|
auto it = m_callbacks.find(ti);
|
|
if (it != m_callbacks.end())
|
|
{
|
|
m_callbacks.erase(it);
|
|
}
|
|
}
|
|
|
|
void UNSUBSCRIBE_ALL()
|
|
{
|
|
m_callbacks.clear();
|
|
}
|
|
|
|
template<typename param_t>
|
|
void RAISE_DEBUG_EVENT(const param_t& p)
|
|
{
|
|
std::type_index ti = typeid(param_t);
|
|
auto it = m_callbacks.find(ti);
|
|
if (it != m_callbacks.end())
|
|
{
|
|
callback_entry<const param_t >* pcallback_entry = boost::any_cast<callback_entry<const param_t >>(&it->second);
|
|
if (!pcallback_entry)
|
|
{
|
|
throw std::runtime_error("Unexpected error: registered tipe holding something else in boost::eny");
|
|
}
|
|
pcallback_entry->m_cb->do_call(p);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace misc_utils
|
|
} // namespace epee
|
|
|
|
template<typename T>
|
|
std::ostream& print_container_content(std::ostream& out, const T& v);
|
|
|
|
namespace std
|
|
{
|
|
template<typename T>
|
|
std::ostream& operator<< (std::ostream& out, const std::vector<T>& v)
|
|
{
|
|
return print_container_content(out, v);
|
|
}
|
|
|
|
template<typename T>
|
|
std::ostream& operator<< (std::ostream& out, const std::list<T>& v)
|
|
{
|
|
return print_container_content(out, v);
|
|
}
|
|
|
|
} // namespace std
|
|
|
|
template<typename T>
|
|
std::ostream& print_container_content(std::ostream& out, const T& v)
|
|
{
|
|
|
|
out << "[";
|
|
if (!v.size())
|
|
{
|
|
out << "]";
|
|
return out;
|
|
}
|
|
|
|
typename T::const_iterator last_it = --v.end();
|
|
|
|
for (typename T::const_iterator it = v.begin(); it != v.end(); it++)
|
|
{
|
|
out << *it;
|
|
if (it != last_it)
|
|
out << ", ";
|
|
}
|
|
out << "]";
|
|
return out;
|
|
}
|
|
|
|
|