Files
UnrealEngineUWP/Engine/Source/Developer/DerivedDataCache/Private/DerivedDataLimitKeyLengthWrapper.h
Andrew Grant a572d8e23f Copying //UE4/Orion-Staging to //UE4/Main (Origin //Orion/Dev-General @ 2870388)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2870336 on 2016/02/17 by Marc.Audy

	Continued splitting up Orion Build
	* Restructure from platform based MakeBuild steps in to a PS4, Server, and Windows Client MakeBuild
	* Cook server data only once for both Windows and Linux (windows reuses Linux server data)
	* Split compilation of Win64 Client and Server such that MakeBuild_Server only builds Server and MakeBuild_WindowsClient only builds Client
	#jira UEB-580
	#rb Ben.Marsh
	#tests Preflight and generated Windows Client and Server work to play game

Change 2870026 on 2016/02/17 by Wes.Hunt

	Don't allow array shrinking when removing the corruption wrapper trailer.
	#rb none
	Updating CIS Counter

Change 2869725 on 2016/02/17 by Dmitry.Rekman

	More analytics and QoS stats added for 0.19.

	#rb none
	#tests Ran Windows client and Linux server on compatible content.

Change 2869705 on 2016/02/16 by Ryan.Gerleve

	Fix replicated properties and call RepNotifies of startup actors when scrubbing in replays.
	This is the engine support for fixing OR-6817, towers not respawning when rewinding replays.

	#rb john.pollard
	#tests golden path, replays, ps4 nomcp

Change 2869644 on 2016/02/16 by Jason.Bestimt

	#ORION_DEV - Merge MAIN (0.18) at CL# 2869635

	#Tests:none
	#RB:none

Change 2869586 on 2016/02/16 by Marcus.Wassmer

	Fix texturestreaming RHI flushes.
	#rb none
	#test goldenpath
	#codereview Gil.Gribb

Change 2869279 on 2016/02/16 by Lukasz.Furman

	fixed minion hit reaction directions
	#orion OR-13953
	#rb Mieszko.Zielinski
	#tests PIE: hit minions with various abilities from different angles, checked velocity of death particles when killed by abilities and towers
	#codereview Dan.Youhon

Change 2869277 on 2016/02/16 by Wes.Hunt

	During cook, when a package is not ready to save, actually early out of the saving code. Saves somewhere in the 130s to 200s range for cooks.
	#rb daniel.lamb
	#tests local windows cooks, preflight PS4 cooks

Change 2869132 on 2016/02/16 by Mieszko.Zielinski

	Added a function to AISenseConfig allowing native-code MaxAge configuration #UE4

	#rb Lukasz.Furman
	#test none required

Change 2868981 on 2016/02/16 by Wes.Hunt

	remove -LogCookStats cmdline check, always log cook stats. -SendCookAnalytics flag is still used.
	This was requested by NickP.
	#rb none
	#tests local windows cooks

Change 2868975 on 2016/02/16 by Wes.Hunt

	Don't submit DDC usage stats for zero-sized events.
	#rb none
	#tests local windows cook

Change 2868956 on 2016/02/16 by Jason.Bestimt

	#ORION_DEV - Merge MAIN (0.18) at CL# 2868926

	#RB:none
	#Tests:none

Change 2868889 on 2016/02/16 by Max.Chen

	Sequencer: Only allow transport control binding when editing level editor sequencers.

	#rb none
	#tests none

Change 2868663 on 2016/02/16 by David.Ratti

	downgrade warning to display

	#rb none
	#tests compile

Change 2868624 on 2016/02/16 by Marcus.Wassmer

	Re-Enable Defrag validation for devgeneral
	#rb none
	#test none

Change 2868493 on 2016/02/16 by Benn.Gallagher

	Added a few more stats to morph target updates to try and narrow down hitches
	#rb Bruce.Nesbit
	#tests pie, -game Win64

Change 2868445 on 2016/02/16 by Dmitry.Rekman

	Linux: report crashes due to stack overflow (OR-14519).

	- Reserve memory for alternative stack for signal handlers. Adds about 128KB memory per thread.
	- Force process spawning to use vfork() when no pipes are needed.
	- Ignore all signals except explicitly handled.
	- Prevent signals from being raised while another one is handled.
	- Added "debug threadrecurse" and "debug threadstackoverflow" to test that.

[CL 2873763 by Andrew Grant in Main branch]
2016-02-19 12:03:17 -05:00

217 lines
6.6 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "DerivedDataCacheUsageStats.h"
/**
* A backend wrapper that limits the key size and uses hashing...in this case it wraps the payload and the payload contains the full key to verify the integrity of the hash
**/
class FDerivedDataLimitKeyLengthWrapper : public FDerivedDataBackendInterface
{
public:
/**
* Constructor
*
* @param InInnerBackend Backend to use for storage, my responsibilities are about key length
*/
FDerivedDataLimitKeyLengthWrapper(FDerivedDataBackendInterface* InInnerBackend, int32 InMaxKeyLength)
: InnerBackend(InInnerBackend)
, MaxKeyLength(InMaxKeyLength)
{
check(InnerBackend);
}
/** return true if this cache is writable **/
virtual bool IsWritable() override
{
return InnerBackend->IsWritable();
}
/**
* Synchronous test for the existence of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @return true if the data probably will be found, this can't be guaranteed because of concurrency in the backends, corruption, etc
*/
virtual bool CachedDataProbablyExists(const TCHAR* CacheKey) override
{
COOK_STAT(auto Timer = UsageStats.TimeProbablyExists());
FString NewKey;
ShortenKey(CacheKey, NewKey);
bool Result = InnerBackend->CachedDataProbablyExists(*NewKey);
if (Result)
{
COOK_STAT(Timer.AddHit(0));
}
return Result;
}
/**
* Synchronous retrieve of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @param OutData Buffer to receive the results, if any were found
* @return true if any data was found, and in this case OutData is non-empty
*/
virtual bool GetCachedData(const TCHAR* CacheKey, TArray<uint8>& OutData) override
{
COOK_STAT(auto Timer = UsageStats.TimeGet());
int64 InnerGetCycles = 0;
FString NewKey;
bool bOk;
if (!ShortenKey(CacheKey, NewKey))
{
// no shortening needed
bOk = InnerBackend->GetCachedData(CacheKey, OutData);
// look for old bug
if (FString(CacheKey).StartsWith(TEXT("TEXTURE2D_0002")))
{
int32 KeyLen = FCString::Strlen(CacheKey) + 1;
if (OutData.Num() > KeyLen && OutData.Last() == 0)
{
int32 Compare = FCStringAnsi::Strcmp(TCHAR_TO_ANSI(CacheKey), (char*)&OutData[OutData.Num() - KeyLen]);
if (Compare == 0)
{
UE_LOG(LogDerivedDataCache, Warning, TEXT("FDerivedDataLimitKeyLengthWrapper: Fixed old bug %s."), CacheKey);
OutData.RemoveAt(OutData.Num() - KeyLen, KeyLen);
}
}
}
}
else
{
bOk = InnerBackend->GetCachedData(*NewKey, OutData);
if (bOk)
{
int32 KeyLen = FCString::Strlen(CacheKey) + 1;
if (OutData.Num() < KeyLen)
{
UE_LOG(LogDerivedDataCache, Warning, TEXT("FDerivedDataLimitKeyLengthWrapper: Short file or Hash Collision, ignoring and deleting %s."), CacheKey);
bOk = false;
}
else
{
int32 Compare = FCStringAnsi::Strcmp(TCHAR_TO_ANSI(CacheKey), (char*)&OutData[OutData.Num() - KeyLen]);
OutData.RemoveAt(OutData.Num() - KeyLen, KeyLen);
if (Compare == 0)
{
UE_LOG(LogDerivedDataCache, Verbose, TEXT("FDerivedDataLimitKeyLengthWrapper: cache hit, key match is ok %s"), CacheKey);
}
else
{
UE_LOG(LogDerivedDataCache, Warning, TEXT("FDerivedDataLimitKeyLengthWrapper: HASH COLLISION, ignoring and deleting %s."), CacheKey);
bOk = false;
}
}
if (!bOk)
{
// _we_ detected corruption, so _we_ will force a flush of the corrupted data
InnerBackend->RemoveCachedData(*NewKey, /*bTransient=*/ false);
}
}
}
if (!bOk)
{
OutData.Empty();
}
else
{
COOK_STAT(Timer.AddHit(OutData.Num()));
}
return bOk;
}
/**
* Asynchronous, fire-and-forget placement of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @param InData Buffer containing the data to cache, can be destroyed after the call returns, immediately
* @param bPutEvenIfExists If true, then do not attempt skip the put even if CachedDataProbablyExists returns true
*/
virtual void PutCachedData(const TCHAR* CacheKey, TArray<uint8>& InData, bool bPutEvenIfExists) override
{
COOK_STAT(auto Timer = UsageStats.TimePut());
if (!InnerBackend->IsWritable())
{
return; // no point in continuing down the chain
}
COOK_STAT(Timer.AddHit(InData.Num()));
FString NewKey;
if (!ShortenKey(CacheKey, NewKey))
{
InnerBackend->PutCachedData(CacheKey, InData, bPutEvenIfExists);
return;
}
TArray<uint8> Data(InData);
check(Data.Num());
int32 KeyLen = FCString::Strlen(CacheKey) + 1;
Data.AddUninitialized(KeyLen);
FCStringAnsi::Strcpy((char*)&Data[Data.Num() - KeyLen], KeyLen, TCHAR_TO_ANSI(CacheKey));
check(Data.Last()==0);
InnerBackend->PutCachedData(*NewKey, Data, bPutEvenIfExists);
}
virtual void RemoveCachedData(const TCHAR* CacheKey, bool bTransient) override
{
if (!InnerBackend->IsWritable())
{
return; // no point in continuing down the chain
}
FString NewKey;
ShortenKey(CacheKey, NewKey);
return InnerBackend->RemoveCachedData(*NewKey, bTransient);
}
virtual void GatherUsageStats(TMap<FString, FDerivedDataCacheUsageStats>& UsageStatsMap, FString&& GraphPath) override
{
COOK_STAT(
{
UsageStatsMap.Add(GraphPath + TEXT(": LimitKeyLength"), UsageStats);
if (InnerBackend)
{
InnerBackend->GatherUsageStats(UsageStatsMap, GraphPath + TEXT(". 0"));
}
});
}
private:
FDerivedDataCacheUsageStats UsageStats;
/** Shorten the cache key and return true if shortening was required **/
bool ShortenKey(const TCHAR* CacheKey, FString& Result)
{
Result = FString(CacheKey);
if (Result.Len() <= MaxKeyLength)
{
return false;
}
FSHA1 HashState;
int32 Length = Result.Len();
HashState.Update((const uint8*)&Length, sizeof(int32));
auto ResultSrc = StringCast<UCS2CHAR>(*Result);
uint32 CRCofPayload(FCrc::MemCrc32(ResultSrc.Get(), Length * sizeof(UCS2CHAR)));
HashState.Update((const uint8*)&CRCofPayload, sizeof(uint32));
HashState.Update((const uint8*)ResultSrc.Get(), Length * sizeof(UCS2CHAR));
HashState.Final();
uint8 Hash[FSHA1::DigestSize];
HashState.GetHash(Hash);
FString HashString = BytesToHex(Hash, FSHA1::DigestSize);
int32 HashStringSize = HashString.Len();
int32 OriginalPart = MaxKeyLength - HashStringSize - 2;
Result = Result.Left(OriginalPart) + TEXT("__") + HashString;
check(Result.Len() == MaxKeyLength && Result.Len() > 0);
return true;
}
/** Backend to use for storage, my responsibilities are about key length **/
FDerivedDataBackendInterface* InnerBackend;
/** Maximum size of the backend key length **/
int32 MaxKeyLength;
};