Files
UnrealEngine/Engine/Source/Runtime/AssetRegistry/Private/AssetDataMap.cpp
2025-05-18 13:04:45 +08:00

815 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AssetRegistry/AssetDataMap.h"
#if UE_ASSETREGISTRY_INDIRECT_ASSETDATA_POINTERS
#include "SetKeyFuncs.h"
#endif
#if UE_ASSETREGISTRY_INDIRECT_ASSETDATA_POINTERS
namespace UE::AssetRegistry::Private
{
struct FAssetObjectNameKeyFuncs
{
explicit FAssetObjectNameKeyFuncs(const FAssetDataMap& InOwner)
: Owner(&InOwner)
{
}
FAssetDataPtrIndex GetInvalidElement()
{
return AssetDataPtrIndexInvalid;
}
bool IsInvalid(FAssetDataPtrIndex Value)
{
return Value == AssetDataPtrIndexInvalid;
}
uint32 GetTypeHash(FAssetDataPtrIndex Value)
{
return Owner->AssetByObjectNameValueToTypeHash(Value);
}
uint32 GetTypeHash(const FCachedAssetKey& Key)
{
return UE::AssetRegistry::Private::GetTypeHash(Key);
}
bool Matches(FAssetDataPtrIndex A, FAssetDataPtrIndex B)
{
return A == B;
}
bool Matches(FAssetDataPtrIndex Value, const FCachedAssetKey& Key)
{
return Owner->AssetByObjectNameValueMatches(Value, Key);
}
const FAssetDataMap* Owner;
};
FAssetDataMap::FAssetDataMap()
{
AssetByObjectName.Reset(new FAssetObjectNameSet(FAssetObjectNameKeyFuncs(*this)));
}
FAssetDataMap::FAssetDataMap(FAssetDataMap&& Other)
{
// We require that AssetByObjectName is non-null in all FAssetDataMap.
AssetByObjectName.Reset(new FAssetObjectNameSet(FAssetObjectNameKeyFuncs(*this)));
*this = MoveTemp(Other);
}
FAssetDataMap& FAssetDataMap::operator=(FAssetDataMap&& Other)
{
// Do not use operator=(TUniquePtr&&) because we require that AssetByObjectName is non-null in all FAssetDataMap,
// and so we need to guarantee that AssetByObjectName is swapped rather than moved and cleared;
// possibly the TUniquePtr class does move and clear rather than swap for operator=&&.
Swap(AssetByObjectName, Other.AssetByObjectName);
// Set the Owner pointers inside the KeyFuncs inside the TSetKeyFuncs to point to the correct FAssetDataMap
AssetByObjectName->SetKeyFuncs(FAssetObjectNameKeyFuncs(*this));
Other.AssetByObjectName->SetKeyFuncs(FAssetObjectNameKeyFuncs(Other));
Swap(AssetDatas, Other.AssetDatas);
Swap(FreeIndex, Other.FreeIndex);
Swap(NumFree, Other.NumFree);
return *this;
}
FAssetDataMap::~FAssetDataMap() = default;
void FAssetDataMap::Empty(int32 ReservedSize)
{
AssetDatas.Empty(ReservedSize);
AssetByObjectName->Empty(ReservedSize);
FreeIndex = AssetDataPtrIndexInvalid;
NumFree = 0;
}
FAssetDataPtrIndex FAssetDataMap::Add(FAssetData* AssetData, bool* bAlreadyInSet)
{
checkf(IsAligned(AssetData, 4),
TEXT("Pointers stored in FAssetDataMap must be 4-byte aligned, because we set the low bits to indicate the data in our containers is not an added pointer."));
FCachedAssetKey Key(AssetData);
uint32 HashKey = GetTypeHash(Key);
const FAssetDataPtrIndex* ExistingValue = AssetByObjectName->FindByHash(HashKey, Key);
if (ExistingValue)
{
if (bAlreadyInSet)
{
*bAlreadyInSet = true;
}
return *ExistingValue;
}
FAssetDataPtrIndex AssignedIndex = AssetDataPtrIndexInvalid;
if (FreeIndex != AssetDataPtrIndexInvalid)
{
AssignedIndex = PopFreeIndex();
AssetDatas[AssignedIndex] = AssetData;
}
else
{
AssignedIndex = AssetDatas.Add(AssetData);
}
AssetByObjectName->AddByHash(HashKey, AssignedIndex);
if (bAlreadyInSet)
{
*bAlreadyInSet = false;
}
return AssignedIndex;
}
void FAssetDataMap::AddKeyLookup(FAssetData* AssetData, FAssetDataPtrIndex AssetIndex, bool* bAlreadyInSet)
{
checkf(IsAligned(AssetData, 4),
TEXT("Pointers stored in FAssetDataMap must be 4-byte aligned, because we set the low bits to indicate the data in our containers is not an added pointer."));
FCachedAssetKey Key(AssetData);
uint32 HashKey = GetTypeHash(Key);
const FAssetDataPtrIndex* ExistingValue = AssetByObjectName->FindByHash(HashKey, Key);
if (ExistingValue)
{
if (bAlreadyInSet)
{
*bAlreadyInSet = true;
}
return;
}
AssetByObjectName->AddByHash(HashKey, AssetIndex);
if (bAlreadyInSet)
{
*bAlreadyInSet = false;
}
}
int32 FAssetDataMap::Remove(const FCachedAssetKey& Key)
{
uint32 HashKey = GetTypeHash(Key);
const FAssetDataPtrIndex* ExistingValue = AssetByObjectName->FindByHash(HashKey, Key);
if (!ExistingValue)
{
return 0;
}
FAssetDataPtrIndex ExistingIndex = *ExistingValue;
AssetByObjectName->RemoveByHash(HashKey, *ExistingValue);
AddToFreeList(ExistingIndex);
return 1;
}
void FAssetDataMap::Shrink()
{
AssetDatas.Shrink();
}
int32 FAssetDataMap::RemoveOnlyKeyLookup(const FCachedAssetKey& Key)
{
uint32 HashKey = GetTypeHash(Key);
const FAssetDataPtrIndex* ExistingValue = AssetByObjectName->FindByHash(HashKey, Key);
if (!ExistingValue)
{
return 0;
}
AssetByObjectName->RemoveByHash(HashKey, *ExistingValue);
return 1;
}
int32 FAssetDataMap::Num() const
{
return AssetDatas.Num() - NumFree;
}
SIZE_T FAssetDataMap::GetAllocatedSize() const
{
return AssetDatas.GetAllocatedSize() + sizeof(*AssetByObjectName) + AssetByObjectName->GetAllocatedSize();
}
TArray<FAssetData*> FAssetDataMap::Array() const
{
TArray<FAssetData*> Result;
Result.Reserve(Num());
for (FAssetData* AssetData : *this)
{
Result.Add(AssetData);
}
return Result;
}
bool FAssetDataMap::Contains(const FCachedAssetKey& Key) const
{
return FindId(Key) != AssetDataPtrIndexInvalid;
}
FAssetData* const* FAssetDataMap::Find(const FCachedAssetKey& Key) const
{
FAssetDataPtrIndex AssetIndex = FindId(Key);
return AssetIndex != AssetDataPtrIndexInvalid ? &AssetDatas[AssetIndex] : nullptr;
}
FAssetDataPtrIndex FAssetDataMap::FindId(const FCachedAssetKey& Key) const
{
const uint32* StoredValue = AssetByObjectName->Find(Key);
if (StoredValue)
{
return static_cast<FAssetDataPtrIndex>(*StoredValue);
}
return AssetDataPtrIndexInvalid;
}
FAssetData* FAssetDataMap::operator[](FAssetDataPtrIndex AssetIndex) const
{
return AssetDatas[static_cast<int32>(AssetIndex)];
}
void FAssetDataMap::Enumerate(
TFunctionRef<bool(FAssetData& AssetData, FAssetDataPtrIndex AssetIndex)> Callback) const
{
FAssetData*const* Start = AssetDatas.GetData();
FAssetData*const* End = AssetDatas.GetData() + AssetDatas.Num();
for (FAssetData*const* Current = Start; Current < End; ++Current)
{
FAssetData* AssetData = *Current;
if (IsInUse(AssetData))
{
FAssetDataPtrIndex AssetIndex = static_cast<FAssetDataPtrIndex>(Current - Start);
if (!Callback(*AssetData, AssetIndex))
{
break;
}
}
}
}
FAssetDataMap::FIterator FAssetDataMap::begin() const
{
return FIterator(*this, -1);
}
FAssetDataMap::FIterator FAssetDataMap::end() const
{
return FIterator(*this, AssetDatas.Num());
}
FAssetDataMap::FIterator::FIterator(const FAssetDataMap& InOwner, int32 InIndex)
: Owner(InOwner)
, Index(InIndex)
{
if (Index < 0)
{
this->operator++();
}
}
FAssetData* FAssetDataMap::FIterator::operator*() const
{
return Owner.AssetDatas[Index];
}
FAssetDataMap::FIterator& FAssetDataMap::FIterator::operator++()
{
++Index;
while (Index < Owner.AssetDatas.Num() && !Owner.IsInUse(Owner.AssetDatas[Index]))
{
++Index;
}
return *this;
}
bool FAssetDataMap::FIterator::operator!=(const FIterator& Other) const
{
return Index != Other.Index;
}
bool FAssetDataMap::IsInUse(const FAssetData* DataFromAssetDatas)
{
return (reinterpret_cast<const UPTRINT>(DataFromAssetDatas) & 0x1) == 0;
}
void FAssetDataMap::AddToFreeList(FAssetDataPtrIndex Index)
{
static_assert(sizeof(UPTRINT) > sizeof(FAssetDataPtrIndex),
"We assume we can fit the entire FAssetDataPtrIndex, plus one additional bit, into a UPTRINT");
UPTRINT& IntValue = reinterpret_cast<UPTRINT&>(AssetDatas[Index]);
IntValue = 0x1 | (static_cast<UPTRINT>(FreeIndex) << 1);
FreeIndex = Index;
++NumFree;
}
FAssetDataPtrIndex FAssetDataMap::PopFreeIndex()
{
FAssetDataPtrIndex Result = FreeIndex;
const UPTRINT& IntValue = reinterpret_cast<const UPTRINT&>(AssetDatas[FreeIndex]);
FreeIndex = static_cast<FAssetDataPtrIndex>(IntValue >> 1);
--NumFree;
return Result;
}
uint32 FAssetDataMap::AssetByObjectNameValueToTypeHash(FAssetDataPtrIndex Value) const
{
if (static_cast<uint32>(AssetDatas.Num()) <= Value)
{
return 0;
}
const FAssetData* AssetData = AssetDatas[static_cast<int32>(Value)];
if (!IsInUse(AssetData))
{
return 0;
}
return GetTypeHash(FCachedAssetKey(AssetData));
}
bool FAssetDataMap::AssetByObjectNameValueMatches(FAssetDataPtrIndex Value, const FCachedAssetKey& Key) const
{
if (static_cast<uint32>(AssetDatas.Num()) <= Value)
{
return false;
}
FAssetData* AssetData = AssetDatas[static_cast<int32>(Value)];
if (IsInUse(AssetData))
{
FCachedAssetKey ExistingKey(AssetData);
if (ExistingKey == Key)
{
return true;
}
}
return false;
}
void FIndirectAssetDataArrays::AddElement(FAssetDataOrArrayIndex& Array, FAssetDataPtrIndex AssetIndex)
{
if (Array.IsEmptyList())
{
Array = FAssetDataOrArrayIndex::CreateAssetDataPtrIndex(AssetIndex);
}
else if (Array.IsAssetDataPtrIndex())
{
int32 Index = AllocateArrayIndex();
check(Arrays[Index].bArray);
TArray<FAssetDataPtrIndex>& IndirectArray = Arrays[Index].Array;
IndirectArray.Add(Array.AsAssetDataPtrIndex());
IndirectArray.Add(AssetIndex);
Array = FAssetDataOrArrayIndex::CreateArrayIndex(static_cast<FAssetDataArrayIndex>(Index));
}
else
{
check(Array.IsAssetDataArrayIndex());
int32 Index = static_cast<int32>(Array.AsAssetDataArrayIndex());
if (Index < 0 || Arrays.Num() <= Index || !Arrays[Index].bArray)
{
ensureMsgf(false, TEXT("Invalid Index %d passed as Array into AddElement. Valid values are [0, %d)."),
Index, Arrays.Num());
// Assign a one-elemenet list, stored as a FAssetDataPtrIndex.
Array = FAssetDataOrArrayIndex::CreateAssetDataPtrIndex(AssetIndex);
}
else
{
TArray<FAssetDataPtrIndex>& IndirectArray = Arrays[Index].Array;
IndirectArray.Add(AssetIndex);
}
}
}
void FIndirectAssetDataArrays::RemoveElement(FAssetDataOrArrayIndex& Array, FAssetDataPtrIndex AssetIndex)
{
if (Array.IsEmptyList())
{
// Nothing to do, removing from an empty list is a noop
}
else if (Array.IsAssetDataPtrIndex())
{
FAssetDataPtrIndex ExistingElement = Array.AsAssetDataPtrIndex();
if (ExistingElement == AssetIndex)
{
// Assign an empty list into Array
Array = FAssetDataOrArrayIndex::CreateEmptyList();
}
else
{
// Nothing to do, removing an element not in the list is a noop
}
}
else
{
check(Array.IsAssetDataArrayIndex());
int32 Index = static_cast<int32>(Array.AsAssetDataArrayIndex());
if (Index < 0 || Arrays.Num() <= Index || !Arrays[Index].bArray)
{
ensureMsgf(false, TEXT("Invalid Index %d passed as Array into RemoveElement. Valid values are [0, %d)."),
Index, Arrays.Num());
// Assign an empty list
Array = FAssetDataOrArrayIndex::CreateEmptyList();
}
else
{
TArray<FAssetDataPtrIndex>& IndirectArray = Arrays[Index].Array;
IndirectArray.RemoveSwap(AssetIndex, EAllowShrinking::No);
if (IndirectArray.Num() <= 1)
{
if (IndirectArray.Num() == 1)
{
Array = FAssetDataOrArrayIndex::CreateAssetDataPtrIndex(IndirectArray[0]);
}
else
{
// This can happen if the same value was present multiple times in the array,
// and no other values were in the array.
Array = FAssetDataOrArrayIndex::CreateEmptyList();
}
IndirectArray.Empty();
ReleaseArrayIndex(Index);
}
else
{
// Array needs to remain as an indirect array, no further action necessary.
}
}
}
}
void FIndirectAssetDataArrays::RemoveAllElements(FAssetDataOrArrayIndex& Array)
{
if (Array.IsEmptyList())
{
// Nothing to do, clearing an empty list is a noop
}
else
{
if (Array.IsAssetDataArrayIndex())
{
int32 Index = static_cast<int32>(Array.AsAssetDataArrayIndex());
if (Index < 0 || Arrays.Num() <= Index || !Arrays[Index].bArray)
{
ensureMsgf(false, TEXT("Invalid Index %d passed as Array into RemoveAllElements. Valid values are [0, %d)."),
Index, Arrays.Num());
}
else
{
ReleaseArrayIndex(Index);
}
}
Array = FAssetDataOrArrayIndex::CreateEmptyList();
}
}
TConstArrayView<FAssetDataPtrIndex> FIndirectAssetDataArrays::Iterate(const FAssetDataOrArrayIndex* ArrayPtr) const
{
if (!ArrayPtr || ArrayPtr->IsEmptyList())
{
return TConstArrayView<FAssetDataPtrIndex>();
}
else if (ArrayPtr->IsAssetDataPtrIndex())
{
static_assert(FAssetDataOrArrayIndex::AssetDataType == 0,
"We rely on the converted value for an FAssetDataOrArrayIndex to FAssetDataPtrIndex being the same bits so we can do a reinterpret_cast on the pointer.");
const FAssetDataPtrIndex* AssetIndexPtr = reinterpret_cast<const FAssetDataPtrIndex*>(ArrayPtr);
return TConstArrayView<FAssetDataPtrIndex>(AssetIndexPtr, 1);
}
else
{
check(ArrayPtr->IsAssetDataArrayIndex());
int32 Index = static_cast<int32>(ArrayPtr->AsAssetDataArrayIndex());
if (Index < 0 || Arrays.Num() <= Index || !Arrays[Index].bArray)
{
return TConstArrayView<FAssetDataPtrIndex>();
}
return Arrays[Index].Array;
}
}
SIZE_T FIndirectAssetDataArrays::GetAllocatedSize() const
{
SIZE_T Result = Arrays.GetAllocatedSize();
for (const FArrayOrNextIndex& ArrayOrNextIndex : Arrays)
{
if (ArrayOrNextIndex.bArray)
{
Result += ArrayOrNextIndex.Array.GetAllocatedSize();
}
}
return Result;
}
void FIndirectAssetDataArrays::Empty()
{
Arrays.Empty();
}
void FIndirectAssetDataArrays::Shrink()
{
Arrays.Shrink();
}
int32 FIndirectAssetDataArrays::AllocateArrayIndex()
{
int32 Index;
if (FreeList != UnusedIndex)
{
Index = static_cast<int32>(FreeList);
check(0 <= Index && Index < Arrays.Num());
FreeList = Arrays[Index].NextIndex;
}
else
{
Index = Arrays.Num();
Arrays.Emplace();
}
FArrayOrNextIndex& ArrayOrNextIndex = Arrays[Index];
check(!ArrayOrNextIndex.bArray);
ArrayOrNextIndex.bArray = true;
new (&ArrayOrNextIndex.Array) TArray<FAssetDataPtrIndex>();
return Index;
}
void FIndirectAssetDataArrays::ReleaseArrayIndex(int32 Index)
{
if (Index < 0 || Arrays.Num() <= Index || !Arrays[Index].bArray)
{
ensureMsgf(false, TEXT("Invalid Index %d passed. Arrays.Num() == %d. Arrays[Index].bArray == %s"),
Index, Arrays.Num(),
0 <= Index && Index < Arrays.Num() ? (Arrays[Index].bArray ? TEXT("true") : TEXT("false")) : TEXT("<Invalid>"));
return;
}
FArrayOrNextIndex& ArrayOrNextIndex = Arrays[Index];
ArrayOrNextIndex.Array.~TArray<FAssetDataPtrIndex>();
ArrayOrNextIndex.bArray = false;
ArrayOrNextIndex.NextIndex = FreeList;
FreeList = static_cast<uint32>(Index);
}
/**
* This shunt to GetTypeHash is necessary for FAssetPackageNameKeyFuncs, because c++ does not provide
* a way to call ::GetTypeHash from a struct function when the struct has a member named GetTypeHash.
*/
FORCEINLINE uint32 AssetRegistryPrivateGetTypeHash(FName Key)
{
return GetTypeHash(Key);
}
struct FAssetPackageNameKeyFuncs
{
explicit FAssetPackageNameKeyFuncs(const FAssetPackageNameMap& InOwner)
: Owner(&InOwner)
{
}
FAssetDataOrArrayIndex GetInvalidElement()
{
return FAssetDataOrArrayIndex::CreateEmptyList();
}
bool IsInvalid(FAssetDataOrArrayIndex Value)
{
return Value.IsEmptyList();
}
uint32 GetTypeHash(FAssetDataOrArrayIndex Value)
{
return Owner->AssetOrArrayByPackageNameValueToTypeHash(Value);
}
uint32 GetTypeHash(FName Key)
{
return AssetRegistryPrivateGetTypeHash(Key);
}
bool Matches(FAssetDataOrArrayIndex A, FAssetDataOrArrayIndex B)
{
return A == B;
}
bool Matches(FAssetDataOrArrayIndex Value, FName PackageName)
{
return Owner->AssetOrArrayByPackageNameValueMatches(Value, PackageName);
}
const FAssetPackageNameMap* Owner;
};
FAssetPackageNameMap::FAssetPackageNameMap(FAssetDataMap& InAssetDataMap, FIndirectAssetDataArrays& InIndirectAssetDataArrays)
: AssetOrArrayByPackageName(new FAssetPackageNameSet(FAssetPackageNameKeyFuncs(*this)))
, AssetDataMap(InAssetDataMap)
, IndirectArrays(InIndirectAssetDataArrays)
{
}
FAssetPackageNameMap& FAssetPackageNameMap::operator=(FAssetPackageNameMap&& Other)
{
Swap(AssetOrArrayByPackageName, Other.AssetOrArrayByPackageName);
// Set the Owner pointers inside the KeyFuncs inside the TSetKeyFuncs to point to the correct FAssetPackageNameMap
AssetOrArrayByPackageName->SetKeyFuncs(FAssetPackageNameKeyFuncs(*this));
Other.AssetOrArrayByPackageName->SetKeyFuncs(FAssetPackageNameKeyFuncs(Other));
// Do not move the references we keep to the other structures on FAssetRegistryState.
// Our contract with our caller is that the references never change, and the caller swaps the data in those other
// structures during the same operation in which it swaps our data.
return *this;
}
FAssetPackageNameMap::~FAssetPackageNameMap()
{
Empty(0);
}
void FAssetPackageNameMap::Empty(int32 ReservedSize)
{
for (FAssetDataOrArrayIndex DataOrArray : *AssetOrArrayByPackageName)
{
IndirectArrays.RemoveAllElements(DataOrArray);
}
AssetOrArrayByPackageName->Empty(ReservedSize);
}
void FAssetPackageNameMap::Shrink()
{
AssetOrArrayByPackageName->ResizeToTargetSize();
}
void FAssetPackageNameMap::Add(FName PackageName, FAssetDataPtrIndex AssetIndex)
{
uint32 PackageNameTypeHash = GetTypeHash(PackageName);
FAssetDataOrArrayIndex OldStoredValue;
const FAssetDataOrArrayIndex* OldPtr = AssetOrArrayByPackageName->FindByHash(PackageNameTypeHash, PackageName);
if (OldPtr)
{
OldStoredValue = *OldPtr;
}
else
{
OldStoredValue = FAssetDataOrArrayIndex::CreateEmptyList();
}
FAssetDataOrArrayIndex NewStoredValue = OldStoredValue;
IndirectArrays.AddElement(NewStoredValue, AssetIndex);
if (NewStoredValue != OldStoredValue)
{
if (!OldStoredValue.IsEmptyList())
{
AssetOrArrayByPackageName->RemoveByHash(PackageNameTypeHash, OldStoredValue);
}
// We are not allowed to store empty lists in AssetOrArrayByPackageName; it should be impossible for
// the list to be empty, but log a failed ensure and handle it if it is.
if (ensure(!NewStoredValue.IsEmptyList()))
{
AssetOrArrayByPackageName->AddByHash(PackageNameTypeHash, NewStoredValue);
}
}
}
void FAssetPackageNameMap::Remove(FName PackageName, FAssetDataPtrIndex AssetIndex)
{
uint32 PackageNameTypeHash = GetTypeHash(PackageName);
const FAssetDataOrArrayIndex* OldPtr = AssetOrArrayByPackageName->FindByHash(PackageNameTypeHash, PackageName);
if (OldPtr)
{
FAssetDataOrArrayIndex OldStoredValue = *OldPtr;
FAssetDataOrArrayIndex NewStoredValue = OldStoredValue;
IndirectArrays.RemoveElement(NewStoredValue, AssetIndex);
if (NewStoredValue != OldStoredValue)
{
AssetOrArrayByPackageName->RemoveByHash(PackageNameTypeHash, OldStoredValue);
if (!NewStoredValue.IsEmptyList())
{
AssetOrArrayByPackageName->AddByHash(PackageNameTypeHash, NewStoredValue);
}
}
}
}
int32 FAssetPackageNameMap::Num() const
{
return AssetOrArrayByPackageName->Num();
}
SIZE_T FAssetPackageNameMap::GetAllocatedSize() const
{
return sizeof(*AssetOrArrayByPackageName) + AssetOrArrayByPackageName->GetAllocatedSize();
}
void FAssetPackageNameMap::GenerateKeyArray(TArray<FName>& OutKeys) const
{
OutKeys.Reserve(OutKeys.Num() + Num());
for (const FIteratorValue& Pair : *this)
{
if (!Pair.Key.IsNone())
{
OutKeys.Add(Pair.Key);
}
}
}
TOptional<TConstArrayView<FAssetDataPtrIndex>> FAssetPackageNameMap::Find(FName PackageName) const
{
const FAssetDataOrArrayIndex* DataOrArrayIndex = AssetOrArrayByPackageName->Find(PackageName);
if (DataOrArrayIndex)
{
return TOptional<TConstArrayView<FAssetDataPtrIndex>>(IndirectArrays.Iterate(DataOrArrayIndex));
}
return TOptional<TConstArrayView<FAssetDataPtrIndex>>();
}
bool FAssetPackageNameMap::Contains(FName PackageName) const
{
return Find(PackageName).IsSet();
}
FAssetPackageNameMap::FIterator FAssetPackageNameMap::begin() const
{
return FIterator(*this);
}
FAssetPackageNameMap::FIterationSentinel FAssetPackageNameMap::end() const
{
return FIterationSentinel();
}
uint32 FAssetPackageNameMap::AssetOrArrayByPackageNameValueToTypeHash(FAssetDataOrArrayIndex Value) const
{
// We only need the first AssetData in the list stored in the given Value
// because all assets in the list have the same packagename
const FAssetData* AssetData = AssetOrArrayIndexToFirstAssetDataPtr(Value);
if (!AssetData)
{
return 0;
}
return GetTypeHash(AssetData->PackageName);
}
bool FAssetPackageNameMap::AssetOrArrayByPackageNameValueMatches(FAssetDataOrArrayIndex Value, FName PackageName) const
{
TConstArrayView<FAssetDataPtrIndex> AssetIndexArray = IndirectArrays.Iterate(&Value);
if (!AssetIndexArray.IsEmpty())
{
// To check whether the elements in this list match the requested PackageName,
// We only need the first AssetData in the list stored in the given StoredValue
// because all assets in the list have the same PackageName.
return AssetDataMap[AssetIndexArray[0]]->PackageName == PackageName;
}
return false;
}
FAssetData* FAssetPackageNameMap::AssetOrArrayIndexToFirstAssetDataPtr(FAssetDataOrArrayIndex DataOrArrayIndex) const
{
TConstArrayView<FAssetDataPtrIndex> AssetIndices = IndirectArrays.Iterate(&DataOrArrayIndex);
if (AssetIndices.Num() > 0)
{
return AssetDataMap[AssetIndices[0]];
}
return nullptr;
}
/**
* The converter from FAssetPackageNameSetIteratorBytes to FAssetPackageNameSet::FIterator, this allows us to
* have a TSetKeyFuncs::FIterator inside of our public iterator, with only a foward declare of TSetKeyFuncs.
*/
inline const FAssetPackageNameSet::FIterator& HashIter(const FAssetPackageNameSetIteratorBytes& HashIterBytes)
{
return reinterpret_cast<const FAssetPackageNameSet::FIterator&>(HashIterBytes);
}
inline FAssetPackageNameSet::FIterator& HashIter(FAssetPackageNameSetIteratorBytes& HashIterBytes)
{
static_assert(sizeof(FAssetPackageNameSetIteratorBytes) >= sizeof(FAssetPackageNameSet::FIterator) &&
alignof(FAssetPackageNameSetIteratorBytes) >= alignof(FAssetPackageNameSet::FIterator),
"FAssetMapHashSetIteratorBytes is a forward declare proxy of FAssetMapHashSetIterator and has to be sized to handle it.");
return reinterpret_cast<FAssetPackageNameSet::FIterator&>(HashIterBytes);
}
FAssetPackageNameMap::FIterator::FIterator(const FAssetPackageNameMap& InOwner)
: Owner(InOwner)
{
new (&HashIter(HashIterBytes)) FAssetPackageNameSet::FIterator(*Owner.AssetOrArrayByPackageName);
}
FAssetPackageNameMap::FIterator::~FIterator()
{
HashIter(HashIterBytes).~FIterator();
}
FAssetPackageNameMap::FIteratorValue FAssetPackageNameMap::FIterator::operator*() const
{
FAssetDataOrArrayIndex Value = *HashIter(HashIterBytes);
// When getting just the key we only need to use the first AssetData in the list stored in the given Value
// because all assetdatas in the list have the same packagename
FAssetData* AssetData = Owner.AssetOrArrayIndexToFirstAssetDataPtr(Value);
FIteratorValue Result;
Result.Key = AssetData ? AssetData->PackageName : NAME_None;
return Result;
}
FAssetPackageNameMap::FIterator& FAssetPackageNameMap::FIterator::operator++()
{
++HashIter(HashIterBytes);
return *this;
}
bool FAssetPackageNameMap::FIterator::operator!=(FIterationSentinel) const
{
// TSetKeyFuncs defines FIterationSentinel as an empty structure, just for type-specific definition of !=,
// so constructing it every time our != is called should have no cost.
return HashIter(HashIterBytes) != FAssetPackageNameSet::FIterationSentinel();
}
} // namespace UE::AssetRegistry::Private
#endif // UE_ASSETREGISTRY_INDIRECT_ASSETDATA_POINTERS