2024-09-20 14:53:15 +00:00
# pragma once
# include "CoreTypes.h"
# include "String/Char.h"
# include "Memory/Memory.h"
# include "Templates/Invoke.h"
# include "TypeTraits/TypeTraits.h"
# include "Miscellaneous/Compare.h"
# include "Miscellaneous/AssertionMacros.h"
# include <cstring>
# include <cwchar>
NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN ( Redcraft )
NAMESPACE_MODULE_BEGIN ( Utility )
# pragma warning(push)
# pragma warning(disable : 4996)
/** Determines search direction for string operations. */
enum class ESearchDirection
{
/** Search from the start, moving forward through the string. */
FromStart ,
/** Search from the end, moving backward through the string. */
FromEnd ,
} ;
/** Set of utility functions operating on C-style null-terminated byte strings. */
template < CCharType T >
struct TCString
{
using CharType = T ;
2024-09-28 13:07:06 +00:00
/** Copies one string to another. The end sentinel is used only for buffer safety and will not append null characters to the destination. */
FORCEINLINE static CharType * Copy ( CharType * Destination , const CharType * DestinationEnd , const CharType * Source , const CharType * SourceEnd )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( Destination & & Source , TEXT ( " Read access violation. Destination and source must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( DestinationEnd = = nullptr & & SourceEnd = = nullptr )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strcpy ( Destination , Source ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcscpy ( Destination , Source ) ;
}
}
2024-09-28 13:07:06 +00:00
size_t SourceLength = TCString : : Length ( Source , SourceEnd ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( DestinationEnd ! = nullptr & & Destination + SourceLength + 1 > DestinationEnd )
2024-09-20 14:53:15 +00:00
{
return nullptr ;
}
Memory : : Memcpy ( Destination , Source , SourceLength * sizeof ( CharType ) ) ;
Destination [ SourceLength ] = LITERAL ( CharType , ' \0 ' ) ;
return Destination ;
}
2024-09-28 13:07:06 +00:00
/** Concatenates two strings. The end sentinel is used only for buffer safety and will not append null characters to the destination. */
FORCEINLINE static CharType * Cat ( CharType * Destination , const CharType * DestinationEnd , const CharType * Source , const CharType * SourceEnd )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( Destination & & Source , TEXT ( " Read access violation. Destination and source must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( DestinationEnd = = nullptr & & SourceEnd = = nullptr )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strcat ( Destination , Source ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcscat ( Destination , Source ) ;
}
}
2024-09-28 13:07:06 +00:00
size_t DestinationLength = TCString : : Length ( Destination , DestinationEnd ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
CharType * Result = Copy ( Destination + DestinationLength , DestinationEnd , Source , SourceEnd ) ;
2024-09-20 14:53:15 +00:00
return Result ? Destination : nullptr ;
}
/** @return The length of a given string. The maximum length is the buffer size. */
2024-09-28 13:07:06 +00:00
NODISCARD FORCEINLINE static size_t Length ( const CharType * InString , const CharType * End )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( End = = nullptr )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strlen ( InString ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcslen ( InString ) ;
}
}
size_t Result = 0 ;
2024-09-28 13:07:06 +00:00
while ( * InString ! = LITERAL ( CharType , ' \0 ' ) & & InString ! = End )
2024-09-20 14:53:15 +00:00
{
+ + Result ;
2024-09-24 07:07:57 +00:00
+ + InString ;
2024-09-20 14:53:15 +00:00
}
return Result ;
}
2024-09-28 13:07:06 +00:00
/** Compares two strings. The end sentinel is used only for buffer safety not for comparison. */
NODISCARD FORCEINLINE static strong_ordering Compare ( const CharType * LHS , const CharType * LHSEnd , const CharType * RHS , const CharType * RHSEnd )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( LHS & & RHS , TEXT ( " Read access violation. LHS and RHS must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( LHSEnd = = nullptr & & RHSEnd = = nullptr )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strcmp ( LHS , RHS ) < = > 0 ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcscmp ( LHS , RHS ) < = > 0 ;
}
}
2024-09-28 13:07:06 +00:00
while ( LHS ! = LHSEnd & & RHS ! = RHSEnd )
2024-09-20 14:53:15 +00:00
{
if ( * LHS ! = * RHS )
{
return * LHS < = > * RHS ;
}
2024-09-24 07:07:57 +00:00
if ( * LHS = = LITERAL ( CharType , ' \0 ' ) & & * RHS = = LITERAL ( CharType , ' \0 ' ) )
{
return strong_ordering : : equal ;
}
+ + LHS ;
+ + RHS ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
if ( LHS ! = LHSEnd & & RHS = = RHSEnd )
{
return * LHS < = > LITERAL ( CharType , ' \0 ' ) ;
}
else if ( LHS = = LHSEnd & & RHS ! = RHSEnd )
{
return LITERAL ( CharType , ' \0 ' ) < = > * RHS ;
}
return strong_ordering : : equal ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that satisfies the predicate. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
2024-09-20 14:53:15 +00:00
template < CPredicate < CharType > F >
2024-09-28 13:07:06 +00:00
NODISCARD FORCEINLINE static const CharType * Find ( const CharType * InString , const CharType * End , F & & InPredicate , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
if ( SearchDirection = = ESearchDirection : : FromStart )
{
2024-09-28 13:07:06 +00:00
while ( InString ! = End )
2024-09-20 14:53:15 +00:00
{
if ( InvokeResult < bool > ( Forward < F > ( InPredicate ) , * InString ) )
{
return InString ;
}
2024-09-24 07:07:57 +00:00
if ( * InString = = LITERAL ( CharType , ' \0 ' ) ) break ;
+ + InString ;
2024-09-20 14:53:15 +00:00
}
}
else
{
2024-09-28 13:07:06 +00:00
size_t Index = TCString : : Length ( InString , End ) ;
const CharType * Iter = InString + Index ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( Iter = = End ) - - Iter ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
while ( Iter ! = InString - 1 )
2024-09-20 14:53:15 +00:00
{
2024-09-28 13:07:06 +00:00
if ( InvokeResult < bool > ( Forward < F > ( InPredicate ) , * Iter ) )
2024-09-20 14:53:15 +00:00
{
2024-09-28 13:07:06 +00:00
return Iter ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
- - Iter ;
2024-09-20 14:53:15 +00:00
}
}
return nullptr ;
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that satisfies the predicate. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
2024-09-20 14:53:15 +00:00
template < CPredicate < CharType > F >
2024-09-28 13:07:06 +00:00
NODISCARD FORCEINLINE static CharType * Find ( CharType * InString , const CharType * End , F & & InPredicate , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : Find ( const_cast < const CharType * > ( InString ) , End , Forward < F > ( InPredicate ) , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static const CharType * FindChar ( const CharType * InString , const CharType * End , CharType Character , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( End = = nullptr )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return SearchDirection = = ESearchDirection : : FromStart ? NAMESPACE_STD : : strchr ( InString , Character ) : NAMESPACE_STD : : strrchr ( InString , Character ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return SearchDirection = = ESearchDirection : : FromStart ? NAMESPACE_STD : : wcschr ( InString , Character ) : NAMESPACE_STD : : wcsrchr ( InString , Character ) ;
}
}
2024-09-28 13:07:06 +00:00
return TCString : : Find ( InString , End , [ Character ] ( CharType C ) { return C = = Character ; } , SearchDirection ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static CharType * FindChar ( CharType * InString , const CharType * End , CharType Character , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : FindChar ( const_cast < const CharType * > ( InString ) , End , Character , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character in a charset. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static const CharType * FindChar ( const CharType * InString , const CharType * End , const CharType * Charset , const CharType * CharsetEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Charset , TEXT ( " Read access violation. InString and Charset must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( End = = nullptr & & CharsetEnd = = nullptr & & SearchDirection = = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strpbrk ( InString , Charset ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcspbrk ( InString , Charset ) ;
}
}
return TCString : : Find
(
2024-09-28 13:07:06 +00:00
InString , End ,
[ Charset , CharsetEnd ] ( CharType C )
2024-09-20 14:53:15 +00:00
{
2024-09-28 13:07:06 +00:00
const CharType * Result = TCString : : FindChar ( Charset , CharsetEnd , C ) ;
2024-09-20 14:53:15 +00:00
return Result ! = nullptr & & * Result ! = LITERAL ( CharType , ' \0 ' ) ;
} ,
SearchDirection
) ;
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character in a charset. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static CharType * FindChar ( CharType * InString , const CharType * End , const CharType * Charset , const CharType * CharsetEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Charset , TEXT ( " Read access violation. InString and Charset must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : FindChar ( const_cast < const CharType * > ( InString ) , End , Charset , CharsetEnd , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that is not the given character. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static const CharType * FindNotChar ( const CharType * InString , const CharType * End , CharType Character , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( InString = = End ) return nullptr ;
2024-09-20 14:53:15 +00:00
if ( Character = = LITERAL ( CharType , ' \0 ' ) & & SearchDirection = = ESearchDirection : : FromStart )
{
return * InString ! = LITERAL ( CharType , ' \0 ' ) ? InString : nullptr ;
}
2024-09-28 13:07:06 +00:00
if ( End = = nullptr & & SearchDirection = = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
const CharType Charset [ ] = { Character , LITERAL ( CharType , ' \0 ' ) } ;
return InString + NAMESPACE_STD : : strspn ( InString , Charset ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
const CharType Charset [ ] = { Character , LITERAL ( CharType , ' \0 ' ) } ;
return InString + NAMESPACE_STD : : wcsspn ( InString , Charset ) ;
}
}
2024-09-28 13:07:06 +00:00
return TCString : : Find ( InString , End , [ Character ] ( CharType C ) { return C ! = Character ; } , SearchDirection ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that is not the given character. The terminating null character is considered to be a part of the string. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static CharType * FindNotChar ( CharType * InString , const CharType * End , CharType Character , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString , TEXT ( " Read access violation. InString must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : FindNotChar ( const_cast < const CharType * > ( InString ) , End , Character , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that is not in the given charset. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static const CharType * FindNotChar ( const CharType * InString , const CharType * End , const CharType * Charset , const CharType * CharsetEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Charset , TEXT ( " Read access violation. InString and Charset must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( End = = nullptr & & CharsetEnd = = nullptr & & SearchDirection = = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
size_t Index = NAMESPACE_STD : : strspn ( InString , Charset ) ;
return InString [ Index ] ! = LITERAL ( CharType , ' \0 ' ) ? InString + Index : nullptr ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
size_t Index = NAMESPACE_STD : : wcsspn ( InString , Charset ) ;
return InString [ Index ] ! = LITERAL ( CharType , ' \0 ' ) ? InString + Index : nullptr ;
}
}
2024-09-28 13:07:06 +00:00
return TCString : : Find ( InString , End , [ Charset , CharsetEnd ] ( CharType C ) { return TCString : : FindChar ( Charset , CharsetEnd , C ) = = nullptr ; } , SearchDirection ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a character that is not in the given charset. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static CharType * FindNotChar ( CharType * InString , const CharType * End , const CharType * Charset , const CharType * CharsetEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Charset , TEXT ( " Read access violation. InString and Charset must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : FindNotChar ( const_cast < const CharType * > ( InString ) , End , Charset , CharsetEnd , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a substring. The end sentinel is used only for buffer safety. */
NODISCARD static const CharType * FindString ( const CharType * InString , const CharType * End , const CharType * Substring , const CharType * SubstringEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Substring , TEXT ( " Read access violation. InString and Substring must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( InString = = End ) return nullptr ;
2024-09-20 14:53:15 +00:00
2024-09-28 13:07:06 +00:00
if ( Substring = = SubstringEnd | | * Substring = = LITERAL ( CharType , ' \0 ' ) )
2024-09-20 14:53:15 +00:00
{
2024-09-28 13:07:06 +00:00
if ( SearchDirection = = ESearchDirection : : FromStart ) return InString ;
else
{
const CharType * Iter = InString + TCString : : Length ( InString , End ) ;
if ( Iter = = End ) - - Iter ;
return Iter ;
}
2024-09-20 14:53:15 +00:00
}
2024-09-28 13:07:06 +00:00
if ( End = = nullptr & & SubstringEnd = = nullptr & & SearchDirection = = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
if constexpr ( CSameAs < CharType , char > )
{
return NAMESPACE_STD : : strstr ( InString , Substring ) ;
}
else if constexpr ( CSameAs < CharType , wchar_t > )
{
return NAMESPACE_STD : : wcsstr ( InString , Substring ) ;
}
}
2024-09-28 13:07:06 +00:00
size_t StringLength = TCString : : Length ( InString , End ) ;
size_t SubstringLength = TCString : : Length ( Substring , SubstringEnd ) ;
2024-09-20 14:53:15 +00:00
if ( StringLength < SubstringLength )
{
return nullptr ;
}
if ( SearchDirection = = ESearchDirection : : FromStart )
{
for ( size_t Index = 0 ; Index < StringLength - SubstringLength ; + + Index )
{
2024-09-28 13:07:06 +00:00
if ( TCString : : Compare ( InString + Index , InString + Index + SubstringLength , Substring , Substring + SubstringLength ) = = 0 )
2024-09-20 14:53:15 +00:00
{
return InString + Index ;
}
}
}
else
{
for ( size_t Index = StringLength - SubstringLength ; Index > 0 ; - - Index )
{
2024-09-28 13:07:06 +00:00
if ( TCString : : Compare ( InString + Index , InString + Index + SubstringLength , Substring , Substring + SubstringLength ) = = 0 )
2024-09-20 14:53:15 +00:00
{
return InString + Index ;
}
}
}
return nullptr ;
}
2024-09-28 13:07:06 +00:00
/** Finds the first or last occurrence of a substring. The end sentinel is used only for buffer safety. */
NODISCARD FORCEINLINE static CharType * FindString ( CharType * InString , const CharType * End , const CharType * Substring , const CharType * SubstringEnd , ESearchDirection SearchDirection = ESearchDirection : : FromStart )
2024-09-20 14:53:15 +00:00
{
2024-09-24 07:07:57 +00:00
checkf ( InString & & Substring , TEXT ( " Read access violation. InString and Substring must not be nullptr. " ) ) ;
2024-09-20 14:53:15 +00:00
check_no_recursion ( ) ;
2024-09-28 13:07:06 +00:00
return const_cast < CharType * > ( TCString : : FindString ( const_cast < const CharType * > ( InString ) , End , Substring , SubstringEnd , SearchDirection ) ) ;
2024-09-20 14:53:15 +00:00
}
} ;
using FCString = TCString < char > ;
using FWCString = TCString < wchar_t > ;
using FU8CString = TCString < char8_t > ;
using FU16CString = TCString < char16_t > ;
using FU32CString = TCString < char32_t > ;
# pragma warning(pop)
NAMESPACE_MODULE_END ( Utility )
NAMESPACE_MODULE_END ( Redcraft )
NAMESPACE_REDCRAFT_END