원본 문자열과 비교용 문자열을 가지고 있는 싱글톤 풀 구조입니다.
struct FNamePool : public TSingleton<FNamePool>
{
private:
#if WITH_CASE_PRESERVING_NAME
TMap<uint32, FNameEntry> DisplayPool;
#endif
TMap<uint32, FNameEntry> ComparisonPool;
public:
/** 풀을 검색해 있으면 반환, 없으면 추가 후 반환*/
FNameEntryId Store(FNameStringView& View);
/** Hash로 원본 문자열을 가져옵니다. */
FNameEntry Resolve(uint32 Hash) const { return *DisplayPool.Find(Hash); }
private:
template <ENameCase Sensitivity>
FNameEntry MakeEntry(const FNameValue<Sensitivity>& Value) const
{
FNameEntry Result;
Result.ComparisonId = Value.ComparisonId;
Result.Header = {
.bIsWide = Value.Name.bIsWide,
.Len = static_cast<uint16>(Value.Name.Len)
};
if (Value.Name.bIsWide)
{
Result.StoreName(Value.Name.Wide, Value.Name.Len);
}
else
{
Result.StoreName(Value.Name.Ansi, Value.Name.Len);
}
return Result;
}
public:
/** 문자열을 찾거나, 없으면 Hash화 해서 저장합니다. */
FNameEntryId Store(const FNameStringView& Name)
{
#if WITH_CASE_PRESERVING_NAME
FNameDisplayValue DisplayValue{ Name };
if (DisplayPool.Find(DisplayValue.Hash))
{
return { DisplayValue.Hash };
}
#endif
const FNameComparisonValue ComparisonValue{ Name };
if (!ComparisonPool.Find(ComparisonValue.Hash))
{
const FNameEntry Entry = MakeEntry(ComparisonValue);
ComparisonPool.Add(ComparisonValue.Hash, Entry);
}
DisplayValue.ComparisonId = { ComparisonValue.Hash };
DisplayPool.Add(DisplayValue.Hash, MakeEntry(DisplayValue));
return { DisplayValue.Hash };
}
};
FNamePool에 저장되는 실제 문자열 데이터이다. 고유한 문자열과 그 문자열의 해시값을 가짐
struct FNameEntry {
#if WITH_CASE_PRESERVING_NAME
FNameEntryId ComparisonId; // 비교를 위한 Hash
#endif
FNameEntryHeader Header; // 문자열 정보
union
{
ANSICHAR AnsiName[NAME_SIZE];
WIDECHAR WideName[NAME_SIZE];
};
};
Entry에 들어갈 Name 정보
struct FNameEntryHeader
{
uint16 bIsWide : 1; // wchar 여부
#if WITH_CASE_PRESERVING_NAME
uint16 Len : 15; // FName의 길이 0 ~ 32767 (2^16 - 1)
#endif
}
World에서 사용 가능한 Public Name. 고유 문자열 테이블의 해시와 해시로 구성됨.
class FName {
friend struct FNameHelper;
uint32 ComparisonIndex; // 비교를 위한 Hash
#if WITH_CASE_PRESERVING_NAME
uint32 DisplayIndex; // 원본 문자열의 Hash
#endif // WITH_CASE_PRESERVING_NAME
public:
FNameEntryId GetComparisonIndex() const { return ComparisonIndex; }
FNameEntryId GetDisplayIndex() const { return DisplayIndex; }
FString ToString() const;
FName(const WIDECHAR* Name);
FName(const ANSICHAR* Name);
FName(const FString& Name);
bool operator==(const FName& Other) const {
return ComparisonIndex == Other.ComparisonIndex;
}
};
해시 생성을 위한 알고리즘입니다.
namespace
{
template <typename CharType>
uint32 HashString(const CharType* Str)
{
uint32 Hash = 5381;
while (*Str)
{
Hash = ((Hash << 5) + Hash) + *Str;
++Str;
}
return Hash;
}
template <typename CharType>
FORCENOINLINE uint32 HashStringLower(const CharType* Str, uint32 InLen)
{
CharType LowerStr[NAME_SIZE];
if constexpr (std::is_same_v<CharType, wchar_t>)
{
for (uint32 i = 0; i < InLen; i++)
{
LowerStr[i] = towlower(Str[i]);
}
LowerStr[InLen] = '\\0';
}
else
{
for (uint32 i = 0; i < InLen; ++i)
{
LowerStr[i] = static_cast<CharType>(tolower(Str[i]));
}
LowerStr[InLen] = '\\0';
}
return HashString(LowerStr);
}
template <ENameCase Sensitivity>
uint32 HashName(FNameStringView InName);
template <>
uint32 HashName<ENameCase::IgnoreCase>(FNameStringView InName)
{
return InName.IsAnsi() ? HashStringLower(InName.Ansi, InName.Len) : HashStringLower(InName.Wide, InName.Len);
}
template <>
uint32 HashName<ENameCase::CaseSensitive>(FNameStringView InName)
{
return InName.IsAnsi() ? HashString(InName.Ansi) : HashString(InName.Wide);
}
}