8#ifndef HPP_ALIB_MONOMEM_HASHTABLE
9#define HPP_ALIB_MONOMEM_HASHTABLE 1
11#if !defined(HPP_ALIB_MONOMEM_DETAIL_HASHTABLEBASE)
15#if !defined (_GLIBCXX_NUMERIC_LIMITS) && !defined(_LIMITS_)
18#if !defined (_GLIBCXX_CMATH) && !defined (_CMATH_)
22namespace alib {
namespace monomem {
452 typename TRecycling = Recycling::Private >
456 #if !defined(ALIB_DOX)
459 using Element=
typename base::Element;
460 using Node =
typename base::Node;
499 using Iterator =
typename base::template TIterator < T>;
525 #if !defined(ALIB_DOX)
552 other.element=
nullptr;
580 other.element=
nullptr;
623 template<
typename TEnableIf= TIfMapped>
633 #if defined(ALIB_DOX)
646 float pBaseLoadFactor = 1.0,
647 float pMaxLoadFactor = 2.0 );
649 template<
typename TEnableIf= TRecycling,
653 float pBaseLoadFactor = 1.0,
654 float pMaxLoadFactor = 2.0 )
655 : base( pAllocator, pBaseLoadFactor, pMaxLoadFactor )
671 float pBaseLoadFactor = 1.0,
672 float pMaxLoadFactor = 2.0 )
674 : base( pAllocator, pRecycler, pBaseLoadFactor, pMaxLoadFactor )
684 if constexpr( !std::is_trivially_destructible<TStored>::value )
698 "Allocator already set.")
699 base::allocator= pAllocator;
712 return base::allocator;
758 return base::size == 0;
772 return base::rehash(
uinteger(std::ceil( expectedSize / base::baseLoadFactor)) );
795 Reserve( expected, reference );
800 if( requiredRecyclables > 0 )
803 Element* newElements= base::allocator->template AllocArray<Element>(requiredRecyclables);
804 for(
auto i= requiredRecyclables - 2; i >= 0 ; --i )
805 newElements[i].next( &newElements[i + 1] );
807 base::recycler.recycle( &newElements[0], &newElements[requiredRecyclables - 1] );
826 return base::recycler.count();
856 base::baseLoadFactor= newBaseLoadFactor;
870 return base::baseLoadFactor;
896 base::setMaxLoadFactor( newMaxLoadFactor );
910 return base::maxLoadFactor;
924 return base::bucketCount;
936 "Bucket number out of range." )
938 return static_cast<uinteger>(base::buckets[bucketNumber].count());
950 return THash()(key) % base::bucketCount;
964 return Insert( T( value ), THash()( TAccess().Key(
reinterpret_cast<TStored&
>(value)) ) );
979 return Insert( T( value ), hashCode );
998 auto hashCode = THash()( TAccess().Key(
reinterpret_cast<TStored&
>(value)) );
999 return Insert( std::move(value), hashCode );
1016 Element* element= base::allocElement(hashCode);
1019 new ( &element->value ) T ( std::move(value) );
1022 base::increaseSize( 1 );
1023 auto bucketIdx= base::insertInBucket( element, hashCode );
1024 return Iterator(
this, bucketIdx, element);
1053 base::increaseSize( 1 );
1055 auto hashCode= THash()( base::keyPortion( element ));
1056 element->fixHashCode( hashCode );
1057 auto bucketIdx= base::insertInBucket( element, hashCode );
1059 return Iterator(
this, bucketIdx, element );
1118 auto hashCode = THash()( TAccess().Key(
reinterpret_cast<TStored&
>(value)) );
1135 Element* element = base::allocElement( hashCode );
1138 base::increaseSize( 1 );
1139 auto bucketIdx= hashCode % base::bucketCount;
1140 base::buckets[bucketIdx].pushFront( element );
1143 new ( &element->value ) T ( std::move(value) );
1147 auto it=
cbegin(bucketIdx);
1149 while( ++it !=
cend(bucketIdx) )
1152 "InsertUnique used while element with same key-portion "
1157 return Iterator(
this, bucketIdx, element);
1176 template<
typename TEnableIf= TIfMapped>
1177 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1204 template<
typename TEnableIf= TIfMapped>
1205 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1231 template<
typename TEnableIf= TIfMapped>
1232 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1235 std::pair<Iterator, bool> result= base::insertOrGet( key, hashCode );
1238 if( result.second ==
false )
1243 new (&base::keyPortion( result.first.element ))
KeyType( key );
1246 new ( &base::mappedPortion( result.first.element ) )
MappedType( std::move( mapped) );
1267 template<
typename TEnableIf= TIfMapped>
1268 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1290 template<
typename TEnableIf= TIfMapped>
1291 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1295 std::pair<Iterator, bool> result= base::insertIfNotExists( key, hashCode );
1298 if( result.second ==
false )
1302 new (&base::keyPortion( result.first.element ))
KeyType( key );
1305 new ( &base::mappedPortion( result.first.element ) )
MappedType( std::move( mapped) );
1332 template<
typename TEnableIf= TIfMapped>
1333 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ( TEnableIf,
void ))
1370 auto hashCode= THash()( TAccess().Key(value) );
1390 std::pair<Iterator, bool> result= base::insertIfNotExists( TAccess().Key(value), hashCode );
1393 if( result.second ==
false )
1397 new ( &result.first.element->value ) T( std::move(value) );
1435 auto hashCode = THash()( base::keyPortion( handle.
element ) );
1436 auto bucketIdx= hashCode % base::bucketCount;
1438 Element* existing= base::findElement( hashCode, base::keyPortion( element ), hashCode );
1439 if ( existing !=
nullptr )
1440 return Iterator(
this, bucketIdx, existing );
1443 element->fixHashCode( hashCode );
1445 bucketIdx= base::increaseSize( 1, hashCode );
1447 base::buckets[bucketIdx].pushFront( element );
1449 return Iterator(
this, bucketIdx, element);
1467 template<
typename... TArgs>
1471 Element* element= base::allocElement( 0 );
1474 new ( &element->value ) TStored( std::forward<TArgs>(args)... );
1477 auto hashCode= THash()( base::keyPortion( element ));
1478 element->fixHashCode( hashCode );
1481 base::increaseSize( 1 );
1482 auto bucketIdx= base::insertInBucket( element, hashCode );
1483 return Iterator(
this, bucketIdx, element);
1513 template<
typename... TArgs>
1517 Element* element= base::allocElement(0);
1520 new ( &element->value ) TStored( std::forward<TArgs>(args)... );
1523 auto hashCode= THash()( base::keyPortion( element ));
1524 element->fixHashCode( hashCode );
1528 auto bucketIdx= base::increaseSize( 1, hashCode );
1530 base::buckets[bucketIdx].pushFront( element );
1532 auto result=
Iterator(
this, bucketIdx, element);
1536 auto it=
cbegin(result.bucketIdx);
1538 while( ++it !=
cend(result.bucketIdx) )
1540 ALIB_ASSERT_ERROR( !base::areEqual(result.element, it.element ),
"MONOMEM/HASHTABLE",
1541 "EmplaceUnique used while element with same key-portion "
1549#if defined(ALIB_DOX)
1575 template<
typename TEnableIf= TIfMapped,
typename... TArgs>
1577 std::pair<Iterator, bool>
1580 template<
typename TEnableIf_MapMode= TIfMapped,
typename... TArgs>
1581 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>, !
ATMP_EQ(TEnableIf_MapMode,
void ) )
1585 std::pair<Iterator, bool> result= base::insertOrGet( key, THash()(key) );
1588 if( result.second ==
false )
1593 new (&base::keyPortion( result.first.element ))
KeyType( key );
1596 new ( &base::mappedPortion( result.first.element ))
MappedType( std::forward<TArgs>( args)... );
1601 template<
typename TEnableIf_SetMode= TIfMapped,
typename... TArgs>
1602 ATMP_T_IF(std::pair<Iterator ALIB_COMMA bool>,
ATMP_EQ(TEnableIf_SetMode,
void )
1605 TArgs&&... >::value )
1609 std::pair<Iterator, bool> result= base::insertOrGet( key, THash()(key) );
1612 if( result.second ==
false )
1616 new (&result.first.element->value) T( key, std::forward<TArgs>( args)... );
1622#if defined(ALIB_DOX)
1660 template<
typename TEnableIf= TIfMapped,
typename... TArgs>
1662 std::pair<Iterator, bool>
1665 template<
typename TEnableIf= TIfMapped,
typename... TArgs>
1667 && std::is_move_constructible<T>::value )
1670 T value( std::forward<TArgs>( args)... );
1673 std::pair<Iterator, bool> result= base::insertIfNotExists( TAccess().Key(value),
1674 THash()(TAccess().Key(value)) );
1677 if( result.second ==
false )
1681 new ( &result.first.element->value ) T( std::move(value) );
1687#if defined(ALIB_DOX)
1714 template<
typename TEnableIf,
typename... TArgs>
1716 std::pair<Iterator, bool>
1720 template<
typename TEnableIf= TIfMapped,
typename... TArgs>
1723 TArgs&&... >::value )
1727 std::pair<Iterator, bool> result= base::insertIfNotExists( key, THash()(key) );
1730 if( result.second ==
false )
1734 new (&base::keyPortion (result.first.element))
KeyType( key );
1737 new (&base::mappedPortion(result.first.element))
MappedType( std::forward<TArgs>( args)... );
1742 template<
typename TEnableIf= TIfMapped,
typename... TArgs>
1745 TArgs&&... >::value )
1749 std::pair<Iterator, bool> result= base::insertIfNotExists( key, THash()(key) );
1752 if( result.second ==
false )
1756 new (&result.first.element->value) T( key, std::forward<TArgs>( args)... );
1784 auto hashCode = THash()(key);
1785 auto bucketIdx= hashCode % base::bucketCount;
1786 Element* elem = base::findElement( bucketIdx, key, hashCode );
1787 return Iterator(
this, elem ==
nullptr ? base::bucketCount : bucketIdx, elem );
1798 auto hashCode = THash()(key);
1799 auto bucketIdx= hashCode % base::bucketCount;
1800 Element* elem = base::findElement( bucketIdx, key, hashCode );
1801 return ConstIterator(
this, elem ==
nullptr ? base::bucketCount : bucketIdx, elem );
1817 auto bucketIdx= hashCode % base::bucketCount;
1818 Element* elem = base::findElement( bucketIdx, key, hashCode );
1819 return Iterator(
this, elem ==
nullptr ? base::bucketCount : bucketIdx, elem );
1835 auto bucketIdx= hashCode % base::bucketCount;
1836 Element* elem = base::findElement( bucketIdx, key, hashCode );
1837 return ConstIterator(
this, elem ==
nullptr ? base::bucketCount : bucketIdx, elem );
1848 auto hashCode= THash()(key);
1849 return base::findElement(hashCode % base::bucketCount, key, hashCode )
1864 return base::findRange( key );
1878 return base::findRange( key );
1901 return Extract( key, THash()(key) );
1919 Node* previous= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
1920 if( previous ==
nullptr )
1951 && pos.table !=
nullptr ,
"MONOMEM/HASHTABLE",
1952 "Illegal iterator." )
1954 Node* previous= base::buckets[pos.bucketIdx].findLastBefore( pos.element );
1955 ALIB_ASSERT_ERROR( previous !=
nullptr,
"MONOMEM/HASHTABLE",
"Illegal iterator: Element not found." )
1971 return Erase( key, THash()(key) );
1988 Node* beforeFirst= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
1989 if( beforeFirst ==
nullptr )
2001 while( last->hasNext()
2002 && base::areEqual( last->next(), key, hashCode ) );
2006 base::recycler.recycle( first, last );
2045 Node* before= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
2046 if( before ==
nullptr )
2050 || !base::areEqual( before->
next()->next(), key, hashCode ),
2051 "MONOMEM/HASHTABLE",
"More than one element found matching the given key")
2055 base::recycler.recycle( elem );
2075 && pos.table !=
nullptr ,
"MONOMEM/HASHTABLE",
2076 "Illegal iterator." )
2078 Iterator result(
this, pos.bucketIdx, pos.element );
2083 Node* previous= base::buckets[pos.bucketIdx].findLastBefore( pos.element );
2086 "Illegal iterator: Element not found." )
2090 toDelete->destruct();
2091 base::recycler.recycle( toDelete );
2111 && start.table !=
nullptr ,
"MONOMEM/HASHTABLE",
2112 "Illegal iterator." )
2115 "Iterators are referring to different hash tables." )
2117 if( start.element ==
end.element )
2118 return Iterator(
this, start.bucketIdx, start.element);
2121 for(
auto bucketIdx= start.bucketIdx; bucketIdx <=
end.bucketIdx; ++bucketIdx )
2124 if( bucketIdx == base::bucketCount )
2130 if( bucketIdx == start.bucketIdx )
2133 previous= base::buckets[start.bucketIdx].findLastBefore( start.element );
2135 "Illegal iterator: Element not found." )
2139 if( base::buckets[bucketIdx].isEmpty() )
2141 previous= &base::buckets[bucketIdx].hook;
2146 bool isLastBucketOfRange= bucketIdx ==
end.bucketIdx;
2153 Element* stop = isLastBucketOfRange ?
end.element :
nullptr;
2154 while( next != stop )
2163 base::recycler.recycle( first, last );
2166 if( isLastBucketOfRange )
2169 if( result.element ==
nullptr )
2192 ALIB_ASSERT_ERROR( pos.element !=
nullptr,
"MONOMEM/HASHTABLE",
"Illegal iterator." )
2196 Element* element= pos.element;
2198 base::buckets[pos.bucketIdx].findAndRemove( element );
2200 element->destruct();
2201 base::recycler.recycle( element);
2221 ALIB_ASSERT_ERROR( start.element !=
nullptr,
"MONOMEM/HASHTABLE",
"Illegal iterator." )
2223 Node* previous= base::buckets[start.bucketIdx].findLastBefore( start.element);
2224 ALIB_ASSERT_ERROR( previous !=
nullptr,
"MONOMEM/HASHTABLE",
"Illegal iterator." )
2225 if( start.element ==
end.element )
2228 Element* first= previous->next();
2237 while( last->next() !=
end.element );
2241 previous->removeRangeBehind( last );
2242 base::recycler.recycle( first, last );
2288 "Bucket number out of range." )
2289 return LocalIterator( bucketNumber, base::buckets[bucketNumber].first() );
2300 "Bucket number out of range." )
2312 "Bucket number out of range." )
2324 "Bucket number out of range." )
2336 "Bucket number out of range." )
2348 "Bucket number out of range." )
2364#if ALIB_DEBUG_MONOMEM
2367# if !defined(HPP_ALIB_CAMP_MESSAGE_REPORT)
2370# if !defined(HPP_ALIB_LANG_FORMAT_FORMATTER)
2376namespace alib {
namespace monomem {
2400template<
typename THashtable>
2402std::tuple<double,double,integer,integer>
2405 auto qtyBuckets = hashtable.BucketCount();
2406 double averageExpected =
static_cast<double>( hashtable.Size() )
2407 /
static_cast<double>( qtyBuckets ) ;
2408 uinteger minimum = std::numeric_limits<uinteger>::max();
2409 uinteger maximum = std::numeric_limits<uinteger>::min();
2412 for(
uinteger i= 0 ; i < qtyBuckets ; ++i )
2414 auto bucketSize= hashtable.BucketSize( i );
2415 sumCheck+= bucketSize;
2416 if( minimum > bucketSize ) minimum= bucketSize;
2417 if( maximum < bucketSize ) maximum= bucketSize;
2419 double diff= averageExpected - double(bucketSize);
2420 diffs+= diff > 0 ? diff : - diff;
2425 "Error: Hashtable::Size() and sum of bucket sizes differ: {} != {}",
2426 hashtable.Size(), sumCheck )
2429 "Error: Hashtable::Size() and sum of bucket sizes differ" )
2431 double deviation= diffs / double(qtyBuckets);
2433 return std::make_tuple( averageExpected, deviation, minimum, maximum );
2458template<
typename THashtable>
2464 double loadFactor= std::get<0>( values );
2465 double deviation = std::get<1>( values );
2466 integer minSize = std::get<2>( values );
2467 integer maxSize = std::get<3>( values );
2470 formatter->Format( result,
"Size: {}\n"
2472 "Load Factor: {:.02} (Base: {:.01} Max: {:.01})\n"
2473 "Deviation: {:.02} (~{:%.1})\n"
2477 hashtable.BucketCount(),
2478 loadFactor, hashtable.BaseLoadFactor(), hashtable.MaxLoadFactor(),
2479 deviation , ( hashtable.Size() != 0
2480 ? deviation / loadFactor
2486 integer* bucketFills=
new integer[
static_cast<size_t>(maxSize + 1)];
2487 for(
integer i= 0; i < maxSize ; ++i)
2490 for(
uinteger i= 0; i < hashtable.BucketCount() ; ++i)
2491 ++bucketFills[hashtable.BucketSize(i)];
2493 formatter->Format( result,
"Bucket Fills: Size #Buckets\n" );
2494 formatter->Format( result,
" -----------------\n" );
2495 for(
integer i= 0; i < maxSize ; ++i)
2496 formatter->Format( result,
" {} {}\n", i, bucketFills[i] );
2497 delete[] bucketFills;
2501 if( detailedBucketList )
2503 formatter->Format(result,
"\nDetailed Bucket List:\n");
2504 auto qtyBuckets = hashtable.BucketCount();
2505 for(
uinteger i= 0 ; i < qtyBuckets ; ++i )
2507 auto bucketSize= hashtable.BucketSize( i );
2508 formatter->Format(result,
"{:3} ({:2}): {!FillCX}\n", i, bucketSize,
2509 static_cast<int>(bucketSize) );
2513 formatter->Release();
2546template<
typename THashtable>
2552 formatter->Format(result,
"\nHashtable dump:\n");
2553 auto qtyBuckets = hashtable.BucketCount();
2554 for(
uinteger i= 0 ; i < qtyBuckets ; ++i )
2556 auto bucketSize= hashtable.BucketSize( i );
2557 formatter->Format(result,
"{:3} ({:2}): ", i, bucketSize );
2560 for(
auto bucketIt= hashtable.begin(i)
2561 ; bucketIt != hashtable.end (i)
2566 formatter->Format(result,
"{}: {}\n", entryNo, *bucketIt );
2569 if( bucketSize == 0)
2573 formatter->Release();
HashTable * table
The table we belong to.
ElementHandle & operator=(ElementHandle &&other)
ElementHandle(HashTable *pTable, Element *pElement)
Element * element
The extracted element.
ElementHandle(ElementHandle &other)=delete
ElementHandle(ElementHandle &&other)
ElementHandle & operator=(const ElementHandle &other)=delete
bool EraseUnique(const KeyType &key)
void BaseLoadFactor(float newBaseLoadFactor)
ConstIterator cend() const
Iterator InsertUnique(T &&value)
ElementHandle Extract(const KeyType &key, size_t hashCode)
ConstLocalIterator end(uinteger bucketNumber) const
ConstLocalIterator cbegin(uinteger bucketNumber) const
typename base::template TIterator< const T > ConstIterator
ConstIterator Find(const KeyType &key, size_t hashCode) const
HashTable(MonoAllocator *pAllocator, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
integer Erase(const KeyType &key)
InsertIfNotExistent(const KeyType &key, const MappedType &mapped)
typename base::template TLocalIterator< T > LocalIterator
integer RecyclablesCount() const
ALIB_WARNINGS_IGNORE_NOTHING_RETURNED Iterator Erase(ConstIterator start, ConstIterator end)
ElementHandle Extract(const KeyType &key)
std::pair< Iterator, bool > InsertIfNotExistent(T &&value)
InsertOrAssign(const KeyType &key, const MappedType &mapped)
Iterator Find(const KeyType &key, size_t hashCode)
LocalIterator end(uinteger bucketNumber)
std::pair< Iterator, bool > EmplaceIfNotExistent(const KeyType &key, TArgs &&... args)
float BaseLoadFactor() const
Iterator EmplaceUnique(TArgs &&... args)
Iterator InsertUnique(T &&value, size_t hashCode)
typename base::template TLocalIterator< const T > ConstLocalIterator
ConstIterator Find(const KeyType &key) const
Iterator Insert(T &&value)
typename base::template TIterator< T > Iterator
uinteger BucketNumber(const KeyType &key) const
ConstIterator end() const
Iterator Insert(T &&value, size_t hashCode)
std::pair< Iterator, Iterator > EqualRange(const KeyType &key)
Iterator InsertUnique(const T &value, size_t hashCode)
bool Contains(const KeyType &key) const
std::pair< Iterator, bool > EmplaceOrAssign(const KeyType &key, TArgs &&... args)
ElementHandle Extract(ConstIterator pos)
Iterator InsertUnique(const T &value)
ConstLocalIterator begin(uinteger bucketNumber) const
ALIB_WARNINGS_RESTORE LocalIterator Erase(ConstLocalIterator pos)
static constexpr bool CachedHashCodes
ConstIterator cbegin() const
float MaxLoadFactor() const
std::pair< ConstIterator, ConstIterator > EqualRange(const KeyType &key) const
std::pair< Iterator, bool > InsertIfNotExistent(const T &value)
void SetAllocatorPostConstruction(MonoAllocator *pAllocator)
Iterator Insert(const T &value)
LocalIterator begin(uinteger bucketNumber)
bool EraseUnique(const KeyType &key, size_t hashCode)
Iterator Insert(ElementHandle &handle)
std::pair< Iterator, bool > EmplaceIfNotExistent(TArgs &&... args)
HashTable(MonoAllocator *pAllocator, TSharedRecycler &pRecycler, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
Iterator Erase(ConstIterator pos)
integer Erase(const KeyType &key, size_t hashCode)
typename base::TMapped MappedType
ConstLocalIterator cend(uinteger bucketNumber) const
Iterator Find(const KeyType &key)
LocalIterator Erase(ConstLocalIterator start, ConstLocalIterator end)
void MaxLoadFactor(float newMaxLoadFactor)
ConstIterator begin() const
Iterator Insert(const T &value, size_t hashCode)
uinteger BucketCount() const
Iterator Emplace(TArgs &&... args)
Iterator InsertIfNotExistent(ElementHandle &handle)
std::pair< Iterator, bool > InsertIfNotExistent(T &&value, size_t hashCode)
void ReserveRecyclables(integer expected, lang::ValueReference reference)
MonoAllocator * GetAllocator() const
void Reserve(integer expected, lang::ValueReference reference)
uinteger BucketSize(uinteger bucketNumber) const
#define ALIB_WARNINGS_RESTORE
#define ATMP_EQ( T, TEqual)
#define ALIB_ASSERT_ERROR(cond,...)
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
#define ALIB_CALLER_PRUNED
#define ALIB_WARNINGS_IGNORE_NOTHING_RETURNED
#define ALIB_ASSERT(cond)
#define ATMP_T_IF(T, Cond)
@ Relative
Referring to a relative value.
@ Absolute
Referring to an absolute value.
static ALIB_FORCE_INLINE void Destruct(T *object)
AString DbgDumpHashtable(const THashtable &hashtable)
AString DbgDumpDistribution(const THashtable &hashtable, bool detailedBucketList)
std::tuple< double, double, integer, integer > DbgGetHashTableDistribution(const THashtable &hashtable)
lang::uinteger uinteger
Type alias in namespace alib.
constexpr CString NewLine()
lang::integer integer
Type alias in namespace alib.
TElement * removeRangeBehind(TElement *last)
void next(SidiNodeBase *p)
HashTableRecycler< T, TStored, TKey, THashCaching, TRecycling >::type recycler
typename HashTableElementType< T, TStored, TKey, THashCaching >::type Element