Compare commits
3 Commits
3df9122ef0
...
93d0ceb2b9
Author | SHA1 | Date | |
---|---|---|---|
93d0ceb2b9 | |||
e5cd9d90a1 | |||
cc318f693f |
@ -2,7 +2,7 @@
|
||||
|
||||
#include "String/Char.h"
|
||||
#include "Memory/Memory.h"
|
||||
#include "String/CString.h"
|
||||
#include "String/StringView.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
@ -236,168 +236,82 @@ void TestCString()
|
||||
{
|
||||
auto TestTCString = []<typename T>(TInPlaceType<T>)
|
||||
{
|
||||
constexpr size_t BUFFER_SIZE = 64;
|
||||
{
|
||||
TStringView<T> Empty;
|
||||
|
||||
T StrA[BUFFER_SIZE];
|
||||
T StrB[BUFFER_SIZE];
|
||||
T StrC[BUFFER_SIZE];
|
||||
T StrD[BUFFER_SIZE];
|
||||
always_check(Empty == LITERAL(T, ""));
|
||||
|
||||
const T* EndA = &StrA[BUFFER_SIZE];
|
||||
const T* EndB = &StrB[BUFFER_SIZE];
|
||||
const T* EndC = &StrC[BUFFER_SIZE];
|
||||
const T* EndD = &StrD[BUFFER_SIZE];
|
||||
TStringView ViewI = LITERAL(T, "#Hello, World! Goodbye, World!#");
|
||||
|
||||
always_check(TCString<T>::Copy(StrA, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr);
|
||||
always_check(TCString<T>::Copy(StrB, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr);
|
||||
always_check(TCString<T>::Copy(StrC, nullptr, LITERAL(T, "World"), nullptr) != nullptr);
|
||||
always_check(TCString<T>::Copy(StrD, nullptr, LITERAL(T, " "), nullptr) != nullptr);
|
||||
ViewI.RemovePrefix(1);
|
||||
ViewI.RemoveSuffix(1);
|
||||
|
||||
always_check(TCString<T>::Length(StrA, &StrA[4]) == 4);
|
||||
always_check(TCString<T>::Length(StrA, EndA ) == 5);
|
||||
always_check(TCString<T>::Length(StrA, nullptr ) == 5);
|
||||
T Buffer[64];
|
||||
|
||||
const T* PtrA = LITERAL(T, "Hel");
|
||||
const T* PtrB = LITERAL(T, "Hello");
|
||||
Memory::Memzero(Buffer);
|
||||
|
||||
always_check(TCString<T>::Compare(PtrA, nullptr, PtrB, &PtrB[3]) == 0);
|
||||
ViewI.Copy(Buffer);
|
||||
|
||||
always_check(TCString<T>::Compare(StrA, nullptr, StrB, nullptr) == TCString<T>::Compare(StrA, EndA, StrB, EndB));
|
||||
always_check(TCString<T>::Compare(StrA, nullptr, StrC, nullptr) == TCString<T>::Compare(StrA, EndA, StrC, EndC));
|
||||
always_check(TCString<T>::Compare(StrA, nullptr, StrC, nullptr) < 0);
|
||||
TStringView ViewII = Buffer;
|
||||
|
||||
Memory::Memzero(StrD);
|
||||
always_check(ViewI == LITERAL(T, "Hello, World! Goodbye, World!"));
|
||||
always_check(ViewII == LITERAL(T, "Hello, World! Goodbye, World!"));
|
||||
|
||||
always_check(TCString<T>::Compare(StrA, EndA , StrD, EndD ) > 0);
|
||||
always_check(TCString<T>::Compare(StrA, nullptr, StrD, nullptr) > 0);
|
||||
TStringView<T> ViewA(ViewI.Begin(), 13);
|
||||
TStringView<T> ViewB(ViewI.Begin(), ViewI.End());
|
||||
TStringView<T> ViewC(&Buffer[0], 13);
|
||||
TStringView<T> ViewD(&Buffer[0]);
|
||||
|
||||
always_check(TCString<T>::Copy(StrD, nullptr, StrA, nullptr) != nullptr);
|
||||
always_check(ViewA == LITERAL(T, "Hello, World!"));
|
||||
always_check(ViewB == LITERAL(T, "Hello, World! Goodbye, World!"));
|
||||
always_check(ViewC == LITERAL(T, "Hello, World!"));
|
||||
always_check(ViewD == LITERAL(T, "Hello, World! Goodbye, World!"));
|
||||
}
|
||||
|
||||
always_check(TCString<T>::Compare(StrA, EndA , StrD, EndD ) == 0);
|
||||
always_check(TCString<T>::Compare(StrA, nullptr, StrD, nullptr) == 0);
|
||||
{
|
||||
TStringView View = LITERAL(T, "Hello, World! Goodbye, World!");
|
||||
|
||||
Memory::Memzero(StrC);
|
||||
Memory::Memzero(StrD);
|
||||
always_check( View.StartsWith(LITERAL(T, "Hello, World!")));
|
||||
always_check(!View.StartsWith(LITERAL(T, "Goodbye, World!")));
|
||||
always_check( View.StartsWith(LITERAL(T, 'H')));
|
||||
always_check(!View.StartsWith(LITERAL(T, 'G')));
|
||||
always_check(!View.EndsWith(LITERAL(T, "Hello, World!")));
|
||||
always_check( View.EndsWith(LITERAL(T, "Goodbye, World!")));
|
||||
always_check( View.EndsWith(LITERAL(T, '!')));
|
||||
always_check(!View.EndsWith(LITERAL(T, '?')));
|
||||
always_check( View.Contains(LITERAL(T, "Hello, World!")));
|
||||
always_check( View.Contains(LITERAL(T, "Goodbye, World!")));
|
||||
always_check( View.Contains(LITERAL(T, '!')));
|
||||
always_check(!View.Contains(LITERAL(T, '?')));
|
||||
}
|
||||
|
||||
always_check(TCString<T>::Copy(StrD, &StrD[4], StrA, nullptr) == nullptr);
|
||||
{
|
||||
TStringView View = LITERAL(T, "Hello, World! Goodbye, World!");
|
||||
|
||||
always_check(TCString<T>::Compare(StrC, EndC , StrD, EndD ) == 0);
|
||||
always_check(TCString<T>::Compare(StrC, nullptr, StrD, nullptr) == 0);
|
||||
always_check(View.Find(LITERAL(T, "")) == 0);
|
||||
always_check(View.Find(LITERAL(T, "World")) == 7);
|
||||
always_check(View.Find(LITERAL(T, 'l')) == 2);
|
||||
always_check(View.RFind(LITERAL(T, "")) == 29);
|
||||
always_check(View.RFind(LITERAL(T, "World")) == 23);
|
||||
always_check(View.RFind(LITERAL(T, 'l')) == 26);
|
||||
|
||||
always_check(TCString<T>::Copy(StrD, nullptr, StrA, &StrA[4]) != nullptr);
|
||||
always_check(View.Find(LITERAL(T, ""), 13) == 13);
|
||||
always_check(View.Find(LITERAL(T, "World"), 13) == 23);
|
||||
always_check(View.Find(LITERAL(T, 'l'), 13) == 26);
|
||||
always_check(View.RFind(LITERAL(T, ""), 13) == 13);
|
||||
always_check(View.RFind(LITERAL(T, "World"), 13) == 7);
|
||||
always_check(View.RFind(LITERAL(T, 'l'), 13) == 10);
|
||||
|
||||
always_check(TCString<T>::Length(StrD, nullptr) == 4);
|
||||
always_check(View.FindFirstOf(LITERAL(T, "eor")) == 1);
|
||||
always_check(View.FindFirstOf(LITERAL(T, 'l')) == 2);
|
||||
always_check(View.FindLastOf(LITERAL(T, "eor")) == 25);
|
||||
always_check(View.FindLastOf(LITERAL(T, 'l')) == 26);
|
||||
|
||||
always_check(TCString<T>::Compare(StrA, &StrA[4], StrD, &StrD[4]) == 0);
|
||||
always_check(TCString<T>::Compare(StrA, nullptr , StrD, nullptr ) > 0);
|
||||
|
||||
const T* PtrC = LITERAL(T, "World!");
|
||||
|
||||
always_check(TCString<T>::Copy( StrB, nullptr, PtrC, &PtrC[5]) != nullptr);
|
||||
always_check(TCString<T>::Compare(StrB, nullptr, LITERAL(T, "World"), nullptr ) == 0);
|
||||
|
||||
Memory::Memzero(StrD);
|
||||
|
||||
always_check(TCString<T>::Cat(StrD, &StrD[8], StrA, nullptr) != nullptr);
|
||||
always_check(TCString<T>::Cat(StrD, &StrD[8], LITERAL(T, " "), nullptr) != nullptr);
|
||||
always_check(TCString<T>::Cat(StrD, &StrD[8], StrB, nullptr) == nullptr);
|
||||
|
||||
always_check(TCString<T>::Compare(StrD, nullptr, LITERAL(T, "Hello "), nullptr) == 0);
|
||||
|
||||
Memory::Memzero(StrD);
|
||||
|
||||
always_check(TCString<T>::Cat(StrD, nullptr, StrA, nullptr) != nullptr);
|
||||
always_check(TCString<T>::Cat(StrD, nullptr, LITERAL(T, " "), nullptr) != nullptr);
|
||||
always_check(TCString<T>::Cat(StrD, nullptr, StrB, nullptr) != nullptr);
|
||||
|
||||
always_check(TCString<T>::Compare(StrD, nullptr, LITERAL(T, "Hello World"), nullptr) == 0);
|
||||
|
||||
always_check(TCString<T>::Copy(StrA, nullptr, LITERAL(T, "Hello"), nullptr) != nullptr);
|
||||
|
||||
always_check(TCString<T>::Find(StrA, nullptr , [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5);
|
||||
always_check(TCString<T>::Find(StrA, EndA , [](T A) { return A == LITERAL(T, '\0'); }) == StrA + 5);
|
||||
always_check(TCString<T>::Find(StrA, nullptr , [](T A) { return A == LITERAL(T, 'o'); }) == StrA + 4);
|
||||
always_check(TCString<T>::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); }) == nullptr);
|
||||
|
||||
always_check(TCString<T>::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'o'); })
|
||||
== TCString<T>::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'l'); })
|
||||
!= TCString<T>::Find(StrA, nullptr, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'o'); })
|
||||
== TCString<T>::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'l'); })
|
||||
!= TCString<T>::Find(StrA, EndA, [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); })
|
||||
== TCString<T>::Find(StrA, &StrA[4], [](T A) { return A == LITERAL(T, 'o'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::Find(StrA, &StrA[3], [](T A) { return A == LITERAL(T, 'l'); })
|
||||
== TCString<T>::Find(StrA, &StrA[3], [](T A) { return A == LITERAL(T, 'l'); }, ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr , LITERAL(T, '\0')) == StrA + 5);
|
||||
always_check(TCString<T>::FindChar(StrA, EndA , LITERAL(T, '\0')) == StrA + 5);
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr , LITERAL(T, 'o')) == StrA + 4);
|
||||
always_check(TCString<T>::FindChar(StrA, &StrA[4], LITERAL(T, 'o')) == nullptr);
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr, LITERAL(T, 'o'))
|
||||
== TCString<T>::FindChar(StrA, nullptr, LITERAL(T, 'o'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr, LITERAL(T, 'l'))
|
||||
!= TCString<T>::FindChar(StrA, nullptr, LITERAL(T, 'l'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, EndA, LITERAL(T, 'o'))
|
||||
== TCString<T>::FindChar(StrA, EndA, LITERAL(T, 'o'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, EndA, LITERAL(T, 'l'))
|
||||
!= TCString<T>::FindChar(StrA, EndA, LITERAL(T, 'l'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, &StrA[4], LITERAL(T, 'o'))
|
||||
== TCString<T>::FindChar(StrA, &StrA[4], LITERAL(T, 'o'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, &StrA[3], LITERAL(T, 'l'))
|
||||
== TCString<T>::FindChar(StrA, &StrA[3], LITERAL(T, 'l'), ESearchDirection::FromEnd));
|
||||
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr , LITERAL(T, ""), nullptr) == nullptr);
|
||||
always_check(TCString<T>::FindChar(StrA, EndA , LITERAL(T, ""), nullptr) == nullptr);
|
||||
always_check(TCString<T>::FindChar(StrA, nullptr , LITERAL(T, "o"), nullptr) == StrA + 4);
|
||||
always_check(TCString<T>::FindChar(StrA, &StrA[4], LITERAL(T, "o"), nullptr) == nullptr);
|
||||
|
||||
always_check(TCString<T>::Copy(StrA, nullptr, LITERAL(T, "HIH"), nullptr) != nullptr);
|
||||
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr , LITERAL(T, '\0')) == StrA);
|
||||
always_check(TCString<T>::FindNotChar(StrA, EndA , LITERAL(T, '\0')) == StrA);
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr , LITERAL(T, 'I')) == StrA);
|
||||
always_check(TCString<T>::FindNotChar(StrA, &StrA[2], LITERAL(T, 'I')) == StrA);
|
||||
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr , LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2);
|
||||
always_check(TCString<T>::FindNotChar(StrA, EndA , LITERAL(T, '\0'), ESearchDirection::FromEnd) == StrA + 2);
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr , LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 3);
|
||||
always_check(TCString<T>::FindNotChar(StrA, &StrA[2], LITERAL(T, 'I'), ESearchDirection::FromEnd) == StrA + 0);
|
||||
|
||||
always_check(TCString<T>::Copy(StrA, nullptr, LITERAL(T, "HIJIH"), nullptr) != nullptr);
|
||||
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr, LITERAL(T, "HIJ"), nullptr) == nullptr);
|
||||
always_check(TCString<T>::FindNotChar(StrA, EndA , LITERAL(T, "HIJ"), nullptr) == nullptr);
|
||||
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr, LITERAL(T, "H J"), nullptr) == StrA + 1);
|
||||
always_check(TCString<T>::FindNotChar(StrA, EndA , LITERAL(T, "H J"), nullptr) == StrA + 1);
|
||||
|
||||
always_check(TCString<T>::FindNotChar(StrA, nullptr, LITERAL(T, "H J"), nullptr, ESearchDirection::FromEnd) == StrA + 3);
|
||||
always_check(TCString<T>::FindNotChar(StrA, EndA , LITERAL(T, "H J"), nullptr, ESearchDirection::FromEnd) == StrA + 3);
|
||||
|
||||
always_check(TCString<T>::Copy(StrA, nullptr, LITERAL(T, "01234567890123456789"), nullptr) != nullptr);
|
||||
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, ""), nullptr) == StrA);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, ""), nullptr, ESearchDirection::FromEnd) == StrA + 20);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "345"), nullptr) == StrA + 3);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "345"), nullptr, ESearchDirection::FromEnd) == StrA + 13);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "012345678901234567890123456789"), nullptr) == nullptr);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "012345678901234567890123456789"), nullptr, ESearchDirection::FromEnd) == nullptr);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "ABC"), nullptr) == nullptr);
|
||||
always_check(TCString<T>::FindString(StrA, nullptr, LITERAL(T, "ABC"), nullptr, ESearchDirection::FromEnd) == nullptr);
|
||||
always_check(View.FindFirstNotOf(LITERAL(T, "Hello! Goodbye!")) == 5);
|
||||
always_check(View.FindFirstNotOf(LITERAL(T, '!')) == 0);
|
||||
always_check(View.FindLastNotOf(LITERAL(T, "Hello! Goodbye!")) == 25);
|
||||
always_check(View.FindLastNotOf(LITERAL(T, '!')) == 27);
|
||||
}
|
||||
};
|
||||
|
||||
TestTCString(InPlaceType<char>);
|
||||
|
@ -17,14 +17,10 @@ NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
NAMESPACE_PRIVATE_BEGIN
|
||||
|
||||
template <CUnsignedIntegral InBlockType> requires (!CSameAs<InBlockType, bool>)
|
||||
using TDefaultBitsetAllocator = TInlineAllocator<(40 - 3 * sizeof(size_t)) / sizeof(InBlockType)>;
|
||||
|
||||
NAMESPACE_PRIVATE_END
|
||||
|
||||
template <CUnsignedIntegral InBlockType, CAllocator<InBlockType> Allocator = NAMESPACE_PRIVATE::TDefaultBitsetAllocator<InBlockType>> requires (!CSameAs<InBlockType, bool>)
|
||||
template <CUnsignedIntegral InBlockType, CAllocator<InBlockType> Allocator = TDefaultBitsetAllocator<InBlockType>> requires (!CSameAs<InBlockType, bool>)
|
||||
class TBitset final
|
||||
{
|
||||
private:
|
||||
@ -216,7 +212,7 @@ public:
|
||||
Impl->Deallocate(Impl.Pointer);
|
||||
|
||||
Impl.Pointer = InValue.Impl.Pointer;
|
||||
|
||||
|
||||
InValue.Impl.BitsetNum = 0;
|
||||
InValue.Impl.BlocksMax = InValue.Impl->CalculateSlackReserve(InValue.NumBlocks());
|
||||
InValue.Impl.Pointer = InValue.Impl->Allocate(InValue.MaxBlocks());
|
||||
@ -306,7 +302,7 @@ public:
|
||||
}
|
||||
|
||||
const BlockType LastBlockBitmask = InValue.Num() % BlockWidth != 0 ? (1ull << InValue.Num() % BlockWidth) - 1 : -1;
|
||||
|
||||
|
||||
Impl.Pointer[LastBlock] &= InValue.Impl.Pointer[LastBlock] & LastBlockBitmask;
|
||||
|
||||
for (size_t Index = LastBlock + 1; Index != NumBlocks(); ++Index)
|
||||
@ -604,7 +600,7 @@ public:
|
||||
uint64 Result = 0;
|
||||
|
||||
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
|
||||
|
||||
|
||||
if constexpr (sizeof(BlockType) == sizeof(uint8))
|
||||
{
|
||||
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
|
||||
@ -697,7 +693,7 @@ public:
|
||||
const size_t BlocksCount = (InCount + BlockWidth - 1) / BlockWidth;
|
||||
|
||||
size_t NumToAllocate = BlocksCount;
|
||||
|
||||
|
||||
NumToAllocate = NumToAllocate > MaxBlocks() ? Impl->CalculateSlackGrow(BlocksCount, MaxBlocks()) : NumToAllocate;
|
||||
NumToAllocate = NumToAllocate < MaxBlocks() ? (bAllowShrinking ? Impl->CalculateSlackShrink(BlocksCount, MaxBlocks()) : MaxBlocks()) : NumToAllocate;
|
||||
|
||||
|
@ -361,7 +361,7 @@ public:
|
||||
using ElementType = TIteratorElementType<I>;
|
||||
|
||||
# if DO_CHECK
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) : Length(1), MaxLength(0) { };
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) : Length(1), MaxLength(0) { }
|
||||
# else
|
||||
FORCEINLINE constexpr TCountedIterator() requires (CDefaultConstructible<IteratorType>) = default;
|
||||
# endif
|
||||
@ -388,20 +388,21 @@ public:
|
||||
template <CInputOrOutputIterator J> requires (!CSameAs<IteratorType, J> && CConvertibleTo<J&&, IteratorType> && CAssignableFrom<IteratorType&, J&&>)
|
||||
FORCEINLINE constexpr TCountedIterator& operator=(TCountedIterator<J>&& InValue) { Current = MoveTemp(InValue).GetBase(); Length = InValue.Num(); check_code({ MaxLength = InValue.MaxLength; }); return *this; }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (CSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.GetBase() == RHS.GetBase(); }
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr bool operator==(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length == RHS.Length; }
|
||||
|
||||
template <CInputOrOutputIterator J> requires (CSizedSentinelFor<J, IteratorType>)
|
||||
NODISCARD friend FORCEINLINE constexpr TCompareThreeWayResult<IteratorType, J> operator<=>(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.GetBase() <=> RHS.GetBase(); }
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr strong_ordering operator<=>(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { return LHS.Length <=> RHS.Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr bool operator==(FDefaultSentinel) const& { return Length == static_cast<ptrdiff>(0); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr strong_ordering operator<=>(FDefaultSentinel) const& { return static_cast<ptrdiff>(0) <=> Length; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() { CheckThis(true); return *Current; }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() const { CheckThis(true); return *Current; }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() { CheckThis(true); return AddressOf(operator*()); }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() const { CheckThis(true); return AddressOf(operator*()); }
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() { CheckThis(true); return *Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator*() const requires (CDereferenceable<const IteratorType>) { CheckThis(true); return *Current; }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorPointerType<IteratorType> operator->() const requires (CContiguousIterator<IteratorType>) { CheckThis(true); return AddressOf(operator*()); }
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TIteratorReferenceType<IteratorType> operator[](ptrdiff Index) const requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = *this + Index; return *Temp; }
|
||||
|
||||
@ -420,7 +421,8 @@ public:
|
||||
|
||||
NODISCARD FORCEINLINE constexpr TCountedIterator operator-(ptrdiff Offset) const requires (CRandomAccessIterator<IteratorType>) { TCountedIterator Temp = *this; Temp -= Offset; return Temp; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, const TCountedIterator& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.GetBase() - RHS.GetBase(); }
|
||||
template <CCommonType<IteratorType> J>
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, const TCountedIterator<J>& RHS) { LHS.CheckThis(); RHS.CheckThis(); return LHS.Length - RHS.Length; }
|
||||
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(const TCountedIterator& LHS, FDefaultSentinel) { CheckThis(); return -LHS.Num(); }
|
||||
NODISCARD friend FORCEINLINE constexpr ptrdiff operator-(FDefaultSentinel, const TCountedIterator& RHS) { CheckThis(); return RHS.Num(); }
|
||||
|
@ -1,442 +0,0 @@
|
||||
#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;
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (DestinationEnd == nullptr && SourceEnd == nullptr)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char>)
|
||||
{
|
||||
return NAMESPACE_STD::strcpy(Destination, Source);
|
||||
}
|
||||
else if constexpr (CSameAs<CharType, wchar_t>)
|
||||
{
|
||||
return NAMESPACE_STD::wcscpy(Destination, Source);
|
||||
}
|
||||
}
|
||||
|
||||
size_t SourceLength = TCString::Length(Source, SourceEnd);
|
||||
|
||||
if (DestinationEnd != nullptr && Destination + SourceLength + 1 > DestinationEnd)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Memory::Memcpy(Destination, Source, SourceLength * sizeof(CharType));
|
||||
|
||||
Destination[SourceLength] = LITERAL(CharType, '\0');
|
||||
|
||||
return Destination;
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (DestinationEnd == nullptr && SourceEnd == nullptr)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char>)
|
||||
{
|
||||
return NAMESPACE_STD::strcat(Destination, Source);
|
||||
}
|
||||
else if constexpr (CSameAs<CharType, wchar_t>)
|
||||
{
|
||||
return NAMESPACE_STD::wcscat(Destination, Source);
|
||||
}
|
||||
}
|
||||
|
||||
size_t DestinationLength = TCString::Length(Destination, DestinationEnd);
|
||||
|
||||
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, const CharType* End)
|
||||
{
|
||||
checkf(InString, TEXT("Read access violation. InString must not be nullptr."));
|
||||
|
||||
if (End == nullptr)
|
||||
{
|
||||
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;
|
||||
|
||||
while (*InString != LITERAL(CharType, '\0') && InString != End)
|
||||
{
|
||||
++Result;
|
||||
++InString;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (LHSEnd == nullptr && RHSEnd == nullptr)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
while (LHS != LHSEnd && RHS != RHSEnd)
|
||||
{
|
||||
if (*LHS != *RHS)
|
||||
{
|
||||
return *LHS <=> *RHS;
|
||||
}
|
||||
|
||||
if (*LHS == LITERAL(CharType, '\0') && *RHS == LITERAL(CharType, '\0'))
|
||||
{
|
||||
return strong_ordering::equal;
|
||||
}
|
||||
|
||||
++LHS;
|
||||
++RHS;
|
||||
}
|
||||
|
||||
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 end sentinel is used only for buffer safety. */
|
||||
template <CPredicate<CharType> F>
|
||||
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."));
|
||||
|
||||
if (SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
while (InString != End)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), *InString))
|
||||
{
|
||||
return InString;
|
||||
}
|
||||
|
||||
if (*InString == LITERAL(CharType, '\0')) break;
|
||||
|
||||
++InString;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t Index = TCString::Length(InString, End);
|
||||
|
||||
const CharType* Iter = InString + Index;
|
||||
|
||||
if (Iter == End) --Iter;
|
||||
|
||||
while (Iter != InString - 1)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), *Iter))
|
||||
{
|
||||
return Iter;
|
||||
}
|
||||
|
||||
--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 end sentinel is used only for buffer safety. */
|
||||
template <CPredicate<CharType> F>
|
||||
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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::Find(const_cast<const CharType*>(InString), End, Forward<F>(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 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."));
|
||||
|
||||
if (End == nullptr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::FindChar(const_cast<const CharType*>(InString), End, Character, SearchDirection));
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (End == nullptr && CharsetEnd == nullptr && SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
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
|
||||
(
|
||||
InString, End,
|
||||
[Charset, CharsetEnd](CharType 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 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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::FindChar(const_cast<const CharType*>(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 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."));
|
||||
|
||||
if (InString == End) return nullptr;
|
||||
|
||||
if (Character == LITERAL(CharType, '\0') && SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
return *InString != LITERAL(CharType, '\0') ? InString : nullptr;
|
||||
}
|
||||
|
||||
if (End == nullptr && SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::FindNotChar(const_cast<const CharType*>(InString), End, Character, SearchDirection));
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (End == nullptr && CharsetEnd == nullptr && SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::FindNotChar(const_cast<const CharType*>(InString), End, Charset, CharsetEnd, SearchDirection));
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
if (InString == End) return nullptr;
|
||||
|
||||
if (Substring == SubstringEnd || *Substring == LITERAL(CharType, '\0'))
|
||||
{
|
||||
if (SearchDirection == ESearchDirection::FromStart) return InString;
|
||||
else
|
||||
{
|
||||
const CharType* Iter = InString + TCString::Length(InString, End);
|
||||
if (Iter == End) --Iter;
|
||||
return Iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (End == nullptr && SubstringEnd == nullptr && SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
if constexpr (CSameAs<CharType, char>)
|
||||
{
|
||||
return NAMESPACE_STD::strstr(InString, Substring);
|
||||
}
|
||||
else if constexpr (CSameAs<CharType, wchar_t>)
|
||||
{
|
||||
return NAMESPACE_STD::wcsstr(InString, Substring);
|
||||
}
|
||||
}
|
||||
|
||||
size_t StringLength = TCString::Length(InString, End);
|
||||
size_t SubstringLength = TCString::Length(Substring, SubstringEnd);
|
||||
|
||||
if (StringLength < SubstringLength)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (SearchDirection == ESearchDirection::FromStart)
|
||||
{
|
||||
for (size_t Index = 0; Index < StringLength - SubstringLength; ++Index)
|
||||
{
|
||||
if (TCString::Compare(InString + Index, InString + Index + SubstringLength, Substring, Substring + SubstringLength) == 0)
|
||||
{
|
||||
return InString + Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t Index = StringLength - SubstringLength; Index > 0; --Index)
|
||||
{
|
||||
if (TCString::Compare(InString + Index, InString + Index + SubstringLength, Substring, Substring + SubstringLength) == 0)
|
||||
{
|
||||
return InString + Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** 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."));
|
||||
|
||||
check_no_recursion();
|
||||
|
||||
return const_cast<CharType*>(TCString::FindString(const_cast<const CharType*>(InString), End, Substring, SubstringEnd, SearchDirection));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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
|
436
Redcraft.Utility/Source/Public/String/StringView.h
Normal file
436
Redcraft.Utility/Source/Public/String/StringView.h
Normal file
@ -0,0 +1,436 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "String/Char.h"
|
||||
#include "Memory/Allocator.h"
|
||||
#include "Templates/Utility.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "Templates/Container.h"
|
||||
#include "Containers/Iterator.h"
|
||||
#include "Containers/ArrayView.h"
|
||||
#include "TypeTraits/TypeTraits.h"
|
||||
#include "Miscellaneous/Compare.h"
|
||||
#include "Memory/ObserverPointer.h"
|
||||
#include "Miscellaneous/AssertionMacros.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
|
||||
NAMESPACE_REDCRAFT_BEGIN
|
||||
NAMESPACE_MODULE_BEGIN(Redcraft)
|
||||
NAMESPACE_MODULE_BEGIN(Utility)
|
||||
|
||||
/**
|
||||
* The class template TStringView describes an object that can refer to a constant contiguous sequence of char-like objects
|
||||
* with the first element of the sequence at position zero. Provides a set of convenient string processing functions.
|
||||
*/
|
||||
template <CCharType T>
|
||||
class TStringView final
|
||||
{
|
||||
public:
|
||||
|
||||
using ElementType = T;
|
||||
|
||||
using Reference = typename TArrayView<const ElementType>::Reference;
|
||||
|
||||
using Iterator = typename TArrayView<const ElementType>::Iterator;
|
||||
using ReverseIterator = typename TArrayView<const ElementType>::ReverseIterator;
|
||||
|
||||
static_assert(CContiguousIterator<Iterator>);
|
||||
|
||||
/** Constructs an empty string view. */
|
||||
FORCEINLINE constexpr TStringView() = default;
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InFirst', 'InFirst' + 'Count'). */
|
||||
template <CContiguousIterator I> requires (CConvertibleTo<TIteratorElementType<I>(*)[], const ElementType(*)[]>)
|
||||
FORCEINLINE constexpr TStringView(I InFirst, size_t InCount) : NativeData(InFirst, InCount) { }
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InFirst', 'InLast'). */
|
||||
template <CContiguousIterator I, CSizedSentinelFor<I> S> requires (CConvertibleTo<TIteratorElementType<I>(*)[], const ElementType(*)[]>)
|
||||
FORCEINLINE constexpr TStringView(I InFirst, S InLast) : NativeData(InFirst, InLast) { }
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InPtr', 'InPtr' + 'Count'). */
|
||||
FORCEINLINE constexpr TStringView(const ElementType* InPtr, size_t Count) : NativeData(InPtr, Count)
|
||||
{
|
||||
checkf(InPtr != nullptr, TEXT("TStringView cannot be initialized by nullptr. Please check the pointer."));
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TStringView(nullptr_t, size_t) = delete;
|
||||
|
||||
/** Constructs a string view that is a view over the range ['InPtr', '\0'). */
|
||||
FORCEINLINE constexpr TStringView(const ElementType* InPtr)
|
||||
{
|
||||
checkf(InPtr != nullptr, TEXT("TStringView cannot be initialized by nullptr. Please check the pointer."));
|
||||
|
||||
size_t Length = 0;
|
||||
|
||||
if constexpr (CSameAs<ElementType, char>)
|
||||
{
|
||||
Length = NAMESPACE_STD::strlen(InPtr);
|
||||
}
|
||||
else if constexpr (CSameAs<ElementType, wchar>)
|
||||
{
|
||||
Length = NAMESPACE_STD::wcslen(InPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (InPtr[Length] != LITERAL(ElementType, '\0')) ++Length;
|
||||
}
|
||||
|
||||
NativeData = TArrayView<const ElementType>(InPtr, Length);
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr TStringView(nullptr_t) = delete;
|
||||
|
||||
/** Defaulted copy constructor copies the size and data pointer. */
|
||||
FORCEINLINE constexpr TStringView(const TStringView&) = default;
|
||||
|
||||
/** Assigns other to *this. This defaulted assignment operator performs a shallow copy of the data pointer and the size. */
|
||||
FORCEINLINE constexpr TStringView& operator=(const TStringView&) noexcept = default;
|
||||
|
||||
/** Compares the contents of two string views. */
|
||||
NODISCARD friend constexpr bool operator==(TStringView LHS, TStringView RHS) { return LHS.NativeData == RHS.NativeData; }
|
||||
|
||||
/** Compares the contents of two string views. */
|
||||
NODISCARD friend constexpr auto operator<=>(TStringView LHS, TStringView RHS) { return LHS.NativeData <=> RHS.NativeData; }
|
||||
|
||||
/** Shrinks the view by moving its start forward. */
|
||||
FORCEINLINE constexpr void RemovePrefix(size_t Count)
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
NativeData = NativeData.Subview(Count);
|
||||
}
|
||||
|
||||
/** Shrinks the view by moving its end backward. */
|
||||
FORCEINLINE constexpr void RemoveSuffix(size_t Count)
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
NativeData = NativeData.Subview(0, Num() - Count);
|
||||
}
|
||||
|
||||
/** Obtains a string view that is a view over the first 'Count' elements of this string view. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView First(size_t Count) const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TStringView(Begin(), Count);
|
||||
}
|
||||
|
||||
/** Obtains a string view that is a view over the last 'Count' elements of this string view. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView Last(size_t Count) const
|
||||
{
|
||||
checkf(Count <= Num(), TEXT("Illegal subview range. Please check Count."));
|
||||
|
||||
return TStringView(End() - Count, Count);
|
||||
}
|
||||
|
||||
/** Copies the elements of this string view to the destination buffer without null-termination. */
|
||||
FORCEINLINE constexpr size_t Copy(ElementType* Dest, size_t Count = DynamicExtent, size_t Offset = 0) const
|
||||
{
|
||||
checkf(Offset <= Num() && (Count == DynamicExtent || Offset + Count <= Num()), TEXT("Illegal subview range. Please check Offset and Count."));
|
||||
|
||||
if (Count == DynamicExtent)
|
||||
{
|
||||
Count = Num() - Offset;
|
||||
}
|
||||
|
||||
Memory::Memcpy(Dest, GetData().Get() + Offset, Count * sizeof(ElementType));
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
/** Obtains a string view that is a view over the 'Count' elements of this string view starting at 'Offset'. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView Substr(size_t Offset, size_t Count = DynamicExtent) const
|
||||
{
|
||||
checkf(Offset <= Num() && (Count == DynamicExtent || Offset + Count <= Num()), TEXT("Illegal subview range. Please check Offset and Count."));
|
||||
|
||||
return Subview(Offset, Count);
|
||||
}
|
||||
|
||||
/** Obtains a string view that is a view over the 'Count' elements of this string view starting at 'Offset'. */
|
||||
NODISCARD FORCEINLINE constexpr TStringView Subview(size_t Offset, size_t Count = DynamicExtent) const
|
||||
{
|
||||
checkf(Offset <= Num() && (Count == DynamicExtent || Offset + Count <= Num()), TEXT("Illegal subview range. Please check Offset and Count."));
|
||||
|
||||
if (Count != DynamicExtent)
|
||||
{
|
||||
return TStringView(Begin() + Offset, Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TStringView(Begin() + Offset, Num() - Offset);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return true if the string view starts with the given prefix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool StartsWith(TStringView Prefix) const
|
||||
{
|
||||
return Num() >= Prefix.Num() && Substr(0, Prefix.Num()) == Prefix;
|
||||
}
|
||||
|
||||
/** @return true if the string view starts with the given prefix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool StartsWith(ElementType Prefix) const
|
||||
{
|
||||
return Num() >= 1 && Front() == Prefix;
|
||||
}
|
||||
|
||||
/** @return true if the string view ends with the given suffix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool EndsWith(TStringView Suffix) const
|
||||
{
|
||||
return Num() >= Suffix.Num() && Substr(Num() - Suffix.Num(), Suffix.Num()) == Suffix;
|
||||
}
|
||||
|
||||
/** @return true if the string view ends with the given suffix, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool EndsWith(ElementType Suffix) const
|
||||
{
|
||||
return Num() >= 1 && Back() == Suffix;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains the given substring, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(TStringView View) const
|
||||
{
|
||||
return Find(View) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains the given character, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(ElementType Char) const
|
||||
{
|
||||
return Find(Char) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return true if the string view contains character that satisfy the given predicate, false otherwise. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD FORCEINLINE constexpr bool Contains(F&& InPredicate) const
|
||||
{
|
||||
return Find(Forward<F>(InPredicate)) != INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given substring, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Find(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
if (View.Num() > Num()) return INDEX_NONE;
|
||||
|
||||
if (View.Num() == 0) return Index;
|
||||
|
||||
for (; Index != Num() - View.Num() + 1; ++Index)
|
||||
{
|
||||
if (Substr(Index).StartsWith(View))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Find(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
for (; Index != Num(); ++Index)
|
||||
{
|
||||
if (NativeData[Index] == Char)
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the character that satisfy the given predicate, or INDEX_NONE if not found. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD FORCEINLINE constexpr size_t Find(F&& InPredicate, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
for (; Index != Num(); ++Index)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), NativeData[Index]))
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given substring, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t RFind(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
if (View.Num() > Num()) return INDEX_NONE;
|
||||
|
||||
if (Index == INDEX_NONE) Index = Num();
|
||||
|
||||
if (View.Num() == 0) return Index;
|
||||
|
||||
for (; Index != View.Num() - 1; --Index)
|
||||
{
|
||||
if (Substr(0, Index).EndsWith(View))
|
||||
{
|
||||
return Index - View.Num();
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t RFind(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
if (Index == INDEX_NONE) Index = Num();
|
||||
|
||||
for (; Index != 0; --Index)
|
||||
{
|
||||
if (NativeData[Index - 1] == Char)
|
||||
{
|
||||
return Index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the character that satisfy the given predicate, or INDEX_NONE if not found. */
|
||||
template <CPredicate<ElementType> F>
|
||||
NODISCARD FORCEINLINE constexpr size_t RFind(F&& InPredicate, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
if (Index == INDEX_NONE) Index = Num();
|
||||
|
||||
for (; Index != 0; --Index)
|
||||
{
|
||||
if (InvokeResult<bool>(Forward<F>(InPredicate), NativeData[Index - 1]))
|
||||
{
|
||||
return Index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstOf(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return Find([View](ElementType Char) { return View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstOf(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return Find(Char, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastOf(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return RFind([View](ElementType Char) { return View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last occurrence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastOf(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return RFind(Char, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first absence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstNotOf(TStringView View, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return Find([View](ElementType Char) { return !View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the first absence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindFirstNotOf(ElementType Char, size_t Index = 0) const
|
||||
{
|
||||
checkf(Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return Find([Char](ElementType C) { return C != Char; }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last absence of the character contained in the given view, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastNotOf(TStringView View, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return RFind([View](ElementType Char) { return !View.Contains(Char); }, Index);
|
||||
}
|
||||
|
||||
/** @return The index of the last absence of the given character, or INDEX_NONE if not found. */
|
||||
NODISCARD FORCEINLINE constexpr size_t FindLastNotOf(ElementType Char, size_t Index = INDEX_NONE) const
|
||||
{
|
||||
checkf(Index == INDEX_NONE || Index < Num(), TEXT("Illegal index. Please check Index."));
|
||||
|
||||
return RFind([Char](ElementType C) { return C != Char; }, Index);
|
||||
}
|
||||
|
||||
/** @return The pointer to the underlying element storage. */
|
||||
NODISCARD FORCEINLINE constexpr TObserverPtr<const ElementType[]> GetData() const { return NativeData.GetData(); }
|
||||
|
||||
/** @return The iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr Iterator Begin() const { return NativeData.Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr Iterator End() const { return NativeData.End(); }
|
||||
|
||||
/** @return The reverse iterator to the first or end element. */
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator RBegin() const { return NativeData.RBegin(); }
|
||||
NODISCARD FORCEINLINE constexpr ReverseIterator REnd() const { return NativeData.REnd(); }
|
||||
|
||||
/** @return The number of elements in the container. */
|
||||
NODISCARD FORCEINLINE constexpr size_t Num() const { return NativeData.Num(); }
|
||||
|
||||
/** @return true if the container is empty, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsEmpty() const { return Num() == 0; }
|
||||
|
||||
/** @return true if the iterator is valid, false otherwise. */
|
||||
NODISCARD FORCEINLINE constexpr bool IsValidIterator(Iterator Iter) const { return Begin() <= Iter && Iter <= End(); }
|
||||
|
||||
/** @return The reference to the requested element. */
|
||||
NODISCARD FORCEINLINE constexpr Reference operator[](size_t Index) const { checkf(Index < Num(), TEXT("Read access violation. Please check IsValidIterator().")); return NativeData[Index]; }
|
||||
|
||||
/** @return The reference to the first or last element. */
|
||||
NODISCARD FORCEINLINE constexpr Reference Front() const { return *Begin(); }
|
||||
NODISCARD FORCEINLINE constexpr Reference Back() const { return *(End() - 1); }
|
||||
|
||||
/** Overloads the GetTypeHash algorithm for TStringView. */
|
||||
NODISCARD friend FORCEINLINE constexpr size_t GetTypeHash(TStringView A) { return GetTypeHash(A.NativeData); }
|
||||
|
||||
ENABLE_RANGE_BASED_FOR_LOOP_SUPPORT
|
||||
|
||||
private:
|
||||
|
||||
TArrayView<const ElementType> NativeData;
|
||||
|
||||
};
|
||||
|
||||
template <typename I, typename S>
|
||||
TStringView(I, S) -> TStringView<TIteratorElementType<I>>;
|
||||
|
||||
using FStringView = TStringView<char>;
|
||||
using FWStringView = TStringView<wchar>;
|
||||
using FU8StringView = TStringView<u8char>;
|
||||
using FU16StringView = TStringView<u16char>;
|
||||
using FU32StringView = TStringView<u32char>;
|
||||
using FUnicodeStringView = TStringView<unicodechar>;
|
||||
|
||||
NAMESPACE_MODULE_END(Utility)
|
||||
NAMESPACE_MODULE_END(Redcraft)
|
||||
NAMESPACE_REDCRAFT_END
|
Loading…
Reference in New Issue
Block a user