//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
//
// Purpose: 
//
//=============================================================================

#ifndef UTLSTRING_H
#define UTLSTRING_H
#ifdef _WIN32
#pragma once
#endif

#include "tier1/utlmemory.h"
#include "tier1/strtools.h"
#include "limits.h"

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1)
#endif

//-----------------------------------------------------------------------------
// Base class, containing simple memory management
//-----------------------------------------------------------------------------
class CUtlBinaryBlock
{
public:
	CUtlBinaryBlock( int64 growSize = 0, int64 initSize = 0 );
	~CUtlBinaryBlock()
	{
#ifdef _DEBUG
		m_nActualLength = 0x7BADF00D;
#else
		m_nActualLength = 0;
#endif

		// Has to be explicitly called due to the
		// current design of our SDK. Unlike other
		// Source Engine games, we couldn't import
		// the memalloc singleton as the executable
		// is monolithic; we obtain it post init
		// (too late for binding it against the
		// new/delete operators..).
		m_Memory.~CUtlMemory();
	}

	// NOTE: nInitialLength indicates how much of the buffer starts full
	CUtlBinaryBlock( void* pMemory, int64 nSizeInBytes, int64 nInitialLength );
	CUtlBinaryBlock( const void* pMemory, int64 nSizeInBytes );
	
	CUtlBinaryBlock( const CUtlBinaryBlock& src );
	CUtlBinaryBlock &operator=( const CUtlBinaryBlock &src );

#if VALVE_CPP11
	CUtlBinaryBlock( CUtlBinaryBlock&& src );
	CUtlBinaryBlock &operator=( CUtlBinaryBlock&& src );
#endif

	void		Get( void *pValue, int64 nMaxLen ) const;
	void		Set( const void *pValue, int64 nLen );
	const void	*Get( ) const;
	void		*Get( );

	unsigned char& operator[]( int64 i );
	const unsigned char& operator[]( int64 i ) const;

	int64		Length() const;
	void		SetLength( int64 nLength );	// Undefined memory will result
	bool		IsEmpty() const;
	void		Clear();
	void		Purge();

	bool		IsReadOnly() const;


	// Test for equality
	bool operator==( const CUtlBinaryBlock &src ) const;

private:
	CUtlMemory<unsigned char> m_Memory;
	int64 m_nActualLength;
};


//-----------------------------------------------------------------------------
// class inlines
//-----------------------------------------------------------------------------

#if VALVE_CPP11
inline CUtlBinaryBlock::CUtlBinaryBlock( CUtlBinaryBlock&& src )
: m_Memory( Move(src.m_Memory) )
, m_nActualLength( src.m_nActualLength )
{
	src.m_nActualLength = 0;
}

inline CUtlBinaryBlock& CUtlBinaryBlock::operator= ( CUtlBinaryBlock&& src )
{
	int64 length = src.m_nActualLength;
	src.m_nActualLength = 0;

	m_Memory = Move( src.m_Memory );
	m_nActualLength = length;

	return *this;
}
#endif

inline const void *CUtlBinaryBlock::Get( ) const
{
	return m_Memory.Base();
}

inline void *CUtlBinaryBlock::Get( )
{
	return m_Memory.Base();
}

inline int64 CUtlBinaryBlock::Length() const
{
	return m_nActualLength;
}

inline unsigned char& CUtlBinaryBlock::operator[]( int64 i )
{
	return m_Memory[i];
}

inline const unsigned char& CUtlBinaryBlock::operator[]( int64 i ) const
{
	return m_Memory[i];
}

inline bool CUtlBinaryBlock::IsReadOnly() const
{
	return m_Memory.IsReadOnly();
}

inline bool CUtlBinaryBlock::IsEmpty() const
{
	return Length() == 0;
}

inline void CUtlBinaryBlock::Clear()
{
	SetLength( 0 );
}

inline void CUtlBinaryBlock::Purge()
{
	SetLength( 0 );
	m_Memory.Purge();
}


//-----------------------------------------------------------------------------
// Simple string class. 
// NOTE: This is *not* optimal! Use in tools, but not runtime code
//-----------------------------------------------------------------------------
class CUtlString
{
public:
	CUtlString(); // = default;
	CUtlString( const char *pString ); // initialize from c-style string

	// Attaches the string to external memory. Useful for avoiding a copy
	CUtlString( void* pMemory, int64 nSizeInBytes, int64 nInitialLength );
	CUtlString( const void* pMemory, int64 nSizeInBytes );

	// Copy/move constructor/assignment
	// Moves are extremely efficient as the underlying memory is not copied, just the pointers.
	CUtlString( const CUtlString& string ); // = default;
	CUtlString &operator=( const CUtlString &src ); // = default;

#if VALVE_CPP11
	CUtlString( CUtlString&& moveFrom );    // = default;
	CUtlString &operator=( CUtlString&& moveFrom ); // = default;
#endif

	// Also can assign from a regular C-style string
	CUtlString &operator=( const char *src );

	char *Access() { return Get(); }
	const char	*Get( ) const;
	char		*Get();
	char*		GetForModify();

	void Clear() { Set( NULL ); }

	// CUtlString can be used anywhere a c-style string would be required
	// via this implicit conversion
	operator const char*() const;

	// for compatibility switching items from UtlSymbol
	const char  *String() const { return Get(); }

	// Returns strlen
	int64		Length() const;
	bool		IsEmpty() const;

	// GS - Added for chromehtml
	bool IsValid()
	{
		return (Length() != 0);
	}

	// Sets the length (used to serialize into the buffer )
	// Note: If nLen != 0, then this adds an extra byte for a null-terminator.	
	void		Set( const char *pValue );
	void		SetLength( int64 nLen );
	void		Purge();

	void		Swap(CUtlString &src);

	// Case Change
	void		ToUpper();
	void		ToLower( );
	void		Append( const char *pchAddition );
	void		Append(const char *pchAddition, int64 nMaxChars);
	void		Append(char chAddition) {
		char temp[2] = { chAddition, 0 };
		Append(temp);
	}

	// Strips the trailing slash
	void		StripTrailingSlash();

	char operator[] ( int64 idx ) const;
	char& operator[] ( int64 idx );

	// Test for equality
	bool operator==( const CUtlString &src ) const;
	bool operator==( const char *src ) const;
	bool operator!=( const CUtlString &src ) const { return !operator==( src ); }
	bool operator!=( const char *src ) const { return !operator==( src ); }

	// If these are not defined, CUtlString as rhs will auto-convert
	// to const char* and do logical operations on the raw pointers. Ugh.
	inline friend bool operator==( const char *lhs, const CUtlString &rhs ) { return rhs.operator==( lhs ); }
	inline friend bool operator!=( const char *lhs, const CUtlString &rhs ) { return rhs.operator!=( lhs ); }

	CUtlString &operator+=( const CUtlString &rhs );
	CUtlString &operator+=( const char *rhs );
	CUtlString &operator+=( char c );
	CUtlString &operator+=( int64 rhs );
	CUtlString &operator+=( double rhs );
	
	CUtlString operator+( const char *pOther )const;
	//CUtlString operator+( int64 rhs )const;

	bool MatchesPattern( const CUtlString &Pattern, int nFlags = 0 );		// case SENSITIVE, use * for wildcard in pattern string

#if ! defined(SWIG)
	// Don't let SWIG see the PRINTF_FORMAT_STRING attribute or it will complain.
	int Format( PRINTF_FORMAT_STRING const char *pFormat, ... ) FMTFUNCTION( 2, 3 );
	int FormatV( PRINTF_FORMAT_STRING const char *pFormat, va_list marker );
#else
	int Format( const char *pFormat, ... );
	int FormatV( const char *pFormat, va_list marker );
#endif
	
	void SetDirect( const char *pValue, int64 nChars );

	// Defining AltArgumentType_t hints that associative container classes should
	// also implement Find/Insert/Remove functions that take const char* params.
	typedef const char *AltArgumentType_t;

	// Take a piece out of the string.
	// If you only specify nStart, it'll go from nStart to the end.
	// You can use negative numbers and it'll wrap around to the start.
	CUtlString Slice( int64 nStart=0, int64 nEnd=INT_MAX );

	// Grab a substring starting from the left or the right side.
	CUtlString Left( int64 nChars );
	CUtlString Right( int64 nChars );

	CUtlString Remove(char const *pTextToRemove, bool bCaseSensitive) const;

	// Replace all instances of one character with another.
	CUtlString Replace( char cFrom, char cTo );

	/// Replace one string with the other (single pass).  Passing a NULL to pchTo is same as calling Remove
	CUtlString Replace( char const *pchFrom, const char *pchTo, bool bCaseSensitive = false ) const; 

	/// helper func for caseless replace
	CUtlString ReplaceCaseless( char const *pchFrom, const char *pchTo ) const { return Replace( pchFrom, pchTo, false ); }

	void RemoveDotSlashes(char separator = CORRECT_PATH_SEPARATOR);

	// Trim whitespace
	void		TrimLeft( const char *szTargets = "\t\r\n " );
	void		TrimRight( const char *szTargets = "\t\r\n " );
	void		Trim( const char *szTargets = "\t\r\n " );

	// Calls right through to V_MakeAbsolutePath.
	CUtlString AbsPath( const char *pStartingDir=NULL ) const;	

	CUtlString AbsPath(const char *pStartingDir, bool bLowercaseName) const
	{
		CUtlString result = AbsPath(pStartingDir);
		if (bLowercaseName)
		{
			result.ToLower();
		}
		return result;
	}

	// Gets the filename (everything except the path.. c:\a\b\c\somefile.txt -> somefile.txt).
	CUtlString UnqualifiedFilename() const;
	
	// Strips off one directory. Uses V_StripLastDir but strips the last slash also!
	CUtlString DirName();

	// Get a string with the extension removed (with V_StripExtension).
	CUtlString StripExtension() const;

	// Get a string with the filename removed (uses V_UnqualifiedFileName and also strips the last slash)
	CUtlString StripFilename() const;

	// Get a string with the base filename (with V_FileBase).
	CUtlString GetBaseFilename() const;

	// Get a string with the file extension (with V_FileBase).
	CUtlString GetExtension() const;

	// Works like V_ComposeFileName.
	static CUtlString PathJoin( const char *pStr1, const char *pStr2 );

	// These can be used for utlvector sorts.
	static int64 __cdecl SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 );
	static int64 __cdecl SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 );

	// From Src2

	void FixSlashes( char cSeparator = CORRECT_PATH_SEPARATOR )
	{
		for ( int64 nLength = Length() - 1; nLength >= 0; nLength-- )
		{
			char *pname = (char*)&m_Storage[ nLength ];
			if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR )
			{
				*pname = cSeparator;
			}
		}
	}

	bool IsEqual_CaseSensitive( const char *src ) const
	{
		if ( !src )
		{
			return ( Length() == 0 );
		}
		return ( V_strcmp( Get(), src ) == 0 );
	}

	bool IsEqual_CaseInsensitive(const char *src) const
	{
		if (!src)
		{
			return (Length() == 0);
		}
		return (V_stricmp(Get(), src) == 0);
	}


private:
	CUtlBinaryBlock m_Storage;
};

//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------

#if VALVE_CPP11
inline CUtlString::CUtlString( CUtlString&& moveFrom )
: m_Storage( Move( moveFrom.m_Storage ) )
{
}

inline CUtlString& CUtlString::operator=( CUtlString&& moveFrom )
{
	m_Storage = Move( moveFrom.m_Storage );
	return *this;
}
#endif

inline bool CUtlString::IsEmpty() const
{
	return Length() == 0;
}

inline int64 __cdecl CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 )
{
	return V_stricmp( pString1->String(), pString2->String() );
}

inline int64 __cdecl CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 )
{
	return V_strcmp( pString1->String(), pString2->String() );
}

inline char CUtlString::operator [] ( int64 index ) const
{
	return Get()[index];
}

inline char& CUtlString::operator[] ( int64 index )
{
	return Access()[index];
}

inline CUtlString::operator const char *( ) const
{
	return Get();
}

//-----------------------------------------------------------------------------
// Purpose: Implementation of low-level string functionality for character types.
//-----------------------------------------------------------------------------

template < typename T >
class StringFuncs
{
public:
	static T		*Duplicate( const T *pValue );
	static void		 Copy( T *out_pOut, const T *pIn, int iLength );
	static int		 Compare( const T *pLhs, const T *pRhs );
	static int		 Length( const T *pValue );
	static const T  *FindChar( const T *pStr, const T cSearch );
	static const T	*EmptyString();
};

template < >
class StringFuncs<char>
{
public:
	static char		  *Duplicate( const char *pValue ) { return _strdup( pValue ); }
	static void		   Copy( char *out_pOut, const char *pIn, int iLength ) { strncpy( out_pOut, pIn, iLength ); }
	static int		   Compare( const char *pLhs, const char *pRhs ) { return strcmp( pLhs, pRhs ); }
	static int		   Length( const char *pValue ) { return (int)strlen( pValue ); }
	static const char *FindChar( const char *pStr, const char cSearch ) { return strchr( pStr, cSearch ); }
	static const char *EmptyString() { return ""; }
};

template < >
class StringFuncs<wchar_t>
{
public:
	static wchar_t		 *Duplicate( const wchar_t *pValue ) { return _wcsdup( pValue ); }
	static void			  Copy( wchar_t *out_pOut, const wchar_t  *pIn, int iLength ) { wcsncpy( out_pOut, pIn, iLength ); }
	static int			  Compare( const wchar_t *pLhs, const wchar_t *pRhs ) { return wcscmp( pLhs, pRhs ); }
	static int			  Length( const wchar_t *pValue ) { return (int) wcslen( pValue ); }
	static const wchar_t *FindChar( const wchar_t *pStr, const wchar_t cSearch ) { return wcschr( pStr, cSearch ); }
	static const wchar_t *EmptyString() { return L""; }
};

//-----------------------------------------------------------------------------
// Dirt-basic auto-release string class. Not intended for manipulation,
// can be stored in a container or forwarded as a functor parameter.
// Note the benefit over CUtlString: sizeof(CUtlConstString) == sizeof(char*).
// Also note: null char* pointers are treated identically to empty strings.
//-----------------------------------------------------------------------------

template < typename T = char >
class CUtlConstStringBase
{
public:
	CUtlConstStringBase() : m_pString( NULL ) {}
	CUtlConstStringBase( const T *pString ) : m_pString( NULL ) { Set( pString ); }
	CUtlConstStringBase( const CUtlConstStringBase& src ) : m_pString( NULL ) { Set( src.m_pString ); }
	~CUtlConstStringBase() { Set( NULL ); }

	void Set( const T *pValue );
	void Clear() { Set( NULL ); }

	const T *Get() const { return m_pString ? m_pString : StringFuncs<T>::EmptyString(); }
	operator const T*() const { return m_pString ? m_pString : StringFuncs<T>::EmptyString(); }

	bool IsEmpty() const { return m_pString == NULL; } // Note: empty strings are never stored by Set

	int Compare( const T *rhs ) const;

	// Logical ops
	bool operator<( const T *rhs ) const { return Compare( rhs ) < 0; }
	bool operator==( const T *rhs ) const { return Compare( rhs ) == 0; }
	bool operator!=( const T *rhs ) const { return Compare( rhs ) != 0; }
	bool operator<( const CUtlConstStringBase &rhs ) const { return Compare( rhs.m_pString ) < 0; }
	bool operator==( const CUtlConstStringBase &rhs ) const { return Compare( rhs.m_pString ) == 0; }
	bool operator!=( const CUtlConstStringBase &rhs ) const { return Compare( rhs.m_pString ) != 0; }

	// If these are not defined, CUtlConstString as rhs will auto-convert
	// to const char* and do logical operations on the raw pointers. Ugh.
	inline friend bool operator<( const T *lhs, const CUtlConstStringBase &rhs ) { return rhs.Compare( lhs ) > 0; }
	inline friend bool operator==( const T *lhs, const CUtlConstStringBase &rhs ) { return rhs.Compare( lhs ) == 0; }
	inline friend bool operator!=( const T *lhs, const CUtlConstStringBase &rhs ) { return rhs.Compare( lhs ) != 0; }

	CUtlConstStringBase &operator=( const T *src ) { Set( src ); return *this; }
	CUtlConstStringBase &operator=( const CUtlConstStringBase &src ) { Set( src.m_pString ); return *this; }

	// Defining AltArgumentType_t is a hint to containers that they should
	// implement Find/Insert/Remove functions that take const char* params.
	typedef const T *AltArgumentType_t;

protected:
	const T *m_pString;
};

template < typename T >
void CUtlConstStringBase<T>::Set( const T *pValue )
{
	if ( pValue != m_pString )
	{
		free( ( void* ) m_pString );
		m_pString = pValue && pValue[0] ? StringFuncs<T>::Duplicate( pValue ) : NULL;
	}
}

template < typename T >
int CUtlConstStringBase<T>::Compare( const T *rhs ) const
{
	if ( m_pString )
	{
		if ( rhs )
			return StringFuncs<T>::Compare( m_pString, rhs );
		else
			return 1;
	}
	else
	{
		if ( rhs )
			return -1;
		else
			return 0;
	}
}

inline CUtlString CUtlString::operator+( const char *pOther )const
{
	CUtlString s = *this;
	s += pOther;
	return s;
}



typedef	CUtlConstStringBase<char>		CUtlConstString;
typedef	CUtlConstStringBase<wchar_t>	CUtlConstWideString;

//-----------------------------------------------------------------------------
// Purpose: General purpose string class good for when it
//			is rarely expected to be empty, and/or will undergo
//			many modifications/appends.
//-----------------------------------------------------------------------------
class CUtlStringBuilder
{
public:
	CUtlStringBuilder();
	CUtlStringBuilder(const char *pchString);
	CUtlStringBuilder(CUtlStringBuilder const &string);
	explicit CUtlStringBuilder(size_t nPreallocateBytes);
	//CUtlStringBuilder( const CUtlStringResult &strMoveSource );
	//explicit CUtlStringBuilder( IFillStringFunctor& func );
	~CUtlStringBuilder();

	// operator=
	CUtlStringBuilder &operator=(const CUtlStringBuilder &src);
	CUtlStringBuilder &operator=(const char *pchString);
	//CUtlStringBuilder &operator=( const CUtlStringResult &strMoveSource );

	// operator==
	bool operator==(CUtlStringBuilder const &src) const;
	bool operator==(const char *pchString) const;

	// operator!=
	bool operator!=(CUtlStringBuilder const &src) const;
	bool operator!=(const char *pchString) const;

	// operator </>, performs case sensitive comparison
	bool operator<(const CUtlStringBuilder &val) const;
	bool operator<(const char *pchString) const;
	bool operator>(const CUtlStringBuilder &val) const;
	bool operator>(const char *pchString) const;

	// operator+=
	CUtlStringBuilder &operator+=(const char *rhs);

	// is valid?
	bool IsValid() const;

	// gets the string
	// never returns NULL, use IsValid() to see if it's never been set
	const char *String() const;
	const char *Get() const { return String(); }
	operator const char *() const { return String(); }

	// returns the string directly (could be NULL)
	// useful for doing inline operations on the string
	char *Access()
	{
		// aggressive warning that there has been a bad error;
		// probably should not be retrieving the string by pointer.
		Assert(!m_data.HasError());

		if (!IsValid() || (Capacity() == 0))
			return NULL;

		return m_data.Access();
	}

	// return false if capacity can't be set. If false is returned,
	// the error state is set.
	bool EnsureCapacity(size_t nLength);

	// append in-place, causing a re-allocation
	void Append(const char *pchAddition);
	void Append(const char *pchAddition, size_t cbLen);
	void Append(const CUtlStringBuilder &str) { Append(str.String(), str.Length()); }
	//void Append( IFillStringFunctor& func );
	void AppendChar(char ch) { Append(&ch, 1); }
	void AppendRepeat(char ch, int64 cCount);

	// sets the string
	void SetValue(const char *pchString);
	void Set(const char *pchString);
	void Clear() { m_data.Clear(); }
	void SetPtr(char *pchString);
	void SetPtr(char *pchString, size_t nLength);
	void Swap(CUtlStringBuilder &src);

	// If you want to take ownership of the ptr, you can use this. So for instance if you had 
	// a CUtlString you wanted to move it to a CUtlStringConst without making a copy, you 
	// could do: CUtlStringConst strConst( strUtl.Detach() );
	// Also used for fast temporaries when a string that does not need to be retained is being 
	// returned from a func. ie: return (str.Detach());
	// All strings in this file can take a CUtlStringResult as a constructor parm and will take ownership directly.
	//CUtlStringResult Detach();

	// Set directly and don't look for a null terminator in pValue.
	// nChars is the string length. "abcd" nChars==3 would copy and null
	// terminate "abc" in the string object.
	void SetDirect(const char *pchString, size_t nChars);

	// Get the length of the string in characters.
	size_t Length() const;
	bool IsEmpty() const;
	size_t Capacity() const { return m_data.Capacity(); } // how much room is there to scribble

#if !defined(SWIG)
	// SWIG fails on PRINTF_FORMAT_STRING
	// Format like sprintf.
	size_t Format(PRINTF_FORMAT_STRING const char *pFormat, ...) FMTFUNCTION(2, 3);
	// format, then append what we crated in the format
	size_t AppendFormat(PRINTF_FORMAT_STRING const char *pFormat, ...) FMTFUNCTION(2, 3);
#else
	size_t Format(const char *pFormat, ...);
	size_t AppendFormat(const char *pFormat, ...);
#endif

	// replace a single character with another, returns hit count
	size_t Replace(char chTarget, char chReplacement);

	// replace a string with another string, returns hit count
	// replacement string might be NULL or "" to remove target substring
	size_t Replace(const char *pstrTarget, const char *pstrReplacement);
	// ASCII-only case-insensitivity.
	size_t ReplaceFastCaseless(const char *pstrTarget, const char *pstrReplacement);

	// replace a sequence of characters at a given point with a new string
	// replacement string might be NULL or "" to remove target substring
	bool ReplaceAt(size_t nIndex, size_t nOldChars, const char *pNewStr, size_t nNewChars = SIZE_MAX);

	ptrdiff_t IndexOf(const char *pstrTarget) const;

	// remove whitespace from the string; anything that is isspace()
	size_t RemoveWhitespace();

	// trim whitepace from the beginning and end of the string
	size_t TrimWhitespace();

	// Allows setting the size to anything under the current
	// capacity.  Typically should not be used unless there was a specific
	// reason to scribble on the string. Will not touch the string contents,
	// but will append a NULL. Returns true if the length was changed.
	bool SetLength(size_t nLen);

	// Take responsibility for the string. May cause a heap alloc.
	char *TakeOwnership(size_t *pnLen, size_t *pnCapacity);

	// For operations that are long and/or complex - if something fails
	// along the way, the error will be set and can be queried at the end.
	// The string is undefined in the error state, but will likely hold the
	// last value before the error occurred.  The string is cleared
	// if ClearError() is called.  The error can be set be the user, and it
	// will also be set if a dynamic allocation fails in string operations
	// where it needs to grow the capacity.
	void SetError()			{ m_data.SetError(true); }
	void ClearError()		{ m_data.ClearError(); }
	bool HasError() const	{ return m_data.HasError(); }

#ifdef DBGFLAG_VALIDATE
	void Validate(CValidator &validator, const char *pchName);	// validate our internal structures
#endif // DBGFLAG_VALIDATE

	size_t VFormat(const char *pFormat, va_list args);
	size_t VAppendFormat(const char *pFormat, va_list args);
	void Truncate(size_t nChars);

	// Access() With no assertion check - should only be used for tests
	char *AccessNoAssert()
	{
		if (!IsValid())
			return NULL;
		return m_data.Access();
	}

	// SetError() With no assertion check - should only be used for tests
	void SetErrorNoAssert() { m_data.SetError(false); }

	size_t ReplaceCaseless(const char *pstrTarget, const char *pstrReplacement) {
		return ReplaceFastCaseless(pstrTarget, pstrReplacement);
	}

private:
	size_t ReplaceInternal(const char *pstrTarget, const char *pstrReplacement, const char *pfnCompare(const char*, const char*));
	operator bool() const	{ return IsValid(); }

	// nChars is the number of characters you want, NOT including the null
	char *PrepareBuffer(size_t nChars, bool bCopyOld = false, size_t nMinCapacity = 0)
	{
		char *pszString = NULL;
		size_t nCapacity = m_data.Capacity();
		if ((nChars <= nCapacity) && (nMinCapacity <= nCapacity))
		{
			// early out leaving it all alone, just update the length,
			// even if it shortens an existing heap string to a width
			// that would fit in the stack buffer.
			pszString = m_data.SetLength(nChars);

			// SetLength will have added the null. Pointer might
			// be NULL if there is an error state and no buffer
			Assert(!pszString || pszString[nChars] == '\0');

			return pszString;
		}

		if (HasError())
			return NULL;

		// Need to actually adjust the capacity
		return InternalPrepareBuffer(nChars, bCopyOld, Max(nChars, nMinCapacity));
	}

	char *InternalPrepareBuffer(size_t nChars, bool bCopyOld, size_t nMinCapacity);

	template <typename T>
	void Swap(T &p1, T &p2)
	{
		T t = p1;
		p1 = p2;
		p2 = t;
	}

	// correct for 32 or 64 bit
	static const uint8 MAX_STACK_STRLEN = (sizeof(void*) == 4 ? 15 : 19);
	enum
	{
		// Note: If it's ever desired to have the embedded string be larger than
		// 63 (0x40-1), just make the sentinal 0xFF, and the error something (0xFF also
		// is fine).  Then shrink the scrap size by 1 and add a uint8 for the error state.
		// The error byte is only valid if the heap is on (already have that restriction).
		// and then embedded strings can get back to being up to 254.  It's not done this
		// way now just to make the tests for IsHeap()/HasError() faster since they
		// are often both tested together the compiler can do nice bit test optimizations.
		STRING_TYPE_SENTINEL = 0x80,
		STRING_TYPE_ERROR = 0x40
	}; // if Data.Stack.BytesLeft() or Data.Heap.sentinel == this value, data is in heap

	union Data
	{
		struct _Heap
		{	// 16 on 32 bit, sentinel == 0xff if Heap is the active union item
		private:
			char *m_pchString;
			uint32 m_nLength;
			uint32 m_nCapacity; // without trailing null; ie: m_pchString[m_nCapacity] = '\0' is not out of bounds
			uint8  scrap[3];
			uint8  sentinel;
		public:
			friend union Data;
			friend char *CUtlStringBuilder::InternalPrepareBuffer(size_t, bool, size_t);
		} Heap;

		struct _Stack
		{
		private:
			// last byte is doing a hack double duty.  It holds how many bytes 
			// are left in the string; so when the string is 'full' it will be
			// '0' and thus suffice as the terminating null.  This is why
			// we hold remaining chars instead of 'string length'
			char m_szString[MAX_STACK_STRLEN + 1];
		public:
			uint8 BytesLeft() const { return (uint8)(m_szString[MAX_STACK_STRLEN]); }
			void SetBytesLeft(char n) { m_szString[MAX_STACK_STRLEN] = n; }

			friend char *CUtlStringBuilder::InternalPrepareBuffer(size_t, bool, size_t);
			friend union Data;
		} Stack;

		// set to a clear state without looking at the current state
		void Construct()
		{
			Stack.m_szString[0] = '\0';
			Stack.SetBytesLeft(MAX_STACK_STRLEN);
		}

		// If we have heap allocated data, free it
		void FreeHeap()
		{
			if (IsHeap() && Heap.m_pchString)
				MemAllocSingleton()->Free(Heap.m_pchString);
		}

		// Back to a clean state, but retain the error state.
		void Clear()
		{
			if (HasError())
				return;

			FreeHeap();
			Heap.m_pchString = NULL;

			Construct();
		}

		bool IsHeap() const { return ((Heap.sentinel & STRING_TYPE_SENTINEL) != 0); }

		char *Access() { return IsHeap() ? Heap.m_pchString : Stack.m_szString; }
		const char *String() const { return IsHeap() ? Heap.m_pchString : Stack.m_szString; }

		size_t Length() const { return IsHeap() ? Heap.m_nLength : (MAX_STACK_STRLEN - Stack.BytesLeft()); }
		bool IsEmpty() const
		{
			if (IsHeap())
				return Heap.m_nLength == 0;
			else
				return Stack.BytesLeft() == MAX_STACK_STRLEN; // empty if all the bytes are available
		}

		size_t Capacity() const { return IsHeap() ? Heap.m_nCapacity : MAX_STACK_STRLEN; }

		// Internally the code often needs the char * after setting the length, so
		// just return it from here for conveniences.
		char *SetLength(size_t nChars);

		// Give the string away and set to an empty state
		char *TakeOwnership(size_t &nLen, size_t &nCapacity)
		{
			MoveToHeap();

			if (HasError())
			{
				nLen = 0;
				nCapacity = 0;
				return NULL;
			}

			nLen = Heap.m_nLength;
			nCapacity = Heap.m_nCapacity;
			char *psz = Heap.m_pchString;
			Construct();
			return psz;
		}

		void SetPtr(char *pchString, size_t nLength);

		// Set the string to an error state
		void SetError(bool bEnableAssert);

		// clear the error state and reset the string
		void ClearError();

		// If string is in the heap and the error bit is set in the sentinel
		// the error state is true.
		bool HasError() const { return IsHeap() && ((Heap.sentinel & STRING_TYPE_ERROR) != 0); }

		// If it's stack based, get it to the heap and return if it is
		// successfully on the heap (or already was)
		bool MoveToHeap();

	private:
		//-----------------------------------------------------------------------------
		// Purpose: Needed facts for string class to work
		//-----------------------------------------------------------------------------
		void StaticAssertTests()
		{
			// If this fails when the heap sentinel and where the stack string stores its bytes left
			// aren't aliases.  This is needed so that regardless of how the 'sentinel' to mark
			// that the string is on the heap is set, it is set as expected on both sides of the union.
			COMPILE_TIME_ASSERT(offsetof(_Heap, sentinel) == (offsetof(_Stack, m_szString) + MAX_STACK_STRLEN));

			// Lots of code assumes it can look at m_data.Stack.m_nBytesLeft for an empty string; which
			// means that it will equal MAX_STACK_STRLEN.  Therefor it must be a different value than
			// the STRING_TYPE_SENTINEL which will be set if the string is in the heap.
			COMPILE_TIME_ASSERT(MAX_STACK_STRLEN < STRING_TYPE_SENTINEL);
			COMPILE_TIME_ASSERT(MAX_STACK_STRLEN < STRING_TYPE_ERROR);

			// this is a no brainer, and I don't know anywhere in the world this isn't true,
			// but this code does take this dependency.
			COMPILE_TIME_ASSERT(0 == '\0');
		}
	};

private: // data
	Data m_data;

};

//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
inline CUtlStringBuilder::CUtlStringBuilder()
{
	m_data.Construct();
}


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
inline CUtlStringBuilder::CUtlStringBuilder(size_t nPreallocateBytes)
{
	if (nPreallocateBytes <= MAX_STACK_STRLEN)
	{
		m_data.Construct();
	}
	else
	{
		m_data.Construct();
		PrepareBuffer(0, false, nPreallocateBytes);
	}
}


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
inline CUtlStringBuilder::CUtlStringBuilder(const char *pchString)
{
	m_data.Construct();
	SetDirect(pchString, pchString ? strlen(pchString) : 0);
}


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
inline CUtlStringBuilder::CUtlStringBuilder(CUtlStringBuilder const &string)
{
	m_data.Construct();
	SetDirect(string.String(), string.Length());

	// attempt the copy before checking for error. On the off chance there
	// is data there that can be set, it will help with debugging.
	if (string.HasError())
		m_data.SetError(false);
}


//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
inline CUtlStringBuilder::~CUtlStringBuilder()
{
	m_data.FreeHeap();
}


//-----------------------------------------------------------------------------
// Purpose: Pre-Widen a string to an expected length
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::EnsureCapacity(size_t nLength)
{
	return PrepareBuffer(Length(), true, nLength) != NULL;
}


//-----------------------------------------------------------------------------
// Purpose: ask if the string has anything in it
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::IsEmpty() const
{
	return m_data.IsEmpty();
}

//-----------------------------------------------------------------------------
// Purpose: assignment
//-----------------------------------------------------------------------------
inline CUtlStringBuilder &CUtlStringBuilder::operator=(const char *pchString)
{
	SetDirect(pchString, pchString ? strlen(pchString) : 0);
	return *this;
}

//-----------------------------------------------------------------------------
// Purpose: assignment
//-----------------------------------------------------------------------------
inline CUtlStringBuilder &CUtlStringBuilder::operator=(CUtlStringBuilder const &src)
{
	if (&src != this)
	{
		SetDirect(src.String(), src.Length());
		// error propagate
		if (src.HasError())
			m_data.SetError(false);
	}

	return *this;
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator==(CUtlStringBuilder const &src) const
{
	return !Q_strcmp(String(), src.String());
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator==(const char *pchString) const
{
	return !Q_strcmp(String(), pchString);
}

//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator!=(CUtlStringBuilder const &src) const
{
	return !(*this == src);
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator!=(const char *pchString) const
{
	return !(*this == pchString);
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator<(CUtlStringBuilder const &val) const
{
	return operator<(val.String());
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator<(const char *pchString) const
{
	return Q_strcmp(String(), pchString) < 0;
}

//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator>(CUtlStringBuilder const &val) const
{
	return Q_strcmp(String(), val.String()) > 0;
}


//-----------------------------------------------------------------------------
// Purpose: comparison
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::operator>(const char *pchString) const
{
	return Q_strcmp(String(), pchString) > 0;
}


//-----------------------------------------------------------------------------
// Return a string with this string and rhs joined together.
inline CUtlStringBuilder& CUtlStringBuilder::operator+=(const char *rhs)
{
	Append(rhs);
	return *this;
}


//-----------------------------------------------------------------------------
// Purpose: returns true if the string is not null
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::IsValid() const
{
	return !HasError();
}


//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
inline const char *CUtlStringBuilder::String() const
{
	const char *pszString = m_data.String();
	if (pszString)
		return pszString;

	// pszString can be NULL in the error state. For const char*
	// never return NULL.
	return "";
}


//-----------------------------------------------------------------------------
// Purpose: Sets the string to be the new value, taking a copy of it
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::SetValue(const char *pchString)
{
	size_t nLen = (pchString ? strlen(pchString) : 0);
	SetDirect(pchString, nLen);
}


//-----------------------------------------------------------------------------
// Purpose: Set directly and don't look for a null terminator in pValue.
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::SetDirect(const char *pchSource, size_t nChars)
{
	if (HasError())
		return;

	if (m_data.IsHeap() && Get() == pchSource)
		return;

	if (!pchSource || !nChars)
	{
		m_data.Clear();
		return;
	}

	char *pszString = PrepareBuffer(nChars);

	if (pszString)
	{
		memcpy(pszString, pchSource, nChars);
		// PrepareBuffer already allocated space for the terminating null, 
		// and inserted it for us. Make sure we didn't clobber it.
		// Also assign it anyways so we don't risk the caller having a buffer
		// running into random bytes.
#ifdef _DEBUG
		// Suppress a bogus noisy warning:
		// warning C6385: Invalid data: accessing 'pszString', the readable size is 'nChars' bytes, but '1001' bytes might be read
		ANALYZE_SUPPRESS(6385);
		Assert(pszString[nChars] == '\0');
		pszString[nChars] = '\0';
#endif
	}
}


//-----------------------------------------------------------------------------
// Purpose: Sets the string to be the new value, taking a copy of it
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::Set(const char *pchString)
{
	SetValue(pchString);
}


//-----------------------------------------------------------------------------
// Purpose: Sets the string to be the new value, taking ownership of the pointer
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::SetPtr(char *pchString)
{
	size_t nLength = pchString ? strlen(pchString) : 0;
	SetPtr(pchString, nLength);
}

//-----------------------------------------------------------------------------
// Purpose: Sets the string to be the new value, taking ownership of the pointer
//			This API will clear the error state if it was set.
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::SetPtr(char *pchString, size_t nLength)
{
	m_data.Clear();

	if (!pchString || !nLength)
	{
		if (pchString)
			MemAllocSingleton()->Free(pchString); // we don't hang onto empty strings.
		return;
	}

	m_data.SetPtr(pchString, nLength);
}

//-----------------------------------------------------------------------------
// Purpose: return the conceptual 'strlen' of the string.
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::Length() const
{
	return m_data.Length();
}


//-----------------------------------------------------------------------------
// Purpose: format something sprintf() style, and take it as the new value of this CUtlStringBuilder
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::Format(const char *pFormat, ...)
{
	va_list args;
	va_start(args, pFormat);
	size_t nLen = VFormat(pFormat, args);
	va_end(args);
	return nLen;
}

int V_vscprintf(const char *format, va_list argptr);

//-----------------------------------------------------------------------------
// Purpose: Helper for Format() method
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::VFormat(const char *pFormat, va_list args)
{
	if (HasError())
		return 0;

	int len = 0;
#ifdef _WIN32
	// how much space will we need?
	len = V_vscprintf(pFormat, args);
#else
	// ISO spec defines the NULL/0 case as being valid and will return the
	// needed length. Verified on PS3 as well.  Ignore that bsd/linux/mac
	// have vasprintf which will allocate a buffer. We'd rather have the
	// self growing buffer management ourselves. Even the best implementations
	// There does not seem to be a magic vasprintf that is significantly
	// faster than 2 passes (some guess and get lucky).

	// Scope ReuseArgs.
	{
		CReuseVaList ReuseArgs(args);
		len = V_vsnprintf(NULL, 0, pFormat, ReuseArgs.m_ReuseList);
	}
#endif
	if (len > 0)
	{
		// get it
		char *pszString = PrepareBuffer(len, true);
		if (pszString)
			len = V_vsnprintf(pszString, len + 1, pFormat, args);
		else
			len = 0;
	}

	Assert(len > 0 || HasError());
	return len;
}


//-----------------------------------------------------------------------------
// format a string and append the result to the string we hold
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::AppendFormat(const char *pFormat, ...)
{
	va_list args;
	va_start(args, pFormat);
	size_t nLen = VAppendFormat(pFormat, args);
	va_end(args);
	return nLen;
}



//-----------------------------------------------------------------------------
// Purpose: implementation helper for AppendFormat()
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::VAppendFormat(const char *pFormat, va_list args)
{
	if (HasError())
		return 0;

	int len = 0;
#ifdef _WIN32
	// how much space will we need?
	len = _vscprintf(pFormat, args);
#else

	// ISO spec defines the NULL/0 case as being valid and will return the
	// needed length. Verified on PS3 as well.  Ignore that bsd/linux/mac
	// have vasprintf which will allocate a buffer. We'd rather have the
	// self growing buffer management ourselves. Even the best implementations
	// There does not seem to be a magic vasprintf that is significantly
	// faster than 2 passes (some guess and get lucky).

	// Scope ReuseArgs.
	{
		CReuseVaList ReuseArgs(args);
		len = vsnprintf(NULL, 0, pFormat, ReuseArgs.m_ReuseList);
	}
#endif
	size_t nOldLen = Length();

	if (len > 0)
	{
		// get it

		char *pszString = PrepareBuffer(nOldLen + len, true);
		if (pszString)
			len = _vsnprintf(&pszString[nOldLen], len + 1, pFormat, args);
		else
			len = 0;
	}

	Assert(len > 0 || HasError());
	return nOldLen + len;
}

//-----------------------------------------------------------------------------
// Purpose: concatenate the provided string to our current content
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::Append(const char *pchAddition)
{
	if (pchAddition && pchAddition[0])
	{
		size_t cchLen = strlen(pchAddition);
		Append(pchAddition, cchLen);
	}
}


//-----------------------------------------------------------------------------
// Purpose: concatenate the provided string to our current content
//			when the additional string length is known
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::Append(const char *pchAddition, size_t cbLen)
{
	if (pchAddition && pchAddition[0] && cbLen)
	{
		if (IsEmpty())
		{
			SetDirect(pchAddition, cbLen);
		}
		else
		{
			size_t cbOld = Length();
			char *pstrNew = PrepareBuffer(cbOld + cbLen, true);

			// make sure we use raw memcpy to get intrinsic
			if (pstrNew)
				memcpy(pstrNew + cbOld, pchAddition, cbLen);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: append a repeated series of a single character
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::AppendRepeat(char ch, int64 cCount)
{
	size_t cbOld = Length();
	char *pstrNew = PrepareBuffer(cbOld + cCount, true);
	if (pstrNew)
		memset(pstrNew + cbOld, ch, cCount);
}

//-----------------------------------------------------------------------------
// Purpose: Swaps string contents
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::Swap(CUtlStringBuilder &src)
{
	// swapping m_data instead of '*this' prevents having to
	// copy dynamic strings. Important that m_data doesn't know
	// any lifetime rules about its members (ie: it should not have
	// a destructor that frees the dynamic string pointer).
	Swap(m_data, src.m_data);
}


//-----------------------------------------------------------------------------
// Purpose: replace all occurrences of one character with another
//-----------------------------------------------------------------------------
inline size_t CUtlStringBuilder::Replace(char chTarget, char chReplacement)
{
	size_t cReplacements = 0;

	if (!IsEmpty() && !HasError())
	{
		char *pszString = Access();
		for (char *pstrWalker = pszString; *pstrWalker != 0; pstrWalker++)
		{
			if (*pstrWalker == chTarget)
			{
				*pstrWalker = chReplacement;
				cReplacements++;
			}
		}
	}

	return cReplacements;
}


//-----------------------------------------------------------------------------
// replace a sequence of characters at a given point with a new string
// replacement string might be NULL or "" to remove target substring
//-----------------------------------------------------------------------------
inline bool CUtlStringBuilder::ReplaceAt(size_t nIndex, size_t nOldChars, const char *pNewStr, size_t nNewChars)
{
	Assert(nIndex < Length() && nIndex + nOldChars <= Length());

	if (nNewChars == SIZE_MAX)
	{
		nNewChars = pNewStr ? V_strlen(pNewStr) : 0;
	}

	size_t nOldLength = Length();
	ptrdiff_t nDelta = nNewChars - nOldChars;
	if (nDelta < 0)
	{
		char *pBuf = Access();
		memmove(pBuf + nIndex + nNewChars, pBuf + nIndex + nOldChars, nOldLength - nIndex - nOldChars);
		SetLength(nOldLength + nDelta);
	}
	else if (nDelta > 0)
	{
		char *pBuf = PrepareBuffer(nOldLength + nDelta, true);
		if (!pBuf)
		{
			return false;
		}

		memmove(pBuf + nIndex + nNewChars, pBuf + nIndex + nOldChars, nOldLength - nIndex - nOldChars);
	}

	if (nNewChars)
	{
		memcpy(Access() + nIndex, pNewStr, nNewChars);
	}

	return true;
}


//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Purpose: Truncates the string to the specified number of characters
//-----------------------------------------------------------------------------
inline void CUtlStringBuilder::Truncate(size_t nChars)
{
	if (IsEmpty() || HasError())
		return;

	size_t nLen = Length();
	if (nLen <= nChars)
		return;

	// we may be shortening enough to fit in the small buffer, but
	// the external buffer is already allocated, so just keep using it.
	m_data.SetLength(nChars);
}


//-----------------------------------------------------------------------------
// Data and memory validation
//-----------------------------------------------------------------------------
#ifdef DBGFLAG_VALIDATE
inline void CUtlStringBuilder::Validate(CValidator &validator, const char *pchName)
{
#ifdef _WIN32
	validator.Push(typeid(*this).raw_name(), this, pchName);
#else
	validator.Push(typeid(*this).name(), this, pchName);
#endif

	if (m_data.IsHeap())
		validator.ClaimMemory(Access());

	validator.Pop();
}
#endif // DBGFLAG_VALIDATE


#endif // UTLSTRING_H
