Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
HashTable.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/ContainerAllocationPolicies.h"
6#include "Containers/ContainerAllocationPolicies.h"
7#include "CoreTypes.h"
8#include "HAL/PlatformAtomics.h"
9#include "HAL/PlatformCrt.h"
10#include "HAL/UnrealMemory.h"
11#include "Math/UnrealMathUtility.h"
12#include "Misc/AssertionMacros.h"
13#include "Serialization/MemoryImageWriter.h"
14#include "Serialization/MemoryImageWriter.h"
15#include "Serialization/MemoryLayout.h"
16#include "Templates/ChooseClass.h"
17#include "Templates/UnrealTemplate.h"
18
19#include <initializer_list>
20
24class FSHA1;
25
26static FORCEINLINE uint32 MurmurFinalize32(uint32 Hash)
27{
28 Hash ^= Hash >> 16;
29 Hash *= 0x85ebca6b;
30 Hash ^= Hash >> 13;
31 Hash *= 0xc2b2ae35;
32 Hash ^= Hash >> 16;
33 return Hash;
34}
35
36static FORCEINLINE uint64 MurmurFinalize64(uint64 Hash)
37{
38 Hash ^= Hash >> 33;
39 Hash *= 0xff51afd7ed558ccdull;
40 Hash ^= Hash >> 33;
41 Hash *= 0xc4ceb9fe1a85ec53ull;
42 Hash ^= Hash >> 33;
43 return Hash;
44}
45
46static FORCEINLINE uint32 Murmur32( std::initializer_list< uint32 > InitList )
47{
48 uint32 Hash = 0;
49 for( auto Element : InitList )
50 {
51 Element *= 0xcc9e2d51;
52 Element = ( Element << 15 ) | ( Element >> (32 - 15) );
53 Element *= 0x1b873593;
54
55 Hash ^= Element;
56 Hash = ( Hash << 13 ) | ( Hash >> (32 - 13) );
57 Hash = Hash * 5 + 0xe6546b64;
58 }
59
60 return MurmurFinalize32(Hash);
61}
62
63/*-----------------------------------------------------------------------------
64 Statically sized hash table, used to index another data structure.
65 Vastly simpler and faster than TMap.
66
67 Example find:
68
69 uint16 Key = HashFunction( ID );
70 for( uint16 i = HashTable.First( Key ); HashTable.IsValid( i ); i = HashTable.Next( i ) )
71 {
72 if( Array[i].ID == ID )
73 {
74 return Array[i];
75 }
76 }
77-----------------------------------------------------------------------------*/
78template< uint16 HashSize, uint16 IndexSize >
80{
81public:
84
85
86 void Clear();
87
88 // Functions used to search
89 uint16 First( uint16 Key ) const;
90 uint16 Next( uint16 Index ) const;
91 bool IsValid( uint16 Index ) const;
92
93 void Add( uint16 Key, uint16 Index );
94 void Remove( uint16 Key, uint16 Index );
95
96protected:
97 uint16 Hash[ HashSize ];
98 uint16 NextIndex[ IndexSize ];
99};
100
101template< uint16 HashSize, uint16 IndexSize >
103{
104 static_assert( ( HashSize & (HashSize - 1) ) == 0, "Hash size must be power of 2" );
105 static_assert( IndexSize - 1 < 0xffff, "Index 0xffff is reserved" );
106 Clear();
107}
108
109template< uint16 HashSize, uint16 IndexSize >
111{
112 static_assert((HashSize & (HashSize - 1)) == 0, "Hash size must be power of 2");
113 static_assert(IndexSize - 1 < 0xffff, "Index 0xffff is reserved");
114}
115
116template< uint16 HashSize, uint16 IndexSize >
117FORCEINLINE void TStaticHashTable< HashSize, IndexSize >::Clear()
118{
119 FMemory::Memset( Hash, 0xff, HashSize * 2 );
120}
121
122// First in hash chain
123template< uint16 HashSize, uint16 IndexSize >
124FORCEINLINE uint16 TStaticHashTable< HashSize, IndexSize >::First( uint16 Key ) const
125{
126 Key &= HashSize - 1;
127 return Hash[ Key ];
128}
129
130// Next in hash chain
131template< uint16 HashSize, uint16 IndexSize >
132FORCEINLINE uint16 TStaticHashTable< HashSize, IndexSize >::Next( uint16 Index ) const
133{
134 checkSlow( Index < IndexSize );
135 return NextIndex[ Index ];
136}
137
138template< uint16 HashSize, uint16 IndexSize >
139FORCEINLINE bool TStaticHashTable< HashSize, IndexSize >::IsValid( uint16 Index ) const
140{
141 return Index != 0xffff;
142}
143
144template< uint16 HashSize, uint16 IndexSize >
145FORCEINLINE void TStaticHashTable< HashSize, IndexSize >::Add( uint16 Key, uint16 Index )
146{
147 checkSlow( Index < IndexSize );
148
149 Key &= HashSize - 1;
150 NextIndex[ Index ] = Hash[ Key ];
151 Hash[ Key ] = Index;
152}
153
154template< uint16 HashSize, uint16 IndexSize >
155inline void TStaticHashTable< HashSize, IndexSize >::Remove( uint16 Key, uint16 Index )
156{
157 checkSlow( Index < IndexSize );
158
159 Key &= HashSize - 1;
160
161 if( Hash[Key] == Index )
162 {
163 // Head of chain
164 Hash[Key] = NextIndex[ Index ];
165 }
166 else
167 {
168 for( uint16 i = Hash[Key]; IsValid(i); i = NextIndex[i] )
169 {
170 if( NextIndex[i] == Index )
171 {
172 // Next = Next->Next
173 NextIndex[i] = NextIndex[ Index ];
174 break;
175 }
176 }
177 }
178}
179
180/*-----------------------------------------------------------------------------
181 Dynamically sized hash table, used to index another data structure.
182 Vastly simpler and faster than TMap.
183
184 Example find:
185
186 uint32 Key = HashFunction( ID );
187 for( uint32 i = HashTable.First( Key ); HashTable.IsValid( i ); i = HashTable.Next( i ) )
188 {
189 if( Array[i].ID == ID )
190 {
191 return Array[i];
192 }
193 }
194-----------------------------------------------------------------------------*/
196{
197public:
198 FHashTable( uint32 InHashSize = 1024, uint32 InIndexSize = 0 );
199 FHashTable( const FHashTable& Other );
200 ~FHashTable();
201
202 void Clear();
203 void Clear( uint32 InHashSize, uint32 InIndexSize = 0 );
204 void Free();
205 void Resize( uint32 NewIndexSize );
206
207 // Functions used to search
208 uint32 First( uint32 Key ) const;
209 uint32 Next( uint32 Index ) const;
210 bool IsValid( uint32 Index ) const;
211
212 void Add( uint32 Key, uint32 Index );
213 void Add_Concurrent( uint32 Key, uint32 Index );
214 void Remove( uint32 Key, uint32 Index );
215
216 // Average # of compares per search
217 float AverageSearch() const;
218
219protected:
220 // Avoids allocating hash until first add
221 static uint32 EmptyHash[1];
222
223 uint32 HashSize;
224 uint32 HashMask;
225 uint32 IndexSize;
226
227 uint32* Hash;
228 uint32* NextIndex;
229};
230
231
232FORCEINLINE FHashTable::FHashTable( uint32 InHashSize, uint32 InIndexSize )
233 : HashSize( InHashSize )
234 , HashMask( 0 )
235 , IndexSize( InIndexSize )
236 , Hash( EmptyHash )
237 , NextIndex( nullptr )
238{
239 check( HashSize > 0 );
240 check( FMath::IsPowerOfTwo( HashSize ) );
241
242 if( IndexSize )
243 {
244 HashMask = HashSize - 1;
245
246 Hash = new uint32[ HashSize ];
247 NextIndex = new uint32[ IndexSize ];
248
250 }
251}
252
254 : HashSize( Other.HashSize )
255 , HashMask( Other.HashMask )
256 , IndexSize( Other.IndexSize )
257 , Hash( EmptyHash )
258{
259 if( IndexSize )
260 {
261 Hash = new uint32[ HashSize ];
262 NextIndex = new uint32[ IndexSize ];
263
266 }
267}
268
270{
271 Free();
272}
273
275{
276 if( IndexSize )
277 {
279 }
280}
281
282FORCEINLINE void FHashTable::Clear( uint32 InHashSize, uint32 InIndexSize )
283{
284 Free();
285
286 HashSize = InHashSize;
287 IndexSize = InIndexSize;
288
289 check( HashSize > 0 );
290 check( FMath::IsPowerOfTwo( HashSize ) );
291
292 if( IndexSize )
293 {
294 HashMask = HashSize - 1;
295
296 Hash = new uint32[ HashSize ];
297 NextIndex = new uint32[ IndexSize ];
298
300 }
301}
302
304{
305 if( IndexSize )
306 {
307 HashMask = 0;
308 IndexSize = 0;
309
310 delete[] Hash;
311 Hash = EmptyHash;
312
313 delete[] NextIndex;
314 NextIndex = nullptr;
315 }
316}
317
318// First in hash chain
319FORCEINLINE uint32 FHashTable::First( uint32 Key ) const
320{
321 Key &= HashMask;
322 return Hash[ Key ];
323}
324
325// Next in hash chain
326FORCEINLINE uint32 FHashTable::Next( uint32 Index ) const
327{
328 checkSlow( Index < IndexSize );
329 checkSlow( NextIndex[Index] != Index ); // check for corrupt tables
330 return NextIndex[ Index ];
331}
332
333FORCEINLINE bool FHashTable::IsValid( uint32 Index ) const
334{
335 return Index != ~0u;
336}
337
338FORCEINLINE void FHashTable::Add( uint32 Key, uint32 Index )
339{
340 if( Index >= IndexSize )
341 {
342 Resize( FMath::Max< uint32 >( 32u, FMath::RoundUpToPowerOfTwo( Index + 1 ) ) );
343 }
344
345 Key &= HashMask;
346 NextIndex[ Index ] = Hash[ Key ];
347 Hash[ Key ] = Index;
348}
349
350// Safe for many threads to add concurrently.
351// Not safe to search the table while other threads are adding.
352// Will not resize. Only use for presized tables.
353FORCEINLINE void FHashTable::Add_Concurrent( uint32 Key, uint32 Index )
354{
355 check( Index < IndexSize );
356
357 Key &= HashMask;
358 NextIndex[ Index ] = FPlatformAtomics::InterlockedExchange( (int32*)&Hash[ Key ], Index );
359}
360
361inline void FHashTable::Remove( uint32 Key, uint32 Index )
362{
363 if( Index >= IndexSize )
364 {
365 return;
366 }
367
368 Key &= HashMask;
369
370 if( Hash[Key] == Index )
371 {
372 // Head of chain
373 Hash[Key] = NextIndex[ Index ];
374 }
375 else
376 {
377 for( uint32 i = Hash[Key]; IsValid(i); i = NextIndex[i] )
378 {
379 if( NextIndex[i] == Index )
380 {
381 // Next = Next->Next
382 NextIndex[i] = NextIndex[ Index ];
383 break;
384 }
385 }
386 }
387}
388
389template<typename InAllocator>
391{
392public:
393 using Allocator = InAllocator;
394
395 using ElementAllocatorType = typename TChooseClass<
396 Allocator::NeedsElementType,
397 typename Allocator::template ForElementType<uint32>,
398 typename Allocator::ForAnyElementType
399 >::Result;
400
401 explicit THashTable(uint32 InHashSize = 1024, uint32 InIndexSize = 0);
402 THashTable(const THashTable& Other) = delete;
404 ~THashTable();
405
406 THashTable& operator=(const THashTable& Other) = delete;
408
410 void Clear();
411 void Resize(uint32 NewIndexSize);
412
413 const uint32* GetNextIndices() const { return (uint32*)NextIndex.GetAllocation(); }
414
415 // Functions used to search
416 uint32 First(uint16 Key) const;
417 uint32 Next(uint32 Index) const;
418 bool IsValid(uint32 Index) const;
419
420 void Add(uint16 Key, uint32 Index);
421 void Remove(uint16 Key, uint32 Index);
422
423private:
424 FORCEINLINE uint32 HashAt(uint32 Index) const { return ((uint32*)Hash.GetAllocation())[Index]; }
425 FORCEINLINE uint32 NextIndexAt(uint32 Index) const { return ((uint32*)NextIndex.GetAllocation())[Index]; }
426 FORCEINLINE uint32& HashAt(uint32 Index) { return ((uint32*)Hash.GetAllocation())[Index]; }
427 FORCEINLINE uint32& NextIndexAt(uint32 Index) { return ((uint32*)NextIndex.GetAllocation())[Index]; }
428
429 ElementAllocatorType Hash;
430 ElementAllocatorType NextIndex;
431 uint32 HashMask;
432 uint32 IndexSize;
433
434public:
436 {
438 {
443 }
444 else
445 {
446 check(false);
447 }
448 }
449
450 void CopyUnfrozen(const FMemoryUnfreezeContent& Context, void* Dst) const
451 {
453 {
454 THashTable* DstTable = new(Dst) THashTable(this->HashMask + 1u, this->IndexSize);
455 FMemory::Memcpy(DstTable->Hash.GetAllocation(), this->Hash.GetAllocation(), (this->HashMask + 1u) * 4);
457 }
458 else
459 {
460 new(Dst) THashTable();
461 }
462 }
463};
464
465template<typename InAllocator>
466FORCEINLINE THashTable<InAllocator>::THashTable(uint32 InHashSize, uint32 InIndexSize)
467 : HashMask(InHashSize - 1u)
468 , IndexSize(InIndexSize)
469{
470 check(InHashSize > 0u && InHashSize <= 0x10000);
471 check(FMath::IsPowerOfTwo(InHashSize));
472
473 Hash.ResizeAllocation(0, InHashSize, sizeof(uint32));
474 FMemory::Memset(Hash.GetAllocation(), 0xff, InHashSize * 4);
475
476 if (IndexSize)
477 {
478 NextIndex.ResizeAllocation(0, IndexSize, sizeof(uint32));
479 }
480}
481
482template<typename InAllocator>
484{
485}
486
487template<typename InAllocator>
488THashTable<InAllocator>& THashTable<InAllocator>::MoveAssign(THashTable&& Other)
489{
490 Hash.MoveToEmpty(Other.Hash);
491 NextIndex.MoveToEmpty(Other.NextIndex);
492 HashMask = Other.HashMask;
493 IndexSize = Other.IndexSize;
494 Other.HashMask = 0u;
495 Other.IndexSize = 0u;
496 return *this;
497}
498
499template<typename InAllocator>
500FORCEINLINE void THashTable<InAllocator>::Clear()
501{
502 if (IndexSize)
503 {
504 FMemory::Memset(Hash.GetAllocation(), 0xff, (HashMask + 1u) * 4);
505 }
506}
507
508// First in hash chain
509template<typename InAllocator>
510FORCEINLINE uint32 THashTable<InAllocator>::First(uint16 Key) const
511{
512 Key &= HashMask;
513 return HashAt(Key);
514}
515
516// Next in hash chain
517template<typename InAllocator>
518FORCEINLINE uint32 THashTable<InAllocator>::Next(uint32 Index) const
519{
520 checkSlow(Index < IndexSize);
521 const uint32 Next = NextIndexAt(Index);
522 checkSlow(Next != Index); // check for corrupt tables
523 return Next;
524}
525
526template<typename InAllocator>
527FORCEINLINE bool THashTable<InAllocator>::IsValid(uint32 Index) const
528{
529 return Index != ~0u;
530}
531
532template<typename InAllocator>
533FORCEINLINE void THashTable<InAllocator>::Add(uint16 Key, uint32 Index)
534{
535 if (Index >= IndexSize)
536 {
537 Resize(FMath::Max<uint32>(32u, FMath::RoundUpToPowerOfTwo(Index + 1)));
538 }
539
540 Key &= HashMask;
541 NextIndexAt(Index) = HashAt(Key);
542 HashAt(Key) = Index;
543}
544
545template<typename InAllocator>
546inline void THashTable<InAllocator>::Remove(uint16 Key, uint32 Index)
547{
548 if (Index >= IndexSize)
549 {
550 return;
551 }
552
553 Key &= HashMask;
554 if (HashAt(Key) == Index)
555 {
556 // Head of chain
557 HashAt(Key) = NextIndexAt(Index);
558 }
559 else
560 {
561 for (uint32 i = HashAt(Key); IsValid(i); i = NextIndexAt(i))
562 {
563 if (NextIndexAt(i) == Index)
564 {
565 // Next = Next->Next
566 NextIndexAt(i) = NextIndexAt(Index);
567 break;
568 }
569 }
570 }
571}
572
573template<typename InAllocator>
574void THashTable<InAllocator>::Resize(uint32 NewIndexSize)
575{
576 if (NewIndexSize != IndexSize)
577 {
578 NextIndex.ResizeAllocation(IndexSize, NewIndexSize, sizeof(uint32));
579 IndexSize = NewIndexSize;
580 }
581}
582
583namespace Freeze
584{
585 template<typename InAllocator>
586 void IntrinsicWriteMemoryImage(FMemoryImageWriter& Writer, const THashTable<InAllocator>& Object, const FTypeLayoutDesc&)
587 {
589 }
590
591 template<typename InAllocator>
592 uint32 IntrinsicUnfrozenCopy(const FMemoryUnfreezeContent& Context, const THashTable<InAllocator>& Object, void* OutDst)
593 {
595 return sizeof(Object);
596 }
597
598 template<typename InAllocator>
599 uint32 IntrinsicAppendHash(const THashTable<InAllocator>* DummyObject, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams, FSHA1& Hasher)
600 {
602 }
603
604 template<typename InAllocator>
605 uint32 IntrinsicGetTargetAlignment(const THashTable<InAllocator>* DummyObject, const FTypeLayoutDesc& TypeDesc, const FPlatformTypeLayoutParameters& LayoutParams)
606 {
607 // Assume alignment of hash-table is drive by pointer
609 }
610}
611
#define checkSlow(expr)
#define check(expr)
static FORCEINLINE uint32 Murmur32(std::initializer_list< uint32 > InitList)
Definition HashTable.h:46
static FORCEINLINE uint64 MurmurFinalize64(uint64 Hash)
Definition HashTable.h:36
static FORCEINLINE uint32 MurmurFinalize32(uint32 Hash)
Definition HashTable.h:26
#define DECLARE_TEMPLATE_INTRINSIC_TYPE_LAYOUT(TemplatePrefix, T)
#define FORCEINLINE
Definition Platform.h:644
FWindowsPlatformAtomics FPlatformAtomics
uint32 HashSize
Definition HashTable.h:223
float AverageSearch() const
void Clear()
Definition HashTable.h:274
static uint32 EmptyHash[1]
Definition HashTable.h:221
uint32 Next(uint32 Index) const
Definition HashTable.h:326
void Clear(uint32 InHashSize, uint32 InIndexSize=0)
Definition HashTable.h:282
uint32 * NextIndex
Definition HashTable.h:228
void Free()
Definition HashTable.h:303
uint32 * Hash
Definition HashTable.h:227
void Add(uint32 Key, uint32 Index)
Definition HashTable.h:338
void Remove(uint32 Key, uint32 Index)
Definition HashTable.h:361
FHashTable(uint32 InHashSize=1024, uint32 InIndexSize=0)
Definition HashTable.h:232
uint32 HashMask
Definition HashTable.h:224
void Resize(uint32 NewIndexSize)
uint32 IndexSize
Definition HashTable.h:225
void Add_Concurrent(uint32 Key, uint32 Index)
Definition HashTable.h:353
bool IsValid(uint32 Index) const
Definition HashTable.h:333
uint32 First(uint32 Key) const
Definition HashTable.h:319
FHashTable(const FHashTable &Other)
Definition HashTable.h:253
THashTable(uint32 InHashSize=1024, uint32 InIndexSize=0)
Definition HashTable.h:466
THashTable & MoveAssign(THashTable &&Other)
Definition HashTable.h:488
uint32 First(uint16 Key) const
Definition HashTable.h:510
FORCEINLINE uint32 HashAt(uint32 Index) const
Definition HashTable.h:424
THashTable(const THashTable &Other)=delete
THashTable & operator=(const THashTable &Other)=delete
void CopyUnfrozen(const FMemoryUnfreezeContent &Context, void *Dst) const
Definition HashTable.h:450
bool IsValid(uint32 Index) const
Definition HashTable.h:527
ElementAllocatorType Hash
Definition HashTable.h:429
void Remove(uint16 Key, uint32 Index)
Definition HashTable.h:546
FORCEINLINE uint32 & NextIndexAt(uint32 Index)
Definition HashTable.h:427
THashTable(THashTable &&Other)
Definition HashTable.h:403
const uint32 * GetNextIndices() const
Definition HashTable.h:413
uint32 HashMask
Definition HashTable.h:431
void Clear()
Definition HashTable.h:500
uint32 IndexSize
Definition HashTable.h:432
ElementAllocatorType NextIndex
Definition HashTable.h:430
FORCEINLINE uint32 NextIndexAt(uint32 Index) const
Definition HashTable.h:425
void Resize(uint32 NewIndexSize)
Definition HashTable.h:574
uint32 Next(uint32 Index) const
Definition HashTable.h:518
FORCEINLINE uint32 & HashAt(uint32 Index)
Definition HashTable.h:426
void WriteMemoryImage(FMemoryImageWriter &Writer) const
Definition HashTable.h:435
THashTable & operator=(THashTable &&Other)
Definition HashTable.h:407
void Add(uint16 Key, uint32 Index)
Definition HashTable.h:533
uint16 Next(uint16 Index) const
Definition HashTable.h:132
uint16 NextIndex[IndexSize]
Definition HashTable.h:98
TStaticHashTable(ENoInit)
Definition HashTable.h:110
uint16 Hash[HashSize]
Definition HashTable.h:97
bool IsValid(uint16 Index) const
Definition HashTable.h:139
void Add(uint16 Key, uint16 Index)
Definition HashTable.h:145
uint16 First(uint16 Key) const
Definition HashTable.h:124
void Remove(uint16 Key, uint16 Index)
Definition HashTable.h:155
uint32 IntrinsicAppendHash(const THashTable< InAllocator > *DummyObject, const FTypeLayoutDesc &TypeDesc, const FPlatformTypeLayoutParameters &LayoutParams, FSHA1 &Hasher)
Definition HashTable.h:599
uint32 IntrinsicUnfrozenCopy(const FMemoryUnfreezeContent &Context, const THashTable< InAllocator > &Object, void *OutDst)
Definition HashTable.h:592
void IntrinsicWriteMemoryImage(FMemoryImageWriter &Writer, const THashTable< InAllocator > &Object, const FTypeLayoutDesc &)
Definition HashTable.h:586
uint32 IntrinsicGetTargetAlignment(const THashTable< InAllocator > *DummyObject, const FTypeLayoutDesc &TypeDesc, const FPlatformTypeLayoutParameters &LayoutParams)
Definition HashTable.h:605
Definition json.hpp:4518
static FORCEINLINE void * Memset(void *Dest, uint8 Char, SIZE_T Count)
static FORCEINLINE void * Memcpy(void *Dest, const void *Src, SIZE_T Count)