From 6c9beec4be68118b664b032cb1f63839c0b6f268 Mon Sep 17 00:00:00 2001 From: Redstone1024 <2824517378@qq.com> Date: Sat, 28 Sep 2024 21:07:06 +0800 Subject: [PATCH] refactor(string): use end sentinel instead of buffer size to simplify implementation and usage --- .../Source/Private/Testing/StringTesting.cpp | 200 +++++++++-------- .../Source/Public/String/CString.h | 202 ++++++++---------- 2 files changed, 197 insertions(+), 205 deletions(-) diff --git a/Redcraft.Utility/Source/Private/Testing/StringTesting.cpp b/Redcraft.Utility/Source/Private/Testing/StringTesting.cpp index f1a496d..bd42aa0 100644 --- a/Redcraft.Utility/Source/Private/Testing/StringTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/StringTesting.cpp @@ -242,149 +242,161 @@ void TestCString() T StrC[BUFFER_SIZE]; T StrD[BUFFER_SIZE]; - always_check(TCString::Copy(StrA, IGNORE_SIZE, LITERAL(T, "Hello"), IGNORE_SIZE) != nullptr); - always_check(TCString::Copy(StrB, IGNORE_SIZE, LITERAL(T, "Hello"), IGNORE_SIZE) != nullptr); - always_check(TCString::Copy(StrC, IGNORE_SIZE, LITERAL(T, "World"), IGNORE_SIZE) != nullptr); - always_check(TCString::Copy(StrD, IGNORE_SIZE, LITERAL(T, " "), IGNORE_SIZE) != nullptr); + const T* EndA = &StrA[BUFFER_SIZE]; + const T* EndB = &StrB[BUFFER_SIZE]; + const T* EndC = &StrC[BUFFER_SIZE]; + const T* EndD = &StrD[BUFFER_SIZE]; - always_check(TCString::Length(StrA, 4) == 4); - always_check(TCString::Length(StrA, BUFFER_SIZE) == 5); - always_check(TCString::Length(StrA, IGNORE_SIZE) == 5); + always_check(TCString::Copy(StrA, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr); + always_check(TCString::Copy(StrB, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr); + always_check(TCString::Copy(StrC, nullptr, LITERAL(T, "World"), nullptr) != nullptr); + always_check(TCString::Copy(StrD, nullptr, LITERAL(T, " "), nullptr) != nullptr); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrB, IGNORE_SIZE) == TCString::Compare(StrA, BUFFER_SIZE, StrB, BUFFER_SIZE)); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrC, IGNORE_SIZE) == TCString::Compare(StrA, BUFFER_SIZE, StrC, BUFFER_SIZE)); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrC, IGNORE_SIZE) < 0); + always_check(TCString::Length(StrA, &StrA[4]) == 4); + always_check(TCString::Length(StrA, EndA ) == 5); + always_check(TCString::Length(StrA, nullptr ) == 5); + + const T* PtrA = LITERAL(T, "Hel"); + const T* PtrB = LITERAL(T, "Hello"); + + always_check(TCString::Compare(PtrA, nullptr, PtrB, &PtrB[3]) == 0); + + always_check(TCString::Compare(StrA, nullptr, StrB, nullptr) == TCString::Compare(StrA, EndA, StrB, EndB)); + always_check(TCString::Compare(StrA, nullptr, StrC, nullptr) == TCString::Compare(StrA, EndA, StrC, EndC)); + always_check(TCString::Compare(StrA, nullptr, StrC, nullptr) < 0); Memory::Memzero(StrD); - always_check(TCString::Compare(StrA, BUFFER_SIZE, StrD, BUFFER_SIZE) > 0); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrD, IGNORE_SIZE) > 0); + always_check(TCString::Compare(StrA, EndA , StrD, EndD ) > 0); + always_check(TCString::Compare(StrA, nullptr, StrD, nullptr) > 0); - always_check(TCString::Copy(StrD, IGNORE_SIZE, StrA, IGNORE_SIZE) != nullptr); + always_check(TCString::Copy(StrD, nullptr, StrA, nullptr) != nullptr); - always_check(TCString::Compare(StrA, BUFFER_SIZE, StrD, BUFFER_SIZE) == 0); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrD, IGNORE_SIZE) == 0); + always_check(TCString::Compare(StrA, EndA , StrD, EndD ) == 0); + always_check(TCString::Compare(StrA, nullptr, StrD, nullptr) == 0); Memory::Memzero(StrC); Memory::Memzero(StrD); - always_check(TCString::Copy(StrD, 4, StrA, IGNORE_SIZE) == nullptr); + always_check(TCString::Copy(StrD, &StrD[4], StrA, nullptr) == nullptr); - always_check(TCString::Compare(StrC, BUFFER_SIZE, StrD, BUFFER_SIZE) == 0); - always_check(TCString::Compare(StrC, IGNORE_SIZE, StrD, IGNORE_SIZE) == 0); + always_check(TCString::Compare(StrC, EndC , StrD, EndD ) == 0); + always_check(TCString::Compare(StrC, nullptr, StrD, nullptr) == 0); - always_check(TCString::Copy(StrD, IGNORE_SIZE, StrA, 4) != nullptr); + always_check(TCString::Copy(StrD, nullptr, StrA, &StrA[4]) != nullptr); - always_check(TCString::Length(StrD, IGNORE_SIZE) == 4); + always_check(TCString::Length(StrD, nullptr) == 4); - always_check(TCString::Compare(StrA, 4, StrD, 4) == 0); - always_check(TCString::Compare(StrA, IGNORE_SIZE, StrD, IGNORE_SIZE) > 0); + always_check(TCString::Compare(StrA, &StrA[4], StrD, &StrD[4]) == 0); + always_check(TCString::Compare(StrA, nullptr , StrD, nullptr ) > 0); - always_check(TCString::Copy( StrB, IGNORE_SIZE, LITERAL(T, "World!"), 5) != nullptr); - always_check(TCString::Compare(StrB, IGNORE_SIZE, LITERAL(T, "World" ), IGNORE_SIZE) == 0); + const T* PtrC = LITERAL(T, "World!"); + + always_check(TCString::Copy( StrB, nullptr, PtrC, &PtrC[5]) != nullptr); + always_check(TCString::Compare(StrB, nullptr, LITERAL(T, "World"), nullptr ) == 0); Memory::Memzero(StrD); - always_check(TCString::Cat(StrD, 8, StrA, IGNORE_SIZE) != nullptr); - always_check(TCString::Cat(StrD, 8, LITERAL(T, " "), IGNORE_SIZE) != nullptr); - always_check(TCString::Cat(StrD, 8, StrB, IGNORE_SIZE) == nullptr); + always_check(TCString::Cat(StrD, &StrD[8], StrA, nullptr) != nullptr); + always_check(TCString::Cat(StrD, &StrD[8], LITERAL(T, " "), nullptr) != nullptr); + always_check(TCString::Cat(StrD, &StrD[8], StrB, nullptr) == nullptr); - always_check(TCString::Compare(StrD, IGNORE_SIZE, LITERAL(T, "Hello "), IGNORE_SIZE) == 0); + always_check(TCString::Compare(StrD, nullptr, LITERAL(T, "Hello "), nullptr) == 0); Memory::Memzero(StrD); - always_check(TCString::Cat(StrD, IGNORE_SIZE, StrA, IGNORE_SIZE) != nullptr); - always_check(TCString::Cat(StrD, IGNORE_SIZE, LITERAL(T, " "), IGNORE_SIZE) != nullptr); - always_check(TCString::Cat(StrD, IGNORE_SIZE, StrB, IGNORE_SIZE) != nullptr); + always_check(TCString::Cat(StrD, nullptr, StrA, nullptr) != nullptr); + always_check(TCString::Cat(StrD, nullptr, LITERAL(T, " "), nullptr) != nullptr); + always_check(TCString::Cat(StrD, nullptr, StrB, nullptr) != nullptr); - always_check(TCString::Compare(StrD, IGNORE_SIZE, LITERAL(T, "Hello World"), IGNORE_SIZE) == 0); + always_check(TCString::Compare(StrD, nullptr, LITERAL(T, "Hello World"), nullptr) == 0); - always_check(TCString::Copy(StrA, IGNORE_SIZE, LITERAL(T, "Hello"), IGNORE_SIZE) != nullptr); + always_check(TCString::Copy(StrA, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr); - always_check(TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5); - always_check(TCString::Find(StrA, BUFFER_SIZE, [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5); - always_check(TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, 'o'); }) == StrA + 4); - always_check(TCString::Find(StrA, 4, [](T A) { return A == LITERAL(T, 'o'); }) == nullptr); + always_check(TCString::Find(StrA, nullptr , [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5); + always_check(TCString::Find(StrA, EndA , [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5); + always_check(TCString::Find(StrA, nullptr , [](T A) { return A == LITERAL(T, 'o'); }) == StrA + 4); + always_check(TCString::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); }) == nullptr); - always_check(TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, 'o'); }) - == TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'o'); }) + == TCString::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); - always_check(TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, 'l'); }) - != TCString::Find(StrA, IGNORE_SIZE, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'l'); }) + != TCString::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); - always_check(TCString::Find(StrA, BUFFER_SIZE, [](T A) { return A == LITERAL(T, 'o'); }) - == TCString::Find(StrA, BUFFER_SIZE, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'o'); }) + == TCString::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); - always_check(TCString::Find(StrA, BUFFER_SIZE, [](T A) { return A == LITERAL(T, 'l'); }) - != TCString::Find(StrA, BUFFER_SIZE, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'l'); }) + != TCString::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); - always_check(TCString::Find(StrA, 4, [](T A) { return A == LITERAL(T, 'o'); }) - == TCString::Find(StrA, 4, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); }) + == TCString::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd)); - always_check(TCString::Find(StrA, 3, [](T A) { return A == LITERAL(T, 'l'); }) - == TCString::Find(StrA, 3, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); + always_check(TCString::Find(StrA, &StrA[3], [](T A) { return A == LITERAL(T, 'l'); }) + == TCString::Find(StrA, &StrA[3], [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, '\0')) == StrA + 5); - always_check(TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, '\0')) == StrA + 5); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, 'o')) == StrA + 4); - always_check(TCString::FindChar(StrA, 4, LITERAL(T, 'o')) == nullptr); + always_check(TCString::FindChar(StrA, nullptr , LITERAL(T, '\0')) == StrA + 5); + always_check(TCString::FindChar(StrA, EndA , LITERAL(T, '\0')) == StrA + 5); + always_check(TCString::FindChar(StrA, nullptr , LITERAL(T, 'o')) == StrA + 4); + always_check(TCString::FindChar(StrA, &StrA[4], LITERAL(T, 'o')) == nullptr); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, 'o')) - == TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, 'o'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, nullptr, LITERAL(T, 'o')) + == TCString::FindChar(StrA, nullptr, LITERAL(T, 'o'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, 'l')) - != TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, 'l'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, nullptr, LITERAL(T, 'l')) + != TCString::FindChar(StrA, nullptr, LITERAL(T, 'l'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, 'o')) - == TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, 'o'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, EndA, LITERAL(T, 'o')) + == TCString::FindChar(StrA, EndA, LITERAL(T, 'o'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, 'l')) - != TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, 'l'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, EndA, LITERAL(T, 'l')) + != TCString::FindChar(StrA, EndA, LITERAL(T, 'l'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, 4, LITERAL(T, 'o')) - == TCString::FindChar(StrA, 4, LITERAL(T, 'o'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, &StrA[4], LITERAL(T, 'o')) + == TCString::FindChar(StrA, &StrA[4], LITERAL(T, 'o'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, 3, LITERAL(T, 'l')) - == TCString::FindChar(StrA, 3, LITERAL(T, 'l'), ESearchDirection::FromEnd)); + always_check(TCString::FindChar(StrA, &StrA[3], LITERAL(T, 'l')) + == TCString::FindChar(StrA, &StrA[3], LITERAL(T, 'l'), ESearchDirection::FromEnd)); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, ""), IGNORE_SIZE) == nullptr); - always_check(TCString::FindChar(StrA, BUFFER_SIZE, LITERAL(T, ""), IGNORE_SIZE) == nullptr); - always_check(TCString::FindChar(StrA, IGNORE_SIZE, LITERAL(T, "o"), IGNORE_SIZE) == StrA + 4); - always_check(TCString::FindChar(StrA, 4, LITERAL(T, "o"), IGNORE_SIZE) == nullptr); + always_check(TCString::FindChar(StrA, nullptr , LITERAL(T, ""), nullptr) == nullptr); + always_check(TCString::FindChar(StrA, EndA , LITERAL(T, ""), nullptr) == nullptr); + always_check(TCString::FindChar(StrA, nullptr , LITERAL(T, "o"), nullptr) == StrA + 4); + always_check(TCString::FindChar(StrA, &StrA[4], LITERAL(T, "o"), nullptr) == nullptr); - always_check(TCString::Copy(StrA, IGNORE_SIZE, LITERAL(T, "HIH"), IGNORE_SIZE) != nullptr); + always_check(TCString::Copy(StrA, nullptr, LITERAL(T, "HIH"), nullptr) != nullptr); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, '\0')) == StrA); - always_check(TCString::FindNotChar(StrA, BUFFER_SIZE, LITERAL(T, '\0')) == StrA); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, 'I')) == StrA); - always_check(TCString::FindNotChar(StrA, 2, LITERAL(T, 'I')) == StrA); + always_check(TCString::FindNotChar(StrA, nullptr , LITERAL(T, '\0')) == StrA); + always_check(TCString::FindNotChar(StrA, EndA , LITERAL(T, '\0')) == StrA); + always_check(TCString::FindNotChar(StrA, nullptr , LITERAL(T, 'I')) == StrA); + always_check(TCString::FindNotChar(StrA, &StrA[2], LITERAL(T, 'I')) == StrA); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2); - always_check(TCString::FindNotChar(StrA, BUFFER_SIZE, LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 3); - always_check(TCString::FindNotChar(StrA, 2, LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 0); + always_check(TCString::FindNotChar(StrA, nullptr , LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2); + always_check(TCString::FindNotChar(StrA, EndA , LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2); + always_check(TCString::FindNotChar(StrA, nullptr , LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 3); + always_check(TCString::FindNotChar(StrA, &StrA[2], LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 0); - always_check(TCString::Copy(StrA, IGNORE_SIZE, LITERAL(T, "HIJIH"), IGNORE_SIZE) != nullptr); + always_check(TCString::Copy(StrA, nullptr, LITERAL(T, "HIJIH"), nullptr) != nullptr); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, "HIJ"), IGNORE_SIZE) == nullptr); - always_check(TCString::FindNotChar(StrA, BUFFER_SIZE, LITERAL(T, "HIJ"), IGNORE_SIZE) == nullptr); + always_check(TCString::FindNotChar(StrA, nullptr, LITERAL(T, "HIJ"), nullptr) == nullptr); + always_check(TCString::FindNotChar(StrA, EndA , LITERAL(T, "HIJ"), nullptr) == nullptr); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, "H J"), IGNORE_SIZE) == StrA + 1); - always_check(TCString::FindNotChar(StrA, BUFFER_SIZE, LITERAL(T, "H J"), IGNORE_SIZE) == StrA + 1); + always_check(TCString::FindNotChar(StrA, nullptr, LITERAL(T, "H J"), nullptr) == StrA + 1); + always_check(TCString::FindNotChar(StrA, EndA , LITERAL(T, "H J"), nullptr) == StrA + 1); - always_check(TCString::FindNotChar(StrA, IGNORE_SIZE, LITERAL(T, "H J"), IGNORE_SIZE, ESearchDirection::FromEnd) == StrA + 3); - always_check(TCString::FindNotChar(StrA, BUFFER_SIZE, LITERAL(T, "H J"), IGNORE_SIZE, ESearchDirection::FromEnd) == StrA + 3); + always_check(TCString::FindNotChar(StrA, nullptr, LITERAL(T, "H J"), nullptr, ESearchDirection::FromEnd) == StrA + 3); + always_check(TCString::FindNotChar(StrA, EndA , LITERAL(T, "H J"), nullptr, ESearchDirection::FromEnd) == StrA + 3); - always_check(TCString::Copy(StrA, IGNORE_SIZE, LITERAL(T, "01234567890123456789"), IGNORE_SIZE) != nullptr); + always_check(TCString::Copy(StrA, nullptr, LITERAL(T, "01234567890123456789"), nullptr) != nullptr); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, ""), IGNORE_SIZE) == StrA); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, ""), IGNORE_SIZE, ESearchDirection::FromEnd) == StrA + 20); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "345"), IGNORE_SIZE) == StrA + 3); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "345"), IGNORE_SIZE, ESearchDirection::FromEnd) == StrA + 13); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "012345678901234567890123456789"), IGNORE_SIZE) == nullptr); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "012345678901234567890123456789"), IGNORE_SIZE, ESearchDirection::FromEnd) == nullptr); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "ABC"), IGNORE_SIZE) == nullptr); - always_check(TCString::FindString(StrA, IGNORE_SIZE, LITERAL(T, "ABC"), IGNORE_SIZE, ESearchDirection::FromEnd) == nullptr); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, ""), nullptr) == StrA); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, ""), nullptr, ESearchDirection::FromEnd) == StrA + 20); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "345"), nullptr) == StrA + 3); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "345"), nullptr, ESearchDirection::FromEnd) == StrA + 13); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "012345678901234567890123456789"), nullptr) == nullptr); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "012345678901234567890123456789"), nullptr, ESearchDirection::FromEnd) == nullptr); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "ABC"), nullptr) == nullptr); + always_check(TCString::FindString(StrA, nullptr, LITERAL(T, "ABC"), nullptr, ESearchDirection::FromEnd) == nullptr); }; TestTCString(InPlaceType); diff --git a/Redcraft.Utility/Source/Public/String/CString.h b/Redcraft.Utility/Source/Public/String/CString.h index c9eb556..72a6405 100644 --- a/Redcraft.Utility/Source/Public/String/CString.h +++ b/Redcraft.Utility/Source/Public/String/CString.h @@ -18,9 +18,6 @@ NAMESPACE_MODULE_BEGIN(Utility) #pragma warning(push) #pragma warning(disable : 4996) -/** Explicit instructions to ignore buffer size, but may lead to buffer overflow attacks. */ -constexpr size_t IGNORE_SIZE = -1; - /** Determines search direction for string operations. */ enum class ESearchDirection { @@ -37,14 +34,12 @@ struct TCString { using CharType = T; - /** Copies one string to another. The size is used only for buffer safety and will not append null characters to the destination. */ - FORCEINLINE static CharType* Copy(CharType* Destination, size_t DestinationSize, const CharType* Source, size_t SourceSize) + /** 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) { checkf(Destination && Source, TEXT("Read access violation. Destination and source must not be nullptr.")); - checkf(DestinationSize != 0 && SourceSize != 0, TEXT("Illegal buffer size. DestinationSize and SourceSize must not be zero.")); - - if (DestinationSize == IGNORE_SIZE && SourceSize == IGNORE_SIZE) + if (DestinationEnd == nullptr && SourceEnd == nullptr) { if constexpr (CSameAs) { @@ -56,9 +51,9 @@ struct TCString } } - size_t SourceLength = TCString::Length(Source, SourceSize); + size_t SourceLength = TCString::Length(Source, SourceEnd); - if (DestinationSize != IGNORE_SIZE && DestinationSize < SourceLength + 1) + if (DestinationEnd != nullptr && Destination + SourceLength + 1 > DestinationEnd) { return nullptr; } @@ -70,14 +65,12 @@ struct TCString return Destination; } - /** Concatenates two strings. The size is used only for buffer safety and will not append null characters to the destination. */ - FORCEINLINE static CharType* Cat(CharType* Destination, size_t DestinationSize, const CharType* Source, size_t SourceSize) + /** 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) { checkf(Destination && Source, TEXT("Read access violation. Destination and source must not be nullptr.")); - checkf(DestinationSize != 0 && SourceSize != 0, TEXT("Illegal buffer size. DestinationSize and SourceSize must not be zero.")); - - if (DestinationSize == IGNORE_SIZE && SourceSize == IGNORE_SIZE) + if (DestinationEnd == nullptr && SourceEnd == nullptr) { if constexpr (CSameAs) { @@ -89,21 +82,19 @@ struct TCString } } - size_t DestinationLength = TCString::Length(Destination, DestinationSize); + size_t DestinationLength = TCString::Length(Destination, DestinationEnd); - CharType* Result = Copy(Destination + DestinationLength, DestinationSize - DestinationLength, Source, SourceSize); + CharType* Result = Copy(Destination + DestinationLength, DestinationEnd, Source, SourceEnd); return Result ? Destination : nullptr; } /** @return The length of a given string. The maximum length is the buffer size. */ - NODISCARD FORCEINLINE static size_t Length(const CharType* InString, size_t SourceSize) + NODISCARD FORCEINLINE static size_t Length(const CharType* InString, const CharType* End) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(SourceSize != 0, TEXT("Illegal buffer size. SourceSize must not be zero.")); - - if (SourceSize == IGNORE_SIZE) + if (End == nullptr) { if constexpr (CSameAs) { @@ -117,24 +108,21 @@ struct TCString size_t Result = 0; - while (*InString != LITERAL(CharType, '\0') && SourceSize != 0) + while (*InString != LITERAL(CharType, '\0') && InString != End) { ++Result; ++InString; - --SourceSize; } return Result; } - /** Compares two strings. The size is used only for buffer safety not for comparison. */ - NODISCARD FORCEINLINE static strong_ordering Compare(const CharType* LHS, size_t LHSSize, const CharType* RHS, size_t RHSSize) + /** 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) { checkf(LHS && RHS, TEXT("Read access violation. LHS and RHS must not be nullptr.")); - checkf(LHSSize != 0 && RHSSize != 0, TEXT("Illegal buffer size. LHSSize and RHSSize must not be zero.")); - - if (LHSSize == IGNORE_SIZE && RHSSize == IGNORE_SIZE) + if (LHSEnd == nullptr && RHSEnd == nullptr) { if constexpr (CSameAs) { @@ -146,7 +134,7 @@ struct TCString } } - while (LHSSize != 0 && RHSSize != 0) + while (LHS != LHSEnd && RHS != RHSEnd) { if (*LHS != *RHS) { @@ -160,24 +148,29 @@ struct TCString ++LHS; ++RHS; - --LHSSize; - --RHSSize; } - return LHSSize <=> RHSSize; + 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; } - /** 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 size is used only for buffer safety. */ + /** 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. */ template F> - NODISCARD FORCEINLINE static const CharType* Find(const CharType* InString, size_t BufferSize, F&& InPredicate, ESearchDirection SearchDirection = ESearchDirection::FromStart) + NODISCARD FORCEINLINE static const CharType* Find(const CharType* InString, const CharType* End, F&& InPredicate, ESearchDirection SearchDirection = ESearchDirection::FromStart) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); - if (SearchDirection == ESearchDirection::FromStart) { - while (BufferSize != 0) + while (InString != End) { if (InvokeResult(Forward(InPredicate), *InString)) { @@ -187,52 +180,47 @@ struct TCString if (*InString == LITERAL(CharType, '\0')) break; ++InString; - --BufferSize; } } else { - size_t Index = TCString::Length(InString, BufferSize); + size_t Index = TCString::Length(InString, End); - if (Index == BufferSize) --Index; + const CharType* Iter = InString + Index; - while (true) + if (Iter == End) --Iter; + + while (Iter != InString - 1) { - if (InvokeResult(Forward(InPredicate), InString[Index])) + if (InvokeResult(Forward(InPredicate), *Iter)) { - return InString + Index; + return Iter; } - if (Index == 0) break; - - --Index; + --Iter; } } return nullptr; } - /** 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 size is used only for buffer safety. */ + /** 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. */ template F> - NODISCARD FORCEINLINE static CharType* Find( CharType* InString, size_t BufferSize, F&& InPredicate, ESearchDirection SearchDirection = ESearchDirection::FromStart) + NODISCARD FORCEINLINE static CharType* Find( CharType* InString, const CharType* End, F&& InPredicate, ESearchDirection SearchDirection = ESearchDirection::FromStart) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::Find(const_cast(InString), BufferSize, Forward(InPredicate), SearchDirection)); + return const_cast(TCString::Find(const_cast(InString), End, Forward(InPredicate), SearchDirection)); } - /** Finds the first or last occurrence of a character. The terminating null character is considered to be a part of the string. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static const CharType* FindChar(const CharType* InString, size_t BufferSize, CharType Character, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); - - if (BufferSize == IGNORE_SIZE) + if (End == nullptr) { if constexpr (CSameAs) { @@ -244,29 +232,25 @@ struct TCString } } - return TCString::Find(InString, BufferSize, [Character](CharType C) { return C == Character; }, SearchDirection); + return TCString::Find(InString, End, [Character](CharType C) { return C == Character; }, SearchDirection); } - /** Finds the first or last occurrence of a character. The terminating null character is considered to be a part of the string. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static CharType* FindChar( CharType* InString, size_t BufferSize, CharType Character, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::FindChar(const_cast(InString), BufferSize, Character, SearchDirection)); + return const_cast(TCString::FindChar(const_cast(InString), End, Character, SearchDirection)); } - /** Finds the first or last occurrence of a character in a charset. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static const CharType* FindChar(const CharType* InString, size_t BufferSize, const CharType* Charset, size_t CharsetSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Charset, TEXT("Read access violation. InString and Charset must not be nullptr.")); - checkf(BufferSize != 0 && CharsetSize != 0, TEXT("Illegal buffer size. BufferSize and CharsetSize must not be zero.")); - - if (BufferSize == IGNORE_SIZE && CharsetSize == IGNORE_SIZE && SearchDirection == ESearchDirection::FromStart) + if (End == nullptr && CharsetEnd == nullptr && SearchDirection == ESearchDirection::FromStart) { if constexpr (CSameAs) { @@ -280,41 +264,39 @@ struct TCString return TCString::Find ( - InString, BufferSize, - [Charset, CharsetSize](CharType C) + InString, End, + [Charset, CharsetEnd](CharType C) { - const CharType* Result = TCString::FindChar(Charset, CharsetSize, C); + const CharType* Result = TCString::FindChar(Charset, CharsetEnd, C); return Result != nullptr && *Result != LITERAL(CharType, '\0'); }, SearchDirection ); } - /** Finds the first or last occurrence of a character in a charset. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static CharType* FindChar( CharType* InString, size_t BufferSize, const CharType* Charset, size_t CharsetSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Charset, TEXT("Read access violation. InString and Charset must not be nullptr.")); - checkf(BufferSize != 0 && CharsetSize != 0, TEXT("Illegal buffer size. BufferSize and CharsetSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::FindChar(const_cast(InString), BufferSize, Charset, CharsetSize, SearchDirection)); + return const_cast(TCString::FindChar(const_cast(InString), End, Charset, CharsetEnd, SearchDirection)); } - /** 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 size is used only for buffer safety. */ - NODISCARD FORCEINLINE static const CharType* FindNotChar(const CharType* InString, size_t BufferSize, CharType Character, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); + if (InString == End) return nullptr; if (Character == LITERAL(CharType, '\0') && SearchDirection == ESearchDirection::FromStart) { return *InString != LITERAL(CharType, '\0') ? InString : nullptr; } - if (BufferSize == IGNORE_SIZE && SearchDirection == ESearchDirection::FromStart) + if (End == nullptr && SearchDirection == ESearchDirection::FromStart) { if constexpr (CSameAs) { @@ -328,29 +310,25 @@ struct TCString } } - return TCString::Find(InString, BufferSize, [Character](CharType C) { return C != Character; }, SearchDirection); + return TCString::Find(InString, End, [Character](CharType C) { return C != Character; }, SearchDirection); } - /** 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 size is used only for buffer safety. */ - NODISCARD FORCEINLINE static CharType* FindNotChar( CharType* InString, size_t BufferSize, CharType Character, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString, TEXT("Read access violation. InString must not be nullptr.")); - checkf(BufferSize != 0, TEXT("Illegal buffer size. BufferSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::FindNotChar(const_cast(InString), BufferSize, Character, SearchDirection)); + return const_cast(TCString::FindNotChar(const_cast(InString), End, Character, SearchDirection)); } - /** Finds the first or last occurrence of a character that is not in the given charset. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static const CharType* FindNotChar(const CharType* InString, size_t BufferSize, const CharType* Charset, size_t CharsetSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Charset, TEXT("Read access violation. InString and Charset must not be nullptr.")); - checkf(BufferSize != 0 && CharsetSize != 0, TEXT("Illegal buffer size. BufferSize and CharsetSize must not be zero.")); - - if (BufferSize == IGNORE_SIZE && CharsetSize == IGNORE_SIZE && SearchDirection == ESearchDirection::FromStart) + if (End == nullptr && CharsetEnd == nullptr && SearchDirection == ESearchDirection::FromStart) { if constexpr (CSameAs) { @@ -364,34 +342,38 @@ struct TCString } } - return TCString::Find(InString, BufferSize, [Charset, CharsetSize](CharType C) { return TCString::FindChar(Charset, CharsetSize, C) == nullptr; }, SearchDirection); + return TCString::Find(InString, End, [Charset, CharsetEnd](CharType C) { return TCString::FindChar(Charset, CharsetEnd, C) == nullptr; }, SearchDirection); } - /** Finds the first or last occurrence of a character that is not in the given charset. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static CharType* FindNotChar( CharType* InString, size_t BufferSize, const CharType* Charset, size_t CharsetSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Charset, TEXT("Read access violation. InString and Charset must not be nullptr.")); - checkf(BufferSize != 0 && CharsetSize != 0, TEXT("Illegal buffer size. BufferSize and CharsetSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::FindNotChar(const_cast(InString), BufferSize, Charset, CharsetSize, SearchDirection)); + return const_cast(TCString::FindNotChar(const_cast(InString), End, Charset, CharsetEnd, SearchDirection)); } - /** Finds the first or last occurrence of a substring. The size is used only for buffer safety. */ - NODISCARD static const CharType* FindString(const CharType* InString, size_t BufferSize, const CharType* Substring, size_t SubstringSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Substring, TEXT("Read access violation. InString and Substring must not be nullptr.")); - checkf(BufferSize != 0 && SubstringSize != 0, TEXT("Illegal buffer size. BufferSize and SubstringSize must not be zero.")); + if (InString == End) return nullptr; - if (*Substring == LITERAL(CharType, '\0')) + if (Substring == SubstringEnd || *Substring == LITERAL(CharType, '\0')) { - return SearchDirection == ESearchDirection::FromStart ? InString : InString + TCString::Length(InString, BufferSize); + if (SearchDirection == ESearchDirection::FromStart) return InString; + else + { + const CharType* Iter = InString + TCString::Length(InString, End); + if (Iter == End) --Iter; + return Iter; + } } - if (BufferSize == IGNORE_SIZE && SubstringSize == IGNORE_SIZE && SearchDirection == ESearchDirection::FromStart) + if (End == nullptr && SubstringEnd == nullptr && SearchDirection == ESearchDirection::FromStart) { if constexpr (CSameAs) { @@ -403,8 +385,8 @@ struct TCString } } - size_t StringLength = TCString::Length(InString, BufferSize); - size_t SubstringLength = TCString::Length(Substring, SubstringSize); + size_t StringLength = TCString::Length(InString, End); + size_t SubstringLength = TCString::Length(Substring, SubstringEnd); if (StringLength < SubstringLength) { @@ -415,7 +397,7 @@ struct TCString { for (size_t Index = 0; Index < StringLength - SubstringLength; ++Index) { - if (TCString::Compare(InString + Index, SubstringLength, Substring, SubstringLength) == 0) + if (TCString::Compare(InString + Index, InString + Index + SubstringLength, Substring, Substring + SubstringLength) == 0) { return InString + Index; } @@ -425,7 +407,7 @@ struct TCString { for (size_t Index = StringLength - SubstringLength; Index > 0; --Index) { - if (TCString::Compare(InString + Index, SubstringLength, Substring, SubstringLength) == 0) + if (TCString::Compare(InString + Index, InString + Index + SubstringLength, Substring, Substring + SubstringLength) == 0) { return InString + Index; } @@ -435,16 +417,14 @@ struct TCString return nullptr; } - /** Finds the first or last occurrence of a substring. The size is used only for buffer safety. */ - NODISCARD FORCEINLINE static CharType* FindString( CharType* InString, size_t BufferSize, const CharType* Substring, size_t SubstringSize, ESearchDirection SearchDirection = ESearchDirection::FromStart) + /** 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) { checkf(InString && Substring, TEXT("Read access violation. InString and Substring must not be nullptr.")); - checkf(BufferSize != 0 && SubstringSize != 0, TEXT("Illegal buffer size. BufferSize and SubstringSize must not be zero.")); - check_no_recursion(); - return const_cast(TCString::FindString(const_cast(InString), BufferSize, Substring, SubstringSize, SearchDirection)); + return const_cast(TCString::FindString(const_cast(InString), End, Substring, SubstringEnd, SearchDirection)); } };