Files
UnrealEngine/Engine/Source/Programs/UnrealBuildAccelerator/Common/Public/UbaRootPaths.h
2025-05-18 13:04:45 +08:00

172 lines
4.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "UbaHash.h"
#include "UbaLogger.h"
namespace uba
{
static constexpr u32 PathsPerRoot = IsWindows ? 4u : 1;
class RootPaths
{
public:
bool RegisterRoot(Logger& logger, const tchar* rootPath, bool includeInKey = true, u8 id = 0);
bool RegisterSystemRoots(Logger& logger, u8 startId = 0);
bool RegisterIgnoredRoot(Logger& logger, const tchar* rootPath);
bool IsEmpty() const;
struct Root
{
TString path;
StringKey shortestPathKey;
u8 index;
bool includeInKey;
};
const Root* FindRoot(const StringView& path) const;
const TString& GetRoot(u32 index) const;
TString GetAllRoots() const;
template<typename CharType, typename Func>
bool NormalizeString(Logger& logger, const CharType* str, u64 strLen, const Func& func, bool allowPathsWithoutRoot, const tchar* hint, const tchar* hint2 = TC("")) const;
CasKey NormalizeAndHashFile(Logger& logger, const tchar* filename, bool warnOnFileNotFound = false) const;
static constexpr u8 RootStartByte = ' ';
private:
struct Roots : Vector<Root>
{
u32 shortestRoot = 0;
u32 longestRoot = 0;
};
bool InternalRegisterRoot(Logger& logger, Roots& roots, const tchar* rootPath, bool includeInKey, u8 id);
bool InternalRegisterRoot2(Logger& logger, Roots& roots, const tchar* rootPath, bool includeInKey, u8 index);
const Root* InternalFindRoot(const Roots& roots, const StringView& path) const;
Roots m_roots;
Roots m_ignoredRoots;
};
template<typename CharType, typename Func>
bool RootPaths::NormalizeString(Logger& logger, const CharType* str, u64 strLen, const Func& func, bool allowPathsWithoutRoot, const tchar* hint, const tchar* hint2) const
{
auto strEnd = str + strLen;
auto searchPos = str;
u32 destPos = 0;
#if !PLATFORM_WINDOWS
allowPathsWithoutRoot = true; // Posix uses forward slash and no drive letter so we can't see if a path is in the middle of a path or beginning
#endif
while (true)
{
auto absPathChars = searchPos;
CharType lastChar = 0;
#if PLATFORM_WINDOWS
while (absPathChars < strEnd && !(lastChar == ':' && (*absPathChars == '\\' || *absPathChars == '/')))
{
lastChar = *absPathChars;
++absPathChars;
}
#else
while (absPathChars < strEnd && *absPathChars != '/')
{
lastChar = *absPathChars;
++absPathChars;
}
#endif
if (absPathChars == strEnd)
{
func(searchPos, strEnd - searchPos, ~0u); // Yes, this contains the terminating character too which is a bit weird
return true;
}
auto pathStart = absPathChars - (IsWindows ? 2 : 0);
auto pathEndOrMore = pathStart;
while (pathEndOrMore < strEnd && *pathEndOrMore != '\n')
++pathEndOrMore;
u32 lenOrMore = u32(pathEndOrMore - pathStart);
u32 toCopy = Min(lenOrMore, m_roots.longestRoot);
StringBuffer<512> path;
path.Append(pathStart, toCopy);
auto root = FindRoot(path);
if (!root)
{
bool skip = allowPathsWithoutRoot;
#if PLATFORM_WINDOWS
if (str + 2 <= absPathChars)
{
CharType driveLetter = absPathChars[-2];
if (!(driveLetter >= 'a' && driveLetter <= 'z' || driveLetter >= 'A' && driveLetter <= 'Z')) // Not a drive
skip = true;
else if (str + 3 <= absPathChars && absPathChars[0] == '/' && absPathChars[1] == '/')
{
CharType preDrive = absPathChars[-3];
if (preDrive >= 'a' && preDrive <= 'z' || preDrive >= 'A' && preDrive <= 'Z') // http://, https:// or file://
skip = true;
}
}
#endif
if (skip || InternalFindRoot(m_ignoredRoots, path))
{
u32 len = u32(absPathChars - searchPos) + 1;
destPos += len;
func(searchPos, len, ~0u);
searchPos += len;
continue;
}
if (auto lastQuote = path.Last('\"'))
path.Resize(lastQuote - path.data);
if (auto lineEnd = path.Last('\r'))
path.Resize(lineEnd - path.data);
#if PLATFORM_WINDOWS
logger.Info(TC("PATH WITHOUT ROOT: %s (inside %s at offset %u%s)"), path.data, hint, destPos, hint2);
#endif
return false;
}
if (u32 len = u32(pathStart - searchPos))
{
destPos += len;
func(searchPos, len, ~0u);
}
CharType temp = RootStartByte + CharType(root->index);
func(&temp, 1, destPos);
destPos += 1;
searchPos = pathStart + root->path.size();
}
}
inline bool IsNormalized(const CasKey& key)
{
UBA_ASSERT(key != CasKeyZero);
return (((u8*)&key)[19] & 2) == 2;
}
inline CasKey AsNormalized(const CasKey& key, bool normalized)
{
UBA_ASSERT(key != CasKeyZero);
CasKey newKey = key;
u8 flagField = ((u8*)&key)[19];
((u8*)&newKey)[19] = normalized ? (flagField | 2) : (flagField & ~2);
return newKey;
}
}