UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SherwoodHashTable.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
7
8namespace Experimental
9{
10
11template<typename KeyType, typename ValueType>
13{
15
17 {
18 return A == B;
19 }
21 {
22 return GetTypeHash(Key);
23 }
24};
25
26namespace TSherwoodHashTable_Private
27{
28
37template <typename KeyType, typename ValueType, typename KeyFuncs = TSherwoodHashKeyFuncs<KeyType, ValueType>>
39{
40 using HashType = uint32; // TODO: perhaps we could deduce type of KeyFuncs::GetKeyHash() instead
41
42 // TSherwoodHashTable can be used to implement a set or a map.
43 // In map mode we allocate memory for keys and values, but in set mode we only allocate keys.
44 static constexpr bool bIsMap = !std::is_same_v<ValueType, FNoopStruct>;
45
46 // Minimum probing distance when searching for an entry slot.
47 static constexpr uint32 MinNumLookups = 4;
48
49 // Smallest capacity of non-empty container.
50 static constexpr uint32 MinNumSlots = 4;
51
52 // Ratio between number of stored elements and allocated capacity beyond which the container will be grown (doubled in size).
53 static constexpr float MaxLoadFactor = 0.9f;
54 static_assert(MaxLoadFactor >= 0.5 && MaxLoadFactor <= 0.9, "MaxLoadFactor must be in range [0.5 .. 0.9]");
55
56 // NOTE: Non-trivial type support, move and copy ops are not implemented yet, but can be.
57 static_assert(TIsTrivial<KeyType>::Value, "Key is expected to be a trivial type.");
58 static_assert(TIsTrivial<ValueType>::Value || !bIsMap, "Value is expected to be a trivial type.");
59
60 TSherwoodHashTable() = default;
66 {
67 Empty();
68 }
69
70 template <typename T>
72 {
73 return (T*)FMemory::Malloc(sizeof(T) * Count);
74 }
75
76 static void Deallocate(void* Ptr)
77 {
78 FMemory::Free(Ptr);
79 }
80
81 struct FData
82 {
83 bool HasValue(uint32 i) const
84 {
85 return Distances[i] >= 0;
86 }
87
88 bool IsEmpty(uint32 i) const
89 {
90 return Distances[i] < 0;
91 }
92
93 void AddAt(uint32 i, int8 InDistance, KeyType InKey, ValueType InValue)
94 {
95 Keys[i] = InKey;
96 if (bIsMap)
97 {
98 Values[i] = InValue;
99 }
101 }
102
103 bool IsValid() const { return Distances != nullptr; }
104
106 int8* Distances = nullptr;
107 KeyType* Keys = nullptr;
108 ValueType* Values = nullptr;
109 };
110
115
116 static void DeallocateData(FData& Data)
117 {
118 if (Data.IsValid())
119 {
120 // Assumes that individual elements are already destroyed.
121
122 Deallocate(Data.Distances);
123 Deallocate(Data.Keys);
124 Deallocate(Data.Values);
125 }
126 }
127
128 void Reset()
129 {
130 if (CurrentData.IsValid())
131 {
133 }
134 NumElements = 0;
135 }
136
137 void Empty()
138 {
140
141 CurrentData = FData();
143 MaxLookups = 0;
144 NumElements = 0;
145 }
146
148 {
149 return NumSlotsMinusOne ? NumSlotsMinusOne + 1 : 0;
150 }
151
153 {
154 uint32 Desired = (32-FMath::CountLeadingZeros(InNumSlots)); // FloorLog2
155 return uint8(FMath::Max(MinNumLookups, Desired));
156 }
157
159 {
160 FData Result;
161
162 Result.AllocatedCount = Count;
163 Result.Distances = AllocateUninitialized<int8>(Count);
165 if (bIsMap)
166 {
168 }
169
170 FMemory::Memset(Result.Distances, uint8(-1), Count);
171
172 return Result;
173 }
174
176 {
177 if (CurrentData.IsValid())
178 {
179 int8 Distance = 0;
180 uint32 Cursor = KeyFuncs::GetKeyHash(Key) & NumSlotsMinusOne; // Number of slots is always Pow2
181 for (; CurrentData.Distances[Cursor] >= Distance; ++Cursor, ++Distance)
182 {
183 const KeyType& KeyAtCursor = CurrentData.Keys[Cursor];
184 if (KeyFuncs::Matches(Key, KeyAtCursor))
185 {
187 }
188 }
189 }
190
191 return TTuple<const KeyType*, ValueType*>(nullptr, nullptr);
192 }
193
194 FORCEINLINE_DEBUGGABLE ValueType* FindOrAdd(KeyType Key, ValueType Value, bool* bIsAlreadyInContainerPtr = nullptr)
195 {
196 return FindOrAddByHash(Key, KeyFuncs::GetKeyHash(Key), Value, bIsAlreadyInContainerPtr);
197 }
198
199 FORCEINLINE_DEBUGGABLE ValueType* FindOrAddByHash(KeyType Key, HashType Hash, ValueType Value, bool* bIsAlreadyInContainerPtr = nullptr)
200 {
201 uint32 Cursor = Hash & NumSlotsMinusOne; // Number of slots is always Pow2
202 int8 Distance = 0;
203
204 if (CurrentData.IsValid())
205 {
206 for (; CurrentData.Distances[Cursor] >= Distance; ++Cursor, ++Distance)
207 {
208 if (KeyFuncs::Matches(Key, CurrentData.Keys[Cursor]))
209 {
211 {
213 }
214 return bIsMap ? &CurrentData.Values[Cursor] : nullptr;
215 }
216 }
217 }
218
220 {
222 }
223
224 return Add(Distance, CurrentData, Cursor, Key, Hash, Value);
225 }
226
227 FORCENOINLINE ValueType* Add(int8 Distance, FData& Data, uint32 Cursor, KeyType Key, HashType Hash, ValueType Value)
228 {
230 {
231 Grow();
232 return FindOrAddByHash(Key, Hash, Value);
233 }
234 else if (Data.IsEmpty(Cursor))
235 {
236 Data.AddAt(Cursor, Distance, Key, Value);
237 ++NumElements;
238 return bIsMap ? &Data.Values[Cursor] : nullptr;
239 }
240
241 Swap(Distance, Data.Distances[Cursor]);
242 Swap(Key, Data.Keys[Cursor]);
243 if (bIsMap)
244 {
245 Swap(Value, Data.Values[Cursor]);
246 }
247
248 const uint32 ResultCursor = Cursor;
249 ++Distance;
250 ++Cursor;
251 for (;;)
252 {
253 if (Data.IsEmpty(Cursor))
254 {
255 Data.AddAt(Cursor, Distance, Key, Value);
256 ++NumElements;
257 return bIsMap ? &(Data.Values[ResultCursor]) : nullptr;
258 }
259 else if (Data.Distances[Cursor] < Distance)
260 {
261 Swap(Distance, Data.Distances[Cursor]);
262 Swap(Key, Data.Keys[Cursor]);
263 if (bIsMap)
264 {
265 Swap(Value, Data.Values[Cursor]);
266 }
267 ++Distance;
268 }
269 else
270 {
271 ++Distance;
272 if (Distance == MaxLookups)
273 {
274 Swap(Key, Data.Keys[ResultCursor]);
275 if (bIsMap)
276 {
277 Swap(Value, Data.Values[ResultCursor]);
278 }
279 Grow();
280 return FindOrAdd(Key, Value);
281 }
282 }
283 ++Cursor;
284 }
285 }
286
288 {
289 DesiredNumSlots = FMath::Max(DesiredNumSlots, uint32(FMath::CeilToInt(float(NumElements) / MaxLoadFactor)));
290 if (DesiredNumSlots == 0)
291 {
292 Empty();
293 return;
294 }
295
296 DesiredNumSlots = FMath::RoundUpToPowerOfTwo(DesiredNumSlots);
297
298 if (DesiredNumSlots == NumSlots())
299 {
300 return;
301 }
302
304
308
310
313 NumElements = 0;
314
316 {
317 if (OldData.HasValue(Index))
318 {
319 if (bIsMap)
320 {
321 FindOrAdd(OldData.Keys[Index], OldData.Values[Index]);
322 }
323 else
324 {
325 FindOrAdd(OldData.Keys[Index], ValueType{});
326 }
327 }
328 }
329
331 }
332
333 void Grow()
334 {
335 uint32 NextNumSlots = FMath::Max<uint32>(MinNumSlots, 2u * NumSlots());
337 }
338
340 {
341 uint32 DesiredNumSlots = FMath::Max<uint32>(DesiredNumElements, uint32(FMath::CeilToInt(float(DesiredNumElements) / MaxLoadFactor)));
343 {
345 }
346 }
347};
348
349} // namespace TSherwoodHashTable_Private
350
351template <typename KeyType, typename ValueType, typename KeyFuncs = TSherwoodHashKeyFuncs<KeyType, ValueType>>
353{
354 ValueType& FindOrAdd(KeyType Key, ValueType Value)
355 {
356 return *Table.FindOrAdd(Key, Value);
357 }
358
359 ValueType* Find(KeyType Key)
360 {
361 return Table.Find(Key).Value;
362 }
363
364 const ValueType* Find(KeyType Key) const
365 {
366 return Table.Find(Key).Value;
367 }
368
369 int32 Num() const
370 {
371 return Table.NumElements;
372 }
373
374 void Empty()
375 {
376 Table.Empty();
377 }
378
379 void Reset()
380 {
381 Table.Reset();
382 }
383
385 {
386 Table.Reserve(DesiredNumElements);
387 }
388
389private:
391};
392
393template <typename KeyType, typename KeyFuncs = TSherwoodHashKeyFuncs<KeyType, FNoopStruct>>
395{
396 void Add(KeyType Key, bool* bIsAlreadyInSetPtr = nullptr)
397 {
398 Table.FindOrAdd(Key, FNoopStruct{}, bIsAlreadyInSetPtr);
399 }
400
401 const KeyType* Find(KeyType Key) const
402 {
403 return Table.Find(Key).Key;
404 }
405
406 int32 Num() const
407 {
408 return Table.NumElements;
409 }
410
411 void Empty()
412 {
413 Table.Empty();
414 }
415
416 void Reset()
417 {
418 Table.Reset();
419 }
420
422 {
423 Table.Reserve(DesiredNumElements);
424 }
425
426private:
428};
429
430} // namespace Experimental
#define FORCENOINLINE
Definition AndroidPlatform.h:142
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define FORCEINLINE_DEBUGGABLE
Definition CoreMiscDefines.h:74
FPlatformTypes::int8 int8
An 8-bit signed integer.
Definition Platform.h:1121
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition RobinHoodHashTable.h:18
U16 Index
Definition radfft.cpp:71
Definition SherwoodHashTable.h:13
TTypeTraits< KeyType >::ConstPointerType KeyInitType
Definition SherwoodHashTable.h:14
static FORCEINLINE uint32 GetKeyHash(KeyInitType Key)
Definition SherwoodHashTable.h:20
static FORCEINLINE bool Matches(KeyInitType A, KeyInitType B)
Definition SherwoodHashTable.h:16
ValueType * Values
Definition SherwoodHashTable.h:108
bool IsValid() const
Definition SherwoodHashTable.h:103
bool IsEmpty(uint32 i) const
Definition SherwoodHashTable.h:88
bool HasValue(uint32 i) const
Definition SherwoodHashTable.h:83
void AddAt(uint32 i, int8 InDistance, KeyType InKey, ValueType InValue)
Definition SherwoodHashTable.h:93
static T * AllocateUninitialized(uint32 Count)
Definition SherwoodHashTable.h:71
void Grow()
Definition SherwoodHashTable.h:333
void Reserve(uint32 DesiredNumElements)
Definition SherwoodHashTable.h:339
void Empty()
Definition SherwoodHashTable.h:137
static uint8 ComputeMaxLookups(uint32 InNumSlots)
Definition SherwoodHashTable.h:152
FORCEINLINE_DEBUGGABLE ValueType * FindOrAddByHash(KeyType Key, HashType Hash, ValueType Value, bool *bIsAlreadyInContainerPtr=nullptr)
Definition SherwoodHashTable.h:199
static void Deallocate(void *Ptr)
Definition SherwoodHashTable.h:76
static constexpr float MaxLoadFactor
Definition SherwoodHashTable.h:53
int32 NumElements
Definition SherwoodHashTable.h:114
~TSherwoodHashTable()
Definition SherwoodHashTable.h:65
static FData AllocateData(uint32 Count)
Definition SherwoodHashTable.h:158
static constexpr uint32 MinNumLookups
Definition SherwoodHashTable.h:47
FORCEINLINE_DEBUGGABLE ValueType * FindOrAdd(KeyType Key, ValueType Value, bool *bIsAlreadyInContainerPtr=nullptr)
Definition SherwoodHashTable.h:194
FData CurrentData
Definition SherwoodHashTable.h:111
void Reset()
Definition SherwoodHashTable.h:128
void Rehash(uint32 DesiredNumSlots)
Definition SherwoodHashTable.h:287
uint32 NumSlots() const
Definition SherwoodHashTable.h:147
uint32 HashType
Definition SherwoodHashTable.h:40
TSherwoodHashTable & operator=(const TSherwoodHashTable &)=delete
FORCEINLINE_DEBUGGABLE TTuple< const KeyType *, ValueType * > Find(KeyType Key) const
Definition SherwoodHashTable.h:175
uint32 NumSlotsMinusOne
Definition SherwoodHashTable.h:112
static void DeallocateData(FData &Data)
Definition SherwoodHashTable.h:116
FORCENOINLINE ValueType * Add(int8 Distance, FData &Data, uint32 Cursor, KeyType Key, HashType Hash, ValueType Value)
Definition SherwoodHashTable.h:227
static constexpr uint32 MinNumSlots
Definition SherwoodHashTable.h:50
static constexpr bool bIsMap
Definition SherwoodHashTable.h:44
int8 MaxLookups
Definition SherwoodHashTable.h:113
Definition SherwoodHashTable.h:353
ValueType * Find(KeyType Key)
Definition SherwoodHashTable.h:359
int32 Num() const
Definition SherwoodHashTable.h:369
void Reset()
Definition SherwoodHashTable.h:379
ValueType & FindOrAdd(KeyType Key, ValueType Value)
Definition SherwoodHashTable.h:354
const ValueType * Find(KeyType Key) const
Definition SherwoodHashTable.h:364
void Reserve(uint32 DesiredNumElements)
Definition SherwoodHashTable.h:384
void Empty()
Definition SherwoodHashTable.h:374
Definition SherwoodHashTable.h:395
const KeyType * Find(KeyType Key) const
Definition SherwoodHashTable.h:401
void Empty()
Definition SherwoodHashTable.h:411
void Reset()
Definition SherwoodHashTable.h:416
void Reserve(uint32 DesiredNumElements)
Definition SherwoodHashTable.h:421
int32 Num() const
Definition SherwoodHashTable.h:406
void Add(KeyType Key, bool *bIsAlreadyInSetPtr=nullptr)
Definition SherwoodHashTable.h:396
static FORCENOINLINE CORE_API void Free(void *Original)
Definition UnrealMemory.cpp:685
static UE_FORCEINLINE_HINT void * Memset(void *Dest, uint8 Char, SIZE_T Count)
Definition UnrealMemory.h:119
Definition UnrealTemplate.h:717
Definition IsTrivial.h:15
Definition Tuple.h:652
TCallTraits< T >::ConstPointerType ConstPointerType
Definition UnrealTypeTraits.h:337