// Copyright Epic Games, Inc. All Rights Reserved. #include "DerivedDataCacheKey.h" #include "Algo/AllOf.h" #include "Containers/Set.h" #include "Math/UnrealMathUtility.h" #include "Misc/ScopeRWLock.h" namespace UE::DerivedData::Private { class FCacheBucketOwner : public FCacheBucket { public: static const TCHAR* AllocName(FStringView Bucket); inline explicit FCacheBucketOwner(FStringView Bucket); inline FCacheBucketOwner(FCacheBucketOwner&& Bucket); inline ~FCacheBucketOwner(); FCacheBucketOwner(const FCacheBucketOwner&) = delete; FCacheBucketOwner& operator=(const FCacheBucketOwner&) = delete; using FCacheBucket::operator==; inline bool operator==(FStringView Bucket) const { return ToString().Equals(Bucket, ESearchCase::IgnoreCase); } }; inline const TCHAR* FCacheBucketOwner::AllocName(FStringView Bucket) { FTCHARToUTF8 Bucket8Conversion(Bucket); FAnsiStringView Bucket8 = Bucket8Conversion; const int32 BucketLen = Bucket.Len(); const int32 Bucket8Len = Bucket8.Len(); const int32 PrefixSize = FMath::DivideAndRoundUp(2, sizeof(TCHAR)); TCHAR* Buffer = new TCHAR[PrefixSize + BucketLen + 1 + FMath::DivideAndRoundUp(Bucket8Len + 1, sizeof(TCHAR))]; Buffer += PrefixSize; const TCHAR* const Name = Buffer; reinterpret_cast(Buffer)[LengthOffset] = static_cast(BucketLen); reinterpret_cast(Buffer)[LengthOffset8] = static_cast(Bucket8Len); Bucket.CopyString(Buffer, BucketLen); Buffer += BucketLen; *Buffer++ = TEXT('\0'); ANSICHAR* Buffer8 = reinterpret_cast(Buffer); Bucket8.CopyString(Buffer8, Bucket8Len); Buffer8 += Bucket8Len; *Buffer8++ = '\0'; return Name; } inline FCacheBucketOwner::FCacheBucketOwner(FStringView Bucket) : FCacheBucket(AllocName(Bucket)) { } inline FCacheBucketOwner::FCacheBucketOwner(FCacheBucketOwner&& Bucket) : FCacheBucket(Bucket) { Bucket.Reset(); } inline FCacheBucketOwner::~FCacheBucketOwner() { if (IsValid()) { const int32 PrefixSize = FMath::DivideAndRoundUp(2, sizeof(TCHAR)); delete[] (ToString().GetData() - PrefixSize); } } class FCacheBuckets { public: inline FCacheBucket FindOrAdd(FStringView Name); private: FRWLock Lock; TSet Buckets; }; inline FCacheBucket FCacheBuckets::FindOrAdd(FStringView Name) { const uint32 Hash = GetTypeHash(Name); if (FReadScopeLock ReadLock(Lock); const FCacheBucketOwner* Bucket = Buckets.FindByHash(Hash, Name)) { return *Bucket; } checkf(FCString::IsPureAnsi(Name.GetData(), Name.Len()) && Algo::AllOf(Name, FChar::IsAlnum) && !Name.IsEmpty() && Name.Len() < 256, TEXT("Invalid cache bucket name '%.*s' must be alphanumeric, non-empty, and contain fewer than 256 code units."), Name.Len(), Name.GetData()); FCacheBucketOwner LocalBucket(Name); FWriteScopeLock WriteLock(Lock); return Buckets.FindOrAddByHash(Hash, MoveTemp(LocalBucket)); } FCacheBucket CreateCacheBucket(FStringView Name) { static FCacheBuckets Buckets; return Buckets.FindOrAdd(Name); } } // UE::DerivedData::Private