// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

// This needed to be UnrealString.h to avoid conflicting with
// the Windows platform SDK string.h

#pragma once

#include "CoreTypes.h"
#include "Misc/VarArgs.h"

#ifdef MINIUNREAL
	#include "Misc/AssertionMacros.h"
	#include "HAL/UnrealMemory.h"
	#include "Templates/IsArithmetic.h"
	#include "Templates/UnrealTypeTraits.h"
	#include "Templates/UnrealTemplate.h"
	#include "Containers/Array.h"
	#include "Misc/CString.h"
	#include "Misc/Crc.h"
	#include "Math/UnrealMathUtility.h"
	#include "Templates/Invoke.h"
	#include "Templates/AndOrNot.h"
	#include "Templates/IsArrayOrRefOfType.h"
	#include "Templates/TypeHash.h"

#else

	#include "Misc/OutputDevice.h"
	#include "Misc/AssertionMacros.h"
	#include "HAL/UnrealMemory.h"
	#include "Templates/IsArithmetic.h"
	#include "Templates/UnrealTypeTraits.h"
	#include "Templates/UnrealTemplate.h"
	#include "Math/NumericLimits.h"
	#include "Containers/Array.h"
	#include "Misc/CString.h"
	#include "Misc/Crc.h"
	#include "Math/UnrealMathUtility.h"
	#include "Templates/Invoke.h"
	#include "Templates/IsValidVariadicFunctionArg.h"
	#include "Templates/AndOrNot.h"
	#include "Templates/IsArrayOrRefOfType.h"
	#include "Templates/TypeHash.h"
#endif

struct FStringFormatArg;
template<typename KeyType,typename ValueType,typename SetAllocator ,typename KeyFuncs > class TMap;

/** Determines case sensitivity options for string comparisons. */
namespace ESearchCase
{
	enum Type
	{
		/** Case sensitive. Upper/lower casing must match for strings to be considered equal. */
		CaseSensitive,

		/** Ignore case. Upper/lower casing does not matter when making a comparison. */
		IgnoreCase,
	};
};

/** Determines search direction for string operations. */
namespace ESearchDir
{
	enum Type
	{
		/** Search from the start, moving forward through the string. */
		FromStart,

		/** Search from the end, moving backward through the string. */
		FromEnd,
	};
}

/**
 * A dynamically sizeable string.
 * @see https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/StringHandling/FString/
 */
class CORE_API FString
{
private:
	friend struct TContainerTraits<FString>;

	/** Array holding the character data */
	typedef TArray<TCHAR> DataType;
	DataType Data;

public:
	using ElementType = TCHAR;

	FString() = default;
	FString(FString&&) = default;
	FString(const FString&) = default;
	FString& operator=(FString&&) = default;
	FString& operator=(const FString&) = default;

	/**
	 * Create a copy of the Other string with extra space for characters at the end of the string
	 *
	 * @param Other the other string to create a new copy from
	 * @param ExtraSlack number of extra characters to add to the end of the other string in this string
	 */
	FORCEINLINE FString(const FString& Other, int32 ExtraSlack)
		: Data(Other.Data, ExtraSlack + ((Other.Data.Num() || !ExtraSlack) ? 0 : 1)) // Add 1 if the source string array is empty and we want some slack, because we'll need to include a null terminator which is currently missing
	{
	}

	/**
	 * Create a copy of the Other string with extra space for characters at the end of the string
	 *
	 * @param Other the other string to create a new copy from
	 * @param ExtraSlack number of extra characters to add to the end of the other string in this string
	 */
	FORCEINLINE FString(FString&& Other, int32 ExtraSlack)
		: Data(MoveTemp(Other.Data), ExtraSlack + ((Other.Data.Num() || !ExtraSlack) ? 0 : 1)) // Add 1 if the source string array is empty and we want some slack, because we'll need to include a null terminator which is currently missing
	{
	}

	/**
	 * Constructor using an array of TCHAR
	 *
	 * @param In array of TCHAR
	 */
	template <
		typename CharType,
		typename = typename TEnableIf<TIsCharType<CharType>::Value>::Type // This TEnableIf is to ensure we don't instantiate this constructor for non-char types, like id* in Obj-C
	>
	FORCEINLINE FString(const CharType* Src)
	{
#ifndef MINIUNREAL
		if (Src && *Src)
		{
			int32 SrcLen  = TCString<CharType>::Strlen(Src) + 1;
			int32 DestLen = FPlatformString::ConvertedLength<TCHAR>(Src, SrcLen);
			Data.AddUninitialized(DestLen);

			FPlatformString::Convert(Data.GetData(), DestLen, Src, SrcLen);
		}
#endif
	}

	/** 
	 * Constructor to create FString with specified number of characters from another string with additional character zero
	 *
	 * @param InCount how many characters to copy
	 * @param InSrc String to copy from
	 */
	template <
		typename CharType,
		typename = typename TEnableIf<TIsCharType<CharType>::Value>::Type // This TEnableIf is to ensure we don't instantiate this constructor for non-char types, like id* in Obj-C
	>
	FORCEINLINE explicit FString(int32 InCount, const CharType* InSrc)
	{
#ifndef MINIUNREAL

		if (InSrc && *InSrc)
		{
			int32 DestLen = FPlatformString::ConvertedLength<TCHAR>(InSrc, InCount);
			if (DestLen > 0)
			{
				Data.AddUninitialized(DestLen + 1);

				FPlatformString::Convert(Data.GetData(), DestLen, InSrc, InCount);
				*(Data.GetData() + Data.Num() - 1) = TEXT('\0');
			}
		}
#endif
	}

#ifdef __OBJC__
	/** Convert Objective-C NSString* to FString */
	FORCEINLINE FString(const NSString* In)
	{
		if (In && [In length] > 0)
		{
			// Convert the NSString data into the native TCHAR format for UE4
			// This returns a buffer of bytes, but they can be safely cast to a buffer of TCHARs
#if PLATFORM_TCHAR_IS_4_BYTES
			const CFStringEncoding Encoding = kCFStringEncodingUTF32LE;
#else
			const CFStringEncoding Encoding = kCFStringEncodingUTF16LE;
#endif

			CFRange Range = CFRangeMake(0, CFStringGetLength((__bridge CFStringRef)In));
			CFIndex BytesNeeded;
			if (CFStringGetBytes((__bridge CFStringRef)In, Range, Encoding, '?', false, NULL, 0, &BytesNeeded) > 0)
			{
				const size_t Length = BytesNeeded / sizeof(TCHAR);
				Data.AddUninitialized(Length + 1);
				CFStringGetBytes((__bridge CFStringRef)In, Range, Encoding, '?', false, (uint8*)Data.GetData(), Length * sizeof(TCHAR) + 1, NULL);
				Data[Length] = 0;
			}
		}
	}
#endif

	/**
	 * Copy Assignment from array of TCHAR
	 *
	 * @param Other array of TCHAR
	 */
	FORCEINLINE FString& operator=( const TCHAR* Other )
	{
		if (Data.GetData() != Other)
		{
			int32 Len = (Other && *Other) ? FCString::Strlen(Other)+1 : 0;
			Data.Empty(Len);
			Data.AddUninitialized(Len);
			
			if( Len )
			{
				FMemory::Memcpy( Data.GetData(), Other, Len * sizeof(TCHAR) );
			}
		}
		return *this;
	}

	/**
	 * Return specific character from this string
	 *
	 * @param Index into string
	 * @return Character at Index
	 */
	FORCEINLINE TCHAR& operator[]( int32 Index )
	{
		checkf(IsValidIndex(Index), TEXT("String index out of bounds: Index %i from a string with a length of %i"), Index, Len());
		return Data.GetData()[Index];
	}

	/**
	 * Return specific const character from this string
	 *
	 * @param Index into string
	 * @return const Character at Index
	 */
	FORCEINLINE const TCHAR& operator[]( int32 Index ) const
	{
		checkf(IsValidIndex(Index), TEXT("String index out of bounds: Index %i from a string with a length of %i"), Index, Len());
		return Data.GetData()[Index];
	}

	/**
	 * Iterator typedefs
	 */
	typedef TArray<TCHAR>::TIterator      TIterator;
	typedef TArray<TCHAR>::TConstIterator TConstIterator;

	/** Creates an iterator for the characters in this string */
	FORCEINLINE TIterator CreateIterator()
	{
		return Data.CreateIterator();
	}

	/** Creates a const iterator for the characters in this string */
	FORCEINLINE TConstIterator CreateConstIterator() const
	{
		return Data.CreateConstIterator();
	}

public:
	/**
	 * DO NOT USE DIRECTLY
	 * STL-like iterators to enable range-based for loop support.
	 */
	FORCEINLINE DataType::RangedForIteratorType      begin()       { auto Result = Data.begin();                                   return Result; }
	FORCEINLINE DataType::RangedForConstIteratorType begin() const { auto Result = Data.begin();                                   return Result; }
	FORCEINLINE DataType::RangedForIteratorType      end  ()       { auto Result = Data.end();   if (Data.Num()) { --Result; }     return Result; }
	FORCEINLINE DataType::RangedForConstIteratorType end  () const { auto Result = Data.end();   if (Data.Num()) { --Result; }     return Result; }

public:
	FORCEINLINE uint32 GetAllocatedSize() const
	{
		return Data.GetAllocatedSize();
	}

	/**
	 * Run slow checks on this string
	 */
	FORCEINLINE void CheckInvariants() const
	{
		int32 Num = Data.Num();
		checkSlow(Num >= 0);
		checkSlow(!Num || !Data.GetData()[Num - 1]);
		checkSlow(Data.GetSlack() >= 0);
	}

	/**
	 * Create empty string of given size with zero terminating character
	 *
	 * @param Slack length of empty string to create
	 */
	FORCEINLINE void Empty( int32 Slack=0 )
	{
		Data.Empty(Slack);
	}

	/**
	 * Test whether this string is empty
	 *
	 * @return true if this string is empty, otherwise return false.
	 */
	FORCEINLINE bool IsEmpty() const
	{
		return Data.Num() <= 1;
	}

	/**
	 * Empties the string, but doesn't change memory allocation, unless the new size is larger than the current string.
	 *
	 * @param NewReservedSize The expected usage size (in characters, not including the terminator) after calling this function.
	 */
	FORCEINLINE void Reset(int32 NewReservedSize = 0)
	{
		const int32 NewSizeIncludingTerminator = (NewReservedSize > 0) ? (NewReservedSize + 1) : 0;
		Data.Reset(NewSizeIncludingTerminator);
		if (TCHAR* DataPtr = Data.GetData())
		{
			*DataPtr = TEXT('\0');
		}
	}

	/**
	 * Remove unallocated empty character space from the end of this string
	 */
	FORCEINLINE void Shrink()
	{
		Data.Shrink();
	}

	/**
	 * Tests if index is valid, i.e. greater than or equal to zero, and less than the number of characters in this string (excluding the null terminator).
	 *
	 * @param Index Index to test.
	 *
	 * @returns True if index is valid. False otherwise.
	 */
	FORCEINLINE bool IsValidIndex(int32 Index) const
	{
		return Index >= 0 && Index < Len();
	}

	/**
	 * Get pointer to the string
	 *
	 * @Return Pointer to Array of TCHAR if Num, otherwise the empty string
	 */
	FORCEINLINE const TCHAR* operator*() const
	{
		return Data.Num() ? Data.GetData() : TEXT("");
	}

	/** 
	 *Get string as array of TCHARS 
	 *
	 * @warning: Operations on the TArray<*CHAR> can be unsafe, such as adding
	 *		non-terminating 0's or removing the terminating zero.
	 */
	FORCEINLINE DataType& GetCharArray()
	{
		return Data;
	}

	/** Get string as const array of TCHARS */
	FORCEINLINE const DataType& GetCharArray() const
	{
		return Data;
	}

#ifdef __OBJC__
	/** Convert FString to Objective-C NSString */
	FORCEINLINE NSString* GetNSString() const
	{
#if PLATFORM_TCHAR_IS_4_BYTES
		return [[[NSString alloc] initWithBytes:Data.GetData() length:Len() * sizeof(TCHAR) encoding:NSUTF32LittleEndianStringEncoding] autorelease];
#else
		return [[[NSString alloc] initWithBytes:Data.GetData() length:Len() * sizeof(TCHAR) encoding:NSUTF16LittleEndianStringEncoding] autorelease];
#endif
	}
#endif

	/**
	 * Appends an array of characters to the string.  
	 *
	 * @param Array A pointer to the start of an array of characters to append.  This array need not be null-terminated, and null characters are not treated specially.
	 * @param Count The number of characters to copy from Array.
	 */
	FORCEINLINE void AppendChars(const TCHAR* Array, int32 Count)
	{
		check(Count >= 0);

		if (!Count)
			return;

		checkSlow(Array);

		int32 Index = Data.Num();

		// Reserve enough space - including an extra gap for a null terminator if we don't already have a string allocated
		Data.AddUninitialized(Count + (Index ? 0 : 1));

		TCHAR* EndPtr = Data.GetData() + Index - (Index ? 1 : 0);

		// Copy characters to end of string, overwriting null terminator if we already have one
		CopyAssignItems(EndPtr, Array, Count);

		// (Re-)establish the null terminator
		*(EndPtr + Count) = 0;
	}

	/**
	 * Concatenate this with given string
	 * 
	 * @param Str array of TCHAR to be concatenated onto the end of this
	 * @return reference to this
	 */
	FORCEINLINE FString& operator+=( const TCHAR* Str )
	{
		checkSlow(Str);
		CheckInvariants();

		AppendChars(Str, FCString::Strlen(Str));

		return *this;
	}

	/**
	 * Concatenate this with given char
	 * 
	 * @param inChar other Char to be concatenated onto the end of this string
	 * @return reference to this
	 */
	template <typename CharType>
	FORCEINLINE typename TEnableIf<TIsCharType<CharType>::Value, FString&>::Type operator+=(CharType InChar)
	{
		CheckInvariants();

		if ( InChar != 0 )
		{
			// position to insert the character.  
			// At the end of the string if we have existing characters, otherwise at the 0 position
			int32 InsertIndex = (Data.Num() > 0) ? Data.Num()-1 : 0;	

			// number of characters to add.  If we don't have any existing characters, 
			// we'll need to append the terminating zero as well.
			int32 InsertCount = (Data.Num() > 0) ? 1 : 2;

			Data.AddUninitialized(InsertCount);
			Data[InsertIndex] = InChar;
			Data[InsertIndex+1] = 0;
		}
		return *this;
	}

	/**
	 * Concatenate this with given char
	 * 
	 * @param InChar other Char to be concatenated onto the end of this string
	 * @return reference to this
	 */
	FORCEINLINE FString& AppendChar(const TCHAR InChar)
	{
		*this += InChar;
		return *this;
	}

	FORCEINLINE FString& Append(const FString& Text)
	{
		*this += Text;
		return *this;
	}

	FString& Append(const TCHAR* Text, int32 Count)
	{
		CheckInvariants();

		if (Count != 0)
		{
			// position to insert the character.  
			// At the end of the string if we have existing characters, otherwise at the 0 position
			int32 InsertIndex = (Data.Num() > 0) ? Data.Num() - 1 : 0;

			// number of characters to add.  If we don't have any existing characters, 
			// we'll need to append the terminating zero as well.
			int32 FinalCount = (Data.Num() > 0) ? Count : Count + 1;

			Data.AddUninitialized(FinalCount);

			for (int32 Index = 0; Index < Count; Index++)
			{
				Data[InsertIndex + Index] = Text[Index];
			}

			Data[Data.Num() - 1] = 0;
		}
		return *this;
	}

	/**
	 * Removes characters within the string.
	 *
	 * @param Index           The index of the first character to remove.
	 * @param Count           The number of characters to remove.
	 * @param bAllowShrinking Whether or not to reallocate to shrink the storage after removal.
	 */
	FORCEINLINE void RemoveAt(int32 Index, int32 Count = 1, bool bAllowShrinking = true)
	{
		Data.RemoveAt(Index, Count, bAllowShrinking);
	}

	FORCEINLINE void InsertAt(int32 Index, TCHAR Character)
	{
		if (Character != 0)
		{
			if (Data.Num() == 0)
			{
				*this += Character;
			}
			else
			{
				Data.Insert(Character, Index);
			}
		}
	}

	FORCEINLINE void InsertAt(int32 Index, const FString& Characters)
	{
		if (Characters.Len())
		{
			if (Data.Num() == 0)
			{
				*this += Characters;
			}
			else
			{
				Data.Insert(Characters.Data.GetData(), Characters.Len(), Index);
			}
		}
	}
	/**
	 * Removes the text from the start of the string if it exists.
	 *
	 * @param InPrefix the prefix to search for at the start of the string to remove.
	 * @return true if the prefix was removed, otherwise false.
	 */
	bool RemoveFromStart( const TCHAR* InPrefix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase );

	/**
	 * Removes the text from the start of the string if it exists.
	 *
	 * @param InPrefix the prefix to search for at the start of the string to remove.
	 * @return true if the prefix was removed, otherwise false.
	 */
	bool RemoveFromStart( const FString& InPrefix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase );

	/**
	 * Removes the text from the end of the string if it exists.
	 *
	 * @param InSuffix the suffix to search for at the end of the string to remove.
	 * @return true if the suffix was removed, otherwise false.
	 */
	bool RemoveFromEnd( const TCHAR* InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase );

	/**
	 * Removes the text from the end of the string if it exists.
	 *
	 * @param InSuffix the suffix to search for at the end of the string to remove.
	 * @return true if the suffix was removed, otherwise false.
	 */
	bool RemoveFromEnd( const FString& InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase );

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Str       Pointer to an array of TCHARs (not necessarily null-terminated) to be concatenated onto the end of this.
	 * @param StrLength Exact number of characters from Str to append.
	 */
	void PathAppend(const TCHAR* Str, int32 StrLength);

	/**
	 * Concatenate this with given string
	 * 
	 * @param Str other string to be concatenated onto the end of this
	 * @return reference to this
	 */
	FORCEINLINE FString& operator+=( const FString& Str )
	{
		CheckInvariants();
		Str.CheckInvariants();

		AppendChars(Str.Data.GetData(), Str.Len());

		return *this;
	}

	/**
	 * Concatenates an FString with a TCHAR.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The char on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	template <typename CharType>
	FORCEINLINE friend typename TEnableIf<TIsCharType<CharType>::Value, FString>::Type operator+(const FString& Lhs, CharType Rhs)
	{
		Lhs.CheckInvariants();

		FString Result(Lhs, 1);
		Result += Rhs;

		return Result;
	}

	/**
	 * Concatenates an FString with a TCHAR.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The char on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	template <typename CharType>
	FORCEINLINE friend typename TEnableIf<TIsCharType<CharType>::Value, FString>::Type operator+(FString&& Lhs, CharType Rhs)
	{
		Lhs.CheckInvariants();

		FString Result(MoveTemp(Lhs), 1);
		Result += Rhs;

		return Result;
	}

private:
	template <typename LhsType, typename RhsType>
	FORCEINLINE static FString ConcatFStrings(typename TIdentity<LhsType>::Type Lhs, typename TIdentity<RhsType>::Type Rhs)
	{
		Lhs.CheckInvariants();
		Rhs.CheckInvariants();

		if (Lhs.IsEmpty())
		{
			return MoveTempIfPossible(Rhs);
		}

		int32 RhsLen = Rhs.Len();

		FString Result(MoveTempIfPossible(Lhs), RhsLen);
		Result.AppendChars(Rhs.Data.GetData(), RhsLen);
		
		return Result;
	}

	template <typename RhsType>
	FORCEINLINE static FString ConcatTCHARsToFString(const TCHAR* Lhs, typename TIdentity<RhsType>::Type Rhs)
	{
		checkSlow(Lhs);
		Rhs.CheckInvariants();

		if (!Lhs || !*Lhs)
		{
			return MoveTempIfPossible(Rhs);
		}

		int32 LhsLen = FCString::Strlen(Lhs);
		int32 RhsLen = Rhs.Len();

		// This is not entirely optimal, as if the Rhs is an rvalue and has enough slack space to hold Lhs, then
		// the memory could be reused here without constructing a new object.  However, until there is proof otherwise,
		// I believe this will be relatively rare and isn't worth making the code a lot more complex right now.
		FString Result;
		Result.Data.AddUninitialized(LhsLen + RhsLen + 1);

		TCHAR* ResultData = Result.Data.GetData();
		CopyAssignItems(ResultData, Lhs, LhsLen);
		CopyAssignItems(ResultData + LhsLen, Rhs.Data.GetData(), RhsLen);
		*(ResultData + LhsLen + RhsLen) = 0;
		
		return Result;
	}

	template <typename LhsType>
	FORCEINLINE static FString ConcatFStringToTCHARs(typename TIdentity<LhsType>::Type Lhs, const TCHAR* Rhs)
	{
		Lhs.CheckInvariants();
		checkSlow(Rhs);

		if (!Rhs || !*Rhs)
		{
			return MoveTempIfPossible(Lhs);
		}

		int32 RhsLen = FCString::Strlen(Rhs);

		FString Result(MoveTempIfPossible(Lhs), RhsLen);
		Result.AppendChars(Rhs, RhsLen);
		
		return Result;
	}

public:
	/**
	 * Concatenate two FStrings.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(const FString& Lhs, const FString& Rhs)
	{
		return ConcatFStrings<const FString&, const FString&>(Lhs, Rhs);
	}

	/**
	 * Concatenate two FStrings.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(FString&& Lhs, const FString& Rhs)
	{
		return ConcatFStrings<FString&&, const FString&>(MoveTemp(Lhs), Rhs);
	}

	/**
	 * Concatenate two FStrings.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(const FString& Lhs, FString&& Rhs)
	{
		return ConcatFStrings<const FString&, FString&&>(Lhs, MoveTemp(Rhs));
	}

	/**
	 * Concatenate two FStrings.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(FString&& Lhs, FString&& Rhs)
	{
		return ConcatFStrings<FString&&, FString&&>(MoveTemp(Lhs), MoveTemp(Rhs));
	}

	/**
	 * Concatenates a TCHAR array to an FString.
	 * 
	 * @param Lhs The TCHAR array on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(const TCHAR* Lhs, const FString& Rhs)
	{
		return ConcatTCHARsToFString<const FString&>(Lhs, Rhs);
	}

	/**
	 * Concatenates a TCHAR array to an FString.
	 * 
	 * @param Lhs The TCHAR array on the left-hand-side of the expression.
	 * @param Rhs The FString on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(const TCHAR* Lhs, FString&& Rhs)
	{
		return ConcatTCHARsToFString<FString&&>(Lhs, MoveTemp(Rhs));
	}

	/**
	 * Concatenates an FString to a TCHAR array.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The TCHAR array on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(const FString& Lhs, const TCHAR* Rhs)
	{
		return ConcatFStringToTCHARs<const FString&>(Lhs, Rhs);
	}

	/**
	 * Concatenates an FString to a TCHAR array.
	 * 
	 * @param Lhs The FString on the left-hand-side of the expression.
	 * @param Rhs The TCHAR array on the right-hand-side of the expression.
	 *
	 * @return The concatenated string.
	 */
	FORCEINLINE friend FString operator+(FString&& Lhs, const TCHAR* Rhs)
	{
		return ConcatFStringToTCHARs<FString&&>(MoveTemp(Lhs), Rhs);
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 * 
	 * @param Str path array of TCHAR to be concatenated onto the end of this
	 * @return reference to path
	 */
	FORCEINLINE FString& operator/=( const TCHAR* Str )
	{
		checkSlow(Str);

		PathAppend(Str, FCString::Strlen(Str));
		return *this;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 * 
	 * @param Str path FString to be concatenated onto the end of this
	 * @return reference to path
	 */
	FORCEINLINE FString& operator/=( const FString& Str )
	{
		PathAppend(Str.Data.GetData(), Str.Len());
		return *this;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Lhs Path to concatenate onto.
	 * @param Rhs Path to concatenate.
	 * @return new FString of the path
	 */
	FORCEINLINE friend FString operator/(const FString& Lhs, const TCHAR* Rhs)
	{
		checkSlow(Rhs);

		int32 StrLength = FCString::Strlen(Rhs);

		FString Result(Lhs, StrLength + 1);
		Result.PathAppend(Rhs, StrLength);
		return Result;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Lhs Path to concatenate onto.
	 * @param Rhs Path to concatenate.
	 * @return new FString of the path
	 */
	FORCEINLINE friend FString operator/(FString&& Lhs, const TCHAR* Rhs)
	{
		checkSlow(Rhs);

		int32 StrLength = FCString::Strlen(Rhs);

		FString Result(MoveTemp(Lhs), StrLength + 1);
		Result.PathAppend(Rhs, StrLength);
		return Result;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Lhs Path to concatenate onto.
	 * @param Rhs Path to concatenate.
	 * @return new FString of the path
	 */
	FORCEINLINE friend FString operator/(const FString& Lhs, const FString& Rhs)
	{
		int32 StrLength = Rhs.Len();

		FString Result(Lhs, StrLength + 1);
		Result.PathAppend(Rhs.Data.GetData(), StrLength);
		return Result;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Lhs Path to concatenate onto.
	 * @param Rhs Path to concatenate.
	 * @return new FString of the path
	 */
	FORCEINLINE friend FString operator/(FString&& Lhs, const FString& Rhs)
	{
		int32 StrLength = Rhs.Len();

		FString Result(MoveTemp(Lhs), StrLength + 1);
		Result.PathAppend(Rhs.Data.GetData(), StrLength);
		return Result;
	}

	/**
	 * Concatenate this path with given path ensuring the / character is used between them
	 *
	 * @param Lhs Path to concatenate onto.
	 * @param Rhs Path to concatenate.
	 * @return new FString of the path
	 */
	FORCEINLINE friend FString operator/(const TCHAR* Lhs, const FString& Rhs)
	{
		int32 StrLength = Rhs.Len();

		FString Result(FString(Lhs), StrLength + 1);
		Result.PathAppend(Rhs.Data.GetData(), Rhs.Len());
		return Result;
	}

	/**
	 * Lexicographically test whether the left string is <= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically <= the right string, otherwise false
	 * @note case insensitive
	 */
	FORCEINLINE friend bool operator<=(const FString& Lhs, const FString& Rhs)
	{
#ifndef MINIUNREAL

		return FPlatformString::Stricmp(*Lhs, *Rhs) <= 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is <= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically <= the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator<=(const FString& Lhs, const CharType* Rhs)
	{
#ifndef MINIUNREAL
		return FPlatformString::Stricmp(*Lhs, Rhs) <= 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is <= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically <= the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator<=(const CharType* Lhs, const FString& Rhs)
	{
#ifndef MINIUNREAL
		return FPlatformString::Stricmp(Lhs, *Rhs) <= 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is < the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically < the right string, otherwise false
	 * @note case insensitive
	 */
	FORCEINLINE friend bool operator<(const FString& Lhs, const FString& Rhs)
	{
#ifndef MINIUNREAL
		return FPlatformString::Stricmp(*Lhs, *Rhs) < 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is < the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically < the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator<(const FString& Lhs, const CharType* Rhs)
	{
#ifndef MINIUNREAL
		return FPlatformString::Stricmp(*Lhs, Rhs) < 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is < the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically < the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator<(const CharType* Lhs, const FString& Rhs)
	{
#ifndef MINIUNREAL
		return FPlatformString::Stricmp(Lhs, *Rhs) < 0;
#endif
	}

	/**
	 * Lexicographically test whether the left string is >= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically >= the right string, otherwise false
	 * @note case insensitive
	 */
#ifndef MINIUNREAL

	FORCEINLINE friend bool operator>=(const FString& Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, *Rhs) >= 0;
	}

	/**
	 * Lexicographically test whether the left string is >= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically >= the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator>=(const FString& Lhs, const CharType* Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, Rhs) >= 0;
	}

	/**
	 * Lexicographically test whether the left string is >= the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically >= the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator>=(const CharType* Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(Lhs, *Rhs) >= 0;
	}

	/**
	 * Lexicographically test whether the left string is > the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically > the right string, otherwise false
	 * @note case insensitive
	 */
	FORCEINLINE friend bool operator>(const FString& Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, *Rhs) > 0;
	}

	/**
	 * Lexicographically test whether the left string is > the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically > the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator>(const FString& Lhs, const CharType* Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, Rhs) > 0;
	}

	/**
	 * Lexicographically test whether the left string is > the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically > the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator>(const CharType* Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(Lhs, *Rhs) > 0;
	}

	/**
	 * Lexicographically test whether the left string is == the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically == the right string, otherwise false
	 * @note case insensitive
	 */
	FORCEINLINE friend bool operator==(const FString& Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, *Rhs) == 0;
	}

	/**
	 * Lexicographically test whether the left string is == the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically == the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator==(const FString& Lhs, const CharType* Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, Rhs) == 0;
	}

	/**
	 * Lexicographically test whether the left string is == the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically == the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator==(const CharType* Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(Lhs, *Rhs) == 0;
	}

	/**
	 * Lexicographically test whether the left string is != the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically != the right string, otherwise false
	 * @note case insensitive
	 */
	FORCEINLINE friend bool operator!=(const FString& Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, *Rhs) != 0;
	}

	/**
	 * Lexicographically test whether the left string is != the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically != the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator!=(const FString& Lhs, const CharType* Rhs)
	{
		return FPlatformString::Stricmp(*Lhs, Rhs) != 0;
	}

	/**
	 * Lexicographically test whether the left string is != the right string
	 *
	 * @param Lhs String to compare against.
	 * @param Rhs String to compare against.
	 * @return true if the left string is lexicographically != the right string, otherwise false
	 * @note case insensitive
	 */
	template <typename CharType>
	FORCEINLINE friend bool operator!=(const CharType* Lhs, const FString& Rhs)
	{
		return FPlatformString::Stricmp(Lhs, *Rhs) != 0;
	}

#endif

	/** Get the length of the string, excluding terminating character */
	FORCEINLINE int32 Len() const
	{
		return Data.Num() ? Data.Num() - 1 : 0;
	}

#ifndef MINIUNREAL
	/** Returns the left most given number of characters */
	FORCEINLINE FString Left( int32 Count ) const
	{
		return FString( FMath::Clamp(Count,0,Len()), **this );
	}

	/** Returns the left most characters from the string chopping the given number of characters from the end */
	FORCEINLINE FString LeftChop( int32 Count ) const
	{
		return FString( FMath::Clamp(Len()-Count,0,Len()), **this );
	}

	/** Returns the string to the right of the specified location, counting back from the right (end of the word). */
	FORCEINLINE FString Right( int32 Count ) const
	{
		return FString( **this + Len()-FMath::Clamp(Count,0,Len()) );
	}

	/** Returns the string to the right of the specified location, counting forward from the left (from the beginning of the word). */
	FORCEINLINE FString RightChop( int32 Count ) const
	{
		return FString( **this + Len()-FMath::Clamp(Len()-Count,0,Len()) );
	}

	/** Returns the substring from Start position for Count characters. */
	FORCEINLINE FString Mid( int32 Start, int32 Count=MAX_int32 ) const
	{
		check(Count >= 0);
		uint32 End = Count;
		End += Start;
		Start    = FMath::Clamp( (uint32)Start, (uint32)0,     (uint32)Len() );
		End      = FMath::Clamp( (uint32)End,   (uint32)Start, (uint32)Len() );
		return FString( End-Start, **this + Start );
	}
#endif
	/**
	 * Searches the string for a substring, and returns index into this string
	 * of the first found instance. Can search from beginning or end, and ignore case or not.
	 *
	 * @param SubStr			The string array of TCHAR to search for
	 * @param StartPosition		The start character position to search from
	 * @param SearchCase		Indicates whether the search is case sensitive or not
	 * @param SearchDir			Indicates whether the search starts at the begining or at the end.
	 */
	int32 Find( const TCHAR* SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, 
				ESearchDir::Type SearchDir = ESearchDir::FromStart, int32 StartPosition=INDEX_NONE ) const;

	/**
	 * Searches the string for a substring, and returns index into this string
	 * of the first found instance. Can search from beginning or end, and ignore case or not.
	 *
	 * @param SubStr			The string to search for
	 * @param StartPosition		The start character position to search from
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @param SearchDir			Indicates whether the search starts at the begining or at the end ( defaults to ESearchDir::FromStart )
	 */
	FORCEINLINE int32 Find( const FString& SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, 
							ESearchDir::Type SearchDir = ESearchDir::FromStart, int32 StartPosition=INDEX_NONE ) const
	{
		return Find( *SubStr, SearchCase, SearchDir, StartPosition );
	}

	/** 
	 * Returns whether this string contains the specified substring.
	 *
	 * @param SubStr			Find to search for
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @param SearchDir			Indicates whether the search starts at the begining or at the end ( defaults to ESearchDir::FromStart )
	 * @return					Returns whether the string contains the substring
	 **/
	FORCEINLINE bool Contains(const TCHAR* SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, 
							  ESearchDir::Type SearchDir = ESearchDir::FromStart ) const
	{
		return Find(SubStr, SearchCase, SearchDir) != INDEX_NONE;
	}

	/** 
	 * Returns whether this string contains the specified substring.
	 *
	 * @param SubStr			Find to search for
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @param SearchDir			Indicates whether the search starts at the begining or at the end ( defaults to ESearchDir::FromStart )
	 * @return					Returns whether the string contains the substring
	 **/
	FORCEINLINE bool Contains(const FString& SubStr, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, 
							  ESearchDir::Type SearchDir = ESearchDir::FromStart ) const
	{
		return Find(*SubStr, SearchCase, SearchDir) != INDEX_NONE;
	}

	/**
	 * Searches the string for a character
	 *
	 * @param InChar the character to search for
	 * @param Index out the position the character was found at, INDEX_NONE if return is false
	 * @return true if character was found in this string, otherwise false
	 */
	FORCEINLINE bool FindChar( TCHAR InChar, int32& Index ) const
	{
		return Data.Find(InChar, Index);
	}

	/**
	 * Searches the string for the last occurrence of a character
	 *
	 * @param InChar the character to search for
	 * @param Index out the position the character was found at, INDEX_NONE if return is false
	 * @return true if character was found in this string, otherwise false
	 */
	FORCEINLINE bool FindLastChar( TCHAR InChar, int32& Index ) const
	{
		return Data.FindLast(InChar, Index);
	}

	/**
	 * Searches an initial substring for the last occurrence of a character which matches the specified predicate.
	 *
	 * @param Pred Predicate that takes TCHAR and returns true if TCHAR matches search criteria, false otherwise.
	 * @param Count The number of characters from the front of the string through which to search.
	 *
	 * @return Index of found TCHAR, INDEX_NONE otherwise.
	 */
	template <typename Predicate>
	FORCEINLINE int32 FindLastCharByPredicate(Predicate Pred, int32 Count) const
	{
		check(Count >= 0 && Count <= this->Len());
		return Data.FindLastByPredicate(Pred, Count);
	}

	/**
	 * Searches the string for the last occurrence of a character which matches the specified predicate.
	 *
	 * @param Pred Predicate that takes TCHAR and returns true if TCHAR matches search criteria, false otherwise.
	 * @param StartIndex Index of element from which to start searching. Defaults to last TCHAR in string.
	 *
	 * @return Index of found TCHAR, INDEX_NONE otherwise.
	 */
	template <typename Predicate>
	FORCEINLINE int32 FindLastCharByPredicate(Predicate Pred) const
	{
		return Data.FindLastByPredicate(Pred, this->Len());
	}

	/**
	 * Lexicographically tests whether this string is equivalent to the Other given string
	 * 
	 * @param Other 	The string test against
	 * @param SearchCase 	Whether or not the comparison should ignore case
	 * @return true if this string is lexicographically equivalent to the other, otherwise false
	 */
	FORCEINLINE bool Equals( const FString& Other, ESearchCase::Type SearchCase = ESearchCase::CaseSensitive ) const
	{
		if( SearchCase == ESearchCase::CaseSensitive )
		{
			return FCString::Strcmp( **this, *Other )==0; 
		}
		else
		{
			return FCString::Stricmp( **this, *Other )==0;
		}
	}

	/**
	 * Lexicographically tests how this string compares to the Other given string
	 * 
	 * @param Other 	The string test against
	 * @param SearchCase 	Whether or not the comparison should ignore case
	 * @return 0 if equal, negative if less than, positive if greater than
	 */
	FORCEINLINE int32 Compare( const FString& Other, ESearchCase::Type SearchCase = ESearchCase::CaseSensitive ) const
	{
		if( SearchCase == ESearchCase::CaseSensitive )
		{
			return FCString::Strcmp( **this, *Other ); 
		}
		else
		{
			return FCString::Stricmp( **this, *Other );
		}
	}

	/**
	 * Splits this string at given string position case sensitive.
	 *
	 * @param InStr The string to search and split at
	 * @param LeftS out the string to the left of InStr, not updated if return is false
	 * @param RightS out the string to the right of InStr, not updated if return is false
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @param SearchDir			Indicates whether the search starts at the begining or at the end ( defaults to ESearchDir::FromStart )
	 * @return true if string is split, otherwise false
	 */
	bool Split(const FString& InS, FString* LeftS, FString* RightS, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase,
		ESearchDir::Type SearchDir = ESearchDir::FromStart) const
	{
		#ifndef MINIUNREAL

		int32 InPos = Find(InS, SearchCase, SearchDir);

		if (InPos < 0)	{ return false; }

		if (LeftS)		{ *LeftS = Left(InPos); }
		if (RightS)	{ *RightS = Mid(InPos + InS.Len()); }
		#endif

		return true;
	}

	/** Returns a new string with the characters of this converted to uppercase */
	FString ToUpper() const &;

	/**
	 * Converts all characters in this rvalue string to uppercase and moves it into the returned string.
	 * @return a new string with the characters of this converted to uppercase
	 */
	FString ToUpper() &&;

	/** Converts all characters in this string to uppercase */
	void ToUpperInline();

	/** Returns a new string with the characters of this converted to lowercase */
	FString ToLower() const &;

	/**
	 * Converts all characters in this rvalue string to lowercase and moves it into the returned string.
	 * @return a new string with the characters of this converted to lowercase
	 */
	FString ToLower() &&;

	/** Converts all characters in this string to lowercase */
	void ToLowerInline();

	/** Pad the left of this string for ChCount characters */
	FString LeftPad( int32 ChCount ) const;

	/** Pad the right of this string for ChCount characters */
	FString RightPad( int32 ChCount ) const;
	
	/** Returns true if the string only contains numeric characters */
	bool IsNumeric() const;
	
	/** Removes spaces from the string.  I.E. "Spaces Are Cool" --> "SpacesAreCool". */
	void RemoveSpacesInline();

	/**
	 * Constructs FString object similarly to how classic sprintf works.
	 *
	 * @param Format	Format string that specifies how FString should be built optionally using additional args. Refer to standard printf format.
	 * @param ...		Depending on format function may require additional arguments to build output object.
	 *
	 * @returns FString object that was constructed using format and additional parameters.
	 */
#ifndef MINIUNREAL
	template <typename FmtType, typename... Types>
	static typename TEnableIf<TIsArrayOrRefOfType<FmtType, TCHAR>::Value, FString>::Type Printf(const FmtType& Fmt, Types... Args)
	{
		static_assert(TIsArrayOrRefOfType<FmtType, TCHAR>::Value, "Formatting string must be a TCHAR array.");
		static_assert(TAnd<TIsValidVariadicFunctionArg<Types>...>::Value, "Invalid argument(s) passed to FString::Printf");

		return PrintfImpl(Fmt, Args...);
	}

	template <typename FmtType, typename... Types>
	UE_DEPRECATED(4.20, "The formatting string must now be a TCHAR string literal.")
	static typename TEnableIf<!TIsArrayOrRefOfType<FmtType, TCHAR>::Value, FString>::Type Printf(const FmtType& Fmt, Types... Args)
	{
		// NOTE: When this deprecated function is removed, the return type of the overload above
		//       should be set to simply FString.

		static_assert(TAnd<TIsValidVariadicFunctionArg<Types>...>::Value, "Invalid argument(s) passed to FString::Printf");

		return PrintfImpl(Fmt, Args...);
	}

	/**
	 * Just like Printf, but appends the formatted text to the existing FString instead.
	 * @return a reference to the modified string, so that it can be chained
	 */
	template <typename FmtType, typename... Types>
	typename TEnableIf<TIsArrayOrRefOfType<FmtType, TCHAR>::Value, FString&>::Type Appendf(const FmtType& Fmt, Types... Args)
	{
		static_assert(TIsArrayOrRefOfType<FmtType, TCHAR>::Value, "Formatting string must be a TCHAR array.");
		static_assert(TAnd<TIsValidVariadicFunctionArg<Types>...>::Value, "Invalid argument(s) passed to FString::Appendf");

		AppendfImpl(*this, Fmt, Args...);
		return *this;
	}
#endif

private:
	static FString VARARGS PrintfImpl(const TCHAR* Fmt, ...);
	static void VARARGS AppendfImpl(FString& AppendToMe, const TCHAR* Fmt, ...);
public:

	/**
	 * Format the specified string using the specified arguments. Replaces instances of { Argument } with keys in the map matching 'Argument'
	 * @param InFormatString		A string representing the format expression
	 * @param InNamedArguments		A map of named arguments that match the tokens specified in InExpression
	 * @return A string containing the formatted text
	 */
	static FString Format(const TCHAR* InFormatString, const TMap<FString, FStringFormatArg>& InNamedArguments);

	/**
	 * Format the specified string using the specified arguments. Replaces instances of {0} with indices from the given array matching the index specified in the token
	 * @param InFormatString		A string representing the format expression
	 * @param InOrderedArguments	An array of ordered arguments that match the tokens specified in InExpression
	 * @return A string containing the formatted text
	 */
	static FString Format(const TCHAR* InFormatString, const TArray<FStringFormatArg>& InOrderedArguments);

	/** Returns a string containing only the Ch character */
	static FString Chr( TCHAR Ch );

	/**
	 * Returns a string that is full of a variable number of characters
	 *
	 * @param NumCharacters Number of characters to put into the string
	 * @param Char Character to put into the string
	 * 
	 * @return The string of NumCharacters characters.
	 */
	static FString ChrN( int32 NumCharacters, TCHAR Char );

	/**
	 * Serializes the string.
	 *
	 * @param Ar Reference to the serialization archive.
	 * @param S Reference to the string being serialized.
	 *
	 * @return Reference to the Archive after serialization.
	 */
	friend CORE_API FArchive& operator<<( FArchive& Ar, FString& S );


	/**
	 * Test whether this string starts with given string.
	 *
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return true if this string begins with specified text, false otherwise
	 */
	bool StartsWith(const TCHAR* InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;

	/**
	 * Test whether this string starts with given string.
	 *
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return true if this string begins with specified text, false otherwise
	 */
	bool StartsWith(const FString& InPrefix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;

	/**
	 * Test whether this string ends with given string.
	 *
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return true if this string ends with specified text, false otherwise
	 */
	bool EndsWith(const TCHAR* InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;

	/**
	 * Test whether this string ends with given string.
	 *
	 * @param SearchCase		Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return true if this string ends with specified text, false otherwise
	 */
	bool EndsWith(const FString& InSuffix, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase ) const;

	/**
	 * Searches this string for a given wild card
	 *
	 * @param Wildcard		*?-type wildcard
	 * @param SearchCase	Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return true if this string matches the *?-type wildcard given. 
	 * @warning This is a simple, SLOW routine. Use with caution
	 */
	bool MatchesWildcard(const FString& Wildcard, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;

	/**
	 * Removes whitespace characters from the front of this string.
	 */
	UE_DEPRECATED(4.18, "FString::Trim() has been split into separate functions for copy and mutate semantics. Call FString::TrimStart() to return a copy of the string with whitespace trimmed from the start, or FString::TrimStartInline() to modify an FString object in-place.")
	FString Trim();

	/**
	 * Removes trailing whitespace characters
	 */
	UE_DEPRECATED(4.18, "FString::TrimTrailing() has been split into separate functions for copy and mutate semantics. Call FString::TrimEnd() to return a copy of the string with whitespace trimmed from the start, or FString::TrimEndInline() to modify an FString object in-place.")
	FString TrimTrailing();

	/**
	 * Removes whitespace characters from the start and end of this string. Modifies the string in-place.
	 */
	void TrimStartAndEndInline();

	/**
	 * Removes whitespace characters from the start and end of this string.
	 * @note Unlike Trim() this function returns a copy, and does not mutate the string.
	 */
	FString TrimStartAndEnd() const &;

	/**
	 * Removes whitespace characters from the start and end of this string.
	 * @note Unlike Trim() this function returns a copy, and does not mutate the string.
	 */
	FString TrimStartAndEnd() &&;

	/**
	 * Removes whitespace characters from the start of this string. Modifies the string in-place.
	 */
	void TrimStartInline();

	/**
	 * Removes whitespace characters from the start of this string.
	 * @note Unlike Trim() this function returns a copy, and does not mutate the string.
	 */
	FString TrimStart() const &;

	/**
	 * Removes whitespace characters from the start of this string.
	 * @note Unlike Trim() this function returns a copy, and does not mutate the string.
	 */
	FString TrimStart() &&;

	/**
	 * Removes whitespace characters from the end of this string. Modifies the string in-place.
	 */
	void TrimEndInline();

	/**
	 * Removes whitespace characters from the end of this string.
	 * @note Unlike TrimTrailing() this function returns a copy, and does not mutate the string.
	 */
	FString TrimEnd() const &;

	/**
	 * Removes whitespace characters from the end of this string.
	 * @note Unlike TrimTrailing() this function returns a copy, and does not mutate the string.
	 */
	FString TrimEnd() &&;

	/** 
	 * Trims the inner array after the null terminator.
	 */
	void TrimToNullTerminator();

	/**
	 * Returns a copy of this string with wrapping quotation marks removed.
	 */
	FString TrimQuotes( bool* bQuotesRemoved = nullptr ) const;

	/**
	 * Breaks up a delimited string into elements of a string array.
	 *
	 * @param	InArray		The array to fill with the string pieces
	 * @param	pchDelim	The string to delimit on
	 * @param	InCullEmpty	If 1, empty strings are not added to the array
	 *
	 * @return	The number of elements in InArray
	 */
	int32 ParseIntoArray( TArray<FString>& OutArray, const TCHAR* pchDelim, bool InCullEmpty = true ) const;

	/**
	 * Breaks up a delimited string into elements of a string array, using any whitespace and an 
	 * optional extra delimter, like a ","
	 * @warning Caution!! this routine is O(N^2) allocations...use it for parsing very short text or not at all! 
	 *
	 * @param	InArray			The array to fill with the string pieces
	 * @param	pchExtraDelim	The string to delimit on
	 *
	 * @return	The number of elements in InArray
	 */
	int32 ParseIntoArrayWS( TArray<FString>& OutArray, const TCHAR* pchExtraDelim = nullptr, bool InCullEmpty = true ) const;

	/**
	* Breaks up a delimited string into elements of a string array, using line ending characters
	* @warning Caution!! this routine is O(N^2) allocations...use it for parsing very short text or not at all!
	*
	* @param	InArray			The array to fill with the string pieces	
	*
	* @return	The number of elements in InArray
	*/
	int32 ParseIntoArrayLines(TArray<FString>& OutArray, bool InCullEmpty = true) const;

	/**
	* Breaks up a delimited string into elements of a string array, using the given delimiters
	* @warning Caution!! this routine is O(N^2) allocations...use it for parsing very short text or not at all!
	*
	* @param	InArray			The array to fill with the string pieces
	* @param	DelimArray		The strings to delimit on
	* @param	NumDelims		The number of delimiters.
	*
	* @return	The number of elements in InArray
	*/
	int32 ParseIntoArray(TArray<FString>& OutArray, const TCHAR** DelimArray, int32 NumDelims, bool InCullEmpty = true) const;

	/**
	 * Takes an array of strings and removes any zero length entries.
	 *
	 * @param	InArray	The array to cull
	 *
	 * @return	The number of elements left in InArray
	 */
	static int32 CullArray( TArray<FString>* InArray );

	/**
	 * Returns a copy of this string, with the characters in reverse order
	 */
	FString Reverse() const;

	/**
	 * Reverses the order of characters in this string
	 */
	void ReverseString();

	/**
	 * Replace all occurrences of a substring in this string
	 *
	 * @param From substring to replace
	 * @param To substring to replace From with
	 * @param SearchCase	Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @return a copy of this string with the replacement made
	 */
	FString Replace(const TCHAR* From, const TCHAR* To, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;

	/**
	 * Replace all occurrences of SearchText with ReplacementText in this string.
	 *
	 * @param	SearchText	the text that should be removed from this string
	 * @param	ReplacementText		the text to insert in its place
	 * @param SearchCase	Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 *
	 * @return	the number of occurrences of SearchText that were replaced.
	 */
	int32 ReplaceInline( const TCHAR* SearchText, const TCHAR* ReplacementText, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase );

	/**
	 * Replace all occurrences of a character with another.
	 *
	 * @param SearchChar      Character to remove from this FString
	 * @param ReplacementChar Replacement character
	 * @param SearchCase      Indicates whether the search is case sensitive or not ( defaults to ESearchCase::IgnoreCase )
	 * @note no dynamic allocation
	 */
	void ReplaceCharInline(const TCHAR SearchChar, const TCHAR ReplacementChar, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase)
	{
		if (SearchCase == ESearchCase::IgnoreCase && TChar<TCHAR>::IsAlpha(SearchChar))
		{
			ReplaceCharInlineIgnoreCase(SearchChar, ReplacementChar);
		}
		else
		{
			ReplaceCharInlineCaseSensitive(SearchChar, ReplacementChar);
		}
	}

private:
	void ReplaceCharInlineCaseSensitive(const TCHAR SearchChar, const TCHAR ReplacementChar);
	void ReplaceCharInlineIgnoreCase(const TCHAR SearchChar, const TCHAR ReplacementChar);

public:

	/**
	 * Returns a copy of this string with all quote marks escaped (unless the quote is already escaped)
	 */
	FString ReplaceQuotesWithEscapedQuotes() const;

	/**
	 * Replaces certain characters with the "escaped" version of that character (i.e. replaces "\n" with "\\n").
	 * The characters supported are: { \n, \r, \t, \', \", \\ }.
	 *
	 * @param	Chars	by default, replaces all supported characters; this parameter allows you to limit the replacement to a subset.
	 *
	 * @return	a string with all control characters replaced by the escaped version.
	 */
	FString ReplaceCharWithEscapedChar( const TArray<TCHAR>* Chars = nullptr ) const;

	/**
	 * Removes the escape backslash for all supported characters, replacing the escape and character with the non-escaped version.  (i.e.
	 * replaces "\\n" with "\n".  Counterpart to ReplaceCharWithEscapedChar().
	 * @return copy of this string with replacement made
	 */
	FString ReplaceEscapedCharWithChar( const TArray<TCHAR>* Chars = nullptr ) const;

	/** 
	 * Replaces all instances of '\t' with TabWidth number of spaces
	 * @param InSpacesPerTab - Number of spaces that a tab represents
	 * @return copy of this string with replacement made
	 */
	FString ConvertTabsToSpaces(const int32 InSpacesPerTab);

	// Takes the number passed in and formats the string in comma format ( 12345 becomes "12,345")
	static FString FormatAsNumber( int32 InNumber );

	// To allow more efficient memory handling, automatically adds one for the string termination.
	FORCEINLINE void Reserve(int32 CharacterCount)
	{
		checkSlow(CharacterCount >= 0 && CharacterCount < MAX_int32);
		Data.Reserve(CharacterCount + 1);
	}

	/**
	 * Serializes a string as ANSI char array.
	 *
	 * @param	String			String to serialize
	 * @param	Ar				Archive to serialize with
	 * @param	MinCharacters	Minimum number of characters to serialize.
	 */
	void SerializeAsANSICharArray( FArchive& Ar, int32 MinCharacters=0 ) const;


	/** Converts an integer to a string. */
	static FORCEINLINE FString FromInt( int32 Num )
	{
		FString Ret; 
		Ret.AppendInt(Num); 
		return Ret;
	}

	/** appends the integer InNum to this string */
	void AppendInt( int32 InNum );

	/**
	 * Converts a string into a boolean value
	 *   1, "True", "Yes", GTrue, GYes, and non-zero integers become true
	 *   0, "False", "No", GFalse, GNo, and unparsable values become false
	 *
	 * @return The boolean value
	 */
	bool ToBool() const;

	/**
	 * Converts a buffer to a string
	 *
	 * @param SrcBuffer the buffer to stringify
	 * @param SrcSize the number of bytes to convert
	 *
	 * @return the blob in string form
	 */
	static FString FromBlob(const uint8* SrcBuffer,const uint32 SrcSize);

	/**
	 * Converts a string into a buffer
	 *
	 * @param DestBuffer the buffer to fill with the string data
	 * @param DestSize the size of the buffer in bytes (must be at least string len / 3)
	 *
	 * @return true if the conversion happened, false otherwise
	 */
	static bool ToBlob(const FString& Source,uint8* DestBuffer,const uint32 DestSize);

	/**
	 * Converts a buffer to a string by hex-ifying the elements
	 *
	 * @param SrcBuffer the buffer to stringify
	 * @param SrcSize the number of bytes to convert
	 *
	 * @return the blob in string form
	 */
	static FString FromHexBlob(const uint8* SrcBuffer,const uint32 SrcSize);

	/**
	 * Converts a string into a buffer
	 *
	 * @param DestBuffer the buffer to fill with the string data
	 * @param DestSize the size of the buffer in bytes (must be at least string len / 2)
	 *
	 * @return true if the conversion happened, false otherwise
	 */
	static bool ToHexBlob(const FString& Source,uint8* DestBuffer,const uint32 DestSize);

	/**
	 * Converts a float string with the trailing zeros stripped
	 * For example - 1.234 will be "1.234" rather than "1.234000"
	 * 
	 * @param	InFloat					The float to sanitize
	 * @param	InMinFractionalDigits	The minimum number of fractional digits the number should have (will be padded with zero)
	 *
	 * @return sanitized string version of float
	 */
	static FString SanitizeFloat( double InFloat, const int32 InMinFractionalDigits = 1 );

	/**
	 * Joins a range of 'something that can be concatentated to strings with +=' together into a single string with separators.
	 *
	 * @param	Range		The range of 'things' to concatenate.
	 * @param	Separator	The string used to separate each element.
	 *
	 * @return	The final, joined, separated string.
	 */
	template <typename RangeType>
	static FString Join(const RangeType& Range, const TCHAR* Separator)
	{
		FString Result;
		bool    First = true;
		for (const auto& Element : Range)
		{
			if (First)
			{
				First = false;
			}
			else
			{
				Result += Separator;
			}

			Result += Element;
		}

		return Result;
	}

	/**
	 * Joins a range of elements together into a single string with separators using a projection function.
	 *
	 * @param	Range		The range of 'things' to concatenate.
	 * @param	Separator	The string used to separate each element.
	 * @param	Proj		The projection used to get a string for each element.
	 *
	 * @return	The final, joined, separated string.
	 */
	template <typename RangeType, typename ProjectionType>
	static FString JoinBy(const RangeType& Range, const TCHAR* Separator, ProjectionType Proj)
	{
		FString Result;
		bool    First = true;
		for (const auto& Element : Range)
		{
			if (First)
			{
				First = false;
			}
			else
			{
				Result += Separator;
			}

			Result += Invoke(Proj, Element);
		}

		return Result;
	}

#ifndef MINIUNREAL

	FORCEINLINE void CountBytes(FArchive& Ar) const
	{
		Data.CountBytes(Ar);
	}

#endif

};

template<>
struct TContainerTraits<FString> : public TContainerTraitsBase<FString>
{
	enum { MoveWillEmptyContainer = TContainerTraits<FString::DataType>::MoveWillEmptyContainer };
};

template<> struct TIsZeroConstructType<FString> { enum { Value = true }; };
Expose_TNameOf(FString)

template <>
struct TIsContiguousContainer<FString>
{
	enum { Value = true };
};

inline TCHAR* GetData(FString& String)
{
	return String.GetCharArray().GetData();
}

inline const TCHAR* GetData(const FString& String)
{
	return String.GetCharArray().GetData();
}

inline SIZE_T GetNum(const FString& String)
{
	return String.Len();
}

/** Case insensitive string hash function. */
#ifdef MINIUNREAL
	inline static uint32 GetTypeHash(const FString& S){};
#else
	FORCEINLINE uint32 GetTypeHash(const FString& S)
	{
		return FCrc::Strihash_DEPRECATED(*S);
	}
#endif
/** 
 * Convert an array of bytes to a TCHAR
 * @param In byte array values to convert
 * @param Count number of bytes to convert
 * @return Valid string representing bytes.
 */
inline FString BytesToString(const uint8* In, int32 Count)
{
	FString Result;
	Result.Empty(Count);

	while (Count)
	{
		// Put the byte into an int16 and add 1 to it, this keeps anything from being put into the string as a null terminator
		int16 Value = *In;
		Value += 1;

		Result += TCHAR(Value);

		++In;
		Count--;
	}
	return Result;
}

/** 
 * Convert FString of bytes into the byte array.
 * @param String		The FString of byte values
 * @param OutBytes		Ptr to memory must be preallocated large enough
 * @param MaxBufferSize	Max buffer size of the OutBytes array, to prevent overflow
 * @return	The number of bytes copied
 */
inline int32 StringToBytes( const FString& String, uint8* OutBytes, int32 MaxBufferSize )
{
	int32 NumBytes = 0;
	const TCHAR* CharPos = *String;

	while( *CharPos && NumBytes < MaxBufferSize)
	{
		OutBytes[ NumBytes ] = (int8)(*CharPos - 1);
		CharPos++;
		++NumBytes;
	}
	return NumBytes;
}

/** Returns Char value of Nibble */
inline TCHAR NibbleToTChar(uint8 Num)
{
	if (Num > 9)
	{
		return TEXT('A') + TCHAR(Num - 10);
	}
	return TEXT('0') + TCHAR(Num);
}

/** 
 * Convert a byte to hex
 * @param In byte value to convert
 * @param Result out hex value output
 */
inline void ByteToHex(uint8 In, FString& Result)
{
	Result += NibbleToTChar(In >> 4);
	Result += NibbleToTChar(In & 15);
}

/** 
 * Convert an array of bytes to hex
 * @param In byte array values to convert
 * @param Count number of bytes to convert
 * @return Hex value in string.
 */
inline FString BytesToHex(const uint8* In, int32 Count)
{
	FString Result;
	Result.Empty(Count * 2);

	while (Count)
	{
		ByteToHex(*In++, Result);
		Count--;
	}
	return Result;
}

/**
 * Checks if the TChar is a valid hex character
 * @param Char		The character
 * @return	True if in 0-9 and A-F ranges
 */
inline const bool CheckTCharIsHex( const TCHAR Char )
{
	return ( Char >= TEXT('0') && Char <= TEXT('9') ) || ( Char >= TEXT('A') && Char <= TEXT('F') ) || ( Char >= TEXT('a') && Char <= TEXT('f') );
}

/**
 * Convert a TChar to equivalent hex value as a uint8
 * @param Char		The character
 * @return	The uint8 value of a hex character
 */
inline const uint8 TCharToNibble( const TCHAR Char )
{
	check( CheckTCharIsHex( Char ) );
	if( Char >= TEXT('0') && Char <= TEXT('9') )
	{
		return Char - TEXT('0');
	}
	else if( Char >= TEXT('A') && Char <= TEXT('F') )
	{
		return ( Char - TEXT('A') ) + 10;
	}
	return ( Char - TEXT('a') ) + 10;
}

/** 
 * Convert FString of Hex digits into the byte array.
 * @param HexString		The FString of Hex values
 * @param OutBytes		Ptr to memory must be preallocated large enough
 * @return	The number of bytes copied
 */
inline int32 HexToBytes( const FString& HexString, uint8* OutBytes )
{
	int32 NumBytes = 0;
	const bool bPadNibble = ( HexString.Len() % 2 ) == 1;
	const TCHAR* CharPos = *HexString;
	if( bPadNibble )
	{
		OutBytes[ NumBytes++ ] = TCharToNibble( *CharPos++ );
	}
	while( *CharPos )
	{
		OutBytes[ NumBytes ] = TCharToNibble( *CharPos++ ) << 4;
		OutBytes[ NumBytes ] += TCharToNibble( *CharPos++ );
		++NumBytes;
	}
	return NumBytes;
}

/**
 * Generalized API to convert something to a string. Function named after the (deprecated) Lex namespace, which
 * was deprecated because introducing customization points in a nested namespace didn't work in generic code because
 * it foiled 2-phase template instantiating compilers, which would bind to the qualified name (LexToString) in the first phase,
 * preventing future overloads defined in later headers to be considered for binding.
 */
 /**
 *	Expected functions in this namespace are as follows:
 *		bool								LexTryParseString(T& OutValue, const TCHAR* Buffer);
 *		void 								LexFromString(T& OutValue, const TCHAR* Buffer);
 *		<implicitly convertible to string>	LexToString(T);
 *		                    ^-- Generally this means it can return either FString or const TCHAR* 
 *		                        Generic code that uses ToString should assign to an FString or forward along to other functions
 *		                        that accept types that are also implicitly convertible to FString 
 *
 *	Implement custom functionality externally.
 */

 /** Covert a string buffer to intrinsic types */
inline void LexFromString(int8& OutValue, 		const TCHAR* Buffer)	{	OutValue = FCString::Atoi(Buffer);		}
inline void LexFromString(int16& OutValue,		const TCHAR* Buffer)	{	OutValue = FCString::Atoi(Buffer);		}
inline void LexFromString(int32& OutValue,		const TCHAR* Buffer)	{	OutValue = FCString::Atoi(Buffer);		}
inline void LexFromString(int64& OutValue,		const TCHAR* Buffer)	{	OutValue = FCString::Atoi64(Buffer);	}
inline void LexFromString(uint8& OutValue,		const TCHAR* Buffer)	{	OutValue = FCString::Atoi(Buffer);		}
inline void LexFromString(uint16& OutValue, 	const TCHAR* Buffer)	{	OutValue = FCString::Atoi(Buffer);		}
inline void LexFromString(uint32& OutValue, 	const TCHAR* Buffer)	{	OutValue = FCString::Atoi64(Buffer);	}	//64 because this unsigned and so Atoi might overflow
inline void LexFromString(uint64& OutValue, 	const TCHAR* Buffer)	{	OutValue = FCString::Strtoui64(Buffer, nullptr, 0); }
inline void LexFromString(float& OutValue,		const TCHAR* Buffer)	{	OutValue = FCString::Atof(Buffer);		}
inline void LexFromString(double& OutValue, 	const TCHAR* Buffer)	{	OutValue = FCString::Atod(Buffer);		}
inline void LexFromString(bool& OutValue, 		const TCHAR* Buffer)	{	OutValue = FCString::ToBool(Buffer);	}
inline void LexFromString(FString& OutValue, 	const TCHAR* Buffer)	{	OutValue = Buffer;						}

 /** Convert numeric types to a string */
#ifndef MINIUNREAL
	template<typename T>
	typename TEnableIf<TIsArithmetic<T>::Value, FString>::Type
	LexToString(const T& Value)
	{
		return FString::Printf( TFormatSpecifier<T>::GetFormatSpecifier(), Value );
	}
#endif

template<typename CharType>
typename TEnableIf<TIsCharType<CharType>::Value, FString>::Type
LexToString(const CharType* Ptr)
{
	return FString(Ptr);
}

inline FString LexToString(bool Value)
{
	return Value ? TEXT("true") : TEXT("false");
}
#ifdef MINIUNREAL
	inline static FString LexToString(FString&& Str){};
	inline static FString LexToString(const FString& Str){};
#else
	FORCEINLINE FString LexToString(FString&& Str)
	{
		return MoveTemp(Str);
	}

	FORCEINLINE FString LexToString(const FString& Str)
	{
		return Str;
	}
#endif

/** Helper template to convert to sanitized strings */
template<typename T>
FString LexToSanitizedString(const T& Value)
{
	return LexToString(Value);
}

/** Overloaded for floats */
inline FString LexToSanitizedString(float Value)
{
	return FString::SanitizeFloat(Value);
}

/** Overloaded for doubles */
inline FString LexToSanitizedString(double Value)
{
	return FString::SanitizeFloat(Value);
}


/** Parse a string into this type, returning whether it was successful */
/** Specialization for arithmetic types */
template<typename T>
static typename TEnableIf<TIsArithmetic<T>::Value, bool>::Type
LexTryParseString(T& OutValue, const TCHAR* Buffer)
{
	if (FCString::IsNumeric(Buffer))
	{
		LexFromString(OutValue, Buffer);
		return true;
	}
	return false;
}

/** Try and parse a bool - always returns true */
static bool LexTryParseString(bool& OutValue, const TCHAR* Buffer)
{
	LexFromString(OutValue, Buffer);
	return true;
}


/** Deprecated Lex namespace. Forwards on to Lex prefixed equivalents for backwards compatibility. See Lex-prefixed functions above. */
namespace Lex
{
	
	template<typename T> 
	UE_DEPRECATED(4.20, "Lex::FromString has been deprecated. Please use LexFromString instead.")
	void FromString(T& OutValue, const TCHAR* Buffer) 
	{ 
		LexFromString(OutValue, Buffer); 
	}

#ifndef MINIUNREAL

	template<typename T> 
	UE_DEPRECATED(4.20, "Lex::ToString has been deprecated. Please use LexToString instead.")
#if PLATFORM_COMPILER_HAS_DECLTYPE_AUTO
	decltype(auto) ToString(T&& Value)
#else
	auto ToString(T&& Value) -> decltype(LexToString(Forward<T>(Value)))
#endif
	{
		return LexToString(Forward<T>(Value)); 
	}

	template<typename T>
	UE_DEPRECATED(4.20, "Lex::ToSanitizedString has been deprecated. Please use LexToSanitizedString instead.")
#if PLATFORM_COMPILER_HAS_DECLTYPE_AUTO
	decltype(auto) ToSanitizedString(T&& Value)
#else
	auto ToSanitizedString(T&& Value) -> decltype(LexToSanitizedString(Forward<T>(Value)))
#endif
	{
		return LexToSanitizedString(Forward<T>(Value)); 
	}

	template<typename T>
	UE_DEPRECATED(4.20, "Lex::TryParseString has been deprecated. Please use LexTryParseString instead.")
	bool TryParseString(T& OutValue, const TCHAR* Buffer) 
	{ 
		return LexTryParseString(OutValue, Buffer); 
	}
#endif
}

// Deprecated alias for old LexicalConversion namespace.
// Namespace alias deprecation doesn't exist, so we can't actually mark it with the UE_DEPRECATED() macro.
namespace LexicalConversion = Lex;

/** Shorthand legacy use for Lex functions */
template<typename T>
struct TTypeToString
{
	static FString ToString(const T& Value)				{ return LexToString(Value); }
	static FString ToSanitizedString(const T& Value)	{ return LexToSanitizedString(Value); }
};
template<typename T>
struct TTypeFromString
{
	static void FromString(T& Value, const TCHAR* Buffer) { return LexFromString(Value, Buffer); }
};

/**
 * Gets a non-owning TCHAR pointer from a string type.
 *
 * Can be used generically to get a const TCHAR*, when it is not known if the argument is a TCHAR* or an FString:
 *
 * template <typename T>
 * void LogValue(const T& Val)
 * {
 *     Logf(TEXT("Value: %s"), ToCStr(LexToString(Val)));
 * }
 */
#ifdef MINIUNREAL
	inline static const TCHAR* ToCStr(const TCHAR* Ptr)
	{
		return Ptr;
	}
	inline static const TCHAR* ToCStr(const FString& Str)
	{
		return *Str;
	}
#else
	FORCEINLINE const TCHAR* ToCStr(const TCHAR* Ptr)
	{
		return Ptr;
	}
	FORCEINLINE const TCHAR* ToCStr(const FString& Str)
	{
		return *Str;
	}
#endif
/*----------------------------------------------------------------------------
	Special archivers.
----------------------------------------------------------------------------*/

//
// String output device.
//
#ifdef MINIUNREAL
class FStringOutputDevice : public FString
#else
class FStringOutputDevice : public FString, public FOutputDevice
#endif
{
public:
	FStringOutputDevice( const TCHAR* OutputDeviceName=TEXT("") ):
		FString( OutputDeviceName )
	{
		bAutoEmitLineTerminator = false;
	}
#ifdef MINIUNREAL
	bool bAutoEmitLineTerminator;
#else
	virtual void Serialize( const TCHAR* InData, ELogVerbosity::Type Verbosity, const class FName& Category ) override
	{
		FString::operator+=((TCHAR*)InData);
		if(bAutoEmitLineTerminator)
		{
			*this += LINE_TERMINATOR;
		}
	}

	FStringOutputDevice(FStringOutputDevice&&) = default;
	FStringOutputDevice(const FStringOutputDevice&) = default;
	FStringOutputDevice& operator=(FStringOutputDevice&&) = default;
	FStringOutputDevice& operator=(const FStringOutputDevice&) = default;

	// Make += operator virtual.
	virtual FString& operator+=(const FString& Other)
	{
		return FString::operator+=(Other);
	}
#endif
};

//
// String output device.
//
class FStringOutputDeviceCountLines : public FStringOutputDevice
{
	typedef FStringOutputDevice Super;

	int32 LineCount;
public:
	FStringOutputDeviceCountLines( const TCHAR* OutputDeviceName=TEXT("") )
	:	Super( OutputDeviceName )
	,	LineCount(0)
	{}

#ifndef MINIUNREAL

	virtual void Serialize(const TCHAR* InData, ELogVerbosity::Type Verbosity, const class FName& Category) override
	{
		Super::Serialize(InData, Verbosity, Category);
		int32 TermLength = FCString::Strlen(LINE_TERMINATOR);
		for (;;)
		{
			InData = FCString::Strstr(InData, LINE_TERMINATOR);
			if (!InData)
			{
				break;
			}
			LineCount++;
			InData += TermLength;
		}

		if (bAutoEmitLineTerminator)
		{
			LineCount++;
		}
	}

	/**
	 * Appends other FStringOutputDeviceCountLines object to this one.
	 */
	virtual FStringOutputDeviceCountLines& operator+=(const FStringOutputDeviceCountLines& Other)
	{
		FString::operator+=(Other);

		LineCount += Other.GetLineCount();

		return *this;
	}

	/**
	 * Appends other FString (as well as it's specializations like FStringOutputDevice)
	 * object to this.
	 */
	virtual FString& operator+=(const FString& Other) override
	{
		Log(Other);

		return *this;
	}

#endif

	int32 GetLineCount() const
	{
		return LineCount;
	}

	FStringOutputDeviceCountLines(const FStringOutputDeviceCountLines&) = default;
	FStringOutputDeviceCountLines& operator=(const FStringOutputDeviceCountLines&) = default;

	FORCEINLINE FStringOutputDeviceCountLines(FStringOutputDeviceCountLines&& Other)
		: Super    ((Super&&)Other)
		, LineCount(Other.LineCount)
	{
		Other.LineCount = 0;
	}

	FORCEINLINE FStringOutputDeviceCountLines& operator=(FStringOutputDeviceCountLines&& Other)
	{
		if (this != &Other)
		{
			(Super&)*this = (Super&&)Other;
			LineCount     = Other.LineCount;

			Other.LineCount = 0;
		}
		return *this;
	}
};

/**
 * A helper function to find closing parenthesis that matches the first open parenthesis found. The open parenthesis
 * referred to must be at or further up from the start index.
 *
 * @param TargetString      The string to search in
 * @param StartSearch       The index to start searching at
 * @return the index in the given string of the closing parenthesis
 */
CORE_API int32 FindMatchingClosingParenthesis(const FString& TargetString, const int32 StartSearch = 0);

/**
* Given a display label string, generates an FString slug that only contains valid characters for an FName.
* For example, "[MyObject]: Object Label" becomes "MyObjectObjectLabel" FName slug.
*
* @param DisplayLabel The label string to convert to an FName
*
* @return	The slugged string
*/
CORE_API FString SlugStringForValidName(const FString& DisplayString, const TCHAR* ReplaceWith = TEXT(""));

struct CORE_API FTextRange
{
	FTextRange()
		: BeginIndex(INDEX_NONE)
		, EndIndex(INDEX_NONE)
	{

	}

	FTextRange(int32 InBeginIndex, int32 InEndIndex)
		: BeginIndex(InBeginIndex)
		, EndIndex(InEndIndex)
	{

	}

	FORCEINLINE bool operator==(const FTextRange& Other) const
	{
		return BeginIndex == Other.BeginIndex
			&& EndIndex == Other.EndIndex;
	}

	FORCEINLINE bool operator!=(const FTextRange& Other) const
	{
		return !(*this == Other);
	}

	friend inline uint32 GetTypeHash(const FTextRange& Key)
	{
		uint32 KeyHash = 0;
		KeyHash = HashCombine(KeyHash, GetTypeHash(Key.BeginIndex));
		KeyHash = HashCombine(KeyHash, GetTypeHash(Key.EndIndex));
		return KeyHash;
	}

	int32 Len() const { return EndIndex - BeginIndex; }
	bool IsEmpty() const { return (EndIndex - BeginIndex) <= 0; }
#ifndef MINIUNREAL
	void Offset(int32 Amount) { BeginIndex += Amount; BeginIndex = FMath::Max(0, BeginIndex);  EndIndex += Amount; EndIndex = FMath::Max(0, EndIndex); }
#endif
	bool Contains(int32 Index) const { return Index >= BeginIndex && Index < EndIndex; }
	bool InclusiveContains(int32 Index) const { return Index >= BeginIndex && Index <= EndIndex; }

#ifndef MINIUNREAL

	FTextRange Intersect(const FTextRange& Other) const
	{
		FTextRange Intersected(FMath::Max(BeginIndex, Other.BeginIndex), FMath::Min(EndIndex, Other.EndIndex));
		if (Intersected.EndIndex <= Intersected.BeginIndex)
		{
			return FTextRange(0, 0);
		}

		return Intersected;
	}
#endif

	/**
	 * Produce an array of line ranges from the given text, breaking at any new-line characters
	 */
	static void CalculateLineRangesFromString(const FString& Input, TArray<FTextRange>& LineRanges);

	int32 BeginIndex;
	int32 EndIndex;
};

#ifndef MINIUNREAL
	#include "Misc/StringFormatArg.h"
#endif
