UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
DirectoryTree.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
9#include "HAL/Platform.h"
10#include "Misc/EnumClassFlags.h"
11#include "Misc/PathViews.h"
12#include "Misc/ScopeExit.h"
13#include "Misc/StringBuilder.h"
14#include "Templates/Tuple.h"
16#include "Templates/UniquePtr.h"
17
18namespace UE::DirectoryTree
19{
20
24
25} // namespace UE::DirectoryTree
26
28{
29 None = 0,
40 Recursive = 0x1,
46 ImpliedParent = 0x1 << 1,
53 ImpliedChildren = 0x1 << 2,
54};
56
83template <typename ValueType>
85{
86public:
87 template <typename ViewedValueType> struct TIterator;
90 template <typename ViewedValueType> struct TPointerIterator;
93 struct FIterationSentinel;
94private:
95 struct FIteratorInternal;
96 struct FTreeNode;
97
98public:
100
103
106
112 ValueType& FindOrAdd(FStringView Path, bool* bOutExisted = nullptr);
114 void Empty();
116 void Remove(FStringView Path, bool* bOutExisted = nullptr);
118 void Shrink();
119
121 bool IsEmpty() const;
123 int32 Num() const;
126
128 bool Contains(FStringView Path) const;
130 const ValueType* Find(FStringView Path) const;
132 ValueType* Find(FStringView Path);
139 const ValueType* FindClosestValue(FStringView Path) const;
149 bool TryFindClosestPath(FStringView Path, FStringBuilderBase& OutPath, const ValueType** OutValue = nullptr) const;
159 bool TryFindClosestPath(FStringView Path, FString& OutPath, const ValueType** OutValue = nullptr) const;
164 bool TryFindClosestPath(FStringView Path, FString& OutPath, ValueType** OutValue);
165
170
180
184 FIterationSentinel end() const;
185
189
199
200public:
206 {
207 };
208
214 template <typename ViewedValueType>
216 {
217 public:
219 bool operator!=(const FIterationSentinel&) const;
221
222 explicit operator bool() const;
223
224 // Support Iter->Key and Iter->Value.
226 private:
227 friend class TDirectoryTree<ValueType>;
229 FIteratorInternal Internal;
230 };
231
237 template <typename ViewedValueType>
239 {
240 public:
242 bool operator!=(const FIterationSentinel&) const;
243 TPointerIterator& operator++();
244
245 explicit operator bool() const;
246
247 // Support Iter->Key and Iter->Value.
249 private:
250 friend class TDirectoryTree<ValueType>;
251 TPointerIterator(FTreeNode& Root, EDirectoryTreeGetFlags InFlags, TCHAR InPathSeparator);
252 FIteratorInternal Internal;
253 };
254
255private:
256
274 struct FTreeNode
275 {
276 public:
277 FTreeNode() = default;
278 FTreeNode(FTreeNode&& Other);
279 FTreeNode& operator=(FTreeNode&& Other);
280 FTreeNode(const FTreeNode& Other);
281 FTreeNode& operator=(const FTreeNode& Other);
282
284 void Reset();
285
286 int32 GetNumChildNodes() const;
287
292 ValueType& FindOrAdd(FStringView InRelPath, bool& bOutExisted);
296 ValueType* Find(FStringView InRelPath);
297
304 bool ContainsChildPaths(FStringView InRelPath) const;
305
308
313 ValueType* TryFindClosestPath(FStringView RelPath, FStringBuilderBase* OutPath, TCHAR SeparatorChar);
314
316 bool IsEmpty() const;
318 SIZE_T GetAllocatedSize() const;
319
321 void Shrink();
322
328 bool HasValue() const;
330 ValueType& GetValue();
332 const ValueType& GetValue() const;
334 void SetValue(ValueType&& InValue);
336 void SetDefaultValue();
338 void RemoveValue();
339
340 void FixupDirectChildrenPathSeparator(TCHAR OldSeparator, TCHAR NewSeparator);
341
342 private:
343 static constexpr uint32 NumFlagBits = 1u;
344 static constexpr uint32 FlagsShift = (8u * sizeof(uint32) - NumFlagBits);
345 static constexpr uint32 FlagsMask = 0x1u << FlagsShift;
346 void SetNumChildNodes(int32 InNumChildNodes);
347
353 int32 FindInsertionIndex(FStringView FirstPathComponent, bool& bOutExists) const;
355 FTreeNode& InsertChildNode(int32 InsertionIndex, FString&& RelPath, FTreeNode&& ChildNode);
357 void RemoveChildNodeAt(int32 RemoveIndex);
358
360 static void ConditionalCompactNode(FString& RelPath, FTreeNode& ChildNode);
362 void Realloc(int32 NewCapacity);
363
364 private:
365 friend struct FIteratorInternal;
366
368 TUniquePtr<FString[]> RelPaths;
369 TUniquePtr<FTreeNode[]> ChildNodes;
370 uint32 NumChildNodesAndFlags = 0;
371 int32 CapacityChildNodes = 0;
372 };
373
374 struct FIteratorInternal
375 {
376 public:
377 FIteratorInternal(FTreeNode& Root, EDirectoryTreeGetFlags InFlags, TCHAR InPathSeparator);
378 // Iterators for TDirectoryTree are heavy-weight; they keep a TArray and a TStringBuilder.
379 // Do not copy or move them use them only in for loops, or on the heap:
380 // for (TDirectoryTree<...>::FIterator Iter(Tree.CreateIterator()); Iter; ++Iter)
381 // TUniquePtr<TDirectoryTree<...>::FIterator> IterPtr = MakeUnique<TDirectoryTree<...>::FIterator>(Tree.CreateIterator())
382 FIteratorInternal(FIteratorInternal&& Other) = delete;
383 FIteratorInternal& operator=(FIteratorInternal&& Other) = delete;
384 FIteratorInternal(const FIteratorInternal& Other) = delete;
385 FIteratorInternal& operator=(const FIteratorInternal& Other) = delete;
386
387 TPair<FStringView, ValueType*> operator*() const;
388 bool operator!=(const FIterationSentinel&) const;
389 FIteratorInternal& operator++();
390
391 explicit operator bool() const;
392
393 private:
394 struct FStackData
395 {
396 explicit FStackData(FTreeNode& InNode, ValueType* ParentClosestValue);
397
398 FStringView RemainingChildRelPath;
399 FTreeNode* Node;
400 ValueType* ClosestValue;
401 int32 ChildIndex;
402 int32 PathLenBeforeChildNode;
403 bool bChildRelPathInitialized;
404 bool bChildNodeInitialized;
405 };
406
407 private:
408 void TraverseToValid();
409 void IncrementRemainingChildRelPath(FStackData* Data);
410
411 private:
415 TCHAR PathSeparator;
416 };
417
418private:
419 bool NormalizePathForReading(FStringView& Path, FStringBuilderBase& NormalizeBuffer) const;
420 bool NormalizePathForWriting(FStringView& Path, FStringBuilderBase& NormalizeBuffer);
421 ValueType* TryFindClosestPathInternal(FStringView Path, FStringBuilderBase* OutPath);
422 void InitializePathSeparator(TCHAR InPathSeparator);
423
424private:
425 FTreeNode Root;
426 int32 NumPaths = 0;
427 TCHAR PathSeparator = '/';
428 bool bPathSeparatorInitialized = false;
429 bool bNeedDriveWithoutPathFixup = false;
430};
431
432
433template <typename ValueType>
435{
436 check(Root.IsEmpty());
437}
438
439template <typename ValueType>
441{
442 if (Path.IsEmpty())
443 {
444 if (bOutExisted)
445 {
446 *bOutExisted = Root.HasValue();
447 }
448 if (!Root.HasValue())
449 {
450 Root.SetDefaultValue();
451 ++NumPaths;
452 }
453 return Root.GetValue();
454 }
455
456 if (!bPathSeparatorInitialized)
457 {
459 if (Path.FindChar('/', UnusedIndex))
460 {
461 InitializePathSeparator('/');
462 }
463 else if (Path.FindChar('\\', UnusedIndex))
464 {
465 InitializePathSeparator('\\');
466 }
467 }
468
470 NormalizePathForWriting(Path, NormalizeBuffer);
471
472 bool bExisted;
473 ValueType& Result = Root.FindOrAdd(Path, bExisted);
474 if (!bExisted)
475 {
476 ++NumPaths;
477 }
478
479 if (bOutExisted)
480 {
482 }
483
484 return Result;
485}
486
487template <typename ValueType>
489{
490 Root.Reset();
491 NumPaths = 0;
492 PathSeparator = '/';
493 bPathSeparatorInitialized = false;
494}
495
496template <typename ValueType>
498{
499 bool bExisted;
500 if (Path.IsEmpty())
501 {
502 bExisted = Root.HasValue();
503 if (bExisted)
504 {
505 Root.RemoveValue();
506 }
507 }
508 else
509 {
511 NormalizePathForReading(Path, NormalizeBuffer);
512
513 Root.Remove(Path, bExisted);
514 }
515 if (bExisted)
516 {
517 check(NumPaths > 0);
518 --NumPaths;
519 }
520 if (bOutExisted)
521 {
523 }
524}
525
526template <typename ValueType>
528{
529 Root.Shrink();
530}
531
532template <typename ValueType>
534{
535 return Root.IsEmpty();
536}
537
538template <typename ValueType>
540{
541 return NumPaths;
542}
543
544template <typename ValueType>
546{
547 return Root.GetAllocatedSize();
548}
549
550template <typename ValueType>
552{
553 return Find(Path) != nullptr;
554}
555
556template <typename ValueType>
557inline const ValueType* TDirectoryTree<ValueType>::Find(FStringView Path) const
558{
559 return const_cast<TDirectoryTree*>(this)->Find(Path);
560}
561
562template <typename ValueType>
564{
565 if (Path.IsEmpty())
566 {
567 return Root.HasValue() ? &Root.GetValue() : nullptr;
568 }
569
571 NormalizePathForReading(Path, NormalizeBuffer);
572
573 return Root.Find(Path);
574}
575
576template <typename ValueType>
578{
579 return FindClosestValue(Path) != nullptr;
580}
581
582template <typename ValueType>
584{
585 return const_cast<TDirectoryTree*>(this)->TryFindClosestPathInternal(Path, nullptr);
586}
587
588template <typename ValueType>
590{
591 return TryFindClosestPathInternal(Path, nullptr);
592}
593
594template <typename ValueType>
595inline bool TDirectoryTree<ValueType>::TryFindClosestPath(FStringView Path, FString& OutPath, const ValueType** OutValue) const
596{
597 ValueType* ResultValue;
598 bool bResult = const_cast<TDirectoryTree*>(this)->TryFindClosestPath(Path, OutPath, &ResultValue);
599 if (bResult)
600 {
601 if (OutValue)
602 {
604 }
605 }
606 return bResult;
607}
608
609template <typename ValueType>
611{
612 TStringBuilder<1024> Builder;
613 OutPath.Reset(); // Reset the path even if we fail, to match the behavior when taking a StringBuilderBase
614 ValueType* Result = TryFindClosestPathInternal(Path, &Builder);
615 if (Result)
616 {
617 OutPath.Append(Builder);
618 if (OutValue)
619 {
620 *OutValue = Result;
621 }
622 return true;
623 }
624 else
625 {
626 return false;
627 }
628}
629
630template <typename ValueType>
632{
633 ValueType* ResultValue;
634 bool bResult = const_cast<TDirectoryTree*>(this)->TryFindClosestPath(Path, OutPath, &ResultValue);
635 if (bResult)
636 {
637 if (OutValue)
638 {
640 }
641 }
642 return bResult;
643}
644
645template <typename ValueType>
647{
648 ValueType* Result = TryFindClosestPathInternal(Path, &OutPath);
649 if (Result)
650 {
651 if (OutValue)
652 {
653 *OutValue = Result;
654 }
655 return true;
656 }
657 else
658 {
659 return false;
660 }
661}
662
663template <typename ValueType>
666{
667 if (OutPath)
668 {
669 // We build the path as we go along, before we know whether we will return true,
670 // so we have to reset it now
671 OutPath->Reset();
672 }
673 if (!Path.IsEmpty())
674 {
676 NormalizePathForReading(Path, NormalizeBuffer);
677
678 ValueType* Result = Root.TryFindClosestPath(Path, OutPath, PathSeparator);
679 if (Result)
680 {
681 return Result;
682 }
683 }
684
685 return Root.HasValue() ? &Root.GetValue() : nullptr;
686}
687
688template <typename ValueType>
691{
692 // Drive specifiers without a root are a special case; they break our assumption that if
693 // if FPathViews::IsParentPathOf(DriveSpecifier, PathInThatDrive)
694 // then DriveSpecifier == FirstComponentOfPathInThatDrive.
695 // 'D:' is a parent path of 'D:/Path' but FirstComponent of 'D:/Path' is 'D:/' != 'D:'
696 //
697 // In general usage on e.g. Windows, drive specifiers without a path are interpreted to mean the current
698 // working directory of the given drive. But we don't have that context so that meaning is not applicable.
699 //
700 // We therefore instead interpret them to mean the root of the drive. Append the PathSeparator to make
701 // them the root.
703 {
705 FStringView Remainder;
707 NormalizeBuffer << Volume << PathSeparator << Remainder;
708 Path = NormalizeBuffer.ToView();
709 return true;
710 }
711 return false;
712}
713
714template <typename ValueType>
716{
717 if (NormalizePathForReading(Path, NormalizeBuffer))
718 {
719 // If the call to NormalizePath came from a function that is adding paths to the tree, and
720 // !bPathSeparatorInitialized, then leave a marker that we might need to fix up the added paths
721 // when we encounter the user's desired path separator.
722 bNeedDriveWithoutPathFixup = !bPathSeparatorInitialized;
723 return true;
724 }
725 return false;
726}
727
728template <typename ValueType>
730{
731 check(!bPathSeparatorInitialized);
732
733 // If the requested PathSeparator is not the one we guessed it was when we had to
734 // normalize a drive without a path (e.g. 'D:' -> 'D:/'), then fixup all those
735 // drive children to have the desired separator.
736 bNeedDriveWithoutPathFixup &= (InPathSeparator != PathSeparator);
737 if (bNeedDriveWithoutPathFixup)
738 {
739 // The drives without paths will be direct children of the root so we only need
740 // to fixup direct children
741 Root.FixupDirectChildrenPathSeparator(PathSeparator, InPathSeparator);
742 bNeedDriveWithoutPathFixup = false;
743 }
744
745 PathSeparator = InPathSeparator;
746 bPathSeparatorInitialized = true;
747}
748
749template<typename ValueType>
751{
753 NormalizePathForReading(Path, NormalizeBuffer);
754
755 return Root.ContainsChildPaths(Path);
756}
757
758template<typename ValueType>
761{
762 if (Path.IsEmpty() && !EnumHasAnyFlags(Flags, EDirectoryTreeGetFlags::ImpliedParent) && !Root.HasValue())
763 {
764 return false;
765 }
767 NormalizePathForReading(Path, NormalizeBuffer);
768
770 return Root.TryGetChildren(ReportedPathPrefix, PathSeparator, Path, OutRelativeChildNames, Flags);
771}
772
773template<typename ValueType>
775{
776 *this = Other;
777}
778
779template<typename ValueType>
781{
782 Reset();
783
784 if (Other.HasValue())
785 {
786 SetValue(CopyTemp(Other.GetValue()));
787 }
788
789 Realloc(Other.CapacityChildNodes);
790 int32 NumChildren = Other.GetNumChildNodes();
791 for (int32 i = 0; i < NumChildren; ++i)
792 {
793 RelPaths[i] = Other.RelPaths[i];
794 }
795 for (int32 i = 0; i < NumChildren; ++i)
796 {
797 ChildNodes[i] = Other.ChildNodes[i];
798 }
799
800 SetNumChildNodes(Other.GetNumChildNodes());
801 return *this;
802}
803
804template <typename ValueType>
806{
807 *this = MoveTemp(Other);
808}
809
810template <typename ValueType>
812{
813 if (Other.HasValue())
814 {
815 SetValue(MoveTemp(Other.GetValue()));
816 Other.RemoveValue();
817 }
818 else
819 {
820 RemoveValue();
821 }
822 RelPaths = MoveTemp(Other.RelPaths);
823 ChildNodes = MoveTemp(Other.ChildNodes);
824 SetNumChildNodes(Other.GetNumChildNodes());
825 CapacityChildNodes = MoveTemp(Other.CapacityChildNodes);
826
827 Other.SetNumChildNodes(0);
828 Other.CapacityChildNodes = 0;
829 return *this;
830}
831
832template <typename ValueType>
834{
835 RemoveValue();
836 RelPaths.Reset();
837 ChildNodes.Reset();
838 NumChildNodesAndFlags = 0;
839 CapacityChildNodes = 0;
840}
841
842template <typename ValueType>
844{
845 return (int32)(NumChildNodesAndFlags & ~FlagsMask);
846}
847
848template <typename ValueType>
850{
852 NumChildNodesAndFlags =
854 (NumChildNodesAndFlags & FlagsMask);
855}
856
857template <typename ValueType>
859{
860 return (NumChildNodesAndFlags & FlagsMask) != 0;
861}
862
863template <typename ValueType>
865{
866 return *reinterpret_cast<ValueType*>(&Value);
867}
868
869template<typename ValueType>
870inline const ValueType& TDirectoryTree<ValueType>::FTreeNode::GetValue() const
871{
872 return *reinterpret_cast<const ValueType*>(&Value);
873}
874
875template <typename ValueType>
877{
878 RemoveValue();
879
880 uint32 FlagsValue = 0x1;
881 NumChildNodesAndFlags =
882 (NumChildNodesAndFlags & ~FlagsMask) |
883 (FlagsValue << FlagsShift);
884 new (&Value) ValueType(MoveTemp(InValue));
885}
886
887template <typename ValueType>
889{
890 RemoveValue();
891
892 uint32 FlagsValue = 0x1;
893 NumChildNodesAndFlags =
894 (NumChildNodesAndFlags & ~FlagsMask) |
895 (FlagsValue << FlagsShift);
896 new (&Value) ValueType();
897}
898
899template <typename ValueType>
901{
902 if (HasValue())
903 {
904 uint32 FlagsValue = 0x0;
905 NumChildNodesAndFlags =
906 (NumChildNodesAndFlags & ~FlagsMask) |
907 (FlagsValue << FlagsShift);
908
909 // We need a typedef here because VC won't compile the destructor call below if ValueType itself has a member called ValueType
910 typedef ValueType FDirectoryTreeDestructValueType;
911 ((ValueType*)&Value)->FDirectoryTreeDestructValueType::~FDirectoryTreeDestructValueType();
912 }
913}
914
915template <typename ValueType>
917{
921 check(!FirstComponent.IsEmpty());
922 bool bExists;
924 if (!bExists)
925 {
926 bOutExisted = false;
927 FTreeNode& ChildNode = InsertChildNode(InsertionIndex, FString(InRelPath), FTreeNode());
928 ChildNode.SetDefaultValue();
929 return ChildNode.GetValue();
930 }
931
932
933 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
934 FString& ChildRelPath = RelPaths[InsertionIndex];
935
939 check(!ExistingFirstComponent.IsEmpty());
941 for (int32 RunawayLoop = 0; RunawayLoop <= InRelPath.Len(); ++RunawayLoop)
942 {
943 if (ExistingRemainingPath.IsEmpty())
944 {
945 // We've reached the end of the existing path
946 if (!RemainingPath.IsEmpty())
947 {
948 // We have not reached the end of the input path, so it is a child of the existing path
949 return ChildNode.FindOrAdd(RemainingPath, bOutExisted);
950 }
951 else
952 {
953 // The input path matches the existing path
954 bOutExisted = ChildNode.HasValue();
955 if (!ChildNode.HasValue())
956 {
957 ChildNode.SetDefaultValue();
958 }
959 return ChildNode.GetValue();
960 }
961 }
962 else if (RemainingPath.IsEmpty())
963 {
964 // We've reached the end of the input path, so it is a parent of the existing path
965 bOutExisted = false;
966
967 // Create a new ChildNode and move the existing ChildNode into a child of it
968 // We can modify RelPath in place without messing up the sort order because the nodes are sorted by
969 // FirstComponent only and the FirstComponent is not changing
970 FTreeNode OldTreeNode = MoveTemp(ChildNode);
972 ChildNode.Reset();
973 ChildNode.SetDefaultValue();
975 ChildNode.InsertChildNode(0, MoveTemp(OldRelPath), MoveTemp(OldTreeNode));
976 return ChildNode.GetValue();
977 }
978 else
979 {
980 // Both existing and remaining have more directory components
984 check(!NextFirstComponent.IsEmpty());
989 if (NextFirstComponent.Equals(NextExistingFirstComponent, ESearchCase::IgnoreCase)) // This works around a compiler bug. Should be changed to "NextFirstComponent == NextExistingFirstComponent"
990 {
991 // Next component is also a match, go to the next loop iteration to handle the new remainingpaths
995 continue;
996 }
997 else
998 {
999 // Existing and remaining firstcomponent differ, so they are both child paths of a mutual parent path
1000 // Reconstruct the CommonParentPath
1004 {
1010 }
1011
1012 // Create a new ChildNode and move the existing ChildNode into a child of it
1013 // We can modify RelPath in place without messing up the sort order because the nodes are sorted by
1014 // FirstComponent only and the FirstComponent is not changing
1015 FTreeNode OldTreeNode = MoveTemp(ChildNode);
1017 ChildNode.Reset();
1019 ChildNode.InsertChildNode(0, MoveTemp(OldRelPath), MoveTemp(OldTreeNode));
1020
1021 // The input path is now a child of the modified ChildNode
1022 // It's impossible for FindOrAdd to fail.
1023 // If it ever becomes possible for it to fail, we will have to remove the child we added if it fails.
1024 return ChildNode.FindOrAdd(RemainingPath, bOutExisted);
1025 }
1026 }
1027 }
1028 checkf(false, TEXT("Infinite loop trying to split path %.*s into components."), InRelPath.Len(), InRelPath.GetData());
1029 return GetValue();
1030}
1031
1032template <typename ValueType>
1034{
1035 check(!InRelPath.IsEmpty());
1036
1040 bool bExists;
1042 if (!bExists)
1043 {
1044 bOutExisted = false;
1045 return;
1046 }
1047
1048 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
1049 FString& ChildRelPath = RelPaths[InsertionIndex];
1050
1052 {
1053 bOutExisted = false;
1054 return;
1055 }
1056
1057 if (!RemainingPath.IsEmpty())
1058 {
1059 // The input path is a child of the existing path
1061 if (bOutExisted)
1062 {
1063 // If the remove was successful, the childnode must have had at least two paths, because
1064 // otherwise we would have previously Compacted it to combine its RelPath with RemainingPath.
1065 // In case it only had two paths and now has one path, try to compact it.
1066 ConditionalCompactNode(ChildRelPath, ChildNode);
1067 }
1068 }
1069 else
1070 {
1071 // The input path matches the existing path
1072 bOutExisted = ChildNode.HasValue();
1073 if (ChildNode.GetNumChildNodes() != 0)
1074 {
1075 if (ChildNode.HasValue())
1076 {
1077 ChildNode.RemoveValue();
1078 ConditionalCompactNode(ChildRelPath, ChildNode);
1079 }
1080 }
1081 else
1082 {
1083 RemoveChildNodeAt(InsertionIndex);
1084 }
1085 }
1086}
1087
1088template <typename ValueType>
1090{
1091 check(!InRelPath.IsEmpty());
1092
1096 bool bExists;
1098 if (!bExists)
1099 {
1100 return nullptr;
1101 }
1102
1103 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
1104 FString& ChildRelPath = RelPaths[InsertionIndex];
1105
1107 {
1108 return nullptr;
1109 }
1110
1111 if (!RemainingPath.IsEmpty())
1112 {
1113 // The input path is a child of the existing path
1114 return ChildNode.Find(RemainingPath);
1115 }
1116 else
1117 {
1118 // The input path matches the existing path
1119 return ChildNode.HasValue() ? &ChildNode.GetValue() : nullptr;
1120 }
1121}
1122
1123template<typename ValueType>
1125{
1126 if (InRelPath.IsEmpty())
1127 {
1128 // This is the node we were searching for as an explicit entry
1129 return GetNumChildNodes() > 0;
1130 }
1131
1132 // We are still looking for the requested InRelPath and are not reporting results yet. Look for an existing
1133 // stored child that has the same FirstComponent of its path as InRelPath does.
1137 bool bExists;
1139 if (!bExists)
1140 {
1141 // No child has the same FirstComponent as InRelPath, so InRelPath does not exist in the tree, not even as
1142 // an implied path.
1143 return false;
1144 }
1145
1146 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
1147 FString& ChildRelPath = RelPaths[InsertionIndex];
1148
1154 ExistingFirstComponent)); // Otherwise FindInsertionIndex would have returned bExists=false
1155
1156 // Same logic as TryGetChildren - progressively match the InRelPath against ChildRelPath until one is empty
1157 for (int32 RunawayLoop = 0; RunawayLoop <= InRelPath.Len(); ++RunawayLoop)
1158 {
1159 if (ExistingRemainingPath.IsEmpty())
1160 {
1161 // We've reached the end of the existing path, so InRelPath is either equal to or a child of the
1162 // ChildNode. Delegate to that node to keep searching or return results
1163 return ChildNode.ContainsChildPaths(RemainingPath);
1164 }
1165 else if (RemainingPath.IsEmpty())
1166 {
1167 // We've reached the end of the input path, but not the end of the existing path, so InRelPath is a
1168 // parent of the existing path, and is an implied path rather than an added path.
1169 return true;
1170 }
1171 else
1172 {
1173 // Both existing and remaining have more directory components
1177 check(!NextFirstComponent.IsEmpty());
1183 {
1184 // Next component is also a match, go to the next loop iteration to handle the new remainingpaths
1187
1188 continue;
1189 }
1190 else
1191 {
1192 // The existing child diverges from the path components of InRelPath, so InRelPath does not exist
1193 // in the tree, not even as an implied path.
1194 return false;
1195 }
1196 }
1197 }
1198 checkf(false, TEXT("Infinite loop trying to split path %.*s into components."), InRelPath.Len(), InRelPath.GetData());
1199 return false;
1200}
1201
1202template <typename ValueType>
1206{
1207 if (InRelPath.IsEmpty())
1208 {
1209 // RelPath indicates this node, so append the children of this node to the list.
1210 // Caller is responsible for not calling TryGetChildren on *this if results for *this
1211 // should not be returned due to EDirectoryTreeGetFlags.
1212
1213 int32 NumChildNodes = GetNumChildNodes();
1214 for (int32 Index = 0; Index < NumChildNodes; ++Index)
1215 {
1216 const FTreeNode& ChildNode = ChildNodes[Index];
1217 const FString& ChildRelPath = RelPaths[Index];
1218
1220 {
1221 // When ImpliedChildren are supposed to be reported, iterate over every stored child
1222 // and report the first component of its RelPath as a child.
1223 // If recursive, also report the remaining components of its RelPath, and then
1224 // forward the call to it to return its recursive children.
1228
1233
1235 {
1236 while (!RemainingPath.IsEmpty())
1237 {
1239
1244 }
1245
1248 }
1249 ReportPathPrefix.RemoveSuffix(ReportPathPrefix.Len() - SavedLen);
1250 }
1251 else
1252 {
1253 // When ImpliedChildren are not supposed to be reported, report each stored child by its full relpath,
1254 // unless the child is an implied path. If the child is an implied path, recursively ask the child
1255 // to report its added children. Also, if user requested recursive, ask the child to return its
1256 // recursive children even if it has a value.
1260
1261 if (ChildNode.HasValue())
1262 {
1264 }
1266 {
1269 }
1270 ReportPathPrefix.RemoveSuffix(ReportPathPrefix.Len() - SavedLen);
1271 }
1272 }
1273
1274 return true;
1275 }
1276 else
1277 {
1278 // We are still looking for the requested InRelPath and are not reporting results yet. Look for an existing
1279 // stored child that has the same FirstComponent of its path as InRelPath does.
1283 bool bExists;
1285 if (!bExists)
1286 {
1287 // No child has the same FirstComponent as InRelPath, so InRelPath does not exist in the tree, not even as
1288 // an implied path.
1289 return false;
1290 }
1291
1292 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
1293 FString& ChildRelPath = RelPaths[InsertionIndex];
1294
1298 check(FPathViews::Equals(FirstComponent, ExistingFirstComponent)); // Otherwise FindInsertionIndex would have returned bExists=false
1299
1300 for (int32 RunawayLoop = 0; RunawayLoop <= InRelPath.Len(); ++RunawayLoop)
1301 {
1302 if (ExistingRemainingPath.IsEmpty())
1303 {
1304 // We've reached the end of the existing path, so InRelPath is either equal to or a child of the
1305 // ChildNode. If it is equal to the ChildNode, it is our responsibility to NOT call TryGetChildren
1306 // if the ChildNode is an implied path and ImpliedParent flag is not requested.
1308 !ChildNode.HasValue())
1309 {
1310 return false;
1311 }
1312
1315 }
1316 else if (RemainingPath.IsEmpty())
1317 {
1318 // We've reached the end of the input path, but not the end of the existing path, so InRelPath is a
1319 // parent of the existing path, and is an implied path rather than an added path.
1321 {
1322 return false;
1323 }
1324
1326 {
1327 // When implied children are supposed to be reported, add the next pathcomponent of the remaining
1328 // child path as the first reported child of InRelPath. If recursive, also add all the remaining
1329 // components of the existing child path and then forward on to the child path for all of its children.
1336
1338 {
1339 while (!ExistingRemainingPath.IsEmpty())
1340 {
1343
1347 , InPathSeparator);
1349 }
1350
1353 }
1354
1355 ReportPathPrefix.RemoveSuffix(ReportPathPrefix.Len() - SavedLen);
1356 }
1357 else
1358 {
1359 // When implied children are not supposed to be reported, report the remaining components of the
1360 // existing child path as a single string as the first reported child of InRelPath, but only if
1361 // it is not an implied path. If recursive, forward to the childnode for all of its children.
1363 {
1367
1368 if (ChildNode.HasValue())
1369 {
1371 }
1373 {
1376 }
1377 }
1378 }
1379
1380 return true;
1381 }
1382 else
1383 {
1384 // Both existing and remaining have more directory components
1388 check(!NextFirstComponent.IsEmpty());
1394 {
1395 // Next component is also a match, go to the next loop iteration to handle the new remainingpaths
1398
1399 continue;
1400 }
1401 else
1402 {
1403 // The existing child diverges from the path components of InRelPath, so InRelPath does not exist
1404 // in the tree, not even as an implied path.
1405 return false;
1406 }
1407 }
1408 }
1409 checkf(false, TEXT("Infinite loop trying to split path %.*s into components."), InRelPath.Len(), InRelPath.GetData());
1410 }
1411
1412 check(false); //-V779 Only way to get here is from the checkf in the else block.
1413 return false;
1414}
1415
1416template <typename ValueType>
1419{
1420 check(!InRelPath.IsEmpty());
1421
1425 bool bExists;
1427 if (!bExists)
1428 {
1429 return nullptr;
1430 }
1431
1432 FTreeNode& ChildNode = ChildNodes[InsertionIndex];
1433 const FString& ChildRelPath = RelPaths[InsertionIndex];
1434
1436 {
1437 return nullptr;
1438 }
1439
1440 if (!RemainingPath.IsEmpty())
1441 {
1442 // The input path is a child of the existing path
1444 if (OutPath)
1445 {
1446 SavedOutPathLen = OutPath->Len();
1449 }
1450 ValueType* Result = ChildNode.TryFindClosestPath(RemainingPath, OutPath, InPathSeparator);
1451 if (Result)
1452 {
1453 return Result;
1454 }
1455
1456 if (ChildNode.HasValue())
1457 {
1458 return &ChildNode.GetValue();
1459 }
1460 if (OutPath)
1461 {
1462 OutPath->RemoveSuffix(OutPath->Len() - SavedOutPathLen);
1463 }
1464 return nullptr;
1465 }
1466 else
1467 {
1468 // The input path matches the existing path
1469 if (!ChildNode.HasValue())
1470 {
1471 return nullptr;
1472 }
1473 if (OutPath)
1474 {
1478 }
1479 return &ChildNode.GetValue();
1480 }
1481}
1482
1483template <typename ValueType>
1485{
1486 return !HasValue() && GetNumChildNodes() == 0;
1487}
1488
1489template <typename ValueType>
1491{
1492 SIZE_T Size = 0;
1493 Size = CapacityChildNodes * (sizeof(FTreeNode) + sizeof(FString));
1494 int32 NumChildNodes = GetNumChildNodes();
1495 for (const FString& RelPath : TConstArrayView<FString>(RelPaths.Get(), NumChildNodes))
1496 {
1497 Size += RelPath.GetAllocatedSize();
1498 }
1499 for (const FTreeNode& ChildNode : TConstArrayView<FTreeNode>(ChildNodes.Get(), NumChildNodes))
1500 {
1501 Size += ChildNode.GetAllocatedSize();
1502 }
1503 return Size;
1504}
1505
1506template <typename ValueType>
1508{
1509 int32 NumChildNodes = GetNumChildNodes();
1510 Realloc(NumChildNodes);
1511
1512 for (FString& RelPath : TArrayView<FString>(RelPaths.Get(), NumChildNodes))
1513 {
1514 RelPath.Shrink();
1515 }
1516 for (FTreeNode& ChildNode : TArrayView<FTreeNode>(ChildNodes.Get(), NumChildNodes))
1517 {
1518 ChildNode.Shrink();
1519 }
1520}
1521
1522template <typename ValueType>
1524{
1525 return UE::DirectoryTree::FindInsertionIndex(GetNumChildNodes(), RelPaths, FirstPathComponent, bOutExists);
1526}
1527
1528template <typename ValueType>
1530 FTreeNode&& ChildNode)
1531{
1532 int32 NumChildNodes = GetNumChildNodes();
1534 checkf((((uint32)(NumChildNodes + 1)) & FlagsMask) == 0, TEXT("Overflow"));
1535
1536 if (NumChildNodes == CapacityChildNodes)
1537 {
1539 if (CapacityChildNodes == 0)
1540 {
1541 NewCapacity = 1;
1542 }
1543 else if (CapacityChildNodes < 8)
1544 {
1545 NewCapacity = CapacityChildNodes * 2;
1546 }
1547 else
1548 {
1549 NewCapacity = (CapacityChildNodes * 3) / 2;
1550 checkf(NewCapacity > CapacityChildNodes, TEXT("Overflow"));
1551 }
1552 Realloc(NewCapacity);
1553 check(NumChildNodes < CapacityChildNodes);
1554 }
1555
1557 {
1558 RelPaths[ShiftIndex] = MoveTemp(RelPaths[ShiftIndex - 1]);
1559 ChildNodes[ShiftIndex] = MoveTemp(ChildNodes[ShiftIndex - 1]);
1560 }
1561 RelPaths[InsertionIndex] = MoveTemp(RelPath);
1562 ChildNodes[InsertionIndex] = MoveTemp(ChildNode);
1563 SetNumChildNodes(NumChildNodes + 1);
1564 return ChildNodes[InsertionIndex];
1565}
1566
1567template <typename ValueType>
1569{
1570 int32 NumChildNodes = GetNumChildNodes();
1571 check(0 <= RemoveIndex && RemoveIndex < NumChildNodes);
1572 for (int32 ShiftIndex = RemoveIndex; ShiftIndex < NumChildNodes - 1; ++ShiftIndex)
1573 {
1574 RelPaths[ShiftIndex] = MoveTemp(RelPaths[ShiftIndex + 1]);
1575 ChildNodes[ShiftIndex] = MoveTemp(ChildNodes[ShiftIndex + 1]);
1576 }
1577 RelPaths[NumChildNodes - 1].Empty();
1578 ChildNodes[NumChildNodes - 1].Reset();
1579 SetNumChildNodes(NumChildNodes - 1);
1580}
1581
1582template <typename ValueType>
1584{
1585 if (ChildNode.HasValue())
1586 {
1587 return;
1588 }
1589
1590 int32 NumChildNodes = ChildNode.GetNumChildNodes();
1591 checkf(NumChildNodes > 0, TEXT("Invalid to call ConditionalCompactNode with an empty ChildNode."));
1592 if (NumChildNodes > 1)
1593 {
1594 return;
1595 }
1599 RelPath = NewRelPath;
1600 FTreeNode OldGrandChild = MoveTemp(ChildNode.ChildNodes[0]);
1602}
1603
1604template <typename ValueType>
1606{
1607 if (CapacityChildNodes == NewCapacity)
1608 {
1609 return;
1610 }
1611 int32 NumChildNodes = GetNumChildNodes();
1613 if (NewCapacity > 0)
1614 {
1615 FString* NewRelPaths = new FString[NewCapacity];
1616 FTreeNode* NewChildNodes = new FTreeNode[NewCapacity];
1617 for (int32 Index = 0; Index < NumChildNodes; ++Index)
1618 {
1619 // Index < NumChildNodes <= NewCapacity. Some analyzers miss check(NumChildNodes <= NewCapacity) and need a direct hint.
1621 NewRelPaths[Index] = MoveTemp(RelPaths[Index]);
1622 NewChildNodes[Index] = MoveTemp(ChildNodes[Index]);
1623 }
1624 ChildNodes.Reset(NewChildNodes);
1625 RelPaths.Reset(NewRelPaths);
1626 }
1627 else
1628 {
1629 ChildNodes.Reset();
1630 RelPaths.Reset();
1631 }
1632 CapacityChildNodes = NewCapacity;
1633}
1634
1635template <typename ValueType>
1638{
1639 int32 Num = GetNumChildNodes();
1640 for (int32 Index = 0; Index < Num; ++Index)
1641 {
1642 RelPaths[Index].ReplaceCharInline(OldSeparator, NewSeparator);
1643 }
1644}
1645
1646template <typename ValueType>
1648 : Flags(InFlags)
1649 , PathSeparator(InPathSeparator)
1650{
1651 FStackData& RootData = Stack.Emplace_GetRef(Root, nullptr /* ParentClosestValue */);
1652 // The root node always exists. It can have a value, if empty string was pushed into the tree. Otherwise it does
1653 // not have a value, and is not treated as an implied parent. The highest parent directory of any added path other
1654 // than empty string is a child node of the root node.
1655 if (!RootData.Node->HasValue())
1656 {
1657 // To allow our node-based iteration to work and skip recording a value for the root, we need to skip past the
1658 // "Node's value itself" state (ChildIndex == -1) for the root node, and start at ChildIndex=0.
1659 RootData.ChildIndex = 0;
1660 }
1661 TraverseToValid();
1662}
1663
1664template <typename ValueType>
1666{
1667 check(!Stack.IsEmpty());
1668 const FStackData& Data = Stack.Last();
1669 FTreeNode* Node = Data.Node;
1670 return TPair<FStringView, ValueType*>(Path, Data.ClosestValue);
1671}
1672
1673template <typename ValueType>
1675{
1676 return !Stack.IsEmpty();
1677}
1678
1679template <typename ValueType>
1680inline bool TDirectoryTree<ValueType>::FIteratorInternal::operator!=(const FIterationSentinel&) const
1681{
1682 return (bool)*this;
1683}
1684
1685template <typename ValueType>
1687{
1688 check((bool)*this); // It is invalid to call operator++ on the iterator when it is done
1689
1690 // Stack.Last is pointing at an existing value, move past it
1691 FStackData* Data = &Stack.Last();
1692 if (Data->ChildIndex == -1)
1693 {
1694 // Pointing to the Node's value itself, move to the first child.
1695 Data->ChildIndex = 0;
1696 }
1697 else
1698 {
1699 // Inbetween states are not possible for the top node in the stack. TraverseToValid moves past
1700 // inbetween states before returning.
1701 check(Data->ChildIndex < Data->Node->GetNumChildNodes());
1702 check(Data->bChildRelPathInitialized);
1703 // Empty RemainingChildRelPath is an inbetween state, with or without bChildNodeInitialized.
1704 // TraverseToValid moves past it to add the next child node to the stack before returning.
1705 check(!Data->RemainingChildRelPath.IsEmpty());
1706
1707 // Pointing to somewhere in the iteration of directories in the childnode's relative path.
1708 IncrementRemainingChildRelPath(Data);
1709 }
1710
1711 TraverseToValid();
1712 return *this;
1713}
1714
1715template <typename ValueType>
1717{
1718 while (!Stack.IsEmpty())
1719 {
1720 FStackData* Data = &Stack.Last();
1721 for (;;)
1722 {
1723 if (Data->ChildIndex == -1)
1724 {
1725 // Pointing to the Node's value itself. Return if the node has a value or implied children are allowed
1727 || Data->Node->HasValue())
1728 {
1729 return;
1730 }
1731
1733 {
1734 // In the non-recursive case, we are iterating over the node in Stack[0], and Stack[1]
1735 // is each child node that we need to report. Do not iterate over the children of Stack[1];
1736 // skip to the end of its children.
1737 Data->ChildIndex = Data->Node->GetNumChildNodes();
1738 }
1739 else
1740 {
1741 Data->ChildIndex = 0;
1742 }
1743 }
1744 else if (Data->ChildIndex < Data->Node->GetNumChildNodes())
1745 {
1746 if (!Data->bChildRelPathInitialized)
1747 {
1748 Data->bChildRelPathInitialized = true;
1749 Data->PathLenBeforeChildNode = Path.Len();
1750
1751 // If we are not returning implied children then skip straight to the recursive child node
1753 {
1754 Data->RemainingChildRelPath = FStringView();
1755 int32 FixupStart = Path.Len();
1756 FPathViews::Append(Path, Data->Node->RelPaths[Data->ChildIndex]);
1758 }
1759 else
1760 {
1761 Data->RemainingChildRelPath = Data->Node->RelPaths[Data->ChildIndex];
1762 // Pull the first component of the child relpath into our path, and
1763 // continue the loop to check for whether it is an implied parent or
1764 // the child node itself.
1765 IncrementRemainingChildRelPath(Data);
1766 }
1767 }
1768 else if (!Data->RemainingChildRelPath.IsEmpty())
1769 {
1770 // Pointing to somewhere in the iteration of directories in the childnode's relative path.
1771 // Return it as an implied node.
1772 return;
1773 }
1774 else if (!Data->bChildNodeInitialized)
1775 {
1776 Data->bChildNodeInitialized = true;
1777 Stack.Add(FStackData(Data->Node->ChildNodes[Data->ChildIndex], Data->ClosestValue));
1778 break; // Return to the outer loop to traverse into the node we just pushed
1779 }
1780 else
1781 {
1782 // Pointing to the recursive child node that we just popped, move to the next.
1783 ++Data->ChildIndex;
1784
1785 Data->bChildRelPathInitialized = false;
1786 Data->bChildNodeInitialized = false;
1787 Data->RemainingChildRelPath = FStringView();
1788 Path.RemoveSuffix(Path.Len() - Data->PathLenBeforeChildNode);
1789 Data->PathLenBeforeChildNode = 0;
1790 // Continue on to the next child
1791 }
1792 }
1793 else
1794 {
1795 Stack.Pop(EAllowShrinking::No);
1796 break; // Return to the outer loop to return traversal to the parent node
1797 }
1798 }
1799 }
1800}
1801
1802template <typename ValueType>
1804{
1808 Data->RemainingChildRelPath = RemainingPath;
1809
1810 int32 FixupStart = Path.Len();
1813}
1814
1815template <typename ValueType>
1817 FTreeNode& InNode, ValueType* ParentClosestValue)
1818 : Node(&InNode)
1819 , ClosestValue(InNode.HasValue() ? &InNode.GetValue() : ParentClosestValue)
1820 , ChildIndex(-1)
1821 , PathLenBeforeChildNode(0)
1822 , bChildRelPathInitialized(false)
1823 , bChildNodeInitialized(false)
1824{
1825}
1826
1827template <typename ValueType>
1828template <typename ViewedValueType>
1832{
1833}
1834
1835template <typename ValueType>
1836template <typename ViewedValueType>
1844
1845template <typename ValueType>
1846template <typename ViewedValueType>
1852
1853template <typename ValueType>
1854template <typename ViewedValueType>
1856{
1857 return (bool)*this;
1858}
1859
1860template <typename ValueType>
1861template <typename ViewedValueType>
1868
1869template <typename ValueType>
1870template <typename ViewedValueType>
1872{
1873 return (bool)Internal;
1874}
1875
1876template <typename ValueType>
1877template <typename ViewedValueType>
1881{
1882}
1883
1884template <typename ValueType>
1885template <typename ViewedValueType>
1893
1894template <typename ValueType>
1895template <typename ViewedValueType>
1901
1902template <typename ValueType>
1903template <typename ViewedValueType>
1908
1909template <typename ValueType>
1910template <typename ViewedValueType>
1917
1918template <typename ValueType>
1919template <typename ViewedValueType>
1921{
1922 return (bool)Internal;
1923}
1924
1925template <typename ValueType>
1932
1933template <typename ValueType>
1935{
1936 return FConstIterator(const_cast<FTreeNode&>(Root),
1938 PathSeparator);
1939}
1940
1941template <typename ValueType>
1948
1949template <typename ValueType>
1956
1957template <typename ValueType>
1962
1963template <typename ValueType>
1968
1969template <typename ValueType>
OODEFFUNC typedef void(OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)(void *ptr)
int Volume
Definition AndroidPlatformMisc.cpp:380
#define check(expr)
Definition AssertionMacros.h:314
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define CA_ASSUME(Expr)
Definition CoreMiscDefines.h:126
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
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
EDirectoryTreeGetFlags
Definition DirectoryTree.h:28
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
#define ENUM_CLASS_FLAGS(Enum)
Definition EnumClassFlags.h:6
@ Num
Definition MetalRHIPrivate.h:234
const bool
Definition NetworkReplayStreaming.h:178
TIndexedContainerIterator< TArray< FPreviewAttachedObjectPair >, FPreviewAttachedObjectPair, int32 > TIterator
Definition PreviewAssetAttachComponent.h:68
TStringView< TCHAR > FStringView
Definition StringFwd.h:45
UE_REWRITE T CopyTemp(T &Val)
Definition UnrealTemplate.h:554
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint32 Size
Definition VulkanMemory.cpp:4034
uint32_t uint32
Definition binka_ue_file_header.h:6
static CORE_API bool IsDriveSpecifierWithoutRoot(FStringView InPath)
Definition PathViews.cpp:934
static CORE_API void AppendPath(FStringBuilderBase &InOutPath, FStringView AppendPath)
Definition PathViews.cpp:1093
static CORE_API void SplitFirstComponent(FStringView InPath, FStringView &OutFirstComponent, FStringView &OutRemainder)
Definition PathViews.cpp:866
static CORE_API bool Equals(FStringView A, FStringView B)
Definition PathViews.cpp:365
static CORE_API void SplitVolumeSpecifier(FStringView InPath, FStringView &OutVolumeSpecifier, FStringView &OutRemainder)
Definition PathViews.cpp:974
static void Append(TStringBuilderBase< CharType > &Builder, ArgTypes &&... Args)
Definition PathViews.h:215
static CORE_API bool TryMakeChildPathRelativeTo(FStringView Child, FStringView Parent, FStringView &OutRelPath)
Definition PathViews.cpp:395
Definition ArrayView.h:139
Definition Array.h:670
Definition DirectoryTree.h:85
void Remove(FStringView Path, bool *bOutExisted=nullptr)
Definition DirectoryTree.h:497
bool TryFindClosestPath(FStringView Path, FString &OutPath, const ValueType **OutValue=nullptr) const
Definition DirectoryTree.h:595
FConstIterator CreateConstIterator() const
Definition DirectoryTree.h:1934
void Shrink()
Definition DirectoryTree.h:527
FPointerIterator CreateIteratorForImplied()
Definition DirectoryTree.h:1942
bool IsEmpty() const
Definition DirectoryTree.h:533
TDirectoryTree(TDirectoryTree &&Other)=default
const ValueType * FindClosestValue(FStringView Path) const
Definition DirectoryTree.h:583
FConstPointerIterator CreateConstIteratorForImplied() const
Definition DirectoryTree.h:1950
const ValueType * Find(FStringView Path) const
Definition DirectoryTree.h:557
FIterationSentinel end() const
Definition DirectoryTree.h:1970
bool TryGetChildren(FStringView Path, TArray< FString > &OutRelativeChildNames, EDirectoryTreeGetFlags Flags=EDirectoryTreeGetFlags::None) const
Definition DirectoryTree.h:759
int32 Num() const
Definition DirectoryTree.h:539
bool Contains(FStringView Path) const
Definition DirectoryTree.h:551
TIterator< const ValueType > FConstIterator
Definition DirectoryTree.h:89
TDirectoryTree & operator=(TDirectoryTree &&Other)=default
bool ContainsChildPaths(FStringView Path) const
Definition DirectoryTree.h:750
TDirectoryTree()
Definition DirectoryTree.h:434
ValueType * FindClosestValue(FStringView Path)
Definition DirectoryTree.h:589
ValueType * Find(FStringView Path)
Definition DirectoryTree.h:563
TDirectoryTree & operator=(const TDirectoryTree &Other)=default
FConstIterator begin() const
Definition DirectoryTree.h:1964
ValueType & FindOrAdd(FStringView Path, bool *bOutExisted=nullptr)
Definition DirectoryTree.h:440
SIZE_T GetAllocatedSize() const
Definition DirectoryTree.h:545
FIterator CreateIterator()
Definition DirectoryTree.h:1926
bool TryFindClosestPath(FStringView Path, FString &OutPath, ValueType **OutValue)
Definition DirectoryTree.h:610
bool TryFindClosestPath(FStringView Path, FStringBuilderBase &OutPath, ValueType **OutValue)
Definition DirectoryTree.h:646
void Empty()
Definition DirectoryTree.h:488
FIterator begin()
Definition DirectoryTree.h:1958
bool ContainsPathOrParent(FStringView Path) const
Definition DirectoryTree.h:577
bool TryFindClosestPath(FStringView Path, FStringBuilderBase &OutPath, const ValueType **OutValue=nullptr) const
Definition DirectoryTree.h:631
TPointerIterator< const FMountPoint * > FConstPointerIterator
Definition DirectoryTree.h:92
TIterator< ValueType > FIterator
Definition DirectoryTree.h:88
TPointerIterator< FMountPoint * > FPointerIterator
Definition DirectoryTree.h:91
TDirectoryTree(const TDirectoryTree &Other)=default
Definition Array.h:64
void Reset()
Definition StringBuilder.h:190
Definition StringBuilder.h:509
Definition UniquePtr.h:107
CORE_API void Reset(int32 NewReservedSize=0)
Definition String.cpp.inl:326
UE_FORCEINLINE_HINT int32 Len() const
Definition UnrealString.h.inl:954
IAnalyticsPropertyStore::EStatusCode SetValue(TGetter &&GetterFn, TSetter &&SetterFn, const T &ProposedValue, TCompare &&ConditionFn)
Definition AnalyticsPropertyStore.cpp:34
Flags
Definition SoundFileIOEnums.h:78
GeometryCollection::Facades::FMuscleActivationData Data
Definition MuscleActivationConstraints.h:15
@ IgnoreCase
Definition CString.h:26
T::FDataType GetValue(const UBlackboardComponent &Blackboard, const FName &Name, FBlackboard::FKey &InOutCachedKey, const typename T::FDataType &DefaultValue)
Definition ValueOrBBKey.h:51
const FColor Path(255, 255, 255)
Definition ByteSwap.h:14
FORCEINLINE T * Get(const FObjectPtr &ObjectPtr)
Definition ObjectPtr.h:426
Definition DirectoryTree.cpp:10
void FixupPathSeparator(FStringBuilderBase &InOutPath, int32 StartIndex, TCHAR InPathSeparator)
Definition DirectoryTree.cpp:12
int32 FindInsertionIndex(int32 NumChildNodes, const TUniquePtr< FString[]> &RelPaths, FStringView FirstPathComponent, bool &bOutExists)
Definition DirectoryTree.cpp:26
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71
Definition ArrowWrapper.h:28
Definition DirectoryTree.h:206
Definition DirectoryTree.h:216
bool operator!=(const FIterationSentinel &) const
Definition DirectoryTree.h:1855
TIterator & operator++()
Definition DirectoryTree.h:1863
TPair< FStringView, ViewedValueType & > operator*() const
Definition DirectoryTree.h:1837
TArrowWrapper< TPair< FStringView, ViewedValueType & > > operator->() const
Definition DirectoryTree.h:1848
Definition DirectoryTree.h:239
TPointerIterator & operator++()
Definition DirectoryTree.h:1912
TPair< FStringView, ViewedValueType * > operator*() const
Definition DirectoryTree.h:1887
TArrowWrapper< TPair< FStringView, ViewedValueType * > > operator->() const
Definition DirectoryTree.h:1897
bool operator!=(const FIterationSentinel &) const
Definition DirectoryTree.h:1904
Definition Tuple.h:652
Definition TypeCompatibleBytes.h:24