UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
HitSet.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#if (defined(__AUTORTFM) && __AUTORTFM)
6
7#include "ExternAPI.h"
8#include "Utils.h"
9
10#include <cstdint>
11
12namespace AutoRTFM
13{
14 struct FHitSetEntry
15 {
16 // The address of the write.
17 uintptr_t Address : 48;
18 // Size of the write.
19 uintptr_t Size : 15;
20 // If set, then the write shouldn't be considered by the memory validator.
22
24 {
25 // Note: Clang optimizes this away to a reinterpret cast.
27 memcpy(&Result, this, sizeof(Result));
28 return Result;
29 }
30 };
31
32 static_assert(sizeof(FHitSetEntry) == sizeof(uintptr_t));
33
34 class FHitSet final
35 {
36 public:
37 // Maximum size in bytes for a hit-set write record.
38 // The cutoff here is arbitrarily any number less than UINT16_MAX, but its a
39 // weigh up what a good size is. Because the hitset doesn't detect when you
40 // are trying to write to a subregion of a previous hit (like memset something,
41 // then write to an individual element), we've got to balance the cost of
42 // recording meaningless hits, against the potential to hit again.
43 static constexpr size_t MaxSize = 16;
44
45 private:
46 static constexpr uint8_t LinearProbeDepth = 32;
47 static constexpr uint8_t LogInitialCapacity = 6;
48 static constexpr uint64_t InitialCapacity = static_cast<uint64_t>(1) << LogInitialCapacity;
49
50 // TODO: Revisit a good max capacity for the hitset.
51 static constexpr uint64_t MaxCapacity = static_cast<uint64_t>(128 * 1024 * 1024) / sizeof(FHitSetEntry);
52
53 static_assert(LinearProbeDepth <= InitialCapacity);
54 public:
55
56 ~FHitSet()
57 {
58 Reset();
59 }
60
61 enum class EInsertResult : uint8_t
62 {
63 Exists,
66 };
67
68 // Insert something in the HitSet, returning whether the key already
69 // exists, it was inserted, or not.
71 {
72 return InsertNoResize(Entry.Payload());
73 }
74
75 // Insert something in the HitSet, returning true if we found the key.
76 // If false is returned, we could have inserted or not - depending on
77 // whether the HitSet has reached its max capacity.
79 {
80 while (true)
81 {
82 switch (InsertNoResize(Entry.Payload()))
83 {
84 default:
85 AutoRTFM::InternalUnreachable();
86 case EInsertResult::Inserted:
87 return false;
88 case EInsertResult::Exists:
89 return true;
90 case EInsertResult::NotInserted:
91 if (!Resize())
92 {
93 return false;
94 }
95 continue;
96 }
97 }
98 }
99
101 {
102 return 0 == Count;
103 }
104
105 // Clear out the data stored in the set, but does not reduce the capacity.
106 void Reset()
107 {
108 if (Count)
109 {
110 if (Payload != SmallPayload)
111 {
112 AutoRTFM::Free(Payload);
113 Payload = SmallPayload;
114 }
115 memset(SmallPayload, 0, sizeof(SmallPayload));
117 Count = 0;
118 }
119 }
120
121 uint64_t GetCapacity() const
122 {
123 return Capacity();
124 }
125
126 uint64_t GetCount() const
127 {
128 return Count;
129 }
130
131 private:
132 uintptr_t SmallPayload[InitialCapacity]{};
133 uintptr_t* Payload = SmallPayload;
134 uint64_t Count = 0;
136
137 UE_AUTORTFM_FORCEINLINE uint64_t Capacity() const
138 {
140 return static_cast<uint64_t>(1) << (64 - SixtyFourMinusLogCapacity);
141 }
142
144 {
145 // It seems odd - but subtracting 1 here is totally intentional
146 // because of how we store our capacity as (64 - log2(Capacity)).
148
149 // Check that we haven't overflowed our capacity!
151 }
152
153 bool Resize()
154 {
155 const uint64_t OldCapacity = Capacity();
156
157 if (OldCapacity >= MaxCapacity)
158 {
159 return false;
160 }
161
162 uintptr_t* const OldPayload = Payload;
163 const uint64_t OldCount = Count;
164
165 while (true)
166 {
167 bool bNeedAnotherResize = false;
168
169 Count = 0;
170
172
173 Payload = static_cast<uintptr_t*>(AutoRTFM::AllocateZeroed(Capacity() * sizeof(uintptr_t), alignof(uintptr_t)));
174 AUTORTFM_ASSERT(nullptr != Payload);
175
176 // Now we need to rehash and reinsert all the items.
177 for (size_t I = 0; I < OldCapacity; I++)
178 {
179 // Skip empty locations.
180 if (0 == OldPayload[I])
181 {
182 continue;
183 }
184
186
187 if (EInsertResult::NotInserted == Result)
188 {
189 bNeedAnotherResize = true;
190 break;
191 }
192 else
193 {
194 AUTORTFM_ASSERT(EInsertResult::Inserted == Result);
195 }
196 }
197
199 {
200 if (Payload != SmallPayload)
201 {
202 AutoRTFM::Free(Payload);
203 }
204 continue;
205 }
206
207 break;
208 }
209
211
213 {
214 AutoRTFM::Free(OldPayload);
215 }
216
217 return true;
218 }
219
221 {
222 constexpr uintptr_t Fibonacci = 0x9E3779B97F4A7C15;
224 }
225
227 {
228 // We have a free location in the set.
229 if (0 == Payload[I])
230 {
231 Payload[I] = Raw;
232 Count++;
233 return EInsertResult::Inserted;
234 }
235
236 // We're already in the set.
237 if (Raw == Payload[I])
238 {
239 return EInsertResult::Exists;
240 }
241
242 return EInsertResult::NotInserted;
243 }
244
245 // Insert something in the HitSet, returning true if the insert
246 // succeeded (EG. the key was not already in the set).
248 {
250
251 // The first index will always be in the range of capacity because we
252 // are using a fibonacci hash. So just check if we can insert here.
253 {
254 const uintptr_t I = Hash;
255
257
258 if (EInsertResult::NotInserted != R)
259 {
260 return R;
261 }
262
263 // Otherwise we need to do a linear probe...
264 }
265
266 const uintptr_t Mask = Capacity() - 1;
267
268 // Clang goes way over the top with loop unrolling, and makes this code noticably
269 // slower as a result. So we disable it!
270#ifdef __clang__
271#pragma clang loop unroll(disable)
272#endif
273 for (uint8_t D = 1; D < LinearProbeDepth; D++)
274 {
275 // Capacity is always a power of 2, so we can just mask out the bits.
276 const uintptr_t I = (Hash + D) & Mask;
277
279
280 if (EInsertResult::NotInserted != R)
281 {
282 return R;
283 }
284
285 // Otherwise we need to keep going with our linear probe...
286 }
287
288 return EInsertResult::NotInserted;
289 }
290 };
291}
292
293#endif // (defined(__AUTORTFM) && __AUTORTFM)
#define UE_AUTORTFM_FORCEINLINE
Definition AutoRTFMDefines.h:171
#define UE_AUTORTFM_ASSUME(x)
Definition AutoRTFMDefines.h:174
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
uint32 Size
Definition VulkanMemory.cpp:4034
memcpy(InputBufferBase, BinkBlocksData, BinkBlocksSize)
Definition API.cpp:57
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732