// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/StringView.h" #include "CoreTypes.h" #include "DerivedDataSharedStringFwd.h" #include "HAL/MemoryBase.h" #include "HAL/UnrealMemory.h" #include "Misc/CString.h" #include #define UE_API DERIVEDDATACACHE_API class FCbFieldView; class FCbWriter; namespace UE::DerivedData { /** * A reference to an immutable, shared, reference-counted string. * * Prefer TStringView when there is a clear single point of ownership with a longer lifetime than * the references to the string. This type is meant for cases where a shared string does not have * an obvious owner or where the lifetime is not easy to manage. * * The string is stored as a pointer to the start of the null-terminated string which is preceded * by a 4-byte reference count and a 4-byte size. */ template class TSharedString { public: TSharedString() = default; TSharedString(TSharedString&& String); TSharedString(const TSharedString& String); TSharedString& operator=(TSharedString&& String); TSharedString& operator=(const TSharedString& String); TSharedString(TStringView String); TSharedString& operator=(TStringView String); ~TSharedString() { Reset(); } inline void Reset() { Release(Chars); Chars = nullptr; } [[nodiscard]] inline bool IsEmpty() const { return !Chars; } [[nodiscard]] inline int32 Len() const { return Chars ? reinterpret_cast(Chars)[-1] : 0; } [[nodiscard]] inline const CharType* operator*() const { return Chars ? Chars : &NullChar; } static const TSharedString Empty; private: static inline void AddRef(CharType* Chars); static inline void Release(CharType* Chars); CharType* Chars = nullptr; static constexpr inline CharType NullChar{}; }; template inline const TSharedString TSharedString::Empty; template constexpr inline const CharType* GetData(const TSharedString String) { return *String; } template constexpr inline auto GetNum(const TSharedString String) { return String.Len(); } template inline TSharedString::TSharedString(TSharedString&& String) : Chars(String.Chars) { String.Chars = nullptr; } template TSharedString::TSharedString(const TSharedString& String) : Chars(String.Chars) { AddRef(Chars); } template TSharedString& TSharedString::operator=(TSharedString&& String) { if (this != &String) { Release(Chars); Chars = String.Chars; String.Chars = nullptr; } return *this; } template TSharedString& TSharedString::operator=(const TSharedString& String) { CharType* OldChars = Chars; CharType* NewChars = String.Chars; AddRef(NewChars); Release(OldChars); Chars = NewChars; return *this; } template TSharedString::TSharedString(const TStringView String) { if (const int32 Length = String.Len()) { static_assert(alignof(int32) <= MIN_ALIGNMENT); static_assert(alignof(int32) >= alignof(CharType)); static_assert(sizeof(int32) == sizeof(std::atomic)); const SIZE_T Size = sizeof(int32) + sizeof(int32) + sizeof(CharType) + sizeof(CharType) * Length; int32* const Header = static_cast(FMemory::Malloc(Size)); new(&Header[0]) std::atomic(1); // Ref Count new(&Header[1]) int32(Length); Chars = reinterpret_cast(&Header[2]); String.CopyString(Chars, Length); Chars[Length] = CharType(0); } } template TSharedString& TSharedString::operator=(const TStringView String) { return *this = TSharedString(String); } template void TSharedString::AddRef(CharType* const MaybeNullChars) { if (MaybeNullChars) { static_assert(sizeof(int32) == sizeof(std::atomic)); int32* const Header = reinterpret_cast(MaybeNullChars) - 2; std::atomic& RefCount = reinterpret_cast&>(Header[0]); RefCount.fetch_add(1, std::memory_order_acquire); } } template void TSharedString::Release(CharType* const MaybeNullChars) { if (MaybeNullChars) { static_assert(sizeof(int32) == sizeof(std::atomic)); int32* const Header = reinterpret_cast(MaybeNullChars) - 2; std::atomic& RefCount = reinterpret_cast&>(Header[0]); if (RefCount.fetch_sub(1, std::memory_order_release) == 1) { FMemory::Free(Header); } } } template inline uint32 GetTypeHash(const TSharedString& String) { return GetTypeHash(MakeStringView(String)); } template inline bool operator==(const TSharedString& Lhs, const TSharedString& Rhs) { return MakeStringView(Lhs).Equals(MakeStringView(Rhs), ESearchCase::IgnoreCase); } template inline bool operator<(const TSharedString& Lhs, const TSharedString& Rhs) { return MakeStringView(Lhs).Compare(MakeStringView(Rhs), ESearchCase::IgnoreCase) < 0; } UE_API bool LoadFromCompactBinary(FCbFieldView Field, FUtf8SharedString& OutString); UE_API bool LoadFromCompactBinary(FCbFieldView Field, FWideSharedString& OutString); } // UE::DerivedData #undef UE_API