// Copyright (c) 2019, anonimal, // 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 #include #include #include #include #include #include #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 namespace_accessor: public base_class{}; #define STRINGIFY_EXPAND(s) STRINGIFY(s) #define STRINGIFY(s) #s namespace misc_utils { template void cast_assign_a_to_b(const t_type_a& a, t_type_b& b) { *static_cast(&b) = a; } template 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 t_type get_max_t_val(t_type t) { return (std::numeric_limits::max)(); } template 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(ms,0) ) ); return true; } /************************************************************************/ /* */ /************************************************************************/ template class median_helper { typedef std::multiset ordered_items_container; typedef std::pair queued_item_type; typedef std::list queued_items_container; typedef std::list > 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 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 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 friend std::ostream & operator<< (std::ostream &out, median_helper const &mh); }; // class median_helper template std::ostream & operator<< (std::ostream &s, median_helper 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::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 auto_scope_leave_caller; template 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 auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f) { auto_scope_leave_caller slc(new call_befor_die(f)); return slc; } #define ON_FUNC_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 struct call_basic_param { virtual void do_call(param_t& p) {}; }; template 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 struct call_specific_param : public call_basic_param { 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 auto build_abstract_callback(t_callback cb) -> std::shared_ptr { return std::shared_ptr(new call_specific(cb)); } template auto build_abstract_callback_param(t_callback cb) -> std::shared_ptr> { return std::shared_ptr>(new call_specific_param(cb)); } template bool static_initializer(callback_type cb) { return cb(); }; template 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 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 struct callback_entry { std::shared_ptr > m_cb; }; std::map m_callbacks; template 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 cb_entry = { epee::misc_utils::build_abstract_callback_param(cb) }; m_callbacks[ti] = cb_entry; } template 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 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* pcallback_entry = boost::any_cast>(&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); } } }; template struct expirating_set { typedef std::set main_set; main_set m_set; std::multimap m_expirations; const main_set& get_set() { return m_set; } void add(const key& k, const expiration_type& e) { auto res = m_set.insert(k); m_expirations.insert({ e, res.first }); } void remove_if_expiration_less_than(const expiration_type& e) { while(m_expirations.size() && m_expirations.begin()->first < e) { m_set.erase(m_expirations.begin()->second); m_expirations.erase(m_expirations.begin()); } } }; template struct expirating_map { typedef std::map main_map; main_map m_map; std::multimap m_expirations; const main_map& get_map() { return m_map; } void add(const key& k, const value_type& v, const expiration_type& e) { auto res = m_map.insert(k, v); m_expirations.insert({ e, res.first }); } void remove_if_expiration_less_than(const expiration_type& e) { while (m_expirations.size() && m_expirations.begin()->first < e) { m_map.erase(m_expirations.begin()->second); m_expirations.erase(m_expirations.begin()); } } template inline void serialize(t_archive& a, const unsigned int ver) { std::vector > items; if constexpr (t_archive::is_saving::value) { for (const auto& item: m_expirations) { items.resize(items.size + 1); std::get<2>(items.back()) = item.first; std::get<0>(items.back()) = item.second.first; std::get<1>(items.back()) = item.second.second; } } a & items; if constexpr (!t_archive::is_saving::value) { for (const auto& item : items) { this->add(std::get<0>(item), std::get<1>(item), std::get<2>(item)); items.resize(items.size + 1); std::get<2>(items.back()) = item.first; std::get<0>(items.back()) = item.second.first; std::get<1>(items.back()) = item.second.second; } } } }; // helper class mainly intended for using with std::atomic to repair copy-construction in classes where std::atomic is aggregated template struct void_copy : public parent_t { void_copy() = default; void_copy(void_copy&&) noexcept = default; void_copy& operator=(void_copy&&) noexcept = default; void_copy(const void_copy&) : parent_t{} {} void_copy& operator=(const void_copy&) { return *this; } }; } // namespace misc_utils } // namespace epee template std::ostream& print_container_content(std::ostream& out, const T& v); namespace std { template std::ostream& operator<< (std::ostream& out, const std::vector& v) { return print_container_content(out, v); } template std::ostream& operator<< (std::ostream& out, const std::list& v) { return print_container_content(out, v); } } // namespace std template 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; }