Skip to content

Commit

Permalink
Deduplicate Binary Integer parsing logic (#84582)
Browse files Browse the repository at this point in the history
* Deduplicate TryNumberToInt*

* Deduplicate TryParseInt*IntegerStyle parsing methods

* Deduplicate TryParseInt*HexNumberStyle

* Deduplicate TryParseInt*Number

* Deduplicate TryParseInt*

* Deduplicate ParseInt*

* Deduplicate some more binary integer parsing logic

* Ensure the right overflow message is used for binary integer parsing

* Ensure HasTrailingCharsZero handling is in the right spot

* Resolving PR feedback

* Revert using the public throw helpers
  • Loading branch information
tannergooding authored Apr 17, 2023
1 parent add51b8 commit 07834fd
Show file tree
Hide file tree
Showing 16 changed files with 696 additions and 2,369 deletions.
99 changes: 33 additions & 66 deletions src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public readonly struct Byte
IMinMaxValue<byte>,
IUnsignedNumber<byte>,
IUtf8SpanFormattable,
IUtfChar<byte>
IUtfChar<byte>,
IBinaryIntegerParseAndFormatInfo<byte>
{
private readonly byte m_value; // Do not rename (binary serialization)

Expand Down Expand Up @@ -92,98 +93,44 @@ public override int GetHashCode()
return m_value;
}

public static byte Parse(string s)
{
if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
return Parse((ReadOnlySpan<char>)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static byte Parse(string s) => Parse(s, NumberStyles.Integer, provider: null);

public static byte Parse(string s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
return Parse((ReadOnlySpan<char>)s, style, NumberFormatInfo.CurrentInfo);
}
public static byte Parse(string s, NumberStyles style) => Parse(s, style, provider: null);

public static byte Parse(string s, IFormatProvider? provider)
{
if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
return Parse((ReadOnlySpan<char>)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static byte Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);

// Parses an unsigned byte from a String in the given style. If
// a NumberFormatInfo isn't specified, the current culture's
// NumberFormatInfo is assumed.
public static byte Parse(string s, NumberStyles style, IFormatProvider? provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
return Parse((ReadOnlySpan<char>)s, style, NumberFormatInfo.GetInstance(provider));
if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); }
return Parse(s.AsSpan(), style, provider);
}

public static byte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}

private static byte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
{
Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i);
if (status != Number.ParsingStatus.OK)
{
Number.ThrowOverflowOrFormatException(status, s, TypeCode.Byte);
}

if (i > MaxValue) Number.ThrowOverflowException(TypeCode.Byte);
return (byte)i;
return Number.ParseBinaryInteger<byte>(s, style, NumberFormatInfo.GetInstance(provider));
}

public static bool TryParse([NotNullWhen(true)] string? s, out byte result)
{
if (s == null)
{
result = 0;
return false;
}
public static bool TryParse([NotNullWhen(true)] string? s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result);

return TryParse((ReadOnlySpan<char>)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}

public static bool TryParse(ReadOnlySpan<char> s, out byte result)
{
return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(ReadOnlySpan<char> s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result);

public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);

if (s == null)
if (s is null)
{
result = 0;
return false;
}

return TryParse((ReadOnlySpan<char>)s, style, NumberFormatInfo.GetInstance(provider), out result);
return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}

public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
}

private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out byte result)
{
if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK
|| i > MaxValue)
{
result = 0;
return false;
}
result = (byte)i;
return true;
return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}

public override string ToString()
Expand Down Expand Up @@ -1213,5 +1160,25 @@ static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [MaybeN
static byte IUtfChar<byte>.CastFrom(int value) => (byte)value;
static byte IUtfChar<byte>.CastFrom(uint value) => (byte)value;
static byte IUtfChar<byte>.CastFrom(ulong value) => (byte)value;

//
// IBinaryIntegerParseAndFormatInfo
//

static bool IBinaryIntegerParseAndFormatInfo<byte>.IsSigned => false;

static int IBinaryIntegerParseAndFormatInfo<byte>.MaxDigitCount => 3; // 255

static int IBinaryIntegerParseAndFormatInfo<byte>.MaxHexDigitCount => 2; // 0xFF

static byte IBinaryIntegerParseAndFormatInfo<byte>.MaxValueDiv10 => MaxValue / 10;

static string IBinaryIntegerParseAndFormatInfo<byte>.OverflowMessage => SR.Overflow_Byte;

static bool IBinaryIntegerParseAndFormatInfo<byte>.IsGreaterThanAsUnsigned(byte left, byte right) => left > right;

static byte IBinaryIntegerParseAndFormatInfo<byte>.MultiplyBy10(byte value) => (byte)(value * 10);

static byte IBinaryIntegerParseAndFormatInfo<byte>.MultiplyBy16(byte value) => (byte)(value * 16);
}
}
85 changes: 41 additions & 44 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public readonly struct Char
IMinMaxValue<char>,
IUnsignedNumber<char>,
IUtf8SpanFormattable,
IUtfChar<char>
IUtfChar<char>,
IBinaryIntegerParseAndFormatInfo<char>
{
//
// Member Variables
Expand Down Expand Up @@ -201,29 +202,37 @@ bool IUtf8SpanFormattable.TryFormat(Span<byte> utf8Destination, out int bytesWri

public static char Parse(string s)
{
if (s == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
}
if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); }
return Parse(s.AsSpan());
}

internal static char Parse(ReadOnlySpan<char> s)
{
if (s.Length != 1)
{
throw new FormatException(SR.Format_NeedSingleChar);
ThrowHelper.ThrowFormatException_NeedSingleChar();
}
return s[0];
}

public static bool TryParse([NotNullWhen(true)] string? s, out char result)
{
result = '\0';
if (s == null)
if (s is null)
{
result = '\0';
return false;
}
return TryParse(s.AsSpan(), out result);
}

internal static bool TryParse(ReadOnlySpan<char> s, out char result)
{
if (s.Length != 1)
{
result = '\0';
return false;
}

result = s[0];
return true;
}
Expand Down Expand Up @@ -1509,14 +1518,7 @@ bool IBinaryInteger<char>.TryWriteLittleEndian(Span<byte> destination, out int b

static char INumberBase<char>.Parse(string s, NumberStyles style, IFormatProvider? provider) => Parse(s);

static char INumberBase<char>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)
{
if (s.Length != 1)
{
throw new FormatException(SR.Format_NeedSingleChar);
}
return s[0];
}
static char INumberBase<char>.Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider) => Parse(s);

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -1930,16 +1932,7 @@ static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [MaybeN

static bool INumberBase<char>.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result);

static bool INumberBase<char>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out char result)
{
if (s.Length != 1)
{
result = default;
return false;
}
result = s[0];
return true;
}
static bool INumberBase<char>.TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result);

//
// IParsable
Expand All @@ -1966,25 +1959,9 @@ static bool INumberBase<char>.TryParse(ReadOnlySpan<char> s, NumberStyles style,
// ISpanParsable
//

static char ISpanParsable<char>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider)
{
if (s.Length != 1)
{
throw new FormatException(SR.Format_NeedSingleChar);
}
return s[0];
}
static char ISpanParsable<char>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s);

static bool ISpanParsable<char>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out char result)
{
if (s.Length != 1)
{
result = default;
return false;
}
result = s[0];
return true;
}
static bool ISpanParsable<char>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out char result) => TryParse(s, out result);

//
// ISubtractionOperators
Expand Down Expand Up @@ -2022,5 +1999,25 @@ static bool ISpanParsable<char>.TryParse(ReadOnlySpan<char> s, IFormatProvider?
static char IUtfChar<char>.CastFrom(int value) => (char)value;
static char IUtfChar<char>.CastFrom(uint value) => (char)value;
static char IUtfChar<char>.CastFrom(ulong value) => (char)value;

//
// IBinaryIntegerParseAndFormatInfo
//

static bool IBinaryIntegerParseAndFormatInfo<char>.IsSigned => false;

static int IBinaryIntegerParseAndFormatInfo<char>.MaxDigitCount => 5; // 65_535

static int IBinaryIntegerParseAndFormatInfo<char>.MaxHexDigitCount => 4; // 0xFFFF

static char IBinaryIntegerParseAndFormatInfo<char>.MaxValueDiv10 => (char)(MaxValue / 10);

static string IBinaryIntegerParseAndFormatInfo<char>.OverflowMessage => SR.Overflow_Char;

static bool IBinaryIntegerParseAndFormatInfo<char>.IsGreaterThanAsUnsigned(char left, char right) => left > right;

static char IBinaryIntegerParseAndFormatInfo<char>.MultiplyBy10(char value) => (char)(value * 10);

static char IBinaryIntegerParseAndFormatInfo<char>.MultiplyBy16(char value) => (char)(value * 16);
}
}
Loading

0 comments on commit 07834fd

Please sign in to comment.