FNamePool

원본 문자열과 비교용 문자열을 가지고 있는 싱글톤 풀 구조입니다.

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 };
	}
};

FNameEntry

FNamePool에 저장되는 실제 문자열 데이터이다. 고유한 문자열과 그 문자열의 해시값을 가짐

struct FNameEntry {
#if WITH_CASE_PRESERVING_NAME
	FNameEntryId ComparisonId;	// 비교를 위한 Hash
#endif
	FNameEntryHeader Header;	  // 문자열 정보

	union
	{
		ANSICHAR			AnsiName[NAME_SIZE];
		WIDECHAR			WideName[NAME_SIZE];
	};
};

FNameEntryHeader

Entry에 들어갈 Name 정보

struct FNameEntryHeader
{
	uint16 bIsWide : 1;	// wchar 여부
#if WITH_CASE_PRESERVING_NAME
	uint16 Len : 15;	// FName의 길이 0 ~ 32767 (2^16 - 1)
#endif
}

FName

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;
	}
};

Hash (djb2 Hash Algorithm)

해시 생성을 위한 알고리즘입니다.

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);
	}
}