Revert Version.TryFormat changes (#56051)
authorStephen Toub <stoub@microsoft.com>
Sat, 24 Jul 2021 13:40:29 +0000 (09:40 -0400)
committerGitHub <noreply@github.com>
Sat, 24 Jul 2021 13:40:29 +0000 (09:40 -0400)
* Revert Version.TryFormat changes to use interpolated strings

This was already optimized to use spans directly.  Using our new span-based interpolated string handler was simpler code, but a few more nanoseconds.  Reverting.

* Tweak original code

Make error handling a bit more clear, and use uint formatting rather than int formatting for small perf boost.

src/libraries/System.Private.CoreLib/src/System/Version.cs

index bf0ce8e..469da80 100644 (file)
@@ -197,33 +197,61 @@ namespace System
 
         public bool TryFormat(Span<char> destination, int fieldCount, out int charsWritten)
         {
-            switch (fieldCount)
+            switch ((uint)fieldCount)
             {
-                case 0:
-                    charsWritten = 0;
-                    return true;
+                case > 4:
+                    ThrowArgumentException("4");
+                    break;
+
+                case >= 3 when _Build == -1:
+                    ThrowArgumentException("2");
+                    break;
 
-                case 1:
-                    return ((uint)_Major).TryFormat(destination, out charsWritten);
+                case 4 when _Revision == -1:
+                    ThrowArgumentException("3");
+                    break;
+
+                static void ThrowArgumentException(string failureUpperBound) =>
+                    throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount));
+            }
 
-                case 2:
-                    return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}", out charsWritten);
+            int totalCharsWritten = 0;
 
-                case 3:
-                    if (_Build == -1) throw CreateBoundException("3");
-                    return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}", out charsWritten);
+            for (int i = 0; i < fieldCount; i++)
+            {
+                if (i != 0)
+                {
+                    if (destination.IsEmpty)
+                    {
+                        charsWritten = 0;
+                        return false;
+                    }
 
-                case 4:
-                    if (_Build == -1) throw CreateBoundException("2");
-                    if (_Revision == -1) throw CreateBoundException("3");
-                    return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}.{(uint)_Revision}", out charsWritten);
+                    destination[0] = '.';
+                    destination = destination.Slice(1);
+                    totalCharsWritten++;
+                }
+
+                int value = i switch
+                {
+                    0 => _Major,
+                    1 => _Minor,
+                    2 => _Build,
+                    _ => _Revision
+                };
+
+                if (!((uint)value).TryFormat(destination, out int valueCharsWritten))
+                {
+                    charsWritten = 0;
+                    return false;
+                }
 
-                default:
-                    throw CreateBoundException("4");
+                totalCharsWritten += valueCharsWritten;
+                destination = destination.Slice(valueCharsWritten);
             }
 
-            static Exception CreateBoundException(string failureUpperBound) =>
-                new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount));
+            charsWritten = totalCharsWritten;
+            return true;
         }
 
         bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>