diff --git a/include/ebcl/Sets.hh b/include/ebcl/Sets.hh new file mode 100644 index 0000000..463cf0e --- /dev/null +++ b/include/ebcl/Sets.hh @@ -0,0 +1,196 @@ +/******************************************************************************/ +/* SETS ***********************************************************************/ +/******************************************************************************/ + +#ifndef _H_EBCL_SETS +#define _H_EBCL_SETS +#include +#include +namespace ebcl { + + +/*= SETS =====================================================================*/ + +// Implementation of a set of type Type. The actual implementation is +// determined by the ImplTag type +template< typename Type , typename ImplTag > + class T_SetImplementation; + + +// ArrayBacked implementation tag. InPlace is the amount of items to +// store in-place (0 will cause a fully dynamic array to be used) +M_DEFINE_TEMPLATE_TAG( ArrayBacked , + uint32_t InPlace , + uint32_t Growth = 0 +); + +// IndexBacked implementation tag - the implementation will use a +// T_HashIndex and an array. InitialSize and HashSize affect the index, +// Growth affects both the index and the array. +M_DEFINE_TEMPLATE_TAG( IndexBacked , + uint32_t InitialSize = T_HashIndex::DEFAULT_SIZE , + uint32_t HashSize = T_HashIndex::DEFAULT_SIZE , + uint32_t Growth = T_HashIndex::DEFAULT_GROWTH ); + +/*----------------------------------------------------------------------------*/ + +// Templated horrors used by the main T_Set class. +struct T_SetHelper +{ + // Default implementation to use; also determines the in-place + // storage size + using DefaultImplementation = IndexBacked< >; + + // Default implementation class + template< typename Type + > using DefaultType = T_SetImplementation< + Type , DefaultImplementation >; + + // --------------------------------------------------------------------- + + // In-place storage size + template< typename Type + > static constexpr auto DefaultSize{ + sizeof( DefaultType< Type > ) }; + // In-place storage alignment + template< typename Type + > static constexpr auto DefaultAlign{ + alignof( DefaultType< Type > ) }; + + // --------------------------------------------------------------------- + + // Implementation handler operations + enum E_Operation + { + // New instance; output = storage + INIT , + // Copy instance; output = storage, rArg = source + INIT_COPY , + // Move instance; output = storage, wArg = source + INIT_MOVE , + // Destroy instance; wArg = storage + DESTROY , + + // Get size; rArg = storage, output = result + SIZE , + // Get index of element; wArg = storage, rArg = element, output = result + INDEX_OF , + // Access an element; wArg = index , rArg = storage, output = pointer + ACCESS , + + // Add copy of element; wArg = storage, rArg = element, output = result + ADD_COPY , + // Move & add element; wArg = storage, rArg = element, output = result + ADD_MOVE , + // Delete element; wArg = storage, rArg = element, output = result + REMOVE , + + // Free memory; wArg = storage + FREE , + // Clear contents; wArg = storage + CLEAR , + }; + + // Handler function type + using F_Handler = std::function< + void( E_Operation , void* , void const* , void* ) >; + + // --------------------------------------------------------------------- + + // Handler for in-place implementations + template< typename Type , typename Impl + > struct T_InPlaceHandler + { + static void shdl( E_Operation operation , + void* wArg , + void const* rArg , + void* output ); + }; + + // Handler for heap-allocated implementations + template< typename Type , typename Impl + > struct T_HeapHandler + { + static void shdl( E_Operation operation , + void* wArg , + void const* rArg , + void* output ); + }; + + // Use the in-place handler if the implementation data can fit into the + // in-place storage; otherwise use the heap-allocated handler. + template< + typename Type , typename ImplTag , + typename Impl = T_SetImplementation< Type , ImplTag > + > using T_Handler = std::conditional_t< + sizeof( Impl ) <= DefaultSize< Type > , + T_InPlaceHandler< Type , ImplTag > , + T_HeapHandler< Type , ImplTag > + >; +}; + +/*----------------------------------------------------------------------------*/ + +// A set of elements of type Type +template< typename Type +> class T_Set +{ + private: + + // Storage area for the implementation's data + std::aligned_storage_t< + T_SetHelper::DefaultSize< Type > , + T_SetHelper::DefaultAlign< Type > + > storage_; + + // Handler + T_SetHelper::F_Handler handler_; + + public: + // Default constructor, using the default implementation + T_Set( ) noexcept; + // Create a set with a specific implementation + template< typename Tag + > T_Set( UseTag< Tag > impl ) noexcept; + + // Copy constructor / assignment + T_Set( T_Set const& other ) noexcept; + T_Set& operator =( T_Set const& other ) noexcept; + + // Move constructor / assignment + T_Set( T_Set&& other ) noexcept; + T_Set& operator =( T_Set&& other ) noexcept; + + ~T_Set( ); + + // --------------------------------------------------------------------- + + // Returns amount of elements + uint32_t size( ) const noexcept; + // Checks if item is in the set + bool contains( Type const& item ) const noexcept; + // Returns underlying index of item, or -1 if not in the set + int32_t indexOf( Type const& item ) const noexcept; + + // Access an element using its underlying index + Type const& operator[]( uint32_t index ) const noexcept; + + // --------------------------------------------------------------------- + + // Add copy of item, returns true if added + bool add( Type const& item ) noexcept; + // Add moved item, returns true if added + bool add( Type&& item ) noexcept; + // Remove item, returns true if removed + bool remove( Type const& item ) noexcept; + + // --------------------------------------------------------------------- + + void clear( ) noexcept; + void free( ) noexcept; +}; + + +} +#endif // _H_EBCL_SETS +#include diff --git a/include/ebcl/Utilities.hh b/include/ebcl/Utilities.hh index 7234056..34313be 100644 --- a/include/ebcl/Utilities.hh +++ b/include/ebcl/Utilities.hh @@ -210,6 +210,27 @@ template< typename RT , typename... AT > template< typename T > using F_CFunc = typename T_CFunc< T >::type; +/*----------------------------------------------------------------------------*/ + +// Define a structure that can be used as a tag +#define M_DEFINE_TAG( NAME ) \ + struct NAME { } +// Define a tag structure that can carry arguments +#define M_DEFINE_TEMPLATE_TAG( NAME , ARGS... ) \ + template< ARGS > \ + M_DEFINE_TAG( NAME ) + +// A structure that can be used to carry a tag structure instance +template< typename Tag > +struct UseTag +{ + const Tag tag; + + template< typename ... ArgTypes > + constexpr UseTag( ArgTypes&&... args ) noexcept + : tag( std::forward< ArgTypes >( args ) ... ) { } +}; + /*= MACRO HORRORS ============================================================*/ diff --git a/include/ebcl/inline/Sets.hh b/include/ebcl/inline/Sets.hh new file mode 100644 index 0000000..1b90764 --- /dev/null +++ b/include/ebcl/inline/Sets.hh @@ -0,0 +1,637 @@ +/******************************************************************************/ +/* SETS - INLINE CODE *********************************************************/ +/******************************************************************************/ + +#ifndef _H_EBCL_INLINE_SETS +#define _H_EBCL_INLINE_SETS +#include +namespace ebcl { + + +/*= SET IMPLEMENTATION - FULLY DYNAMIC ARRAY =================================*/ + +template< typename Type , uint32_t Growth > +class T_SetImplementation< Type , ArrayBacked< 0 , Growth > > +{ + private: + T_Array< Type > data_; + + public: + template< uint32_t G = Growth , typename std::enable_if_t< G == 0 , int > = 0 + > T_SetImplementation( ) noexcept; + template< uint32_t G = Growth , typename std::enable_if_t< G != 0 , int > = 0 + > T_SetImplementation( ) noexcept; + + uint32_t size( ) const noexcept; + int32_t indexOf( Type const& item ) const noexcept; + Type const* access( uint32_t item ) const noexcept; + + bool add( Type const& item ) noexcept; + bool add( Type&& item ) noexcept; + bool remove( Type const& item ) noexcept; + + void free( ) noexcept; + void clear( ) noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +#define M_TMPL_ template< typename Type , uint32_t Growth > +#define M_TYPE_ T_SetImplementation< Type , ArrayBacked< 0 , Growth > > + +M_TMPL_ +template< uint32_t G , typename std::enable_if_t< G == 0 , int > > +M_TYPE_::T_SetImplementation( ) noexcept + : data_( ) +{ } + +M_TMPL_ +template< uint32_t G , typename std::enable_if_t< G != 0 , int > > +M_TYPE_::T_SetImplementation( ) noexcept + : data_( Growth ) +{ } + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ uint32_t M_TYPE_::size( ) const noexcept +{ + return data_.size( ); +} + +M_TMPL_ int32_t M_TYPE_::indexOf( + Type const& item ) const noexcept +{ + return data_.indexOf( item ); +} + +M_TMPL_ Type const* M_TYPE_::access( + const uint32_t item ) const noexcept +{ + return &data_[ item ]; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ bool M_TYPE_::add( + Type const& item ) noexcept +{ + const auto ok( indexOf( item ) == -1 ); + if ( ok ) { + data_.add( item ); + } + return ok; +} + +M_TMPL_ bool M_TYPE_::add( + Type&& item ) noexcept +{ + const auto ok( indexOf( item ) == -1 ); + if ( ok ) { + data_.add( std::move( item ) ); + } + return ok; +} + +M_TMPL_ bool M_TYPE_::remove( + Type const& item ) noexcept +{ + const auto index( indexOf( item ) ); + if ( index >= 0 ) { + data_.removeSwap( index ); + } + return index >= 0; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ void M_TYPE_::free( ) noexcept +{ + data_.free( ); +} + +M_TMPL_ void M_TYPE_::clear( ) noexcept +{ + data_.clear( ); +} + +#undef M_TMPL_ +#undef M_TYPE_ + + +/*= SET IMPLEMENTATION - AUTOMATIC ARRAY =====================================*/ + +template< typename Type , uint32_t InPlace , uint32_t Growth > +class T_SetImplementation< Type , ArrayBacked< InPlace , Growth > > +{ + private: + T_AutoArray< Type , InPlace , + Growth == 0 ? ( InPlace * 4 ) : Growth + > data_; + + public: + uint32_t size( ) const noexcept; + int32_t indexOf( Type const& item ) const noexcept; + Type const* access( uint32_t item ) const noexcept; + + bool add( Type const& item ) noexcept; + bool add( Type&& item ) noexcept; + bool remove( Type const& item ) noexcept; + + void free( ) noexcept; + void clear( ) noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +#define M_TMPL_ template< typename Type , uint32_t ISize , uint32_t Growth > +#define M_TYPE_ T_SetImplementation< Type , ArrayBacked< ISize , Growth > > + +M_TMPL_ uint32_t M_TYPE_::size( ) const noexcept +{ + return data_.size( ); +} + +M_TMPL_ int32_t M_TYPE_::indexOf( + Type const& item ) const noexcept +{ + return data_.indexOf( item ); +} + +M_TMPL_ Type const* M_TYPE_::access( + const uint32_t item ) const noexcept +{ + return &data_[ item ]; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ bool M_TYPE_::add( + Type const& item ) noexcept +{ + const auto ok( indexOf( item ) == -1 ); + if ( ok ) { + data_.add( item ); + } + return ok; +} + +M_TMPL_ bool M_TYPE_::add( + Type&& item ) noexcept +{ + const auto ok( indexOf( item ) == -1 ); + if ( ok ) { + data_.add( std::move( item ) ); + } + return ok; +} + +M_TMPL_ bool M_TYPE_::remove( + Type const& item ) noexcept +{ + const auto index( indexOf( item ) ); + if ( index >= 0 ) { + data_.removeSwap( index ); + } + return index >= 0; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ void M_TYPE_::free( ) noexcept +{ + data_.free( ); +} + +M_TMPL_ void M_TYPE_::clear( ) noexcept +{ + data_.clear( ); +} + +#undef M_TMPL_ +#undef M_TYPE_ + + +/*= SET IMPLEMENTATION - INDEX ===============================================*/ + +template< + typename Type , + uint32_t InitialSize , + uint32_t HashSize , + uint32_t Growth +> class T_SetImplementation< Type , IndexBacked< InitialSize , HashSize , Growth > > +{ + static_assert( InitialSize > 0 , "invalid initial size" ); + static_assert( HashSize > 0 , "invalid hash array size" ); + static_assert( Growth > 0 , "invalid growth" ); + + private: + T_HashIndex index_{ HashSize , InitialSize , Growth }; + T_Array< Type > items_{ Growth }; + + public: + uint32_t size( ) const noexcept; + int32_t indexOf( Type const& item ) const noexcept; + Type const* access( uint32_t item ) const noexcept; + + bool add( Type const& item ) noexcept; + bool add( Type&& item ) noexcept; + bool remove( Type const& item ) noexcept; + + void free( ) noexcept; + void clear( ) noexcept; + + private: + uint32_t find( Type const& k , + const uint32_t hash ) const noexcept; +}; + +/*----------------------------------------------------------------------------*/ + +#define M_TMPL_ template< typename Type , uint32_t ISize , uint32_t HSize , uint32_t Growth > +#define M_TYPE_ T_SetImplementation< Type , IndexBacked< ISize , HSize , Growth > > + +M_TMPL_ uint32_t M_TYPE_::size( ) const noexcept +{ + return items_.size( ); +} + +M_TMPL_ int32_t M_TYPE_::indexOf( + Type const& item ) const noexcept +{ + const auto idx{ find( item , ComputeHash( item ) ) }; + return idx == T_HashIndex::INVALID_INDEX ? -1 : int32_t( idx ); +} + +M_TMPL_ Type const* M_TYPE_::access( + const uint32_t item ) const noexcept +{ + return &items_[ item ]; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ bool M_TYPE_::add( + Type const& item ) noexcept +{ + const auto hash{ ComputeHash( item ) }; + const auto idx{ find( item , hash ) }; + if ( idx == T_HashIndex::INVALID_INDEX ) { + index_.add( hash ); + items_.add( item ); + } + return idx == T_HashIndex::INVALID_INDEX; +} + +M_TMPL_ bool M_TYPE_::add( + Type&& item ) noexcept +{ + const auto hash{ ComputeHash( item ) }; + const auto idx{ find( item , hash ) }; + if ( idx == T_HashIndex::INVALID_INDEX ) { + index_.add( hash ); + items_.add( std::move( item ) ); + } + return idx == T_HashIndex::INVALID_INDEX; +} + +M_TMPL_ bool M_TYPE_::remove( + Type const& item ) noexcept +{ + const auto hash{ ComputeHash( item ) }; + const auto idx{ find( item , hash ) }; + if ( idx != T_HashIndex::INVALID_INDEX ) { + items_.removeSwap( idx ); + index_.remove( idx ); + } + return idx != T_HashIndex::INVALID_INDEX; +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ void M_TYPE_::free( ) noexcept +{ + index_.free( ); + items_.free( ); +} + +M_TMPL_ void M_TYPE_::clear( ) noexcept +{ + index_.clear( ); + items_.clear( ); +} + +/*----------------------------------------------------------------------------*/ + +M_TMPL_ uint32_t M_TYPE_::find( + Type const& k , + const uint32_t hash ) const noexcept +{ + uint32_t idx = index_.first( hash ); + while ( idx != T_HashIndex::INVALID_INDEX ) { + // XXX use a match function? +// if ( match_( keys_[ idx ] , k ) ) { +// break; +// } + if ( items_[ idx ] == k ) { + break; + } + idx = index_.next( idx ); + } + return idx; +} + +#undef M_TMPL_ +#undef M_TYPE_ + + +/*= COMMON HANDLER ===========================================================*/ + +// In-place version +template< typename Type , typename ImplTag > +void T_SetHelper::T_InPlaceHandler< Type , ImplTag >::shdl( + const E_Operation operation , + void* const wArg , + void const* const rArg , + void* const output ) +{ + using T_Impl_ = T_SetImplementation< Type , ImplTag >; + + switch ( operation ) { + + case INIT: + ::new ((char*)output) T_Impl_( ); + break; + + case INIT_COPY: + ::new ((char*)output) T_Impl_( *(T_Impl_ const*)rArg ); + break; + + case INIT_MOVE: + ::new ((char*)output) T_Impl_( std::move( *(T_Impl_*)wArg ) ); + break; + + case DESTROY: + ((T_Impl_*)wArg)->~T_Impl_( ); + break; + + //------------------------------------------------------------------ + + case SIZE: + *((uint32_t*) output) = ((T_Impl_ const*)rArg)->size( ); + break; + + case INDEX_OF: + *((uint32_t*) output) = ((T_Impl_ const*)wArg)->indexOf( + *(Type const*) rArg ); + break; + + case ACCESS: + *((Type const**) output) = ((T_Impl_ const*)rArg)->access( + *(uint32_t const*) wArg ); + break; + + //------------------------------------------------------------------ + + case ADD_COPY: + *((bool*)output) = ((T_Impl_*)wArg)->add( *(Type const*) rArg ); + break; + + case ADD_MOVE: + *((bool*)output) = ((T_Impl_*)wArg)->add( + std::move( *const_cast< Type* >( (Type const*) rArg ) ) ); + break; + + case REMOVE: + *((bool*)output) = ((T_Impl_*)wArg)->remove( *((Type const*) rArg) ); + break; + + //------------------------------------------------------------------ + + case FREE: + ((T_Impl_*)wArg)->free( ); + break; + + case CLEAR: + ((T_Impl_*)wArg)->clear( ); + break; + } +} + +/*----------------------------------------------------------------------------*/ + +// Heap-allocated implementation +template< typename Type , typename ImplTag > +void T_SetHelper::T_HeapHandler< Type , ImplTag >::shdl( + E_Operation operation , + void* wArg , + void const* rArg , + void* output ) +{ + using T_Impl_ = T_SetImplementation< Type , ImplTag >; + + switch ( operation ) { + + case INIT: + *(T_Impl_**)output = ::new T_Impl_( ); + break; + + case INIT_COPY: + *(T_Impl_**)output = ::new T_Impl_( *(T_Impl_ const**)rArg ); + break; + + case INIT_MOVE: + *(T_Impl_**)output = ::new T_Impl_( std::move( *(T_Impl_**)wArg ) ); + break; + + case DESTROY: + delete *(T_Impl_**)wArg; + break; + + //------------------------------------------------------------------ + + case SIZE: + *((uint32_t*) output) = (*(T_Impl_ const**)rArg)->size( ); + break; + + case INDEX_OF: + *((uint32_t*) output) = (*(T_Impl_ const**)wArg)->indexOf( + *(Type const*) rArg ); + break; + + case ACCESS: + *((Type const**) output) = (*(T_Impl_ const**)rArg)->access( + *(uint32_t const*) wArg ); + break; + + //------------------------------------------------------------------ + + case ADD_COPY: + *((bool*)output) = (*(T_Impl_**)wArg)->add( *(Type const*) rArg ); + break; + + case ADD_MOVE: + *((bool*)output) = (*(T_Impl_**)wArg)->add( + std::move( *const_cast< Type* >( (Type const*) rArg ) ) ); + break; + + case REMOVE: + *((bool*)output) = (*(T_Impl_**)wArg)->remove( *((Type const*) rArg) ); + break; + + //------------------------------------------------------------------ + + case FREE: + (*(T_Impl_**)wArg)->free( ); + break; + + case CLEAR: + (*(T_Impl_**)wArg)->clear( ); + break; + } +} + + +/*= T_Set ====================================================================*/ + +template< typename Type > +T_Set< Type >::T_Set( ) noexcept + : T_Set( UseTag< T_SetHelper::DefaultImplementation >( ) ) +{ } + +template< typename Type > +template< typename Tag > +T_Set< Type >::T_Set( const UseTag< Tag > ) noexcept + : handler_( T_SetHelper::T_Handler< Type , Tag >::shdl ) +{ + handler_( T_SetHelper::INIT , nullptr , nullptr , &storage_ ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +T_Set< Type >::T_Set( + T_Set const& other ) noexcept + : handler_( other.handler_ ) +{ + handler_( T_SetHelper::INIT_COPY , nullptr , &other.storage_ , &storage_ ); +} + +template< typename Type > +T_Set< Type >& T_Set< Type >::operator =( + T_Set const& other ) noexcept +{ + handler_( T_SetHelper::DESTROY , &storage_ , nullptr , nullptr ); + handler_ = other.handler_; + handler_( T_SetHelper::INIT_COPY , nullptr , &other.storage_ , &storage_ ); +} + + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +T_Set< Type >::T_Set( + T_Set&& other ) noexcept + : handler_( other.handler_ ) +{ + handler_( T_SetHelper::INIT_MOVE , &other.storage_ , nullptr , &storage_ ); +} + +template< typename Type > +T_Set< Type >& T_Set< Type >::operator =( + T_Set&& other ) noexcept +{ + handler_( T_SetHelper::DESTROY , &storage_ , nullptr , nullptr ); + handler_ = other.handler_; + handler_( T_SetHelper::INIT_MOVE , &other.storage_ , nullptr , &storage_ ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +T_Set< Type >::~T_Set( ) +{ + handler_( T_SetHelper::DESTROY , &storage_ , nullptr , nullptr ); +} + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +uint32_t T_Set< Type >::size( ) const noexcept +{ + uint32_t r; + handler_( T_SetHelper::SIZE , nullptr , &storage_ , &r ); + return r; +} + +template< typename Type > +bool T_Set< Type >::contains( + Type const& item ) const noexcept +{ + return indexOf( item ) >= 0; +} + +template< typename Type > +int32_t T_Set< Type >::indexOf( + Type const& item ) const noexcept +{ + int32_t r; + handler_( T_SetHelper::INDEX_OF , const_cast< decltype( storage_ )* >( &storage_ ) , + &item , &r ); + return r; +} + +template< typename Type > +Type const& T_Set< Type >::operator[]( + uint32_t index ) const noexcept +{ + Type* ptr; + handler_( T_SetHelper::ACCESS , &index , &storage_ , &ptr ); + return *ptr; +} + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +bool T_Set< Type >::add( + Type const& item ) noexcept +{ + bool r; + handler_( T_SetHelper::ADD_COPY , &storage_ , &item , &r ); + return r; +} + +template< typename Type > +bool T_Set< Type >::add( + Type&& item ) noexcept +{ + bool r; + handler_( T_SetHelper::ADD_MOVE , &storage_ , &item , &r ); + return r; +} + +template< typename Type > +bool T_Set< Type >::remove( + Type const& item ) noexcept +{ + bool r; + handler_( T_SetHelper::REMOVE , &storage_ , &item , &r ); + return r; +} + +/*----------------------------------------------------------------------------*/ + +template< typename Type > +void T_Set< Type >::free( ) noexcept +{ + handler_( T_SetHelper::FREE , &storage_ , nullptr , nullptr ); +} + +template< typename Type > +void T_Set< Type >::clear( ) noexcept +{ + handler_( T_SetHelper::CLEAR , &storage_ , nullptr , nullptr ); +} + + +} +#endif // _H_EBCL_INLINE_SETS