Files
UnrealEngine/Engine/Source/Programs/Unsync/Private/UnsyncUtil.h
2025-05-18 13:04:45 +08:00

424 lines
9.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "UnsyncCommon.h"
UNSYNC_THIRD_PARTY_INCLUDES_START
#if UNSYNC_PLATFORM_WINDOWS
# include <Windows.h>
# include <intrin.h>
#endif // UNSYNC_PLATFORM_WINDOWS
#include <fmt/format.h>
#include <stdio.h>
#include <chrono>
#include <concepts>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
UNSYNC_THIRD_PARTY_INCLUDES_END
#include "UnsyncHash.h"
#include "UnsyncLog.h"
namespace unsync {
class FBuffer;
using FTimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
using FTimeDuration = std::chrono::duration<double>;
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<uint64>(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<typename HashType>
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<std::string_view> SplitByAny(std::string_view String, const char* SeparatorCharacters);
inline std::vector<std::string_view>
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<typename T>
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<typename T>
TArrayView<T>
MakeView(const T* Ptr, size_t Count)
{
TArrayView<T> Result;
Result.BeginPtr = Ptr;
Result.EndPtr = Ptr + Count;
return Result;
}
template<typename T>
TArrayView<T>
MakeView(const std::vector<T>& 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 <class T>
concept TIsIntegral = std::is_integral_v<T>;
template <class T>
concept TIsSignedIntegral = TIsIntegral<T> && static_cast<T>(-1) < static_cast<T>(0);
template <class T>
concept TIsUnsignedIntegral = TIsIntegral<T> && !TIsSignedIntegral<T>;
template<typename T>
requires TIsUnsignedIntegral<T>
inline uint32
CheckedNarrow(T X)
{
uint32 Narrowed = static_cast<uint32>(X);
UNSYNC_ASSERTF(X == static_cast<uint64>(Narrowed), L"Value %llu does not fit into uint32", X);
return uint32(X);
}
template<typename T>
requires TIsSignedIntegral<T>
inline int32
CheckedNarrow(T X)
{
int32 Narrowed = static_cast<int32>(X);
UNSYNC_ASSERTF(X == static_cast<int64>(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<typename StorageT>
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<typename StorageT>
inline bool
BitArrayGet(const StorageT* Storage, uint64 BitIndex)
{
TBitArrayInfo<StorageT> Info(BitIndex);
return (Storage[Info.ElemIndex] & Info.BitMask) != 0;
}
template<typename StorageT>
inline void
BitArraySet(StorageT* Storage, uint64 BitIndex, bool bValue)
{
TBitArrayInfo<StorageT> 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<typename T>
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