UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
PakIntervalTree.inl
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5enum
6{
8};
9
10typedef uint32 TIntervalTreeIndex; // this is the arg type of TSparseArray::operator[]
11
13
14static FORCEINLINE uint16 GetRequestPakIndexLow(FJoinedOffsetAndPakIndex Joined)
15{
16 return uint16((Joined >> 48) & 0xffff);
17}
18
19static FORCEINLINE int64 GetRequestOffset(FJoinedOffsetAndPakIndex Joined)
20{
21 return int64(Joined & 0xffffffffffffll);
22}
23
25{
26 check(Offset >= 0);
28}
29
30static uint32 GNextSalt = 1;
31
32// This is like TSparseArray, only a bit safer and I needed some restrictions on resizing.
33template<class TItem>
35{
36 TArray<TItem> Items;
37 TArray<int32> FreeItems; //@todo make this into a linked list through the existing items
38 uint32 Salt;
39 uint32 SaltMask;
40public:
42 {
43 check(GNextSalt < 4);
44 Salt = (GNextSalt++) << 30;
45 SaltMask = MAX_uint32 << 30;
46 verify((Alloc() & ~SaltMask) == IntervalTreeInvalidIndex); // we want this to always have element zero so we can figure out an index from a pointer
47 }
49 {
50 int32 Result;
51 if (FreeItems.Num())
52 {
53 Result = FreeItems.Pop();
54 }
55 else
56 {
57 Result = Items.Num();
58 Items.AddUninitialized();
59
60 }
61 new ((void*)&Items[Result]) TItem();
62 return Result | Salt;;
63 }
65 {
66 if (FreeItems.Num() + Items.GetSlack() < NeededNewNum)
67 {
68 Items.Reserve(Items.Num() + NeededNewNum);
69 }
70 }
72 {
74 check((InIndex & SaltMask) == Salt && Index != IntervalTreeInvalidIndex && Index >= 0 && Index < (uint32)Items.Num()); //&& !FreeItems.Contains(Index));
75 return Items[Index];
76 }
78 {
80 check((InIndex & SaltMask) == Salt && Index != IntervalTreeInvalidIndex && Index >= 0 && Index < (uint32)Items.Num()); //&& !FreeItems.Contains(Index));
81 Items[Index].~TItem();
82 FreeItems.Push(Index);
83 if (FreeItems.Num() + 1 == Items.Num())
84 {
85 // get rid everything to restore memory coherence
86 Items.Empty();
87 FreeItems.Empty();
88 verify((Alloc() & ~SaltMask) == IntervalTreeInvalidIndex); // we want this to always have element zero so we can figure out an index from a pointer
89 }
90 }
92 {
94 check((InIndex & SaltMask) == Salt && Index != IntervalTreeInvalidIndex && Index >= 0 && Index < (uint32)Items.Num()); // && !FreeItems.Contains(Index));
95 }
96};
97
116
117static TIntervalTreeAllocator<FIntervalTreeNode> GIntervalTreeNodeNodeAllocator;
118
119static FORCEINLINE uint64 HighBit(uint64 x)
120{
121 return x & (1ull << 63);
122}
123
124static FORCEINLINE bool IntervalsIntersect(uint64 Min1, uint64 Max1, uint64 Min2, uint64 Max2)
125{
126 return !(Max2 < Min1 || Max1 < Min2);
127}
128
129template<typename TItem>
130// this routine assume that the pointers remain valid even though we are reallocating
131static void AddToIntervalTree_Dangerous(
132 TIntervalTreeIndex* RootNode,
138 uint32 MaxShift
139)
140{
141 while (true)
142 {
143 if (*RootNode == IntervalTreeInvalidIndex)
144 {
145 *RootNode = GIntervalTreeNodeNodeAllocator.Alloc();
146 }
147
150 FIntervalTreeNode& Root = GIntervalTreeNodeNodeAllocator.Get(*RootNode);
151
152 if (MinShifted == MaxShifted && CurrentShift < MaxShift)
153 {
154 CurrentShift++;
155 RootNode = (!MinShifted) ? &Root.LeftChildOrRootOfLeftList : &Root.RightChildOrRootOfRightList;
156 }
157 else
158 {
159 TItem& Item = Allocator.Get(Index);
160 if (MinShifted != MaxShifted) // crosses middle
161 {
162 Item.Next = Root.RootOfOnList;
163 Root.RootOfOnList = Index;
164 }
165 else // we are at the leaf
166 {
167 if (!MinShifted)
168 {
169 Item.Next = Root.LeftChildOrRootOfLeftList;
170 Root.LeftChildOrRootOfLeftList = Index;
171 }
172 else
173 {
174 Item.Next = Root.RightChildOrRootOfRightList;
175 Root.RightChildOrRootOfRightList = Index;
176 }
177 }
178 return;
179 }
180 }
181}
182
183template<typename TItem>
184static void AddToIntervalTree(
185 TIntervalTreeIndex* RootNode,
188 uint32 StartShift,
189 uint32 MaxShift
190)
191{
192 GIntervalTreeNodeNodeAllocator.EnsureNoRealloc(1 + MaxShift - StartShift);
193 TItem& Item = Allocator.Get(Index);
194 check(Item.Next == IntervalTreeInvalidIndex);
195 uint64 MinInterval = GetRequestOffset(Item.OffsetAndPakIndex);
196 uint64 MaxInterval = MinInterval + Item.Size - 1;
197 AddToIntervalTree_Dangerous(RootNode, Allocator, Index, MinInterval, MaxInterval, StartShift, MaxShift);
198
199}
200
201template<typename TItem>
202static FORCEINLINE bool ScanNodeListForRemoval(
203 TIntervalTreeIndex* Iter,
208)
209{
210 while (*Iter != IntervalTreeInvalidIndex)
211 {
212
213 TItem& Item = Allocator.Get(*Iter);
214 if (*Iter == Index)
215 {
216 *Iter = Item.Next;
217 Item.Next = IntervalTreeInvalidIndex;
218 return true;
219 }
220 Iter = &Item.Next;
221 }
222 return false;
223}
224
225template<typename TItem>
226static bool RemoveFromIntervalTree(
227 TIntervalTreeIndex* RootNode,
233 uint32 MaxShift
234)
235{
236 bool bResult = false;
237 if (*RootNode != IntervalTreeInvalidIndex)
238 {
241 FIntervalTreeNode& Root = GIntervalTreeNodeNodeAllocator.Get(*RootNode);
242
243 if (!MinShifted && !MaxShifted)
244 {
245 if (CurrentShift == MaxShift)
246 {
247 bResult = ScanNodeListForRemoval(&Root.LeftChildOrRootOfLeftList, Allocator, Index, MinInterval, MaxInterval);
248 }
249 else
250 {
251 bResult = RemoveFromIntervalTree(&Root.LeftChildOrRootOfLeftList, Allocator, Index, MinInterval, MaxInterval, CurrentShift + 1, MaxShift);
252 }
253 }
254 else if (!MinShifted && MaxShifted)
255 {
256 bResult = ScanNodeListForRemoval(&Root.RootOfOnList, Allocator, Index, MinInterval, MaxInterval);
257 }
258 else
259 {
260 if (CurrentShift == MaxShift)
261 {
262 bResult = ScanNodeListForRemoval(&Root.RightChildOrRootOfRightList, Allocator, Index, MinInterval, MaxInterval);
263 }
264 else
265 {
266 bResult = RemoveFromIntervalTree(&Root.RightChildOrRootOfRightList, Allocator, Index, MinInterval, MaxInterval, CurrentShift + 1, MaxShift);
267 }
268 }
269 if (bResult)
270 {
271 if (Root.LeftChildOrRootOfLeftList == IntervalTreeInvalidIndex && Root.RootOfOnList == IntervalTreeInvalidIndex && Root.RightChildOrRootOfRightList == IntervalTreeInvalidIndex)
272 {
273 check(&Root == &GIntervalTreeNodeNodeAllocator.Get(*RootNode));
274 GIntervalTreeNodeNodeAllocator.Free(*RootNode);
275 *RootNode = IntervalTreeInvalidIndex;
276 }
277 }
278 }
279 return bResult;
280}
281
282template<typename TItem>
283static bool RemoveFromIntervalTree(
284 TIntervalTreeIndex* RootNode,
287 uint32 StartShift,
288 uint32 MaxShift
289)
290{
291 TItem& Item = Allocator.Get(Index);
292 uint64 MinInterval = GetRequestOffset(Item.OffsetAndPakIndex);
293 uint64 MaxInterval = MinInterval + Item.Size - 1;
294 return RemoveFromIntervalTree(RootNode, Allocator, Index, MinInterval, MaxInterval, StartShift, MaxShift);
295}
296
297template<typename TItem>
298static FORCEINLINE void ScanNodeListForRemovalFunc(
299 TIntervalTreeIndex* Iter,
304)
305{
306 while (*Iter != IntervalTreeInvalidIndex)
307 {
308 TItem& Item = Allocator.Get(*Iter);
309 uint64 Offset = uint64(GetRequestOffset(Item.OffsetAndPakIndex));
310 uint64 LastByte = Offset + uint64(Item.Size) - 1;
311
312 // save the value and then clear it.
313 TIntervalTreeIndex NextIndex = Item.Next;
314 if (IntervalsIntersect(MinInterval, MaxInterval, Offset, LastByte) && Func(*Iter))
315 {
316 *Iter = NextIndex; // this may have already be deleted, so cannot rely on the memory block
317 }
318 else
319 {
320 Iter = &Item.Next;
321 }
322 }
323}
324
325template<typename TItem>
326static void MaybeRemoveOverlappingNodesInIntervalTree(
327 TIntervalTreeIndex* RootNode,
332 uint64 MaxNode,
334 uint32 MaxShift,
336)
337{
338 if (*RootNode != IntervalTreeInvalidIndex)
339 {
342 FIntervalTreeNode& Root = GIntervalTreeNodeNodeAllocator.Get(*RootNode);
343 uint64 Center = (MinNode + MaxNode + 1) >> 1;
344
345 //UE_LOG(LogTemp, Warning, TEXT("Exploring Node %X [%d, %d] %d%d interval %llX %llX node interval %llX %llX center %llX "), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted, MinInterval, MaxInterval, MinNode, MaxNode, Center);
346
347
348 if (!MinShifted)
349 {
350 if (CurrentShift == MaxShift)
351 {
352 //UE_LOG(LogTemp, Warning, TEXT("LeftBottom %X [%d, %d] %d%d"), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted);
353 ScanNodeListForRemovalFunc(&Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, MaxInterval, Func);
354 }
355 else
356 {
357 //UE_LOG(LogTemp, Warning, TEXT("LeftRecur %X [%d, %d] %d%d"), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted);
358 MaybeRemoveOverlappingNodesInIntervalTree(&Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, FMath::Min(MaxInterval, Center - 1), MinNode, Center - 1, CurrentShift + 1, MaxShift, Func);
359 }
360 }
361
362 //UE_LOG(LogTemp, Warning, TEXT("Center %X [%d, %d] %d%d"), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted);
363 ScanNodeListForRemovalFunc(&Root.RootOfOnList, Allocator, MinInterval, MaxInterval, Func);
364
365 if (MaxShifted)
366 {
367 if (CurrentShift == MaxShift)
368 {
369 //UE_LOG(LogTemp, Warning, TEXT("RightBottom %X [%d, %d] %d%d"), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted);
370 ScanNodeListForRemovalFunc(&Root.RightChildOrRootOfRightList, Allocator, MinInterval, MaxInterval, Func);
371 }
372 else
373 {
374 //UE_LOG(LogTemp, Warning, TEXT("RightRecur %X [%d, %d] %d%d"), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted);
375 MaybeRemoveOverlappingNodesInIntervalTree(&Root.RightChildOrRootOfRightList, Allocator, FMath::Max(MinInterval, Center), MaxInterval, Center, MaxNode, CurrentShift + 1, MaxShift, Func);
376 }
377 }
378
379 //UE_LOG(LogTemp, Warning, TEXT("Done Exploring Node %X [%d, %d] %d%d interval %llX %llX node interval %llX %llX center %llX "), *RootNode, CurrentShift, MaxShift, !!MinShifted, !!MaxShifted, MinInterval, MaxInterval, MinNode, MaxNode, Center);
380
381 if (Root.LeftChildOrRootOfLeftList == IntervalTreeInvalidIndex && Root.RootOfOnList == IntervalTreeInvalidIndex && Root.RightChildOrRootOfRightList == IntervalTreeInvalidIndex)
382 {
383 check(&Root == &GIntervalTreeNodeNodeAllocator.Get(*RootNode));
384 GIntervalTreeNodeNodeAllocator.Free(*RootNode);
385 *RootNode = IntervalTreeInvalidIndex;
386 }
387 }
388}
389
390
391template<typename TItem>
392static FORCEINLINE bool ScanNodeList(
398)
399{
400 while (Iter != IntervalTreeInvalidIndex)
401 {
402 TItem& Item = Allocator.Get(Iter);
403 uint64 Offset = uint64(GetRequestOffset(Item.OffsetAndPakIndex));
404 uint64 LastByte = Offset + uint64(Item.Size) - 1;
405 if (IntervalsIntersect(MinInterval, MaxInterval, Offset, LastByte))
406 {
407 if (!Func(Iter))
408 {
409 return false;
410 }
411 }
412 Iter = Item.Next;
413 }
414 return true;
415}
416
417template<typename TItem>
418static bool OverlappingNodesInIntervalTree(
419 TIntervalTreeIndex RootNode,
424 uint64 MaxNode,
426 uint32 MaxShift,
428)
429{
430 if (RootNode != IntervalTreeInvalidIndex)
431 {
434 FIntervalTreeNode& Root = GIntervalTreeNodeNodeAllocator.Get(RootNode);
435 uint64 Center = (MinNode + MaxNode + 1) >> 1;
436
437 if (!MinShifted)
438 {
439 if (CurrentShift == MaxShift)
440 {
441 if (!ScanNodeList(Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, MaxInterval, Func))
442 {
443 return false;
444 }
445 }
446 else
447 {
448 if (!OverlappingNodesInIntervalTree(Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, FMath::Min(MaxInterval, Center - 1), MinNode, Center - 1, CurrentShift + 1, MaxShift, Func))
449 {
450 return false;
451 }
452 }
453 }
454 if (!ScanNodeList(Root.RootOfOnList, Allocator, MinInterval, MaxInterval, Func))
455 {
456 return false;
457 }
458 if (MaxShifted)
459 {
460 if (CurrentShift == MaxShift)
461 {
462 if (!ScanNodeList(Root.RightChildOrRootOfRightList, Allocator, MinInterval, MaxInterval, Func))
463 {
464 return false;
465 }
466 }
467 else
468 {
469 if (!OverlappingNodesInIntervalTree(Root.RightChildOrRootOfRightList, Allocator, FMath::Max(MinInterval, Center), MaxInterval, Center, MaxNode, CurrentShift + 1, MaxShift, Func))
470 {
471 return false;
472 }
473 }
474 }
475 }
476 return true;
477}
478
479template<typename TItem>
480static bool ScanNodeListWithShrinkingInterval(
486)
487{
488 while (Iter != IntervalTreeInvalidIndex)
489 {
490 TItem& Item = Allocator.Get(Iter);
491 uint64 Offset = uint64(GetRequestOffset(Item.OffsetAndPakIndex));
492 uint64 LastByte = Offset + uint64(Item.Size) - 1;
493 //UE_LOG(LogTemp, Warning, TEXT("Test Overlap %llu %llu %llu %llu"), MinInterval, MaxInterval, Offset, LastByte);
494 if (IntervalsIntersect(MinInterval, MaxInterval, Offset, LastByte))
495 {
496 //UE_LOG(LogTemp, Warning, TEXT("Overlap %llu %llu %llu %llu"), MinInterval, MaxInterval, Offset, LastByte);
497 if (!Func(Iter))
498 {
499 return false;
500 }
501 }
502 Iter = Item.Next;
503 }
504 return true;
505}
506
507template<typename TItem>
508static bool OverlappingNodesInIntervalTreeWithShrinkingInterval(
509 TIntervalTreeIndex RootNode,
514 uint64 MaxNode,
516 uint32 MaxShift,
518)
519{
520 if (RootNode != IntervalTreeInvalidIndex)
521 {
522
524 int64 MaxShifted = HighBit(FMath::Min(MaxInterval, MaxNode) << CurrentShift); // since MaxInterval is changing, we cannot clamp it during recursion.
525 FIntervalTreeNode& Root = GIntervalTreeNodeNodeAllocator.Get(RootNode);
526 uint64 Center = (MinNode + MaxNode + 1) >> 1;
527
528 if (!MinShifted)
529 {
530 if (CurrentShift == MaxShift)
531 {
532 if (!ScanNodeListWithShrinkingInterval(Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, MaxInterval, Func))
533 {
534 return false;
535 }
536 }
537 else
538 {
539 if (!OverlappingNodesInIntervalTreeWithShrinkingInterval(Root.LeftChildOrRootOfLeftList, Allocator, MinInterval, MaxInterval, MinNode, Center - 1, CurrentShift + 1, MaxShift, Func)) // since MaxInterval is changing, we cannot clamp it during recursion.
540 {
541 return false;
542 }
543 }
544 }
545 if (!ScanNodeListWithShrinkingInterval(Root.RootOfOnList, Allocator, MinInterval, MaxInterval, Func))
546 {
547 return false;
548 }
549 MaxShifted = HighBit(FMath::Min(MaxInterval, MaxNode) << CurrentShift); // since MaxInterval is changing, we cannot clamp it during recursion.
550 if (MaxShifted)
551 {
552 if (CurrentShift == MaxShift)
553 {
554 if (!ScanNodeListWithShrinkingInterval(Root.RightChildOrRootOfRightList, Allocator, MinInterval, MaxInterval, Func))
555 {
556 return false;
557 }
558 }
559 else
560 {
561 if (!OverlappingNodesInIntervalTreeWithShrinkingInterval(Root.RightChildOrRootOfRightList, Allocator, FMath::Max(MinInterval, Center), MaxInterval, Center, MaxNode, CurrentShift + 1, MaxShift, Func))
562 {
563 return false;
564 }
565 }
566 }
567 }
568 return true;
569}
570
571
572template<typename TItem>
573static void MaskInterval(
578 uint32 BytesToBitsShift,
579 uint64* Bits
580)
581{
582 TItem& Item = Allocator.Get(Index);
583 uint64 Offset = uint64(GetRequestOffset(Item.OffsetAndPakIndex));
584 uint64 LastByte = Offset + uint64(Item.Size) - 1;
586 uint64 InterMaxInterval = FMath::Min(MaxInterval, LastByte);
588 {
589 uint32 FirstBit = uint32((InterMinInterval - MinInterval) >> BytesToBitsShift);
590 uint32 LastBit = uint32((InterMaxInterval - MinInterval) >> BytesToBitsShift);
592 uint32 LastQWord = LastBit >> 6;
595 if (FirstQWord == LastQWord)
596 {
598 }
599 else
600 {
603 {
605 }
606 Bits[LastQWord] |= (MAX_uint64 >> (63 - LastBitQWord));
607 }
608 }
609}
610
611
612
613template<typename TItem>
614static void OverlappingNodesInIntervalTreeMask(
615 TIntervalTreeIndex RootNode,
620 uint64 MaxNode,
622 uint32 MaxShift,
623 uint32 BytesToBitsShift,
624 uint64* Bits
625)
626{
627 OverlappingNodesInIntervalTree(
628 RootNode,
629 Allocator,
632 MinNode,
633 MaxNode,
635 MaxShift,
636 [&Allocator, MinInterval, MaxInterval, BytesToBitsShift, Bits](TIntervalTreeIndex Index) -> bool
637 {
638 MaskInterval(Index, Allocator, MinInterval, MaxInterval, BytesToBitsShift, Bits);
639 return true;
640 }
641 );
642}
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define check(expr)
Definition AssertionMacros.h:314
#define verify(expr)
Definition AssertionMacros.h:319
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
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 MAX_uint32
Definition NumericLimits.h:21
#define MAX_uint64
Definition NumericLimits.h:22
uint32 TIntervalTreeIndex
Definition PakIntervalTree.inl:10
@ IntervalTreeInvalidIndex
Definition PakIntervalTree.inl:7
uint64 FJoinedOffsetAndPakIndex
Definition PakIntervalTree.inl:12
uint32 Offset
Definition VulkanMemory.cpp:4033
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition PakIntervalTree.inl:99
TIntervalTreeIndex LeftChildOrRootOfLeftList
Definition PakIntervalTree.inl:101
TIntervalTreeIndex RightChildOrRootOfRightList
Definition PakIntervalTree.inl:103
TIntervalTreeIndex RootOfOnList
Definition PakIntervalTree.inl:102
~FIntervalTreeNode()
Definition PakIntervalTree.inl:111
FIntervalTreeNode()
Definition PakIntervalTree.inl:105
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUninitialized()
Definition Array.h:1664
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT void Push(ElementType &&Item)
Definition Array.h:1224
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
UE_NODEBUG UE_FORCEINLINE_HINT SizeType GetSlack() const
Definition Array.h:1069
void Empty(SizeType Slack=0)
Definition Array.h:2273
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition AssetRegistryState.h:50
Definition PakIntervalTree.inl:35
FORCEINLINE TItem & Get(TIntervalTreeIndex InIndex)
Definition PakIntervalTree.inl:71
FORCEINLINE void CheckIndex(TIntervalTreeIndex InIndex)
Definition PakIntervalTree.inl:91
void EnsureNoRealloc(int32 NeededNewNum)
Definition PakIntervalTree.inl:64
FORCEINLINE void Free(TIntervalTreeIndex InIndex)
Definition PakIntervalTree.inl:77
TIntervalTreeAllocator()
Definition PakIntervalTree.inl:41
TIntervalTreeIndex Alloc()
Definition PakIntervalTree.inl:48
@ Bits
Definition PacketView.h:34
U16 Index
Definition radfft.cpp:71