From: ts2do Date: Wed, 19 Feb 2020 07:18:47 +0000 (-0600) Subject: Optimize integral ToString (without generics) (#32528) X-Git-Tag: submit/tizen/20210909.063632~9636 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7c7e8edbf02a17c1c0fb307f5483f1b91cc8fd4c;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Optimize integral ToString (without generics) (#32528) - Make the fast path for Number.FormatXX & Number.TryFormatXX inlineable - Make parameterless ToString in SByte, Int16, Int32, and Int64 directly invoke the default formatting method --- diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index ed556ee..88bd953 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -69,12 +69,12 @@ namespace System public override string ToString() { - return Number.FormatInt32(m_value, null, null); + return Number.Int32ToDecStr(m_value); } public string ToString(IFormatProvider? provider) { - return Number.FormatInt32(m_value, null, provider); + return ToString(null, provider); } public string ToString(string? format) @@ -84,23 +84,12 @@ namespace System public string ToString(string? format, IFormatProvider? provider) { - if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x0000FFFF); - return Number.FormatUInt32(temp, format, provider); - } - - return Number.FormatInt32(m_value, format, provider); + return Number.FormatInt32(m_value, 0x0000FFFF, format, provider); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) { - if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x0000FFFF); - return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); - } - return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, destination, out charsWritten); } public static short Parse(string s) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index e1707f0..2e71773 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -79,27 +79,27 @@ namespace System public override string ToString() { - return Number.FormatInt32(m_value, null, null); + return Number.Int32ToDecStr(m_value); } public string ToString(string? format) { - return Number.FormatInt32(m_value, format, null); + return ToString(format, null); } public string ToString(IFormatProvider? provider) { - return Number.FormatInt32(m_value, null, provider); + return ToString(null, provider); } public string ToString(string? format, IFormatProvider? provider) { - return Number.FormatInt32(m_value, format, provider); + return Number.FormatInt32(m_value, ~0, format, provider); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) { - return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + return Number.TryFormatInt32(m_value, ~0, format, provider, destination, out charsWritten); } public static int Parse(string s) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index ae513d1..d4d22e0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -76,7 +76,7 @@ namespace System public override string ToString() { - return Number.FormatInt64(m_value, null, null); + return Number.Int64ToDecStr(m_value); } public string ToString(IFormatProvider? provider) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index ee9e67d..2317048 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -685,143 +685,165 @@ namespace System return false; } - public static unsafe string FormatInt32(int value, ReadOnlySpan format, IFormatProvider? provider) + private static char GetHexBase(char fmt) { - // Fast path for default format with a non-negative value - if (value >= 0 && format.Length == 0) - { - return UInt32ToDecStr((uint)value, digits: -1); - } + // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase + // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. + return (char)(fmt - ('X' - 'A' + 10)); + } - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + public static string FormatInt32(int value, int hexMask, string? format, IFormatProvider? provider) + { + // Fast path for default format + if (string.IsNullOrEmpty(format)) { return value >= 0 ? - UInt32ToDecStr((uint)value, digits) : - NegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign); + UInt32ToDecStr((uint)value, digits: -1) : + NegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign); } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. - return Int32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[Int32NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength); - - Int32ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + return FormatInt32Slow(value, hexMask, format, provider); - if (fmt != 0) + static unsafe string FormatInt32Slow(int value, int hexMask, string? format, IFormatProvider? provider) + { + ReadOnlySpan formatSpan = format; + char fmt = ParseFormatSpecifier(formatSpan, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') + { + return value >= 0 ? + UInt32ToDecStr((uint)value, digits) : + NegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign); + } + else if (fmtUpper == 'X') { - NumberToString(ref sb, ref number, fmt, digits, info); + return Int32ToHexStr(value & hexMask, GetHexBase(fmt), digits); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[Int32NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength); + + Int32ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, formatSpan, info); + } + return sb.ToString(); } - return sb.ToString(); } } - public static unsafe bool TryFormatInt32(int value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatInt32(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { - // Fast path for default format with a non-negative value - if (value >= 0 && format.Length == 0) - { - return TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten); - } - - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + // Fast path for default format + if (format.Length == 0) { return value >= 0 ? - TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) : - TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten) : + TryNegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. - return TryInt32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[Int32NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength); - - Int32ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + return TryFormatInt32Slow(value, hexMask, format, provider, destination, out charsWritten); - if (fmt != 0) + static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') + { + return value >= 0 ? + TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) : + TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') { - NumberToString(ref sb, ref number, fmt, digits, info); + return TryInt32ToHexStr(value & hexMask, GetHexBase(fmt), digits, destination, out charsWritten); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[Int32NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength); + + Int32ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); } - return sb.TryCopyTo(destination, out charsWritten); } } - public static unsafe string FormatUInt32(uint value, ReadOnlySpan format, IFormatProvider? provider) + public static string FormatUInt32(uint value, string? format, IFormatProvider? provider) { // Fast path for default format - if (format.Length == 0) + if (string.IsNullOrEmpty(format)) { return UInt32ToDecStr(value, digits: -1); } - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') - { - return UInt32ToDecStr(value, digits); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. - return Int32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[UInt32NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength); + return FormatUInt32Slow(value, format, provider); - UInt32ToNumber(value, ref number); - - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); - - if (fmt != 0) + static unsafe string FormatUInt32Slow(uint value, string? format, IFormatProvider? provider) + { + ReadOnlySpan formatSpan = format; + char fmt = ParseFormatSpecifier(formatSpan, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') { - NumberToString(ref sb, ref number, fmt, digits, info); + return UInt32ToDecStr(value, digits); + } + else if (fmtUpper == 'X') + { + return Int32ToHexStr((int)value, GetHexBase(fmt), digits); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[UInt32NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength); + + UInt32ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, formatSpan, info); + } + return sb.ToString(); } - return sb.ToString(); } } - public static unsafe bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { // Fast path for default format if (format.Length == 0) @@ -829,182 +851,197 @@ namespace System return TryUInt32ToDecStr(value, digits: -1, destination, out charsWritten); } - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') - { - return TryUInt32ToDecStr(value, digits, destination, out charsWritten); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. - return TryInt32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[UInt32NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength); - - UInt32ToNumber(value, ref number); - - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + return TryFormatUInt32Slow(value, format, provider, destination, out charsWritten); - if (fmt != 0) + static unsafe bool TryFormatUInt32Slow(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') + { + return TryUInt32ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') { - NumberToString(ref sb, ref number, fmt, digits, info); + return TryInt32ToHexStr((int)value, GetHexBase(fmt), digits, destination, out charsWritten); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[UInt32NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength); + + UInt32ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); } - return sb.TryCopyTo(destination, out charsWritten); } } - public static unsafe string FormatInt64(long value, ReadOnlySpan format, IFormatProvider? provider) + public static string FormatInt64(long value, string? format, IFormatProvider? provider) { - // Fast path for default format with a non-negative value - if (value >= 0 && format.Length == 0) - { - return UInt64ToDecStr((ulong)value, digits: -1); - } - - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + // Fast path for default format + if (string.IsNullOrEmpty(format)) { return value >= 0 ? - UInt64ToDecStr((ulong)value, digits) : - NegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits); + UInt64ToDecStr((ulong)value, digits: -1) : + NegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign); } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - byte* pDigits = stackalloc byte[Int64NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength); + return FormatInt64Slow(value, format, provider); - Int64ToNumber(value, ref number); - - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); - - if (fmt != 0) + static unsafe string FormatInt64Slow(long value, string? format, IFormatProvider? provider) + { + ReadOnlySpan formatSpan = format; + char fmt = ParseFormatSpecifier(formatSpan, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') { - NumberToString(ref sb, ref number, fmt, digits, info); + return value >= 0 ? + UInt64ToDecStr((ulong)value, digits) : + NegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign); + } + else if (fmtUpper == 'X') + { + return Int64ToHexStr(value, GetHexBase(fmt), digits); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[Int64NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength); + + Int64ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, formatSpan, info); + } + return sb.ToString(); } - return sb.ToString(); } } - public static unsafe bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { - // Fast path for default format with a non-negative value - if (value >= 0 && format.Length == 0) - { - return TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten); - } - - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') + // Fast path for default format + if (format.Length == 0) { return value >= 0 ? - TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) : - TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return TryInt64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); + TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten) : + TryNegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - byte* pDigits = stackalloc byte[Int64NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength); + return TryFormatInt64Slow(value, format, provider, destination, out charsWritten); - Int64ToNumber(value, ref number); - - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); - - if (fmt != 0) + static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') { - NumberToString(ref sb, ref number, fmt, digits, info); + return value >= 0 ? + TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) : + TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + return TryInt64ToHexStr(value, GetHexBase(fmt), digits, destination, out charsWritten); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[Int64NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength); + + Int64ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); } - return sb.TryCopyTo(destination, out charsWritten); } } - public static unsafe string FormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider? provider) + public static string FormatUInt64(ulong value, string? format, IFormatProvider? provider) { // Fast path for default format - if (format.Length == 0) + if (string.IsNullOrEmpty(format)) { return UInt64ToDecStr(value, digits: -1); } - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') - { - return UInt64ToDecStr(value, digits); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[UInt64NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength); - - UInt64ToNumber(value, ref number); + return FormatUInt64Slow(value, format, provider); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); - - if (fmt != 0) + static unsafe string FormatUInt64Slow(ulong value, string? format, IFormatProvider? provider) + { + ReadOnlySpan formatSpan = format; + char fmt = ParseFormatSpecifier(formatSpan, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') + { + return UInt64ToDecStr(value, digits); + } + else if (fmtUpper == 'X') { - NumberToString(ref sb, ref number, fmt, digits, info); + return Int64ToHexStr((long)value, GetHexBase(fmt), digits); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[UInt64NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength); + + UInt64ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, formatSpan, info); + } + return sb.ToString(); } - return sb.ToString(); } } - public static unsafe bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { // Fast path for default format if (format.Length == 0) @@ -1012,40 +1049,42 @@ namespace System return TryUInt64ToDecStr(value, digits: -1, destination, out charsWritten); } - char fmt = ParseFormatSpecifier(format, out int digits); - char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison - if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D') - { - return TryUInt64ToDecStr(value, digits, destination, out charsWritten); - } - else if (fmtUpper == 'X') - { - // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase - // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code - // produces lowercase. - return TryInt64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten); - } - else - { - NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); - - byte* pDigits = stackalloc byte[UInt64NumberBufferLength]; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength); + return TryFormatUInt64Slow(value, format, provider, destination, out charsWritten); - UInt64ToNumber(value, ref number); - - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); - - if (fmt != 0) + static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + { + char fmt = ParseFormatSpecifier(format, out int digits); + char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison + if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D') { - NumberToString(ref sb, ref number, fmt, digits, info); + return TryUInt64ToDecStr(value, digits, destination, out charsWritten); + } + else if (fmtUpper == 'X') + { + return TryInt64ToHexStr((long)value, GetHexBase(fmt), digits, destination, out charsWritten); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberFormatInfo info = NumberFormatInfo.GetInstance(provider); + + byte* pDigits = stackalloc byte[UInt64NumberBufferLength]; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength); + + UInt64ToNumber(value, ref number); + + char* stackPtr = stackalloc char[CharStackBufferSize]; + ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + + if (fmt != 0) + { + NumberToString(ref sb, ref number, fmt, digits, info); + } + else + { + NumberToStringFormat(ref sb, ref number, format, info); + } + return sb.TryCopyTo(destination, out charsWritten); } - return sb.TryCopyTo(destination, out charsWritten); } } @@ -1080,6 +1119,13 @@ namespace System number.CheckConsistency(); } + public static string Int32ToDecStr(int value) + { + return value >= 0 ? + UInt32ToDecStr((uint)value, -1) : + NegativeInt32ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign); + } + private static unsafe string NegativeInt32ToDecStr(int value, int digits, string sNegative) { Debug.Assert(value < 0); @@ -1313,6 +1359,13 @@ namespace System number.CheckConsistency(); } + public static string Int64ToDecStr(long value) + { + return value >= 0 ? + UInt64ToDecStr((ulong)value, -1) : + NegativeInt64ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign); + } + private static unsafe string NegativeInt64ToDecStr(long input, int digits, string sNegative) { Debug.Assert(input < 0); diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 0d6d5e6..5618457 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -74,37 +74,27 @@ namespace System // Provides a string representation of a byte. public override string ToString() { - return Number.FormatInt32(m_value, null, null); + return Number.Int32ToDecStr(m_value); } - public string ToString(IFormatProvider? provider) + public string ToString(string? format) { - return Number.FormatInt32(m_value, null, provider); + return ToString(format, null); } - public string ToString(string? format) + public string ToString(IFormatProvider? provider) { - return ToString(format, null); + return ToString(null, provider); } public string ToString(string? format, IFormatProvider? provider) { - if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x000000FF); - return Number.FormatUInt32(temp, format, provider); - } - return Number.FormatInt32(m_value, format, provider); + return Number.FormatInt32(m_value, 0x000000FF, format, provider); } public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) { - if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) - { - uint temp = (uint)(m_value & 0x000000FF); - return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten); - } - return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten); + return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, destination, out charsWritten); } [CLSCompliant(false)]