summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/Version.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/Version.cs')
-rw-r--r--src/mscorlib/shared/System/Version.cs340
1 files changed, 155 insertions, 185 deletions
diff --git a/src/mscorlib/shared/System/Version.cs b/src/mscorlib/shared/System/Version.cs
index a2140ab137..9c66d9b904 100644
--- a/src/mscorlib/shared/System/Version.cs
+++ b/src/mscorlib/shared/System/Version.cs
@@ -197,82 +197,118 @@ namespace System
return accumulator;
}
- public override String ToString()
+ public override string ToString() =>
+ ToString(DefaultFormatFieldCount);
+
+ public string ToString(int fieldCount) =>
+ fieldCount == 0 ? string.Empty :
+ fieldCount == 1 ? _Major.ToString() :
+ StringBuilderCache.GetStringAndRelease(ToCachedStringBuilder(fieldCount));
+
+ public bool TryFormat(Span<char> destination, out int charsWritten) =>
+ TryFormat(destination, DefaultFormatFieldCount, out charsWritten);
+
+ public bool TryFormat(Span<char> destination, int fieldCount, out int charsWritten)
{
- if (_Build == -1) return (ToString(2));
- if (_Revision == -1) return (ToString(3));
- return (ToString(4));
+ if (fieldCount == 0)
+ {
+ charsWritten = 0;
+ return true;
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/22403: fieldCount==1 can just use int.TryFormat
+
+ StringBuilder sb = ToCachedStringBuilder(fieldCount);
+ if (sb.Length <= destination.Length)
+ {
+ sb.CopyTo(0, destination, sb.Length);
+ StringBuilderCache.Release(sb);
+ charsWritten = sb.Length;
+ return true;
+ }
+
+ StringBuilderCache.Release(sb);
+ charsWritten = 0;
+ return false;
}
- public String ToString(int fieldCount)
+ private int DefaultFormatFieldCount =>
+ _Build == -1 ? 2 :
+ _Revision == -1 ? 3 :
+ 4;
+
+ private StringBuilder ToCachedStringBuilder(int fieldCount)
{
- StringBuilder sb;
- switch (fieldCount)
+ if (fieldCount == 1)
{
- case 0:
- return (String.Empty);
- case 1:
- return (_Major.ToString());
- case 2:
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- default:
- if (_Build == -1)
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ return sb;
+ }
+ else if (fieldCount == 2)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ return sb;
+ }
+ else
+ {
+ if (_Build == -1)
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
+ }
- if (fieldCount == 3)
- {
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- sb.Append('.');
- AppendPositiveNumber(_Build, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- }
+ if (fieldCount == 3)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Build, sb);
+ return sb;
+ }
- if (_Revision == -1)
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
+ if (_Revision == -1)
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
+ }
- if (fieldCount == 4)
- {
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- sb.Append('.');
- AppendPositiveNumber(_Build, sb);
- sb.Append('.');
- AppendPositiveNumber(_Revision, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- }
+ if (fieldCount == 4)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Build, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Revision, sb);
+ return sb;
+ }
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
}
}
+ // TODO https://github.com/dotnet/corefx/issues/22616:
+ // Use StringBuilder.Append(int) once it's been updated to use spans internally.
//
- // AppendPositiveNumber is an optimization to append a number to a StringBuilder object without
+ // AppendNonNegativeNumber is an optimization to append a number to a StringBuilder object without
// doing any boxing and not even creating intermediate string.
// Note: as we always have positive numbers then it is safe to convert the number to string
// regardless of the current culture as we'll not have any punctuation marks in the number
- //
- private const int ZERO_CHAR_VALUE = (int)'0';
- private static void AppendPositiveNumber(int num, StringBuilder sb)
+ private static void AppendNonNegativeNumber(int num, StringBuilder sb)
{
Debug.Assert(num >= 0, "AppendPositiveNumber expect positive numbers");
int index = sb.Length;
- int reminder;
-
do
{
- reminder = num % 10;
- num = num / 10;
- sb.Insert(index, (char)(ZERO_CHAR_VALUE + reminder));
+ num = Math.DivRem(num, 10, out int remainder);
+ sb.Insert(index, (char)('0' + remainder));
} while (num > 0);
}
@@ -282,104 +318,108 @@ namespace System
{
throw new ArgumentNullException(nameof(input));
}
- Contract.EndContractBlock();
- VersionResult r = new VersionResult();
- r.Init(nameof(input), true);
- if (!TryParseVersion(input, ref r))
- {
- throw r.GetVersionParseException();
- }
- return r.m_parsedVersion;
+ return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true);
}
- public static bool TryParse(string input, out Version result)
- {
- VersionResult r = new VersionResult();
- r.Init(nameof(input), false);
- bool b = TryParseVersion(input, ref r);
- result = r.m_parsedVersion;
- return b;
- }
+ public static Version Parse(ReadOnlySpan<char> input) =>
+ ParseVersion(input, throwOnFailure: true);
- private static bool TryParseVersion(string version, ref VersionResult result)
+ public static bool TryParse(string input, out Version result)
{
- int major, minor, build, revision;
-
- if ((Object)version == null)
+ if (input == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNullException);
+ result = null;
return false;
}
- String[] parsedComponents = version.Split('.');
- int parsedComponentsLength = parsedComponents.Length;
- if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4))
+ return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null;
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> input, out Version result) =>
+ (result = ParseVersion(input, throwOnFailure: false)) != null;
+
+ private static Version ParseVersion(ReadOnlySpan<char> input, bool throwOnFailure)
+ {
+ // Find the separator between major and minor. It must exist.
+ int majorEnd = input.IndexOf('.');
+ if (majorEnd < 0)
{
- result.SetFailure(ParseFailureKind.ArgumentException);
- return false;
+ if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
+ return null;
}
- if (!TryParseComponent(parsedComponents[0], nameof(version), ref result, out major))
+ // Find the ends of the optional minor and build portions.
+ // We musn't have any separators after build.
+ int buildEnd = -1;
+ int minorEnd = input.IndexOf('.', majorEnd + 1);
+ if (minorEnd != -1)
{
- return false;
+ buildEnd = input.IndexOf('.', minorEnd + 1);
+ if (buildEnd != -1)
+ {
+ if (input.IndexOf('.', buildEnd + 1) != -1)
+ {
+ if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
+ return null;
+ }
+ }
}
- if (!TryParseComponent(parsedComponents[1], nameof(version), ref result, out minor))
+ int major, minor, build, revision;
+
+ // Parse the major version
+ if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major))
{
- return false;
+ return null;
}
- parsedComponentsLength -= 2;
-
- if (parsedComponentsLength > 0)
+ if (minorEnd != -1)
{
- if (!TryParseComponent(parsedComponents[2], "build", ref result, out build))
+ // If there's more than a major and minor, parse the minor, too.
+ if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor))
{
- return false;
+ return null;
}
- parsedComponentsLength--;
-
- if (parsedComponentsLength > 0)
+ if (buildEnd != -1)
{
- if (!TryParseComponent(parsedComponents[3], "revision", ref result, out revision))
- {
- return false;
- }
- else
- {
- result.m_parsedVersion = new Version(major, minor, build, revision);
- }
+ // major.minor.build.revision
+ return
+ TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) &&
+ TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ?
+ new Version(major, minor, build, revision) :
+ null;
}
else
{
- result.m_parsedVersion = new Version(major, minor, build);
+ // major.minor.build
+ return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ?
+ new Version(major, minor, build) :
+ null;
}
}
else
{
- result.m_parsedVersion = new Version(major, minor);
+ // major.minor
+ return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ?
+ new Version(major, minor) :
+ null;
}
-
- return true;
}
- private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent)
+ private static bool TryParseComponent(ReadOnlySpan<char> component, string componentName, bool throwOnFailure, out int parsedComponent)
{
- if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent))
+ if (throwOnFailure)
{
- result.SetFailure(ParseFailureKind.FormatException, component);
- return false;
- }
-
- if (parsedComponent < 0)
- {
- result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName);
- return false;
+ if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0)
+ {
+ throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version);
+ }
+ return true;
}
- return true;
+ return int.TryParse(component, out parsedComponent, NumberStyles.Integer, CultureInfo.InvariantCulture) && parsedComponent >= 0;
}
public static bool operator ==(Version v1, Version v2)
@@ -422,75 +462,5 @@ namespace System
{
return (v2 <= v1);
}
-
- internal enum ParseFailureKind
- {
- ArgumentNullException,
- ArgumentException,
- ArgumentOutOfRangeException,
- FormatException
- }
-
- internal struct VersionResult
- {
- internal Version m_parsedVersion;
- internal ParseFailureKind m_failure;
- internal string m_exceptionArgument;
- internal string m_argumentName;
- internal bool m_canThrow;
-
- internal void Init(string argumentName, bool canThrow)
- {
- m_canThrow = canThrow;
- m_argumentName = argumentName;
- }
-
- internal void SetFailure(ParseFailureKind failure)
- {
- SetFailure(failure, String.Empty);
- }
-
- internal void SetFailure(ParseFailureKind failure, string argument)
- {
- m_failure = failure;
- m_exceptionArgument = argument;
- if (m_canThrow)
- {
- throw GetVersionParseException();
- }
- }
-
- internal Exception GetVersionParseException()
- {
- switch (m_failure)
- {
- case ParseFailureKind.ArgumentNullException:
- return new ArgumentNullException(m_argumentName);
- case ParseFailureKind.ArgumentException:
- return new ArgumentException(SR.Arg_VersionString);
- case ParseFailureKind.ArgumentOutOfRangeException:
- return new ArgumentOutOfRangeException(m_exceptionArgument, SR.ArgumentOutOfRange_Version);
- case ParseFailureKind.FormatException:
- // Regenerate the FormatException as would be thrown by Int32.Parse()
- try
- {
- Int32.Parse(m_exceptionArgument, CultureInfo.InvariantCulture);
- }
- catch (FormatException e)
- {
- return e;
- }
- catch (OverflowException e)
- {
- return e;
- }
- Debug.Assert(false, "Int32.Parse() did not throw exception but TryParse failed: " + m_exceptionArgument);
- return new FormatException(SR.Format_InvalidString);
- default:
- Debug.Assert(false, "Unmatched case in Version.GetVersionParseException() for value: " + m_failure);
- return new ArgumentException(SR.Arg_VersionString);
- }
- }
- }
}
}