| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- // Licensed to the .NET Foundation under one or more agreements.
- // 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.Globalization;
- using System.Diagnostics;
- using System.Text;
- using System.Runtime.CompilerServices;
- namespace System
- {
- // A Version object contains four hierarchical numeric components: major, minor,
- // build and revision. Build and revision may be unspecified, which is represented
- // internally as a -1. By definition, an unspecified component matches anything
- // (both unspecified and specified), and an unspecified component is "less than" any
- // specified component.
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public sealed class Version : ICloneable, IComparable, IComparable<Version>, IEquatable<Version>, ISpanFormattable
- {
- // AssemblyName depends on the order staying the same
- private readonly int _Major; // Do not rename (binary serialization)
- private readonly int _Minor; // Do not rename (binary serialization)
- private readonly int _Build = -1; // Do not rename (binary serialization)
- private readonly int _Revision = -1; // Do not rename (binary serialization)
- public Version(int major, int minor, int build, int revision)
- {
- if (major < 0)
- throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
- if (minor < 0)
- throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
- if (build < 0)
- throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version);
- if (revision < 0)
- throw new ArgumentOutOfRangeException(nameof(revision), SR.ArgumentOutOfRange_Version);
- _Major = major;
- _Minor = minor;
- _Build = build;
- _Revision = revision;
- }
- public Version(int major, int minor, int build)
- {
- if (major < 0)
- throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
- if (minor < 0)
- throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
- if (build < 0)
- throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version);
- _Major = major;
- _Minor = minor;
- _Build = build;
- }
- public Version(int major, int minor)
- {
- if (major < 0)
- throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
- if (minor < 0)
- throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
- _Major = major;
- _Minor = minor;
- }
- public Version(string version)
- {
- Version v = Version.Parse(version);
- _Major = v.Major;
- _Minor = v.Minor;
- _Build = v.Build;
- _Revision = v.Revision;
- }
- public Version()
- {
- _Major = 0;
- _Minor = 0;
- }
- private Version(Version version)
- {
- Debug.Assert(version != null);
- _Major = version._Major;
- _Minor = version._Minor;
- _Build = version._Build;
- _Revision = version._Revision;
- }
- public object Clone()
- {
- return new Version(this);
- }
- // Properties for setting and getting version numbers
- public int Major
- {
- get { return _Major; }
- }
- public int Minor
- {
- get { return _Minor; }
- }
- public int Build
- {
- get { return _Build; }
- }
- public int Revision
- {
- get { return _Revision; }
- }
- public short MajorRevision
- {
- get { return (short)(_Revision >> 16); }
- }
- public short MinorRevision
- {
- get { return (short)(_Revision & 0xFFFF); }
- }
- public int CompareTo(object version)
- {
- if (version == null)
- {
- return 1;
- }
- Version v = version as Version;
- if (v == null)
- {
- throw new ArgumentException(SR.Arg_MustBeVersion);
- }
- return CompareTo(v);
- }
- public int CompareTo(Version value)
- {
- return
- object.ReferenceEquals(value, this) ? 0 :
- value is null ? 1 :
- _Major != value._Major ? (_Major > value._Major ? 1 : -1) :
- _Minor != value._Minor ? (_Minor > value._Minor ? 1 : -1) :
- _Build != value._Build ? (_Build > value._Build ? 1 : -1) :
- _Revision != value._Revision ? (_Revision > value._Revision ? 1 : -1) :
- 0;
- }
- public override bool Equals(object obj)
- {
- return Equals(obj as Version);
- }
- public bool Equals(Version obj)
- {
- return object.ReferenceEquals(obj, this) ||
- (!(obj is null) &&
- _Major == obj._Major &&
- _Minor == obj._Minor &&
- _Build == obj._Build &&
- _Revision == obj._Revision);
- }
- public override int GetHashCode()
- {
- // Let's assume that most version numbers will be pretty small and just
- // OR some lower order bits together.
- int accumulator = 0;
- accumulator |= (_Major & 0x0000000F) << 28;
- accumulator |= (_Minor & 0x000000FF) << 20;
- accumulator |= (_Build & 0x000000FF) << 12;
- accumulator |= (_Revision & 0x00000FFF);
- return accumulator;
- }
- 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 (fieldCount == 0)
- {
- charsWritten = 0;
- return true;
- }
- else if (fieldCount == 1)
- {
- return _Major.TryFormat(destination, out charsWritten);
- }
- 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;
- }
- bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
- {
- // format and provider are ignored.
- return TryFormat(destination, out charsWritten);
- }
- private int DefaultFormatFieldCount =>
- _Build == -1 ? 2 :
- _Revision == -1 ? 3 :
- 4;
- private StringBuilder ToCachedStringBuilder(int fieldCount)
- {
- // 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.
- if (fieldCount == 2)
- {
- StringBuilder sb = StringBuilderCache.Acquire();
- sb.Append(_Major);
- sb.Append('.');
- sb.Append(_Minor);
- return sb;
- }
- else
- {
- if (_Build == -1)
- {
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
- }
- if (fieldCount == 3)
- {
- StringBuilder sb = StringBuilderCache.Acquire();
- sb.Append(_Major);
- sb.Append('.');
- sb.Append(_Minor);
- sb.Append('.');
- sb.Append(_Build);
- return sb;
- }
- if (_Revision == -1)
- {
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
- }
- if (fieldCount == 4)
- {
- StringBuilder sb = StringBuilderCache.Acquire();
- sb.Append(_Major);
- sb.Append('.');
- sb.Append(_Minor);
- sb.Append('.');
- sb.Append(_Build);
- sb.Append('.');
- sb.Append(_Revision);
- return sb;
- }
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
- }
- }
- public static Version Parse(string input)
- {
- if (input == null)
- {
- throw new ArgumentNullException(nameof(input));
- }
- return ParseVersion(input.AsSpan(), throwOnFailure: true);
- }
- public static Version Parse(ReadOnlySpan<char> input) =>
- ParseVersion(input, throwOnFailure: true);
- public static bool TryParse(string input, out Version result)
- {
- if (input == null)
- {
- result = null;
- return false;
- }
- return (result = ParseVersion(input.AsSpan(), 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)
- {
- if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
- return null;
- }
- // Find the ends of the optional minor and build portions.
- // We musn't have any separators after build.
- int buildEnd = -1;
- int minorEnd = input.Slice(majorEnd + 1).IndexOf('.');
- if (minorEnd != -1)
- {
- minorEnd += (majorEnd + 1);
- buildEnd = input.Slice(minorEnd + 1).IndexOf('.');
- if (buildEnd != -1)
- {
- buildEnd += (minorEnd + 1);
- if (input.Slice(buildEnd + 1).Contains('.'))
- {
- if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
- return null;
- }
- }
- }
- int minor, build, revision;
- // Parse the major version
- if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out int major))
- {
- return null;
- }
- if (minorEnd != -1)
- {
- // 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 null;
- }
- if (buildEnd != -1)
- {
- // 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
- {
- // major.minor.build
- return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ?
- new Version(major, minor, build) :
- null;
- }
- }
- else
- {
- // major.minor
- return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ?
- new Version(major, minor) :
- null;
- }
- }
- private static bool TryParseComponent(ReadOnlySpan<char> component, string componentName, bool throwOnFailure, out int parsedComponent)
- {
- if (throwOnFailure)
- {
- if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0)
- {
- throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version);
- }
- return true;
- }
- return int.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent) && parsedComponent >= 0;
- }
- // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(Version v1, Version v2)
- {
- // Test "right" first to allow branch elimination when inlined for null checks (== null)
- // so it can become a simple test
- if (v2 is null)
- {
- // return true/false not the test result https://github.com/dotnet/coreclr/issues/914
- return (v1 is null) ? true : false;
- }
- // Quick reference equality test prior to calling the virtual Equality
- return ReferenceEquals(v2, v1) ? true : v2.Equals(v1);
- }
- public static bool operator !=(Version v1, Version v2)
- {
- return !(v1 == v2);
- }
- public static bool operator <(Version v1, Version v2)
- {
- if ((object)v1 == null)
- throw new ArgumentNullException(nameof(v1));
- return (v1.CompareTo(v2) < 0);
- }
- public static bool operator <=(Version v1, Version v2)
- {
- if ((object)v1 == null)
- throw new ArgumentNullException(nameof(v1));
- return (v1.CompareTo(v2) <= 0);
- }
- public static bool operator >(Version v1, Version v2)
- {
- return (v2 < v1);
- }
- public static bool operator >=(Version v1, Version v2)
- {
- return (v2 <= v1);
- }
- }
- }
|