UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
HashTable.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
6#include "CoreTypes.h"
8#include "HAL/PlatformCrt.h"
9#include "HAL/UnrealMemory.h"
15
16#include <initializer_list>
17
21class FSHA1;
22
24{
25 Hash ^= Hash >> 16;
26 Hash *= 0x85ebca6b;
27 Hash ^= Hash >> 13;
28 Hash *= 0xc2b2ae35;
29 Hash ^= Hash >> 16;
30 return Hash;
31}
32
34{
35 Hash ^= Hash >> 33;
36 Hash *= 0xff51afd7ed558ccdull;
37 Hash ^= Hash >> 33;
38 Hash *= 0xc4ceb9fe1a85ec53ull;
39 Hash ^= Hash >> 33;
40 return Hash;
41}
42
43inline uint32 Murmur32( std::initializer_list< uint32 > InitList )
44{
45 uint32 Hash = 0;
46 for( auto Element : InitList )
47 {
48 Element *= 0xcc9e2d51;
49 Element = ( Element << 15 ) | ( Element >> (32 - 15) );
50 Element *= 0x1b873593;
51
52 Hash ^= Element;
53 Hash = ( Hash << 13 ) | ( Hash >> (32 - 13) );
54 Hash = Hash * 5 + 0xe6546b64;
55 }
56
57 return MurmurFinalize32( Hash );
58}
59
60inline uint64 Murmur64( std::initializer_list< uint64 > InitList )
61{
62 uint64 Hash = 0;
63 for( auto Element : InitList )
64 {
65 Element *= 0x87c37b91114253d5ull;
66 Element = ( Element << 31 ) | ( Element >> (64 - 31) );
67 Element *= 0x4cf5ad432745937full;
68
69 Hash ^= Element;
70 Hash = ( Hash << 27 ) | ( Hash >> (64 - 27) );
71 Hash = Hash * 5 + 0x52dce729;
72 }
73
74 return MurmurFinalize64( Hash );
75}
76
77/*-----------------------------------------------------------------------------
78 Statically sized hash table, used to index another data structure.
79 Vastly simpler and faster than TMap.
80
81 Example find:
82
83 uint16 Key = HashFunction( ID );
84 for( uint16 i = HashTable.First( Key ); HashTable.IsValid( i ); i = HashTable.Next( i ) )
85 {
86 if( Array[i].ID == ID )
87 {
88 return Array[i];
89 }
90 }
91-----------------------------------------------------------------------------*/
92template< uint16 HashSize, uint16 IndexSize >
94{
95public:
98
99
100 void Clear();
101
102 // Functions used to search
103 uint16 First( uint16 Key ) const;
105 bool IsValid( uint16 Index ) const;
106
107 void Add( uint16 Key, uint16 Index );
108 void Remove( uint16 Key, uint16 Index );
109
110protected:
111 uint16 Hash[ HashSize ];
112 uint16 NextIndex[ IndexSize ];
113};
114
115template< uint16 HashSize, uint16 IndexSize >
117{
118 static_assert( ( HashSize & (HashSize - 1) ) == 0, "Hash size must be power of 2" );
119 static_assert( IndexSize - 1 < 0xffff, "Index 0xffff is reserved" );
120 Clear();
121}
122
123template< uint16 HashSize, uint16 IndexSize >
125{
126 static_assert((HashSize & (HashSize - 1)) == 0, "Hash size must be power of 2");
127 static_assert(IndexSize - 1 < 0xffff, "Index 0xffff is reserved");
128}
129
130template< uint16 HashSize, uint16 IndexSize >
135
136// First in hash chain
137template< uint16 HashSize, uint16 IndexSize >
139{
140 Key &= HashSize - 1;
141 return Hash[ Key ];
142}
143
144// Next in hash chain
145template< uint16 HashSize, uint16 IndexSize >
147{
148 checkSlow( Index < IndexSize );
149 return NextIndex[ Index ];
150}
151
152template< uint16 HashSize, uint16 IndexSize >
157
158template< uint16 HashSize, uint16 IndexSize >
160{
161 checkSlow( Index < IndexSize );
162
163 Key &= HashSize - 1;
164 NextIndex[ Index ] = Hash[ Key ];
165 Hash[ Key ] = Index;
166}
167
168template< uint16 HashSize, uint16 IndexSize >
170{
171 checkSlow( Index < IndexSize );
172
173 Key &= HashSize - 1;
174
175 if( Hash[Key] == Index )
176 {
177 // Head of chain
178 Hash[Key] = NextIndex[ Index ];
179 }
180 else
181 {
182 for( uint16 i = Hash[Key]; IsValid(i); i = NextIndex[i] )
183 {
184 if( NextIndex[i] == Index )
185 {
186 // Next = Next->Next
187 NextIndex[i] = NextIndex[ Index ];
188 break;
189 }
190 }
191 }
192}
193
194/*-----------------------------------------------------------------------------
195 Dynamically sized hash table, used to index another data structure.
196 Vastly simpler and faster than TMap.
197
198 Example find:
199
200 uint32 Key = HashFunction( ID );
201 for( uint32 i = HashTable.First( Key ); HashTable.IsValid( i ); i = HashTable.Next( i ) )
202 {
203 if( Array[i].ID == ID )
204 {
205 return Array[i];
206 }
207 }
208-----------------------------------------------------------------------------*/
210{
211public:
215 ~FHashTable();
216
217 void Clear();
219 void Free();
225 inline uint32 GetIndexSize() const { return IndexSize; }
226 inline uint32 GetHashSize() const { return HashSize; }
228
229 // Functions used to search
230 uint32 First( uint32 Key ) const;
231 uint32 Next( uint32 Index ) const;
232 bool IsValid( uint32 Index ) const;
233
234 void Add( uint32 Key, uint32 Index );
235 // Safe to call concurrently with other threads calling Add_Concurrent with different values for Index.
236 void Add_Concurrent( uint32 Key, uint32 Index );
237 void Remove( uint32 Key, uint32 Index );
238
239 // Average # of compares per search
240 CORE_API float AverageSearch() const;
241
244
245protected:
246 // Avoids allocating hash until first add
247 CORE_API static uint32 EmptyHash[1];
248
252
255};
256
257
259 : HashSize( InHashSize )
260 , HashMask( 0 )
261 , IndexSize( InIndexSize )
262 , Hash( EmptyHash )
263 , NextIndex( nullptr )
264{
265 check( HashSize > 0 );
267
268 if( IndexSize )
269 {
270 HashMask = HashSize - 1;
271
272 Hash = new uint32[ HashSize ];
273 NextIndex = new uint32[ IndexSize ];
274
275 FMemory::Memset( Hash, 0xff, HashSize * 4 );
276 }
277}
278
280 : HashSize( Other.HashSize )
281 , HashMask( Other.HashMask )
282 , IndexSize( Other.IndexSize )
283 , Hash( EmptyHash )
284{
285 if( IndexSize )
286 {
287 Hash = new uint32[ HashSize ];
288 NextIndex = new uint32[ IndexSize ];
289
290 FMemory::Memcpy( Hash, Other.Hash, HashSize * 4 );
291 FMemory::Memcpy( NextIndex, Other.NextIndex, IndexSize * 4 );
292 }
293}
294
296 : HashSize( Other.HashSize )
297 , HashMask( Other.HashMask )
298 , IndexSize( Other.IndexSize )
299 , Hash(Other.Hash)
300 , NextIndex(Other.NextIndex)
301{
302 Other.HashSize = 0;
303 Other.HashMask = 0;
304 Other.IndexSize = 0;
305 Other.Hash = EmptyHash;
306 Other.NextIndex = nullptr;
307}
308
310{
311 Free();
312 HashSize = Other.HashSize;
313 HashMask = Other.HashMask;
314 IndexSize = Other.IndexSize;
315
316 if (IndexSize)
317 {
318 Hash = new uint32[HashSize];
320
322 FMemory::Memcpy(NextIndex, Other.NextIndex, IndexSize * 4);
323 }
324 return *this;
325}
326
328{
329 Free();
330
331 HashSize = Other.HashSize;
332 HashMask = Other.HashMask;
333 IndexSize = Other.IndexSize;
334 Hash = Other.Hash;
335 NextIndex = Other.NextIndex;
336
337 Other.HashSize = 0;
338 Other.HashMask = 0;
339 Other.IndexSize = 0;
340 Other.Hash = EmptyHash;
341 Other.NextIndex = nullptr;
342 return *this;
343}
344
349
350inline void FHashTable::Clear()
351{
352 if( IndexSize )
353 {
354 FMemory::Memset( Hash, 0xff, HashSize * 4 );
355 }
356}
357
359{
360 Free();
361
364
365 check( HashSize > 0 );
367
368 if( IndexSize )
369 {
370 HashMask = HashSize - 1;
371
372 Hash = new uint32[ HashSize ];
373 NextIndex = new uint32[ IndexSize ];
374
375 FMemory::Memset( Hash, 0xff, HashSize * 4 );
376 }
377}
378
379inline void FHashTable::Free()
380{
381 if( IndexSize )
382 {
383 HashMask = 0;
384 IndexSize = 0;
385
386 delete[] Hash;
387 Hash = EmptyHash;
388
389 delete[] NextIndex;
390 NextIndex = nullptr;
391 }
392}
393
394// First in hash chain
395inline uint32 FHashTable::First( uint32 Key ) const
396{
397 Key &= HashMask;
398 return Hash[ Key ];
399}
400
401// Next in hash chain
403{
405 checkSlow( NextIndex[Index] != Index ); // check for corrupt tables
406 return NextIndex[ Index ];
407}
408
410{
411 return Index != ~0u;
412}
413
415{
416 if( Index >= IndexSize )
417 {
418 Resize( FMath::Max< uint32 >( 32u, FMath::RoundUpToPowerOfTwo( Index + 1 ) ) );
419 }
420
421 Key &= HashMask;
422 NextIndex[ Index ] = Hash[ Key ];
423 Hash[ Key ] = Index;
424}
425
426// Safe for many threads to add concurrently.
427// Not safe to search the table while other threads are adding.
428// Will not resize. Only use for presized tables.
430{
431 check( Index < IndexSize );
432
433 Key &= HashMask;
434 NextIndex[ Index ] = FPlatformAtomics::InterlockedExchange( (int32*)&Hash[ Key ], Index );
435}
436
438{
439 if( Index >= IndexSize )
440 {
441 return;
442 }
443
444 Key &= HashMask;
445
446 if( Hash[Key] == Index )
447 {
448 // Head of chain
449 Hash[Key] = NextIndex[ Index ];
450 }
451 else
452 {
453 for( uint32 i = Hash[Key]; IsValid(i); i = NextIndex[i] )
454 {
455 if( NextIndex[i] == Index )
456 {
457 // Next = Next->Next
458 NextIndex[i] = NextIndex[ Index ];
459 break;
460 }
461 }
462 }
463}
464
465template<typename InAllocator>
467{
468public:
470
471 using ElementAllocatorType = std::conditional_t<
472 Allocator::NeedsElementType,
473 typename Allocator::template ForElementType<uint32>,
474 typename Allocator::ForAnyElementType
475 >;
476
477 explicit THashTable(uint32 InHashSize = 1024, uint32 InIndexSize = 0);
478 THashTable(const THashTable& Other) = delete;
480 ~THashTable();
481
484
486 void Clear();
488
489 const uint32* GetNextIndices() const { return (uint32*)NextIndex.GetAllocation(); }
490
491 // Functions used to search
492 uint32 First(uint16 Key) const;
493 uint32 Next(uint32 Index) const;
494 bool IsValid(uint32 Index) const;
495
496 void Add(uint16 Key, uint32 Index);
497 void Remove(uint16 Key, uint32 Index);
498
499private:
500 UE_FORCEINLINE_HINT uint32 HashAt(uint32 Index) const { return ((uint32*)Hash.GetAllocation())[Index]; }
501 UE_FORCEINLINE_HINT uint32 NextIndexAt(uint32 Index) const { return ((uint32*)NextIndex.GetAllocation())[Index]; }
502 UE_FORCEINLINE_HINT uint32& HashAt(uint32 Index) { return ((uint32*)Hash.GetAllocation())[Index]; }
503 UE_FORCEINLINE_HINT uint32& NextIndexAt(uint32 Index) { return ((uint32*)NextIndex.GetAllocation())[Index]; }
504
506 ElementAllocatorType NextIndex;
507 uint32 HashMask;
508 uint32 IndexSize;
509
510public:
512 {
514 {
515 this->Hash.WriteMemoryImage(Writer, StaticGetTypeLayoutDesc<uint32>(), this->HashMask + 1u);
516 this->NextIndex.WriteMemoryImage(Writer, StaticGetTypeLayoutDesc<uint32>(), this->IndexSize);
517 Writer.WriteBytes(this->HashMask);
518 Writer.WriteBytes(this->IndexSize);
519 }
520 else
521 {
522 check(false);
523 }
524 }
525
526 void CopyUnfrozen(const FMemoryUnfreezeContent& Context, void* Dst) const
527 {
529 {
530 THashTable* DstTable = ::new(Dst) THashTable(this->HashMask + 1u, this->IndexSize);
531 FMemory::Memcpy(DstTable->Hash.GetAllocation(), this->Hash.GetAllocation(), (this->HashMask + 1u) * 4);
532 FMemory::Memcpy(DstTable->NextIndex.GetAllocation(), this->NextIndex.GetAllocation(), this->IndexSize * 4);
533 }
534 else
535 {
536 ::new(Dst) THashTable();
537 }
538 }
539};
540
541template<typename InAllocator>
543 : HashMask(InHashSize - 1u)
544 , IndexSize(InIndexSize)
545{
546 check(InHashSize > 0u && InHashSize <= 0x10000);
548
549 Hash.ResizeAllocation(0, InHashSize, sizeof(uint32));
550 FMemory::Memset(Hash.GetAllocation(), 0xff, InHashSize * 4);
551
552 if (IndexSize)
553 {
554 NextIndex.ResizeAllocation(0, IndexSize, sizeof(uint32));
555 }
556}
557
558template<typename InAllocator>
562
563template<typename InAllocator>
565{
566 Hash.MoveToEmpty(Other.Hash);
567 NextIndex.MoveToEmpty(Other.NextIndex);
568 HashMask = Other.HashMask;
569 IndexSize = Other.IndexSize;
570 Other.HashMask = 0u;
571 Other.IndexSize = 0u;
572 return *this;
573}
574
575template<typename InAllocator>
577{
578 if (IndexSize)
579 {
580 FMemory::Memset(Hash.GetAllocation(), 0xff, (HashMask + 1u) * 4);
581 }
582}
583
584// First in hash chain
585template<typename InAllocator>
587{
588 Key &= HashMask;
589 return HashAt(Key);
590}
591
592// Next in hash chain
593template<typename InAllocator>
595{
596 checkSlow(Index < IndexSize);
597 const uint32 Next = NextIndexAt(Index);
598 checkSlow(Next != Index); // check for corrupt tables
599 return Next;
600}
601
602template<typename InAllocator>
607
608template<typename InAllocator>
610{
611 if (Index >= IndexSize)
612 {
613 Resize(FMath::Max<uint32>(32u, FMath::RoundUpToPowerOfTwo(Index + 1)));
614 }
615
616 Key &= HashMask;
617 NextIndexAt(Index) = HashAt(Key);
618 HashAt(Key) = Index;
619}
620
621template<typename InAllocator>
623{
624 if (Index >= IndexSize)
625 {
626 return;
627 }
628
629 Key &= HashMask;
630 if (HashAt(Key) == Index)
631 {
632 // Head of chain
633 HashAt(Key) = NextIndexAt(Index);
634 }
635 else
636 {
637 for (uint32 i = HashAt(Key); IsValid(i); i = NextIndexAt(i))
638 {
639 if (NextIndexAt(i) == Index)
640 {
641 // Next = Next->Next
642 NextIndexAt(i) = NextIndexAt(Index);
643 break;
644 }
645 }
646 }
647}
648
649template<typename InAllocator>
651{
652 if (NewIndexSize != IndexSize)
653 {
654 NextIndex.ResizeAllocation(IndexSize, NewIndexSize, sizeof(uint32));
655 IndexSize = NewIndexSize;
656 }
657}
658
659namespace Freeze
660{
661 template<typename InAllocator>
663 {
664 Object.WriteMemoryImage(Writer);
665 }
666
667 template<typename InAllocator>
669 {
670 Object.CopyUnfrozen(Context, OutDst);
671 return sizeof(Object);
672 }
673
674 template<typename InAllocator>
676 {
677 return AppendHashForNameAndSize(TypeDesc.Name, sizeof(THashTable<InAllocator>), Hasher);
678 }
679
680 template<typename InAllocator>
682 {
683 // Assume alignment of hash-table is drive by pointer
684 return FMath::Min(8u, LayoutParams.MaxFieldAlignment);
685 }
686}
687
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
ENoInit
Definition CoreMiscDefines.h:158
uint64 MurmurFinalize64(uint64 Hash)
Definition HashTable.h:33
uint64 Murmur64(std::initializer_list< uint64 > InitList)
Definition HashTable.h:60
uint32 Murmur32(std::initializer_list< uint32 > InitList)
Definition HashTable.h:43
uint32 MurmurFinalize32(uint32 Hash)
Definition HashTable.h:23
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define DECLARE_TEMPLATE_INTRINSIC_TYPE_LAYOUT(TemplatePrefix, T)
Definition MemoryLayout.h:661
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition HashTable.h:210
uint32 HashSize
Definition HashTable.h:249
void Add_Concurrent(uint32 Key, uint32 Index)
Definition HashTable.h:429
~FHashTable()
Definition HashTable.h:345
void Free()
Definition HashTable.h:379
bool IsValid(uint32 Index) const
Definition HashTable.h:409
CORE_API float AverageSearch() const
Definition HashTable.cpp:47
void Clear()
Definition HashTable.h:350
uint32 * NextIndex
Definition HashTable.h:254
FHashTable & operator=(const FHashTable &Other)
Definition HashTable.h:309
CORE_API void Resize(uint32 NewIndexSize)
Definition HashTable.cpp:7
uint32 * Hash
Definition HashTable.h:253
void Remove(uint32 Key, uint32 Index)
Definition HashTable.h:437
uint32 HashMask
Definition HashTable.h:250
uint32 GetHashSize() const
Definition HashTable.h:226
void Add(uint32 Key, uint32 Index)
Definition HashTable.h:414
uint32 First(uint32 Key) const
Definition HashTable.h:395
uint32 IndexSize
Definition HashTable.h:251
FHashTable(uint32 InHashSize=1024, uint32 InIndexSize=0)
Definition HashTable.h:258
uint32 Next(uint32 Index) const
Definition HashTable.h:402
CORE_API SIZE_T GetAllocatedSize() const
Definition HashTable.cpp:39
uint32 GetIndexSize() const
Definition HashTable.h:225
static CORE_API uint32 EmptyHash[1]
Definition HashTable.h:5
Definition MemoryImageWriter.h:14
CORE_API uint32 WriteBytes(const void *Data, uint32 Size)
Definition MemoryImage.cpp:2143
Definition MemoryImageWriter.h:78
Definition MemoryImage.h:49
Definition SecureHash.h:314
Definition HashTable.h:467
THashTable & MoveAssign(THashTable &&Other)
Definition HashTable.h:564
THashTable(const THashTable &Other)=delete
THashTable & operator=(const THashTable &Other)=delete
void CopyUnfrozen(const FMemoryUnfreezeContent &Context, void *Dst) const
Definition HashTable.h:526
void Remove(uint16 Key, uint32 Index)
Definition HashTable.h:622
std::conditional_t< Allocator::NeedsElementType, typename Allocator::template ForElementType< uint32 >, typename Allocator::ForAnyElementType > ElementAllocatorType
Definition HashTable.h:475
bool IsValid(uint32 Index) const
Definition HashTable.h:603
THashTable(THashTable &&Other)
Definition HashTable.h:479
InAllocator Allocator
Definition HashTable.h:469
const uint32 * GetNextIndices() const
Definition HashTable.h:489
uint32 First(uint16 Key) const
Definition HashTable.h:586
~THashTable()
Definition HashTable.h:559
void Clear()
Definition HashTable.h:576
void Resize(uint32 NewIndexSize)
Definition HashTable.h:650
void Add(uint16 Key, uint32 Index)
Definition HashTable.h:609
THashTable(uint32 InHashSize=1024, uint32 InIndexSize=0)
Definition HashTable.h:542
uint32 Next(uint32 Index) const
Definition HashTable.h:594
void WriteMemoryImage(FMemoryImageWriter &Writer) const
Definition HashTable.h:511
THashTable & operator=(THashTable &&Other)
Definition HashTable.h:483
Definition HashTable.h:94
bool IsValid(uint16 Index) const
Definition HashTable.h:153
void Clear()
Definition HashTable.h:131
uint16 NextIndex[IndexSize]
Definition HashTable.h:112
TStaticHashTable()
Definition HashTable.h:116
uint16 Hash[HashSize]
Definition HashTable.h:111
uint16 Next(uint16 Index) const
Definition HashTable.h:146
TStaticHashTable(ENoInit)
Definition HashTable.h:124
void Add(uint16 Key, uint16 Index)
Definition HashTable.h:159
uint16 First(uint16 Key) const
Definition HashTable.h:138
void Remove(uint16 Key, uint16 Index)
Definition HashTable.h:169
Definition Array.h:3955
CORE_API uint32 AppendHashForNameAndSize(const TCHAR *Name, uint32 Size, FSHA1 &Hasher)
Definition MemoryImage.cpp:568
UE_NODEBUG void IntrinsicWriteMemoryImage(FMemoryImageWriter &Writer, const TArray< T, AllocatorType > &Object, const FTypeLayoutDesc &)
Definition Array.h:3957
UE_NODEBUG uint32 IntrinsicUnfrozenCopy(const FMemoryUnfreezeContent &Context, const TArray< T, AllocatorType > &Object, void *OutDst)
Definition Array.h:3963
UE_NODEBUG uint32 IntrinsicGetTargetAlignment(const TArray< T, AllocatorType > *DummyObject, const FTypeLayoutDesc &TypeDesc, const FPlatformTypeLayoutParameters &LayoutParams)
Definition Array.h:3976
UE_NODEBUG uint32 IntrinsicAppendHash(const TArray< T, AllocatorType > *DummyObject, const FTypeLayoutDesc &TypeDesc, const FPlatformTypeLayoutParameters &LayoutParams, FSHA1 &Hasher)
Definition Array.h:3970
U16 Index
Definition radfft.cpp:71
static constexpr UE_FORCEINLINE_HINT bool IsPowerOfTwo(T Value)
Definition UnrealMathUtility.h:519
static UE_FORCEINLINE_HINT void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:160
static UE_FORCEINLINE_HINT void * Memset(void *Dest, uint8 Char, SIZE_T Count)
Definition UnrealMemory.h:119
Definition MemoryLayout.h:799
Definition MemoryLayout.h:108
const TCHAR * Name
Definition MemoryLayout.h:127
Definition ContainerAllocationPolicies.h:256