2021-01-21 01:57:01 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "DerivedDataCacheKey.h"
|
|
|
|
|
|
2021-02-24 11:46:41 -04:00
|
|
|
#include "Containers/Set.h"
|
2021-08-05 22:29:19 -04:00
|
|
|
#include "Containers/StringConv.h"
|
2021-05-31 12:43:42 -04:00
|
|
|
#include "DerivedDataCachePrivate.h"
|
2022-06-27 15:21:57 -04:00
|
|
|
#include "Hash/xxhash.h"
|
2022-04-05 16:27:17 -04:00
|
|
|
#include "IO/IoHash.h"
|
2021-03-11 18:12:08 -04:00
|
|
|
#include "Math/UnrealMathUtility.h"
|
2021-02-24 11:46:41 -04:00
|
|
|
#include "Misc/ScopeRWLock.h"
|
2021-08-06 12:51:28 -04:00
|
|
|
#include "Misc/StringBuilder.h"
|
2022-06-17 14:45:17 -04:00
|
|
|
#include "Serialization/CompactBinary.h"
|
2022-06-20 23:40:01 -04:00
|
|
|
#include "Serialization/CompactBinarySerialization.h"
|
2022-01-26 12:31:58 -05:00
|
|
|
#include "Serialization/CompactBinaryWriter.h"
|
2022-04-05 16:27:17 -04:00
|
|
|
#include "String/Find.h"
|
2021-01-21 01:57:01 -04:00
|
|
|
|
2021-04-19 14:28:07 -04:00
|
|
|
namespace UE::DerivedData::Private
|
2021-03-10 17:33:38 -04:00
|
|
|
{
|
2021-01-21 01:57:01 -04:00
|
|
|
|
2021-04-19 14:28:07 -04:00
|
|
|
class FCacheBucketOwner : public FCacheBucket
|
|
|
|
|
{
|
|
|
|
|
public:
|
2022-06-27 15:21:57 -04:00
|
|
|
inline explicit FCacheBucketOwner(FUtf8StringView Bucket);
|
2021-04-19 14:28:07 -04:00
|
|
|
inline FCacheBucketOwner(FCacheBucketOwner&& Bucket);
|
|
|
|
|
inline ~FCacheBucketOwner();
|
|
|
|
|
|
|
|
|
|
FCacheBucketOwner(const FCacheBucketOwner&) = delete;
|
|
|
|
|
FCacheBucketOwner& operator=(const FCacheBucketOwner&) = delete;
|
|
|
|
|
|
|
|
|
|
using FCacheBucket::operator==;
|
|
|
|
|
|
2022-06-27 15:21:57 -04:00
|
|
|
inline bool operator==(FUtf8StringView Bucket) const { return ToString().Equals(Bucket, ESearchCase::IgnoreCase); }
|
|
|
|
|
|
|
|
|
|
static inline uint32 GetBucketHash(const FUtf8StringView Bucket)
|
|
|
|
|
{
|
|
|
|
|
const int32 Len = Bucket.Len();
|
|
|
|
|
check(Len <= MaxNameLen);
|
|
|
|
|
UTF8CHAR LowerBucket[MaxNameLen];
|
|
|
|
|
UTF8CHAR* LowerBucketIt = LowerBucket;
|
|
|
|
|
for (const UTF8CHAR& Char : Bucket)
|
|
|
|
|
{
|
|
|
|
|
*LowerBucketIt++ = TChar<UTF8CHAR>::ToLower(Char);
|
|
|
|
|
}
|
|
|
|
|
return uint32(FXxHash64::HashBuffer(LowerBucket, Len).Hash);
|
|
|
|
|
}
|
2021-05-25 11:02:36 -04:00
|
|
|
|
|
|
|
|
friend inline uint32 GetTypeHash(const FCacheBucketOwner& Bucket)
|
|
|
|
|
{
|
2022-06-27 15:21:57 -04:00
|
|
|
return GetBucketHash(Bucket.ToString());
|
2021-05-25 11:02:36 -04:00
|
|
|
}
|
2021-04-19 14:28:07 -04:00
|
|
|
};
|
|
|
|
|
|
2022-06-27 15:21:57 -04:00
|
|
|
inline FCacheBucketOwner::FCacheBucketOwner(FUtf8StringView Bucket)
|
2021-04-19 14:28:07 -04:00
|
|
|
{
|
2022-06-27 15:21:57 -04:00
|
|
|
checkf(FCacheBucket::IsValidName(Bucket),
|
|
|
|
|
TEXT("A cache bucket name must be alphanumeric, non-empty, and contain at most %d code units. Name: '%s'"),
|
|
|
|
|
*WriteToString<256>(Bucket), FCacheBucket::MaxNameLen);
|
|
|
|
|
|
|
|
|
|
static_assert(sizeof(ANSICHAR) == sizeof(UTF8CHAR));
|
2021-04-19 14:28:07 -04:00
|
|
|
const int32 BucketLen = Bucket.Len();
|
2021-08-06 12:51:28 -04:00
|
|
|
const int32 PrefixSize = FMath::DivideAndRoundUp<int32>(1, sizeof(ANSICHAR));
|
2022-06-27 15:21:57 -04:00
|
|
|
UTF8CHAR* Buffer = new UTF8CHAR[PrefixSize + BucketLen + 1];
|
2021-04-19 14:28:07 -04:00
|
|
|
Buffer += PrefixSize;
|
2022-06-27 15:21:57 -04:00
|
|
|
Name = reinterpret_cast<ANSICHAR*>(Buffer);
|
2021-04-19 14:28:07 -04:00
|
|
|
|
|
|
|
|
reinterpret_cast<uint8*>(Buffer)[LengthOffset] = static_cast<uint8>(BucketLen);
|
|
|
|
|
Bucket.CopyString(Buffer, BucketLen);
|
|
|
|
|
Buffer += BucketLen;
|
2022-06-27 15:21:57 -04:00
|
|
|
*Buffer = UTF8CHAR('\0');
|
2021-04-19 14:28:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline FCacheBucketOwner::FCacheBucketOwner(FCacheBucketOwner&& Bucket)
|
|
|
|
|
: FCacheBucket(Bucket)
|
|
|
|
|
{
|
|
|
|
|
Bucket.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline FCacheBucketOwner::~FCacheBucketOwner()
|
|
|
|
|
{
|
2021-08-06 12:51:28 -04:00
|
|
|
if (Name)
|
2021-04-19 14:28:07 -04:00
|
|
|
{
|
2021-08-06 12:51:28 -04:00
|
|
|
const int32 PrefixSize = FMath::DivideAndRoundUp<int32>(1, sizeof(ANSICHAR));
|
|
|
|
|
delete[] (Name - PrefixSize);
|
2021-04-19 14:28:07 -04:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-21 01:57:01 -04:00
|
|
|
|
2021-02-24 11:46:41 -04:00
|
|
|
class FCacheBuckets
|
2021-01-21 01:57:01 -04:00
|
|
|
{
|
2021-02-24 11:46:41 -04:00
|
|
|
public:
|
2021-08-06 12:51:28 -04:00
|
|
|
template <typename CharType>
|
|
|
|
|
inline FCacheBucket FindOrAdd(TStringView<CharType> Name);
|
2021-02-24 11:46:41 -04:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
FRWLock Lock;
|
2021-04-19 14:28:07 -04:00
|
|
|
TSet<FCacheBucketOwner> Buckets;
|
2021-02-24 11:46:41 -04:00
|
|
|
};
|
|
|
|
|
|
2021-08-06 12:51:28 -04:00
|
|
|
template <typename CharType>
|
2022-06-27 15:21:57 -04:00
|
|
|
inline FCacheBucket FCacheBuckets::FindOrAdd(const TStringView<CharType> Name)
|
2021-04-19 14:28:07 -04:00
|
|
|
{
|
2022-06-27 15:21:57 -04:00
|
|
|
const auto NameCast = StringCast<UTF8CHAR, FCacheBucket::MaxNameLen + 1>(Name.GetData(), Name.Len());
|
|
|
|
|
const FUtf8StringView NameView = NameCast;
|
|
|
|
|
uint32 Hash = 0;
|
2021-08-06 12:51:28 -04:00
|
|
|
|
2022-06-27 15:21:57 -04:00
|
|
|
if (NameView.Len() <= FCacheBucket::MaxNameLen)
|
2021-04-19 14:28:07 -04:00
|
|
|
{
|
2022-06-27 15:21:57 -04:00
|
|
|
Hash = FCacheBucketOwner::GetBucketHash(NameView);
|
|
|
|
|
FReadScopeLock ReadLock(Lock);
|
|
|
|
|
if (const FCacheBucketOwner* Bucket = Buckets.FindByHash(Hash, NameView))
|
|
|
|
|
{
|
|
|
|
|
return *Bucket;
|
|
|
|
|
}
|
2021-04-19 14:28:07 -04:00
|
|
|
}
|
|
|
|
|
|
2022-06-27 15:21:57 -04:00
|
|
|
FCacheBucketOwner LocalBucket(NameView);
|
2021-04-19 14:28:07 -04:00
|
|
|
FWriteScopeLock WriteLock(Lock);
|
|
|
|
|
return Buckets.FindOrAddByHash(Hash, MoveTemp(LocalBucket));
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-06 12:51:28 -04:00
|
|
|
static FCacheBuckets& GetCacheBuckets()
|
2021-02-24 11:46:41 -04:00
|
|
|
{
|
|
|
|
|
static FCacheBuckets Buckets;
|
2021-08-06 12:51:28 -04:00
|
|
|
return Buckets;
|
2021-01-21 01:57:01 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-19 14:28:07 -04:00
|
|
|
} // UE::DerivedData::Private
|
2021-08-06 12:51:28 -04:00
|
|
|
|
|
|
|
|
namespace UE::DerivedData
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
FCacheBucket::FCacheBucket(FUtf8StringView InName)
|
|
|
|
|
: FCacheBucket(Private::GetCacheBuckets().FindOrAdd(InName))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FCacheBucket::FCacheBucket(FWideStringView InName)
|
|
|
|
|
: FCacheBucket(Private::GetCacheBuckets().FindOrAdd(InName))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 23:40:01 -04:00
|
|
|
FCbWriter& operator<<(FCbWriter& Writer, const FCacheBucket Bucket)
|
2022-06-17 14:45:17 -04:00
|
|
|
{
|
2022-06-20 23:40:01 -04:00
|
|
|
Writer.AddString(Bucket.ToString());
|
|
|
|
|
return Writer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LoadFromCompactBinary(FCbFieldView Field, FCacheBucket& OutBucket)
|
|
|
|
|
{
|
|
|
|
|
if (const FUtf8StringView Bucket = Field.AsString(); !Field.HasError() && FCacheBucket::IsValidName(Bucket))
|
2022-06-17 14:45:17 -04:00
|
|
|
{
|
2022-06-20 23:40:01 -04:00
|
|
|
OutBucket = FCacheBucket(Bucket);
|
|
|
|
|
return true;
|
2022-06-17 14:45:17 -04:00
|
|
|
}
|
2022-06-20 23:40:01 -04:00
|
|
|
OutBucket.Reset();
|
|
|
|
|
return false;
|
2022-06-17 14:45:17 -04:00
|
|
|
}
|
|
|
|
|
|
2022-01-26 12:31:58 -05:00
|
|
|
FCbWriter& operator<<(FCbWriter& Writer, const FCacheKey& Key)
|
|
|
|
|
{
|
|
|
|
|
Writer.BeginObject();
|
2022-06-20 23:40:01 -04:00
|
|
|
Writer << ANSITEXTVIEW("Bucket") << Key.Bucket;
|
|
|
|
|
Writer << ANSITEXTVIEW("Hash") << Key.Hash;
|
2022-01-26 12:31:58 -05:00
|
|
|
Writer.EndObject();
|
|
|
|
|
return Writer;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 23:40:01 -04:00
|
|
|
bool LoadFromCompactBinary(const FCbFieldView Field, FCacheKey& OutKey)
|
|
|
|
|
{
|
|
|
|
|
bool bOk = Field.IsObject();
|
|
|
|
|
bOk &= LoadFromCompactBinary(Field[ANSITEXTVIEW("Bucket")], OutKey.Bucket);
|
|
|
|
|
bOk &= LoadFromCompactBinary(Field[ANSITEXTVIEW("Hash")], OutKey.Hash);
|
|
|
|
|
return bOk;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-05 16:27:17 -04:00
|
|
|
FCacheKey ConvertLegacyCacheKey(const FStringView Key)
|
|
|
|
|
{
|
|
|
|
|
FTCHARToUTF8 Utf8Key(Key);
|
|
|
|
|
TUtf8StringBuilder<64> Utf8Bucket;
|
|
|
|
|
Utf8Bucket << ANSITEXTVIEW("Legacy");
|
|
|
|
|
if (const int32 BucketEnd = String::FindFirstChar(Utf8Key, '_'); BucketEnd != INDEX_NONE)
|
|
|
|
|
{
|
|
|
|
|
Utf8Bucket << FUtf8StringView(Utf8Key).Left(BucketEnd);
|
|
|
|
|
}
|
|
|
|
|
const FCacheBucket Bucket(Utf8Bucket);
|
|
|
|
|
return {Bucket, FIoHash::HashBuffer(MakeMemoryView(Utf8Key))};
|
|
|
|
|
}
|
2022-01-26 12:31:58 -05:00
|
|
|
|
2021-08-06 12:51:28 -04:00
|
|
|
} // UE::DerivedData
|