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>::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L'));
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0'), 16));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f'), 16));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F'), 16));
always_check(0x0 == TChar<T>::ToDigit(LITERAL(T, '0')));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'f')));
always_check(0xF == TChar<T>::ToDigit(LITERAL(T, 'F')));
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0, 16));
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF, 16));
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF, 16));
always_check(LITERAL(T, '0') == TChar<T>::FromDigit(0x0));
always_check(LITERAL(T, 'f') != TChar<T>::FromDigit(0xF));
always_check(LITERAL(T, 'F') == TChar<T>::FromDigit(0xF));
};
Test(InPlaceType<char>);

View File

@ -1,7 +1,6 @@
#pragma once
#include "CoreTypes.h"
#include "Templates/Optional.h"
#include "TypeTraits/TypeTraits.h"
#include "Miscellaneous/AssertionMacros.h"
@ -809,7 +808,7 @@ struct TChar
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());
@ -835,16 +834,23 @@ struct TChar
static_assert(sizeof(DigitFromChar) == 256);
if constexpr (sizeof(CharType) > 1) if (InChar >> 8) return Invalid;
if (DigitFromChar[InChar] >= Base) return Invalid;
if constexpr (sizeof(CharType) > 1) if (InChar >> 8) return DigitFromChar[0];
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];
}

View File

@ -212,11 +212,11 @@ struct TStringHelper
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);
}
@ -632,13 +632,13 @@ constexpr U TStringView<T>::ToInt(unsigned Base) const
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;
Value = static_cast<UnsignedU>(LastValue * Base + *Digit);
Value = static_cast<UnsignedU>(LastValue * Base + Digit);
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."));
constexpr const ElementType* DigitToChar = LITERAL(ElementType, "0123456789ABCDEF");
static_assert(TChar<ElementType>::IsASCII());
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 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 0x10: do { *--Iter = DigitToChar[Unsigned & 0b01111]; Unsigned >>= 4; } while (Unsigned != 0); break;
case 0X20: do { *--Iter = DigitToChar[Unsigned & 0b11111]; Unsigned >>= 5; } while (Unsigned != 0); break;
case 0x10: do { *--Iter = TChar<ElementType>::FromDigit(Unsigned & 0b01111); Unsigned >>= 4; } while (Unsigned != 0); break;
case 0X20: do { *--Iter = TChar<ElementType>::FromDigit(Unsigned & 0b11111); Unsigned >>= 5; } while (Unsigned != 0); break;
case 3:
case 5:
@ -832,7 +832,7 @@ void TString<T, Allocator>::AppendInt(U Value, unsigned Base)
case 7:
case 9:
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, '-');
@ -900,8 +900,8 @@ struct TStringFloatSerializer
for (char& Char : Buffer)
{
const auto Digit = FChar::ToDigit(Char, Base);
if (Digit) Char = *FChar::FromDigit(*Digit, Base);
const auto Digit = FChar::ToDigit(Char);
if (Digit < Base) Char = FChar::FromDigit(Digit);
}
Result.Append(Buffer.Begin(), Buffer.End());