// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "UnsyncCommon.h" UNSYNC_THIRD_PARTY_INCLUDES_START #if UNSYNC_PLATFORM_WINDOWS # include # include #endif // UNSYNC_PLATFORM_WINDOWS #include #include #include #include #include #include #include #include UNSYNC_THIRD_PARTY_INCLUDES_END #include "UnsyncHash.h" #include "UnsyncLog.h" namespace unsync { class FBuffer; using FTimePoint = std::chrono::time_point; using FTimeDuration = std::chrono::duration; constexpr auto operator""_KB(unsigned long long X) -> uint64 { return X * (1ull << 10); } constexpr auto operator""_MB(unsigned long long X) -> uint64 { return X * (1ull << 20); } constexpr auto operator""_GB(unsigned long long X) -> uint64 { return X * (1ull << 30); } inline FTimePoint TimePointNow() { return std::chrono::high_resolution_clock::now(); } inline double DurationSec(FTimePoint TimeBegin, FTimePoint TimeEnd) { return FTimeDuration(TimeEnd - TimeBegin).count(); } inline double DurationMs(FTimePoint TimeBegin, FTimePoint TimeEnd) { return DurationSec(TimeBegin, TimeEnd) * 1000.0; } inline uint64 CalcChunkSize(uint64 ChunkIndex, uint64 ChunkSize, uint64 TotalDataSize) { uint64 Begin = ChunkIndex * ChunkSize; uint64 End = std::min(TotalDataSize, (ChunkIndex + 1) * ChunkSize); return End - Begin; } inline uint64 MakeU64(uint32 H, uint32 L) { return uint64(L) | (uint64(H) << 32); } struct FRange { uint64 Offset = 0; uint64 Size = 0; }; struct FTimingLogger { bool bEnabled = false; FTimePoint TimeBegin = FTimePoint{}; std::string Name; ELogLevel LogLevel; FTimingLogger(const char* InName, ELogLevel InLogLevel, bool InEnabled = true); ~FTimingLogger(); void Finish(); }; inline double SizeMb(double Size) { return double(Size) / double(1 << 20); } inline double SizeMb(uint64 Size) { return SizeMb(double(Size)); } inline uint64 DivUp(uint64 Num, uint64 Den) { return (Num + Den - 1) / Den; } inline uint32 NextPow2(uint32 V) { V--; V |= V >> 1; V |= V >> 2; V |= V >> 4; V |= V >> 8; V |= V >> 16; V++; return V; } std::string BytesToHexString(const uint8* Data, uint64 Size); // Converts input bytes to hexadecimal ACII. Returns how many characters were written to output. uint64 BytesToHexChars(char* Output, uint64 OutputSize, const uint8* Input, uint64 InputSize); uint64 BytesToHexChars(wchar_t* Output, uint64 OutputSize, const uint8* Input, uint64 InputSize); template std::string HashToHexString(const HashType& Hash) { return BytesToHexString(Hash.Data, Hash.Size()); } std::wstring ConvertUtf8ToWide(std::string_view StringUtf8); std::string ConvertWideToUtf8(std::wstring_view StringWide); void ConvertWideToUtf8(std::wstring_view StringWide, std::string& Result); std::string_view AsStringView(const FBuffer& Buffer); inline std::string ToString(const std::wstring_view WideStringView) { return ConvertWideToUtf8(WideStringView); } inline std::string ToString(const std::string_view StringView) { return std::string(StringView); } std::string ToString(const FPath& Path); std::string StringToLower(std::string_view Input); std::wstring StringToLower(std::wstring_view Input); std::wstring StringToUpper(std::wstring_view Input); std::string StringEscape(const std::string_view Input); bool StringEquals(const std::string_view A, const std::string_view B, bool bCaseSensitive = true); bool StringStartsWith(const std::string_view String, const std::string_view Prefix, bool bCaseSensitive = true); inline bool UncasedStringEquals(const std::string_view A, const std::string_view B) { return StringEquals(A, B, false); } inline bool IsAsciiAlphabetCharacter(const char C) { return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z'); } inline bool IsAsciiNumericCharacter(const char C) { return C >= '0' && C <= '9'; } inline bool IsAsciiAlphaNumericCharacter(const char C) { return IsAsciiAlphabetCharacter(C) || IsAsciiNumericCharacter(C); } std::vector SplitByAny(std::string_view String, const char* SeparatorCharacters); inline std::vector SplitBy(std::string_view String, char SeparatorCharacter) { char Chars[2] = {SeparatorCharacter, 0}; return SplitByAny(String, Chars); } // Takes a drive-based path (e.g. P:/Foo/Bar) and converts it to universal form (e.g. //server/Foo/Bar), if possible. // Otherwise, returns original path. FPath GetUniversalPath(const FPath& Path); template struct TArrayView { const T* BeginPtr = nullptr; const T* EndPtr = nullptr; const T* begin() const { return BeginPtr; } // NOLINT const T* end() const { return EndPtr; } // NOLINT size_t Size() const { return end() - begin(); } }; template TArrayView MakeView(const T* Ptr, size_t Count) { TArrayView Result; Result.BeginPtr = Ptr; Result.EndPtr = Ptr + Count; return Result; } template TArrayView MakeView(const std::vector& Container) { return MakeView(Container.data(), Container.size()); } inline uint64 AlignDownToMultiplePow2(uint64 X, uint64 MultiplePow2) { return X & (~(MultiplePow2 - 1)); } inline uint64 AlignUpToMultiplePow2(uint64 X, uint64 MultiplePow2) { uint64 Remainder = X & (MultiplePow2 - 1); if (Remainder) { X += MultiplePow2 - Remainder; } return X; } template concept TIsIntegral = std::is_integral_v; template concept TIsSignedIntegral = TIsIntegral && static_cast(-1) < static_cast(0); template concept TIsUnsignedIntegral = TIsIntegral && !TIsSignedIntegral; template requires TIsUnsignedIntegral inline uint32 CheckedNarrow(T X) { uint32 Narrowed = static_cast(X); UNSYNC_ASSERTF(X == static_cast(Narrowed), L"Value %llu does not fit into uint32", X); return uint32(X); } template requires TIsSignedIntegral inline int32 CheckedNarrow(T X) { int32 Narrowed = static_cast(X); UNSYNC_ASSERTF(X == static_cast(Narrowed), L"Value %lld does not fit into int32", X); return Narrowed; } inline uint32 Xorshift32(uint32& State) { uint32 X = State; X ^= X << 13; X ^= X >> 17; X ^= X << 5; State = X; return X; } inline void FillRandomBytes(uint8* Output, uint64 Size, uint32 Seed) { for (uint64 I = 0; I < Size; ++I) { Output[I] = Xorshift32(Seed) & 0xFF; } } #if !UNSYNC_COMPILER_MSVC inline uint8 _BitScanReverse64(unsigned long* Index, uint64 Mask) { if (Mask == 0) { return 0; } *Index = 63 - __builtin_clzll(Mask); return 1; } #endif // !UNSYNC_COMPILER_MSVC inline uint64 FloorLog264(uint64 X) { unsigned long XLog2; long Mask = -long(_BitScanReverse64(&XLog2, X) != 0); return XLog2 & Mask; } inline uint32 CountLeadingZeros32(uint32 X) { unsigned long XLog2; _BitScanReverse64(&XLog2, (uint64(X) << 1) | 1); return 32 - XLog2; } inline uint64 CountLeadingZeros64(uint64 X) { unsigned long XLog2; long Mask = -long(_BitScanReverse64(&XLog2, X) != 0); return ((63 - XLog2) & Mask) | (64 & ~Mask); } template struct TBitArrayInfo { static constexpr size_t ElemSizeInBits = sizeof(StorageT) * 8; const uint64 ElemIndex; const StorageT BitMask; TBitArrayInfo(uint64 BitIndex) : ElemIndex(BitIndex / ElemSizeInBits), BitMask(StorageT(1) << (BitIndex % ElemSizeInBits)) {} }; template inline bool BitArrayGet(const StorageT* Storage, uint64 BitIndex) { TBitArrayInfo Info(BitIndex); return (Storage[Info.ElemIndex] & Info.BitMask) != 0; } template inline void BitArraySet(StorageT* Storage, uint64 BitIndex, bool bValue) { TBitArrayInfo Info(BitIndex); if (bValue) { Storage[Info.ElemIndex] |= Info.BitMask; } else { Storage[Info.ElemIndex] &= ~Info.BitMask; } } FPath NormalizeFilenameWide(std::wstring_view InFilename); FPath NormalizeFilenameUtf8(std::string_view InFilename); FPath GetAbsoluteNormalPath(const FPath& InPath); const FBuffer& GetSystemRootCerts(); template inline std::string_view ToStringView(const T& Buf) { return std::string_view(Buf.data(), Buf.size()); } inline std::string ToString(const fmt::memory_buffer& Buf) { return std::string(ToStringView(Buf)); } inline void Append(fmt::memory_buffer& Buf, std::string_view V) { Buf.append(V); } inline void Append(fmt::memory_buffer& Buf, const char* S) { Buf.append(std::string_view(S)); } void OpenUrlInDefaultBrowser(const char* Address); FPath GetUserHomeDirectory(); FHash256 GetAnonymizedMachineId(std::string_view Salt = {}); std::string GetAnonymizedMachineIdString(std::string_view Salt = {}); // Returns string in format 'Error code 123: Some description.` std::string FormatSystemErrorMessage(int32 ErrorCode); bool LooksLikeUrl(std::string_view Str); bool LooksLikeHash160(const std::string_view Str); bool LooksLikeHash160(const std::wstring_view Str); // Json formatting helpers void FormatJsonKeyValueStr(std::wstring& Output, std::wstring_view K, std::wstring_view V, std::wstring_view Suffix = {}); void FormatJsonKeyValueStr(std::string& Output, std::string_view K, std::string_view V, std::string_view Suffix = {}); void FormatJsonKeyValueUInt(std::wstring& Output, std::wstring_view K, uint64 V, std::wstring_view Suffix = {}); void FormatJsonKeyValueUInt(std::string& Output, std::string_view K, uint64 V, std::string_view Suffix = {}); void FormatJsonKeyValueBool(std::wstring& Output, std::wstring_view K, bool V, std::wstring_view Suffix = {}); void FormatJsonKeyValueBool(std::string& Output, std::string_view K, bool V, std::string_view Suffix = {}); } // namespace unsync