UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
CompactHashTable.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
6
7/* Compact hash table has two distinct features to keep it small
8 1. The Index type adapts to the number of elements, so a smaller table will use a smaller type
9 2. Holes in the table are patched up so all slots up to count are guaranteed to be valid
10
11 For performance reasons we only reset the HashTable part of the lookup table since the indexes will be unused until added.
12 It's the users responsibility to maintain correct item count
13 !!Important!! It's also the users reponsibility to move items from the end of the list into their new spots when removing items.
14
15 You are be able to maintain different views (hashes) of the same data since hole patching is deterministic between two tables.
16 This can be used to allow searching against the same data using different fields as names
17*/
18
19/* Helper to write code against specific types resolves by number of elements. Usage:
20 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) // Code
21 UE_COMPACTHASHTABLE_CALLBYTYPE(NumElements)
22 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
23*/
24#define UE_COMPACTHASHTABLE_CALLBYTYPE(NextIndexCount) \
25 switch (UE::Core::CompactHashTable::GetTypeSize(NextIndexCount)) { \
26 case 1: { UE_COMPACTHASHTABLE_EXECUTEBYTYPE(uint8); } break; \
27 case 2: { UE_COMPACTHASHTABLE_EXECUTEBYTYPE(uint16); } break; \
28 case 4: { UE_COMPACTHASHTABLE_EXECUTEBYTYPE(uint32); } break; \
29 default: checkNoEntry(); }
30
32{
33 // Remove an element from the list and patch up any next index references, after this the spot is empty and needs to be filled
34 template<typename IndexType>
35 inline void RemoveInternal(const uint32 Index, const uint32 Key, IndexType* HashData, const uint32 HashCount, IndexType* NextIndexData, const uint32 NextIndexCount)
36 {
37 const uint32 HashIndex = Key & (HashCount - 1);
38
39 for (IndexType* Lookup = &HashData[HashIndex]; *Lookup < NextIndexCount; Lookup = &NextIndexData[*Lookup])
40 {
41 if (*Lookup == Index)
42 {
43 // Next = Next->Next
44 *Lookup = NextIndexData[Index];
45 return;
46 }
47 }
48 }
49}
50
51/* Helpers for interacting with compact hash table memory layouts */
53{
55 {
56 return 1 + int(IndexCount > 0xff) + int(IndexCount > 0xffff) * 2;
57 }
58
60 {
61 return 0 + int(IndexCount > 0xff) + int(IndexCount > 0xffff);
62 }
63
64 [[nodiscard]] UE_FORCEINLINE_HINT constexpr size_t GetMemoryRequiredInBytes(uint32 IndexCount, uint32 HashCount)
65 {
66 return ((size_t)IndexCount + HashCount) << GetTypeShift(IndexCount);
67 }
68
70 {
71 return 4; // only support uint32 for now, pick highest alignment so we don't change alignment between allocations
72 }
73
74 /* Calculate the size of the hash table from the number of elements in the set */
75 [[nodiscard]] inline constexpr size_t GetHashCount(uint32 NumElements)
76 {
77 if (!NumElements)
78 {
79 return 0;
80 }
81 if (NumElements < 8)
82 {
83 return 4;
84 }
85
86 // Always use the power of 2 smaller than the current size to prioritize size over speed just a little bit
87 return FGenericPlatformMath::RoundUpToPowerOfTwo(NumElements / 2 + 1); // 255 == 128, 256 == 256
88 }
89
90 /* Return the first index of a key from the hash table portion */
91 template<typename IndexType>
92 [[nodiscard]] inline uint32 GetFirst(uint32 Key, const IndexType* HashData, const uint32 HashCount)
93 {
94 const uint32 HashIndex = Key & (HashCount - 1);
95 const IndexType FirstIndex = HashData[HashIndex];
96
97 // Convert smaller type INDEX_NONE to uint32 INDEX_NONE if data is invalid
98 constexpr IndexType InvalidIndex = (IndexType)INDEX_NONE;
99 return FirstIndex == InvalidIndex ? (uint32)INDEX_NONE : (uint32)FirstIndex;
100 }
101
102 /* Return the first index of a key from the hash table portion */
103 template<typename IndexType>
104 [[nodiscard]] inline uint32 GetFirstByIndex(uint32 HashIndex, const IndexType* HashData, const uint32 HashCount)
105 {
106 checkSlow(HashIndex <= HashCount);
107 const IndexType FirstIndex = HashData[HashIndex];
108
109 // Convert smaller type INDEX_NONE to uint32 INDEX_NONE if data is invalid
110 constexpr IndexType InvalidIndex = (IndexType)INDEX_NONE;
111 return FirstIndex == InvalidIndex ? (uint32)INDEX_NONE : (uint32)FirstIndex;
112 }
113
114 /* Given an existing index, return the next index in case there was a collision in the has table */
115 template<typename IndexType>
116 [[nodiscard]] inline uint32 GetNext(uint32 Index, const IndexType* NextIndexData, const uint32 NextIndexCount)
117 {
118 checkSlow(Index < NextIndexCount);
119 const IndexType NextIndex = NextIndexData[Index];
120
121 // Convert smaller type INDEX_NONE to uint32 INDEX_NONE if data is invalid
122 constexpr IndexType InvalidIndex = (IndexType)INDEX_NONE;
123 return NextIndex == InvalidIndex ? (uint32)INDEX_NONE : (uint32)NextIndex;
124 }
125
126 /* Do a full search for an existing element in the table given a function to compare if a found element is what you're looking for */
127 template<typename IndexType, typename PredicateType>
128 [[nodiscard]] inline uint32 Find(uint32 Key, const IndexType* HashData, const uint32 HashCount, const IndexType* NextIndexData, const uint32 NextIndexCount, const PredicateType& Predicate)
129 {
130 for (IndexType ElementIndex = HashData[Key & (HashCount - 1)]; ElementIndex != (IndexType)INDEX_NONE; ElementIndex = NextIndexData[ElementIndex])
131 {
132 if (Predicate(ElementIndex))
133 {
134 // Return the first match, regardless of whether the set has multiple matches for the key or not.
135 return ElementIndex;
136 }
137 checkSlow(ElementIndex < NextIndexCount);
138 }
139 return INDEX_NONE;
140 }
141
142 /* Insert new element into the hash table */
143 template<typename IndexType>
144 inline void Add(uint32 Index, uint32 Key, IndexType* HashData, const uint32 HashCount, IndexType* NextIndexData, const uint32 NextIndexCount)
145 {
146 checkSlow(Index < NextIndexCount);
147
148 const uint32 HashIndex = Key & (HashCount - 1);
149 NextIndexData[Index] = HashData[HashIndex];
150 HashData[HashIndex] = (IndexType)Index;
151 }
152
153 /* Remove an element from the list, move the last element into the now empty slot
154 If the item to remove is the last element then the last element's key will be ignored (you can skip calculating it if it's expensive)
155 */
156 template<typename IndexType>
157 inline void Remove(const uint32 Index, const uint32 Key, const uint32 LastIndex, uint32 OptLastKey, IndexType* HashData, const uint32 HashCount, IndexType* NextIndexData, const uint32 NextIndexCount)
158 {
159 checkSlow(LastIndex < NextIndexCount && Index <= LastIndex);
160
161 UE::Core::CompactHashTable::Private::RemoveInternal<IndexType>(Index, Key, HashData, HashCount, NextIndexData, NextIndexCount);
162
163 if (Index != LastIndex)
164 {
165 // Remove the last element and add it into the empty spot
166 UE::Core::CompactHashTable::Private::RemoveInternal<IndexType>(LastIndex, OptLastKey, HashData, HashCount, NextIndexData, NextIndexCount);
167
168 const uint32 HashIndex = OptLastKey & (HashCount - 1);
169 NextIndexData[Index] = HashData[HashIndex];
170 HashData[HashIndex] = (IndexType)Index;
171 }
172 }
173
174 /* Remove an element from the list, shift all indexes down to preserve the order of elements in a list
175 This is a very expensive operation so it should only be used if absolutely necessary (i.e. generally only for user facing data)
176 */
177 template<typename IndexType>
178 inline void RemoveStable(const uint32 Index, const uint32 Key, IndexType* HashData, const uint32 HashCount, IndexType* NextIndexData, const uint32 NextIndexCount)
179 {
180 checkSlow(Index < NextIndexCount);
181
182 UE::Core::CompactHashTable::Private::RemoveInternal<IndexType>(Index, Key, HashData, HashCount, NextIndexData, NextIndexCount);
183
184 // For the hash indexes, just decrement any that are bigger than the removed element
185 for (uint32 HashIndex = 0; HashIndex < HashCount; ++HashIndex)
186 {
187 if (HashData[HashIndex] > Index && HashData[HashIndex] != (IndexType)INDEX_NONE)
188 {
189 --HashData[HashIndex];
190 }
191 }
192
193 // Decrement values for all next index elements that are before the removed element
195 {
196 if (NextIndexData[NextIndexIndex] > Index && NextIndexData[NextIndexIndex] != (IndexType)INDEX_NONE)
197 {
198 --NextIndexData[NextIndexIndex];
199 }
200 }
201
202 // Move AND Decrement values for all next index elements that are after the removed element
203 for (uint32 NextIndexIndex = Index + 1; NextIndexIndex < NextIndexCount; ++NextIndexIndex)
204 {
205 if (NextIndexData[NextIndexIndex] > Index && NextIndexData[NextIndexIndex] != (IndexType)INDEX_NONE)
206 {
207 NextIndexData[NextIndexIndex - 1] = NextIndexData[NextIndexIndex] - 1;
208 }
209 else
210 {
211 NextIndexData[NextIndexIndex - 1] = NextIndexData[NextIndexIndex];
212 }
213 }
214 }
215}
216
217/* Helper to lookup a type from a type size, todo: Add support for 3 byte lookup */
218template<uint32 TypeSize> struct TCompactHashTypeLookupBySize;
219template<> struct TCompactHashTypeLookupBySize<1> { using Type = uint8; };
220template<> struct TCompactHashTypeLookupBySize<2> { using Type = uint16; };
221template<> struct TCompactHashTypeLookupBySize<4> { using Type = uint32; };
222
223/* Helper for making a fixed size hash table that manages its own memory */
224template<uint32 ElementCount, uint32 HashCount = UE::Core::CompactHashTable::GetHashCount(ElementCount)>
226{
227public:
229
231 {
232 static_assert(FMath::IsPowerOfTwo(HashCount));
233 Reset();
234 }
236
238 {
239 FMemory::Memset(HashData, 0xff, sizeof(HashData));
240 }
241
243 {
244 return UE::Core::CompactHashTable::GetFirst(Key, HashData, HashCount);
245 }
246
248 {
249 return UE::Core::CompactHashTable::GetFirstByIndex(HashIndex, HashData, HashCount);
250 }
251
257
258 template<typename PredicateType>
259 [[nodiscard]] inline uint32 Find(uint32 Key, uint32 CurrentCount, const PredicateType& Predicate) const
260 {
261 checkSlow(CurrentCount <= ElementCount);
262 return UE::Core::CompactHashTable::Find(Key, HashData, HashCount, NextIndexData, CurrentCount, Predicate);
263 }
264
265 inline void Add(uint32 CurrentCount, uint32 Key)
266 {
267 checkSlow(CurrentCount < ElementCount);
269 }
270
272 {
273 UE::Core::CompactHashTable::Remove(Index, Key, LastIndex, OptLastKey, HashData, HashCount, NextIndexData, ElementCount);
274 }
275
276protected:
277 IndexType NextIndexData[ElementCount]; // Collision redirector to next index for keys that hash to the same initial index
278 IndexType HashData[HashCount]; // First index lookup from key
279};
280
281/* Helper for interacting with existing hash table memory */
283{
284public:
285 explicit FConstCompactHashTableView() = default;
286
297
299 {
300 return HashCount;
301 }
302
303 // Functions used to search
304 [[nodiscard]] inline uint32 GetFirst(uint32 Key) const
305 {
306 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::GetFirst(Key, (const Type *)HashData, HashCount)
308 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
309 return INDEX_NONE;
310 }
311
312 // Advanced used for manual inspection of the hash data
313 [[nodiscard]] inline uint32 GetFirstByIndex(uint32 HashIndex) const
314 {
315 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::GetFirstByIndex(HashIndex, (const Type *)HashData, HashCount)
317 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
318 return INDEX_NONE;
319 }
320
322 {
324
325 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::GetNext(Index, (const Type *)NextIndexData, CurrentCount)
327 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
328 return INDEX_NONE;
329 }
330
331 template<typename PredicateType>
332 [[nodiscard]] inline uint32 Find(uint32 Key, uint32 CurrentCount, const PredicateType& Predicate) const
333 {
335 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::Find(Key, (const Type *)HashData, HashCount, (const Type *)NextIndexData, CurrentCount, Predicate)
337 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
338 return INDEX_NONE;
339 }
340
341protected:
342 const uint8* NextIndexData = nullptr;
343 const uint8* HashData = nullptr;
346};
347
348/* Helper for interacting with existing hash table memory */
350{
351public:
356
357 inline void Reset() const
358 {
360
361 // The const_casts are a little gross but I don't wan't to maintain duplicate copies of the const functions
362 FMemory::Memset(const_cast<uint8*>(HashData), 0xff, size_t(HashCount) << TypeShift);
363 }
364
365 inline void Add(uint32 Index, uint32 Key) const
366 {
368 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) UE::Core::CompactHashTable::Add(Index, Key, (Type*)(HashData), HashCount, (Type*)(NextIndexData), NextIndexCount)
370 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
371 }
372
374 {
375 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::Remove(Index, Key, LastIndex, OptLastKey, (Type*)(HashData), HashCount, (Type*)(NextIndexData), NextIndexCount)
377 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
378 }
379
381 {
382 #define UE_COMPACTHASHTABLE_EXECUTEBYTYPE(Type) return UE::Core::CompactHashTable::RemoveStable(Index, Key, (Type*)(HashData), HashCount, (Type*)(NextIndexData), NextIndexCount)
384 #undef UE_COMPACTHASHTABLE_EXECUTEBYTYPE
385 }
386};
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define checkfSlow(expr, format,...)
Definition AssertionMacros.h:333
#define UE_COMPACTHASHTABLE_CALLBYTYPE(NextIndexCount)
Definition CompactHashTable.h:24
ENoInit
Definition CoreMiscDefines.h:158
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
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
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Core.Build.cs:8
Definition CompactHashTable.h:350
UE_FORCEINLINE_HINT void RemoveStable(uint32 Index, uint32 Key) const
Definition CompactHashTable.h:380
void Reset() const
Definition CompactHashTable.h:357
void Add(uint32 Index, uint32 Key) const
Definition CompactHashTable.h:365
UE_FORCEINLINE_HINT FCompactHashTableView(uint8 *Memory, uint32 InNextIndexCount, uint32 InHashCount, size_t MemorySize)
Definition CompactHashTable.h:352
UE_FORCEINLINE_HINT void Remove(uint32 Index, uint32 Key, uint32 LastIndex, uint32 OptLastKey) const
Definition CompactHashTable.h:373
Definition CompactHashTable.h:283
const uint8 * NextIndexData
Definition CompactHashTable.h:342
uint32 GetFirst(uint32 Key) const
Definition CompactHashTable.h:304
uint32 GetHashCount() const
Definition CompactHashTable.h:298
uint32 GetNext(uint32 Index, uint32 CurrentCount) const
Definition CompactHashTable.h:321
uint32 NextIndexCount
Definition CompactHashTable.h:344
uint32 HashCount
Definition CompactHashTable.h:345
const uint8 * HashData
Definition CompactHashTable.h:343
uint32 Find(uint32 Key, uint32 CurrentCount, const PredicateType &Predicate) const
Definition CompactHashTable.h:332
FConstCompactHashTableView()=default
uint32 GetFirstByIndex(uint32 HashIndex) const
Definition CompactHashTable.h:313
FConstCompactHashTableView(const uint8 *Memory, uint32 InNextIndexCount, uint32 InHashCount, size_t MemorySize)
Definition CompactHashTable.h:287
Definition CompactHashTable.h:226
typename TCompactHashTypeLookupBySize< UE::Core::CompactHashTable::GetTypeSize(ElementCount)>::Type IndexType
Definition CompactHashTable.h:228
IndexType NextIndexData[ElementCount]
Definition CompactHashTable.h:277
UE_FORCEINLINE_HINT void Reset()
Definition CompactHashTable.h:237
TStaticCompactHashTable()
Definition CompactHashTable.h:230
UE_FORCEINLINE_HINT uint32 GetFirst(uint32 Key) const
Definition CompactHashTable.h:242
void Add(uint32 CurrentCount, uint32 Key)
Definition CompactHashTable.h:265
UE_FORCEINLINE_HINT uint32 GetFirstByIndex(uint32 HashIndex) const
Definition CompactHashTable.h:247
TStaticCompactHashTable(ENoInit)
Definition CompactHashTable.h:235
uint32 GetNext(uint32 Index, uint32 CurrentCount) const
Definition CompactHashTable.h:252
IndexType HashData[HashCount]
Definition CompactHashTable.h:278
UE_FORCEINLINE_HINT void Remove(uint32 Index, uint32 Key, uint32 LastIndex, uint32 OptLastKey)
Definition CompactHashTable.h:271
uint32 Find(uint32 Key, uint32 CurrentCount, const PredicateType &Predicate) const
Definition CompactHashTable.h:259
Definition CompactHashTable.h:32
void RemoveInternal(const uint32 Index, const uint32 Key, IndexType *HashData, const uint32 HashCount, IndexType *NextIndexData, const uint32 NextIndexCount)
Definition CompactHashTable.h:35
Definition CompactHashTable.h:32
uint32 GetFirst(uint32 Key, const IndexType *HashData, const uint32 HashCount)
Definition CompactHashTable.h:92
void Add(uint32 Index, uint32 Key, IndexType *HashData, const uint32 HashCount, IndexType *NextIndexData, const uint32 NextIndexCount)
Definition CompactHashTable.h:144
void Remove(const uint32 Index, const uint32 Key, const uint32 LastIndex, uint32 OptLastKey, IndexType *HashData, const uint32 HashCount, IndexType *NextIndexData, const uint32 NextIndexCount)
Definition CompactHashTable.h:157
void RemoveStable(const uint32 Index, const uint32 Key, IndexType *HashData, const uint32 HashCount, IndexType *NextIndexData, const uint32 NextIndexCount)
Definition CompactHashTable.h:178
uint32 GetFirstByIndex(uint32 HashIndex, const IndexType *HashData, const uint32 HashCount)
Definition CompactHashTable.h:104
constexpr size_t GetHashCount(uint32 NumElements)
Definition CompactHashTable.h:75
UE_FORCEINLINE_HINT constexpr uint32 GetTypeShift(uint32 IndexCount)
Definition CompactHashTable.h:59
UE_FORCEINLINE_HINT constexpr size_t GetMemoryRequiredInBytes(uint32 IndexCount, uint32 HashCount)
Definition CompactHashTable.h:64
UE_FORCEINLINE_HINT constexpr size_t GetMemoryAlignment()
Definition CompactHashTable.h:69
UE_FORCEINLINE_HINT constexpr uint32 GetTypeSize(uint32 IndexCount)
Definition CompactHashTable.h:54
uint32 GetNext(uint32 Index, const IndexType *NextIndexData, const uint32 NextIndexCount)
Definition CompactHashTable.h:116
uint32 Find(uint32 Key, const IndexType *HashData, const uint32 HashCount, const IndexType *NextIndexData, const uint32 NextIndexCount, const PredicateType &Predicate)
Definition CompactHashTable.h:128
Definition AdvancedWidgetsModule.cpp:13
U16 Index
Definition radfft.cpp:71
static constexpr UE_FORCEINLINE_HINT uint32 RoundUpToPowerOfTwo(uint32 Arg)
Definition GenericPlatformMath.h:833
static constexpr UE_FORCEINLINE_HINT bool IsPowerOfTwo(T Value)
Definition UnrealMathUtility.h:519
static UE_FORCEINLINE_HINT void * Memset(void *Dest, uint8 Char, SIZE_T Count)
Definition UnrealMemory.h:119
uint8 Type
Definition CompactHashTable.h:219
uint16 Type
Definition CompactHashTable.h:220
uint32 Type
Definition CompactHashTable.h:221
Definition CompactHashTable.h:218