|
#ifndef HGUARD_NETRUSH_INPUT_ANYVALUESET_HPP__ |
|
#define HGUARD_NETRUSH_INPUT_ANYVALUESET_HPP__ |
|
|
|
#include <memory> |
|
#include <typeindex> |
|
#include <vector> |
|
|
|
#include <boost/container/flat_map.hpp> |
|
#include <boost/container/flat_set.hpp> |
|
#include <boost/optional.hpp> |
|
|
|
#include <utilcxx/assert.hpp> |
|
|
|
namespace netrush { |
|
namespace input { |
|
|
|
/** Set that can contain multiple value types. |
|
The contained types must be Comparable, Copyable and CopyConstructible. |
|
The identity of values is determined by comparison (operator< and operator==), |
|
which allows for the type to have a different identity than it's value, |
|
in which case overwriting can be used to change the non-observable state while keeping the identity. |
|
*/ |
|
class AnyValueSet |
|
{ |
|
public: |
|
|
|
AnyValueSet() = default; |
|
|
|
AnyValueSet( const AnyValueSet& other ) |
|
{ |
|
insert( other ); |
|
} |
|
|
|
AnyValueSet& operator=( const AnyValueSet& other ) |
|
{ |
|
clear(); |
|
insert( other ); |
|
return *this; |
|
} |
|
|
|
AnyValueSet( AnyValueSet&& other ) |
|
: m_size_changed( std::move(other.m_size_changed) ) |
|
, m_size( std::move(other.m_size) ) |
|
, m_set_index( std::move(other.m_set_index) ) |
|
{} |
|
|
|
AnyValueSet& operator=( AnyValueSet&& other ) |
|
{ |
|
m_size_changed = std::move(other.m_size_changed); |
|
m_size = std::move(other.m_size); |
|
m_set_index = std::move(other.m_set_index); |
|
return *this; |
|
} |
|
|
|
|
|
/** Add a new value if not already stored. |
|
@return True if the value have been added, false otherwise. |
|
*/ |
|
template< class ValueType > |
|
bool add( ValueType&& value ) |
|
{ |
|
auto& value_set = find_or_create_set<ValueType>(); |
|
bool was_added = value_set.add( std::forward<ValueType>(value) ); |
|
if( was_added ) |
|
size_changed(); |
|
return was_added; |
|
} |
|
|
|
/** Add a new value and overwrite the previous one even if it was already stored. |
|
@return True if the value have been added, false if the value was overwritten. |
|
*/ |
|
template< class ValueType > |
|
bool overwrite( ValueType&& value ) |
|
{ |
|
auto& value_set = find_or_create_set<ValueType>(); |
|
bool was_added = value_set.overwrite( std::forward<ValueType>( value ) ); |
|
if( was_added ) |
|
size_changed(); |
|
return was_added; |
|
} |
|
|
|
/** Remove a value if it was stored. */ |
|
template< class ValueType > |
|
void remove( const ValueType& value ) |
|
{ |
|
if( auto* value_set = find_set<ValueType>() ) |
|
{ |
|
value_set->remove( value ); |
|
size_changed(); |
|
} |
|
} |
|
|
|
/** Remove all values contained in the provided set which are stored in this one. */ |
|
void remove( const AnyValueSet& other ) |
|
{ |
|
if( other.empty() || this->empty() ) |
|
return; |
|
|
|
auto current_slot = begin( m_set_index ); |
|
const auto end_current = end( m_set_index ); |
|
|
|
auto other_slot = begin( other.m_set_index ); |
|
const auto end_other = end( other.m_set_index ); |
|
|
|
while( current_slot != end_current |
|
&& other_slot != end_other ) |
|
{ |
|
if( current_slot->first == other_slot->first ) |
|
{ |
|
auto& current_set_ptr = current_slot->second; |
|
auto& other_set_ptr = other_slot->second; |
|
UCX_ASSERT_NOT_NULL( current_set_ptr ); |
|
UCX_ASSERT_NOT_NULL( other_set_ptr ); |
|
current_set_ptr->remove( *other_set_ptr ); |
|
size_changed(); |
|
++other_slot; |
|
} |
|
++current_slot; |
|
} |
|
} |
|
|
|
/** Search for the value comparable to the provided key. |
|
The key can have a different type than the value or the same type. |
|
If the value type is specified, the |
|
@return Either a copy of the stored value or none if not found. |
|
*/ |
|
template< class ValueType, class KeyType > |
|
boost::optional<ValueType> find( const KeyType& key ) const |
|
{ |
|
if( auto* value_set = find_set<ValueType>() ) |
|
{ |
|
return value_set->find( key ); |
|
} |
|
return {}; |
|
} |
|
|
|
/** Specializatoin of find() in case the value type is not specified. */ |
|
template< class ValueType > |
|
boost::optional<ValueType> find( const ValueType& value ) const |
|
{ |
|
return find<ValueType, ValueType>( value ); |
|
} |
|
|
|
/** @return true if the specified key is comparable to a stored value of the specified value type, false otherwise. */ |
|
template< class ValueType, class KeyType > |
|
bool contains( const KeyType& key ) const |
|
{ |
|
if( auto* value_set = find_set<ValueType>() ) |
|
{ |
|
return value_set->contains( key ); |
|
} |
|
return false; |
|
} |
|
|
|
/** @return true if the value is comparable to a stored value of the same type. */ |
|
template< class ValueType > |
|
bool contains( const ValueType& value ) const |
|
{ |
|
return contains<ValueType, ValueType>( value ); |
|
} |
|
|
|
/** Insert all the values from the provided set which are not already stored into this one. */ |
|
void insert( const AnyValueSet& other ) |
|
{ |
|
for( const auto& set_pair : other.m_set_index ) |
|
{ |
|
auto& set_ptr = set_pair.second; |
|
UCX_ASSERT_NOT_NULL( set_ptr ); |
|
|
|
if( auto value_set = find_set( set_pair.first ) ) |
|
{ |
|
value_set->insert( *set_ptr ); |
|
} |
|
else |
|
{ |
|
m_set_index.emplace( set_pair.first, set_ptr->clone() ); |
|
} |
|
size_changed(); |
|
} |
|
|
|
} |
|
|
|
/** Insert all the values from the provided set which are not already stored into this one. */ |
|
void insert_overwrite( const AnyValueSet& other ) |
|
{ |
|
for( const auto& set_pair : other.m_set_index ) |
|
{ |
|
auto& set_ptr = set_pair.second; |
|
UCX_ASSERT_NOT_NULL( set_ptr ); |
|
|
|
if( auto value_set = find_set( set_pair.first ) ) |
|
{ |
|
value_set->insert_overwrite( *set_ptr ); |
|
} |
|
else |
|
{ |
|
m_set_index.emplace( set_pair.first, set_ptr->clone() ); |
|
} |
|
size_changed(); |
|
} |
|
|
|
} |
|
|
|
/** Copy and return all the values of the specified type. */ |
|
template< class ValueType > |
|
std::vector<ValueType> all() const |
|
{ |
|
if( auto* value_set = find_set<ValueType>() ) |
|
{ |
|
return value_set->values(); |
|
} |
|
return {}; |
|
} |
|
|
|
/** @return How many values are currently stored for all types. */ |
|
size_t size() const |
|
{ |
|
if( m_size_changed ) |
|
{ |
|
m_size_changed = false; |
|
compute_size(); |
|
} |
|
|
|
return m_size; |
|
} |
|
|
|
/** @return true if there is at least one value stored of any type, false otherwise. */ |
|
bool empty() const |
|
{ |
|
return m_set_index.empty() |
|
|| size() == 0; |
|
} |
|
|
|
/** Destroy all values of the provided type. */ |
|
template< class ValueType > |
|
void clear() |
|
{ |
|
if( auto* value_set = find_set<ValueType>() ) |
|
{ |
|
value_set->clear(); |
|
size_changed(); |
|
} |
|
} |
|
|
|
/** Destroy all values of all types. */ |
|
void clear() |
|
{ |
|
for( const auto& set_slot : m_set_index ) |
|
{ |
|
auto& set_ptr = set_slot.second; |
|
UCX_ASSERT_NOT_NULL( set_ptr ); |
|
set_ptr->clear(); |
|
} |
|
size_changed(); |
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
struct Set |
|
{ |
|
virtual void insert( const Set& other ) = 0; |
|
virtual void insert_overwrite( const Set& other ) = 0; |
|
virtual void remove( const Set& other ) = 0; |
|
virtual std::unique_ptr<Set> clone() = 0; |
|
virtual size_t size() const = 0; |
|
virtual void clear() = 0; |
|
virtual ~Set() = default; |
|
}; |
|
|
|
template< class T > |
|
class SetOf : public Set |
|
{ |
|
boost::container::flat_set<T> m_values; |
|
public: |
|
SetOf() = default; |
|
|
|
template< class Iterator > |
|
SetOf( Iterator&& it_begin, Iterator&& it_end ) |
|
: m_values( std::forward<Iterator>( it_begin ), std::forward<Iterator>( it_end ) ) |
|
{} |
|
|
|
/** Add a new value if not already stored. |
|
@return True if the value have been added, false otherwise. |
|
*/ |
|
bool add( T value ) |
|
{ |
|
auto result = m_values.emplace( std::move( value ) ); |
|
return result.second; |
|
} |
|
|
|
/** Add a new value and overwrite the previous one even if it was already there. |
|
@return True if the value have been added, false if the value was overwritten. |
|
*/ |
|
bool overwrite( T value ) |
|
{ |
|
auto result = m_values.insert( value ); |
|
if( !result.second ) |
|
{ |
|
*result.first = value; |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
void remove( const T& value ) { m_values.erase( value ); } |
|
|
|
template< class KeyType > |
|
boost::optional<T> find( const KeyType& key ) const |
|
{ |
|
for( const auto& value : m_values ) |
|
{ |
|
if( value == key ) |
|
return value; |
|
} |
|
|
|
return {}; |
|
} |
|
|
|
template< class KeyType > |
|
bool contains( const KeyType& key ) const |
|
{ |
|
for( const auto& value : m_values ) |
|
{ |
|
if( value == key ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void insert( const Set& other ) override |
|
{ |
|
const auto& specific_set = static_cast<const SetOf<T>&>( other ); |
|
m_values.insert( begin(specific_set.m_values), end(specific_set.m_values) ); |
|
} |
|
|
|
void insert_overwrite( const Set& other ) override |
|
{ |
|
const auto& specific_set = static_cast<const SetOf<T>&>( other ); |
|
for( auto&& value : specific_set.m_values ) |
|
{ |
|
overwrite( value ); |
|
} |
|
} |
|
|
|
void remove( const Set& other ) override |
|
{ |
|
const auto& specific_set = static_cast<const SetOf<T>&>( other ); |
|
m_values.erase( std::remove_if( begin( m_values ), end( m_values ) |
|
, [&]( const T& value ) { return specific_set.contains( value ); } ) |
|
, end( m_values ) ); |
|
} |
|
|
|
std::unique_ptr<Set> clone() override |
|
{ |
|
return std::make_unique<SetOf<T>>( begin( m_values ), end( m_values ) ); |
|
} |
|
|
|
size_t size() const override { return m_values.size(); } |
|
|
|
std::vector<T> values() const |
|
{ |
|
return { begin( m_values ), end( m_values ) }; |
|
} |
|
|
|
void clear() override { m_values.clear(); } |
|
}; |
|
|
|
Set* find_set( std::type_index type_id ) const |
|
{ |
|
auto find_it = m_set_index.find( type_id ); |
|
if( find_it != end( m_set_index ) ) |
|
{ |
|
auto& set_ptr = find_it->second; |
|
auto& specific_set = *set_ptr; |
|
return &specific_set; |
|
} |
|
return nullptr; |
|
} |
|
|
|
|
|
template< class T, typename ValueType = std::decay<T>::type > |
|
SetOf<ValueType>* find_set() const |
|
{ |
|
return static_cast<SetOf<ValueType>*>( find_set( typeid( ValueType ) ) ); |
|
} |
|
|
|
template< class T, typename ValueType = std::decay<T>::type > |
|
SetOf<ValueType>& find_or_create_set() |
|
{ |
|
if( auto* specific_set = find_set<ValueType>() ) |
|
{ |
|
return *specific_set; |
|
} |
|
|
|
auto new_set = std::make_unique<SetOf<ValueType>>(); |
|
auto position = m_set_index.emplace( typeid( ValueType ), std::move(new_set) ).first; |
|
auto& set_ptr = position->second; |
|
return static_cast<SetOf<ValueType>&>( *set_ptr ); |
|
} |
|
|
|
inline void size_changed() const { m_size_changed = true; } |
|
|
|
void compute_size() const |
|
{ |
|
m_size = 0; |
|
for( const auto& set_pair : m_set_index ) |
|
{ |
|
auto& set_ptr = set_pair.second; |
|
m_size += set_ptr->size(); |
|
} |
|
} |
|
|
|
|
|
boost::container::flat_map<std::type_index, std::unique_ptr<Set>> m_set_index; |
|
mutable bool m_size_changed = false; |
|
mutable size_t m_size = 0; |
|
|
|
}; |
|
|
|
|
|
|
|
}} |
|
|
|
|
|
#endif |