Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deduplicate Binary Integer parsing logic #84582

Merged
merged 13 commits into from
Apr 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
@@ -23,7 +23,8 @@ public readonly struct Byte
IMinMaxValue<byte>,
IUnsignedNumber<byte>,
IUtf8SpanFormattable,
IUtfChar<byte>
IUtfChar<byte>,
IBinaryIntegerParseAndFormatInfo<byte>
tannergooding marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly byte m_value; // Do not rename (binary serialization)

@@ -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);
tannergooding marked this conversation as resolved.
Show resolved Hide resolved
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);
tannergooding marked this conversation as resolved.
Show resolved Hide resolved

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()
@@ -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
@@ -36,7 +36,8 @@ public readonly struct Char
IMinMaxValue<char>,
IUnsignedNumber<char>,
IUtf8SpanFormattable,
IUtfChar<char>
IUtfChar<char>,
IBinaryIntegerParseAndFormatInfo<char>
{
//
// Member Variables
@@ -200,29 +201,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;
}

tannergooding marked this conversation as resolved.
Show resolved Hide resolved
result = s[0];
return true;
}
@@ -1508,14 +1517,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)]
@@ -1929,16 +1931,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
@@ -1965,25 +1958,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
@@ -2021,5 +1998,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