summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Toub <stoub@microsoft.com>2018-04-04 22:19:52 -0400
committerStephen Toub <stoub@microsoft.com>2018-04-04 23:52:00 -0400
commitb41c8cc8f60e0c05c79f82ccf9aa82a21b41a280 (patch)
tree4ca0531db24cbfaf5edda47e0d34ea8c87ce349a
parent1acd737dc869218f3e8d2a89bf7fb297fd71e232 (diff)
downloadcoreclr-b41c8cc8f60e0c05c79f82ccf9aa82a21b41a280.tar.gz
coreclr-b41c8cc8f60e0c05c79f82ccf9aa82a21b41a280.tar.bz2
coreclr-b41c8cc8f60e0c05c79f82ccf9aa82a21b41a280.zip
Use FormattingHelpers.Count{Hex}Digits in {u}int/long.ToString/TryFormat
Currently we create a temporary buffer on the stack, format into it, and then copy from that stack buffer into either the target span (for TryFormat) or into a new string (for ToString. Following the approach as (and sharing the same code from) Utf8Formatter, where it first counts the number of digits in the output in order to determine an exact length, this commit changes the implementation to skip the temporary buffer and just format directly into the destination span or string. This results in a very measurable performance boost.
-rw-r--r--src/mscorlib/shared/System/Number.Formatting.cs335
1 files changed, 196 insertions, 139 deletions
diff --git a/src/mscorlib/shared/System/Number.Formatting.cs b/src/mscorlib/shared/System/Number.Formatting.cs
index 24d5db1da9..74395ab2f5 100644
--- a/src/mscorlib/shared/System/Number.Formatting.cs
+++ b/src/mscorlib/shared/System/Number.Formatting.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers.Text;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
@@ -246,9 +247,7 @@ namespace System
private const int DoublePrecision = 15;
private const int ScaleNAN = unchecked((int)0x80000000);
private const int ScaleINF = 0x7FFFFFFF;
- private const int MaxUInt32HexDigits = 8;
private const int MaxUInt32DecDigits = 10;
- private const int MaxUInt64DecDigits = 20;
private const int CharStackBufferSize = 32;
private const string PosNumberFormat = "#";
@@ -972,18 +971,20 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length;
- int index = bufferLength;
-
- char* buffer = stackalloc char[bufferLength];
- char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits);
- for (int i = sNegative.Length - 1; i >= 0; i--)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((uint)(-value))) + sNegative.Length;
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
{
- *(--p) = sNegative[i];
- }
+ char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits);
+ Debug.Assert(p == buffer + sNegative.Length);
- Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p);
- return new string(p, 0, (int)(buffer + bufferLength - p));
+ for (int i = sNegative.Length - 1; i >= 0; i--)
+ {
+ *(--p) = sNegative[i];
+ }
+ Debug.Assert(p == buffer);
+ }
+ return result;
}
private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, string sNegative, Span<char> destination, out int charsWritten)
@@ -993,18 +994,26 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferLength = Math.Max(digits, MaxUInt32DecDigits) + sNegative.Length;
- int index = bufferLength;
-
- char* buffer = stackalloc char[bufferLength];
- char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits);
- for (int i = sNegative.Length - 1; i >= 0; i--)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((uint)(-value))) + sNegative.Length;
+ if (bufferLength > destination.Length)
{
- *(--p) = sNegative[i];
+ charsWritten = 0;
+ return false;
}
- Debug.Assert(buffer + bufferLength - p >= 0 && buffer <= p);
- return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten);
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+ {
+ char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits);
+ Debug.Assert(p == buffer + sNegative.Length);
+
+ for (int i = sNegative.Length - 1; i >= 0; i--)
+ {
+ *(--p) = sNegative[i];
+ }
+ Debug.Assert(p == buffer);
+ }
+ return true;
}
private static unsafe string Int32ToHexStr(int value, char hexBase, int digits)
@@ -1012,11 +1021,14 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferLength = Math.Max(digits, MaxUInt32HexDigits);
- char* buffer = stackalloc char[bufferLength];
-
- char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits);
- return new string(p, 0, (int)(buffer + bufferLength - p));
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((uint)value));
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
+ {
+ char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits);
+ Debug.Assert(p == buffer);
+ }
+ return result;
}
private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span<char> destination, out int charsWritten)
@@ -1024,11 +1036,20 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferLength = Math.Max(digits, MaxUInt32HexDigits);
- char* buffer = stackalloc char[bufferLength];
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((uint)value));
+ if (bufferLength > destination.Length)
+ {
+ charsWritten = 0;
+ return false;
+ }
- char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits);
- return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten);
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+ {
+ char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits);
+ Debug.Assert(p == buffer);
+ }
+ return true;
}
private static unsafe char* Int32ToHexChars(char* buffer, uint value, int hexBase, int digits)
@@ -1073,56 +1094,62 @@ namespace System
private static unsafe string UInt32ToDecStr(uint value, int digits)
{
- if (digits <= 1)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
{
- char* buffer = stackalloc char[MaxUInt32DecDigits];
-
- char* start = buffer + MaxUInt32DecDigits;
- char* p = start;
- do
+ char* p = buffer + bufferLength;
+ if (digits <= 1)
+ {
+ do
+ {
+ // TODO https://github.com/dotnet/coreclr/issues/3439
+ uint div = value / 10;
+ *(--p) = (char)('0' + value - (div * 10));
+ value = div;
+ }
+ while (value != 0);
+ }
+ else
{
- // TODO https://github.com/dotnet/coreclr/issues/3439
- uint div = value / 10;
- *(--p) = (char)('0' + value - (div * 10));
- value = div;
+ p = UInt32ToDecChars(p, value, digits);
}
- while (value != 0);
-
- return new string(p, 0, (int)(start - p));
- }
- else
- {
- int bufferSize = Math.Max(digits, MaxUInt32DecDigits);
- char* buffer = stackalloc char[bufferSize];
- char* p = UInt32ToDecChars(buffer + bufferSize, value, digits);
- return new string(p, 0, (int)(buffer + bufferSize - p));
+ Debug.Assert(p == buffer);
}
+ return result;
}
private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span<char> destination, out int charsWritten)
{
- if (digits <= 1)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+ if (bufferLength > destination.Length)
{
- char* buffer = stackalloc char[MaxUInt32DecDigits];
- char* start = buffer + MaxUInt32DecDigits;
- char* p = start;
- do
- {
- // TODO https://github.com/dotnet/coreclr/issues/3439
- uint div = value / 10;
- *(--p) = (char)('0' + value - (div * 10));
- value = div;
- }
- while (value != 0);
- return TryCopyTo(p, (int)(start - p), destination, out charsWritten);
+ charsWritten = 0;
+ return false;
}
- else
+
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
{
- int bufferSize = Math.Max(digits, MaxUInt32DecDigits);
- char* buffer = stackalloc char[bufferSize];
- char* p = UInt32ToDecChars(buffer + bufferSize, value, digits);
- return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten);
+ char* p = buffer + bufferLength;
+ if (digits <= 1)
+ {
+ do
+ {
+ // TODO https://github.com/dotnet/coreclr/issues/3439
+ uint div = value / 10;
+ *(--p) = (char)('0' + value - (div * 10));
+ value = div;
+ }
+ while (value != 0);
+ }
+ else
+ {
+ p = UInt32ToDecChars(p, value, digits);
+ }
+ Debug.Assert(p == buffer);
}
+ return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -1176,24 +1203,26 @@ namespace System
ulong value = (ulong)(-input);
- int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length;
- int index = bufferLength;
-
- char* buffer = stackalloc char[bufferLength];
- char* p = buffer + bufferLength;
- while (High32(value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value)) + sNegative.Length;
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
{
- p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
- digits -= 9;
- }
- p = UInt32ToDecChars(p, Low32(value), digits);
+ char* p = buffer + bufferLength;
+ while (High32(value) != 0)
+ {
+ p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
+ digits -= 9;
+ }
+ p = UInt32ToDecChars(p, Low32(value), digits);
+ Debug.Assert(p == buffer + sNegative.Length);
- for (int i = sNegative.Length - 1; i >= 0; i--)
- {
- *(--p) = sNegative[i];
+ for (int i = sNegative.Length - 1; i >= 0; i--)
+ {
+ *(--p) = sNegative[i];
+ }
+ Debug.Assert(p == buffer);
}
-
- return new string(p, 0, (int)(buffer + bufferLength - p));
+ return result;
}
private static unsafe bool TryNegativeInt64ToDecStr(long input, int digits, string sNegative, Span<char> destination, out int charsWritten)
@@ -1207,64 +1236,80 @@ namespace System
ulong value = (ulong)(-input);
- int bufferLength = Math.Max(digits, MaxUInt64DecDigits) + sNegative.Length;
- int index = bufferLength;
-
- char* buffer = stackalloc char[bufferLength];
- char* p = buffer + bufferLength;
- while (High32(value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((ulong)(-input))) + sNegative.Length;
+ if (bufferLength > destination.Length)
{
- p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
- digits -= 9;
+ charsWritten = 0;
+ return false;
}
- p = UInt32ToDecChars(p, Low32(value), digits);
- for (int i = sNegative.Length - 1; i >= 0; i--)
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
{
- *(--p) = sNegative[i];
- }
+ char* p = buffer + bufferLength;
+ while (High32(value) != 0)
+ {
+ p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
+ digits -= 9;
+ }
+ p = UInt32ToDecChars(p, Low32(value), digits);
+ Debug.Assert(p == buffer + sNegative.Length);
- return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten);
+ for (int i = sNegative.Length - 1; i >= 0; i--)
+ {
+ *(--p) = sNegative[i];
+ }
+ Debug.Assert(p == buffer);
+ }
+ return true;
}
private static unsafe string Int64ToHexStr(long value, char hexBase, int digits)
{
- int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2);
- char* buffer = stackalloc char[bufferLength];
- int index = bufferLength;
-
- char* p;
- if (High32((ulong)value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((ulong)value));
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
{
- p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8);
- p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8);
- }
- else
- {
- p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1));
+ char* p = buffer + bufferLength;
+ if (High32((ulong)value) != 0)
+ {
+ p = Int32ToHexChars(p, Low32((ulong)value), hexBase, 8);
+ p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8);
+ }
+ else
+ {
+ p = Int32ToHexChars(p, Low32((ulong)value), hexBase, Math.Max(digits, 1));
+ }
+ Debug.Assert(p == buffer);
}
-
- return new string(p, 0, (int)(buffer + bufferLength - p));
+ return result;
}
private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span<char> destination, out int charsWritten)
{
- int bufferLength = Math.Max(digits, MaxUInt32HexDigits * 2);
- char* buffer = stackalloc char[bufferLength];
- int index = bufferLength;
-
- char* p;
- if (High32((ulong)value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((ulong)value));
+ if (bufferLength > destination.Length)
{
- p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, 8);
- p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8);
+ charsWritten = 0;
+ return false;
}
- else
+
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
{
- p = Int32ToHexChars(buffer + index, Low32((ulong)value), hexBase, Math.Max(digits, 1));
+ char* p = buffer + bufferLength;
+ if (High32((ulong)value) != 0)
+ {
+ p = Int32ToHexChars(p, Low32((ulong)value), hexBase, 8);
+ p = Int32ToHexChars(p, High32((ulong)value), hexBase, digits - 8);
+ }
+ else
+ {
+ p = Int32ToHexChars(p, Low32((ulong)value), hexBase, Math.Max(digits, 1));
+ }
+ Debug.Assert(p == buffer);
}
-
- return TryCopyTo(p, (int)(buffer + bufferLength - p), destination, out charsWritten);
+ return true;
}
private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number)
@@ -1293,17 +1338,20 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferSize = Math.Max(digits, MaxUInt64DecDigits);
- char* buffer = stackalloc char[bufferSize];
- char* p = buffer + bufferSize;
- while (High32(value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+ string result = string.FastAllocateString(bufferLength);
+ fixed (char* buffer = result)
{
- p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
- digits -= 9;
+ char* p = buffer + bufferLength;
+ while (High32(value) != 0)
+ {
+ p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
+ digits -= 9;
+ }
+ p = UInt32ToDecChars(p, Low32(value), digits);
+ Debug.Assert(p == buffer);
}
- p = UInt32ToDecChars(p, Low32(value), digits);
-
- return new string(p, 0, (int)(buffer + bufferSize - p));
+ return result;
}
private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span<char> destination, out int charsWritten)
@@ -1311,17 +1359,26 @@ namespace System
if (digits < 1)
digits = 1;
- int bufferSize = Math.Max(digits, MaxUInt64DecDigits);
- char* buffer = stackalloc char[bufferSize];
- char* p = buffer + bufferSize;
- while (High32(value) != 0)
+ int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+ if (bufferLength > destination.Length)
{
- p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
- digits -= 9;
+ charsWritten = 0;
+ return false;
}
- p = UInt32ToDecChars(p, Low32(value), digits);
- return TryCopyTo(p, (int)(buffer + bufferSize - p), destination, out charsWritten);
+ charsWritten = bufferLength;
+ fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+ {
+ char* p = buffer + bufferLength;
+ while (High32(value) != 0)
+ {
+ p = UInt32ToDecChars(p, Int64DivMod1E9(ref value), 9);
+ digits -= 9;
+ }
+ p = UInt32ToDecChars(p, Low32(value), digits);
+ Debug.Assert(p == buffer);
+ }
+ return true;
}
internal static unsafe char ParseFormatSpecifier(ReadOnlySpan<char> format, out int digits)