refactor(string): refactor character and digit conversion to be non-invalidable

This commit is contained in:
Redstone1024 2024-11-12 13:32:43 +08:00
parent fe60fc33e0
commit bc93455d0e
3 changed files with 31 additions and 25 deletions

View File

@ -77,13 +77,13 @@ void TestChar()
always_check(TChar<T>::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i')); always_check(TChar<T>::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i'));
always_check(TChar<T>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L')); always_check(TChar<T>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L'));
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'), 16)); always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0')));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f'), 16)); always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f')));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F'), 16)); always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F')));
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0, 16)); always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0));
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF, 16)); always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF));
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF, 16)); always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF));
}; };
Test(InPlaceType<char>); Test(InPlaceType<char>);

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "CoreTypes.h" #include "CoreTypes.h"
#include "Templates/Optional.h"
#include "TypeTraits/TypeTraits.h" #include "TypeTraits/TypeTraits.h"
#include "Miscellaneous/AssertionMacros.h" #include "Miscellaneous/AssertionMacros.h"
@ -809,7 +808,7 @@ struct TChar
return InChar; return InChar;
} }
NODISCARD FORCEINLINE static constexpr TOptional<unsigned> ToDigit(CharType InChar, unsigned Base = 10) NODISCARD FORCEINLINE static constexpr unsigned ToDigit(CharType InChar)
{ {
static_assert(TChar::IsASCII()); static_assert(TChar::IsASCII());
@ -835,16 +834,23 @@ struct TChar
static_assert(sizeof(DigitFromChar) == 256); static_assert(sizeof(DigitFromChar) == 256);
if constexpr (sizeof(CharType) > 1) if (InChar >> 8) return Invalid; if constexpr (sizeof(CharType) > 1) if (InChar >> 8) return DigitFromChar[0];
if (DigitFromChar[InChar] >= Base) return Invalid;
return DigitFromChar[InChar]; return DigitFromChar[InChar];
} }
NODISCARD FORCEINLINE static constexpr TOptional<CharType> FromDigit(unsigned InDigit, unsigned Base = 10) NODISCARD FORCEINLINE static constexpr CharType FromDigit(unsigned InDigit)
{ {
if (InDigit > Base) return Invalid; checkf(InDigit < 36, TEXT("Digit must be in the range [0, 35]."));
return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
}
NODISCARD FORCEINLINE static constexpr CharType FromDigit(unsigned InDigit, bool bLowercase)
{
checkf(InDigit < 36, TEXT("Digit must be in the range [0, 35]."));
if (bLowercase) return LITERAL(CharType, "0123456789abcdefghijklmnopqrstuvwxyz")[InDigit];
return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit]; return LITERAL(CharType, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")[InDigit];
} }

View File

@ -212,11 +212,11 @@ struct TStringHelper
while (!View.IsEmpty()) while (!View.IsEmpty())
{ {
auto Digit = TChar<T>::ToDigit(View.Front(), Base); auto Digit = TChar<T>::ToDigit(View.Front());
if (!Digit) break; if (Digit >= Base) break;
Result = Result * static_cast<NumberType>(Base) + static_cast<NumberType>(*Digit); Result = Result * static_cast<NumberType>(Base) + static_cast<NumberType>(Digit);
View.RemovePrefix(1); View.RemovePrefix(1);
} }
@ -632,13 +632,13 @@ constexpr U TStringView<T>::ToInt(unsigned Base) const
for (ElementType Char : View) for (ElementType Char : View)
{ {
auto Digit = TChar<ElementType>::ToDigit(Char, Base); auto Digit = TChar<ElementType>::ToDigit(Char);
if (!Digit) break; if (Digit >= Base) break;
LastValue = Value; LastValue = Value;
Value = static_cast<UnsignedU>(LastValue * Base + *Digit); Value = static_cast<UnsignedU>(LastValue * Base + Digit);
if (Value < LastValue) if (Value < LastValue)
{ {
@ -794,7 +794,7 @@ void TString<T, Allocator>::AppendInt(U Value, unsigned Base)
{ {
checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base.")); checkf(Base >= 2 && Base <= 36, TEXT("Illegal base. Please check the base."));
constexpr const ElementType* DigitToChar = LITERAL(ElementType, "0123456789ABCDEF"); static_assert(TChar<ElementType>::IsASCII());
using UnsignedU = TMakeUnsigned<U>; using UnsignedU = TMakeUnsigned<U>;
@ -823,8 +823,8 @@ void TString<T, Allocator>::AppendInt(U Value, unsigned Base)
case 0x02: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00001)); Unsigned >>= 1; } while (Unsigned != 0); break; case 0x02: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00001)); Unsigned >>= 1; } while (Unsigned != 0); break;
case 0x04: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00011)); Unsigned >>= 2; } while (Unsigned != 0); break; case 0x04: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00011)); Unsigned >>= 2; } while (Unsigned != 0); break;
case 0x08: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00111)); Unsigned >>= 3; } while (Unsigned != 0); break; case 0x08: do { *--Iter = static_cast<ElementType>('0' + (Unsigned & 0b00111)); Unsigned >>= 3; } while (Unsigned != 0); break;
case 0x10: do { *--Iter = DigitToChar[Unsigned & 0b01111]; Unsigned >>= 4; } while (Unsigned != 0); break; case 0x10: do { *--Iter = TChar<ElementType>::FromDigit(Unsigned & 0b01111); Unsigned >>= 4; } while (Unsigned != 0); break;
case 0X20: do { *--Iter = DigitToChar[Unsigned & 0b11111]; Unsigned >>= 5; } while (Unsigned != 0); break; case 0X20: do { *--Iter = TChar<ElementType>::FromDigit(Unsigned & 0b11111); Unsigned >>= 5; } while (Unsigned != 0); break;
case 3: case 3:
case 5: case 5:
@ -832,7 +832,7 @@ void TString<T, Allocator>::AppendInt(U Value, unsigned Base)
case 7: case 7:
case 9: case 9:
case 10: do { *--Iter = static_cast<ElementType>('0' + Unsigned % Base); Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break; case 10: do { *--Iter = static_cast<ElementType>('0' + Unsigned % Base); Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break;
default: do { *--Iter = DigitToChar[Unsigned % Base]; Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break; default: do { *--Iter = TChar<ElementType>::FromDigit(Unsigned % Base); Unsigned = static_cast<UnsignedU>(Unsigned / Base); } while (Unsigned != 0); break;
} }
if constexpr (CSigned<U>) if (bNegative) *--Iter = LITERAL(T, '-'); if constexpr (CSigned<U>) if (bNegative) *--Iter = LITERAL(T, '-');
@ -900,8 +900,8 @@ struct TStringFloatSerializer
for (char& Char : Buffer) for (char& Char : Buffer)
{ {
const auto Digit = FChar::ToDigit(Char, Base); const auto Digit = FChar::ToDigit(Char);
if (Digit) Char = *FChar::FromDigit(*Digit, Base); if (Digit < Base) Char = FChar::FromDigit(Digit);
} }
Result.Append(Buffer.Begin(), Buffer.End()); Result.Append(Buffer.Begin(), Buffer.End());