IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
RuntimeVersion.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Immutable;
3using System.Linq;
4using System.Numerics;
5using System.Text;
6using System.Text.RegularExpressions;
7
9{
10
15 internal readonly struct RuntimeVersion : IComparable<RuntimeVersion>
16 {
17
21 static class VersionPattern
22 {
23
24 // $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
25 // RE limits the format of version strings
26 // ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
27 static readonly string VNUM = "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
28 static readonly string PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
29 static readonly string BUILD = "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
30 static readonly string OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
31 static readonly string VSTR_FORMAT = VNUM + PRE + BUILD + OPT;
32
33 public static readonly string VNUM_GROUP = "VNUM";
34 public static readonly string PRE_GROUP = "PRE";
35 public static readonly string PLUS_GROUP = "PLUS";
36 public static readonly string BUILD_GROUP = "BUILD";
37 public static readonly string OPT_GROUP = "OPT";
38
39 public static readonly Regex VSTR_PATTERN = new Regex(VSTR_FORMAT, RegexOptions.Compiled);
40
41 }
42
49 public static RuntimeVersion Parse(string value)
50 {
51 if (IsSimpleNumber(value.AsSpan()))
52 return new RuntimeVersion([int.Parse(value)], null, null, null);
53
54 var m = VersionPattern.VSTR_PATTERN.Match(value);
55 if (m.Success == false)
56 throw new ArgumentException("Invalid version string: '" + value.ToString() + "'", nameof(value));
57
58 // version is a dot-separated list of integers of arbitrary length
59 var split = m.Groups[VersionPattern.VNUM_GROUP].Value.Split('.');
60 var version = ImmutableArray.CreateBuilder<int>(split.Length);
61 for (int i = 0; i < split.Length; i++)
62 version.Add(int.Parse(split[i]));
63
64 var preGroup = m.Groups[VersionPattern.PRE_GROUP];
65 var pre = preGroup.Success ? preGroup.Value : null;
66
67 var buildGroup = m.Groups[VersionPattern.BUILD_GROUP];
68 var build = buildGroup.Success ? (int?)int.Parse(buildGroup.Value) : null;
69
70 var optionalGroup = m.Groups[VersionPattern.OPT_GROUP];
71 var optional = optionalGroup.Success ? optionalGroup.Value : null;
72
73 // empty '+'
74 if (build is null)
75 {
76 if (m.Groups[VersionPattern.PLUS_GROUP].Success)
77 {
78 if (optional is not null)
79 {
80 if (pre is not null)
81 throw new ArgumentException($"'+' found with pre-release and optional components: '{value}'");
82 }
83 else
84 {
85 throw new ArgumentException($"'+' found with neither build or optional components: '{value}'");
86 }
87 }
88 else
89 {
90 if (optional is not null && pre is null)
91 throw new ArgumentException($"optional component must be preceded by a pre-release component or '+': '{value}'");
92 }
93 }
94
95 return new RuntimeVersion(version.DrainToImmutable(), pre, build, optional);
96 }
97
103 static bool IsSimpleNumber(ReadOnlySpan<char> s)
104 {
105 for (int i = 0; i < s.Length; i++)
106 {
107 var c = s[i];
108 var lowerBound = (i > 0) ? '0' : '1';
109 if (c < lowerBound || c > '9')
110 return false;
111 }
112
113 return true;
114 }
115
116 readonly ImmutableArray<int> _version;
117 readonly string? _pre;
118 readonly int? _build;
119 readonly string? _optional;
120
128 public RuntimeVersion(ImmutableArray<int> version, string? pre, int? build, string? optional)
129 {
130 _version = version;
131 _pre = pre;
132 _build = build;
133 _optional = optional;
134 }
135
139 public readonly int Feature => _version[0];
140
144 public readonly int Interim => _version.Length > 1 ? _version[1] : 0;
145
149 public readonly int Update => _version.Length > 2 ? _version[2] : 0;
150
154 public ImmutableArray<int> Version => _version;
155
159 public readonly string? Pre => _pre;
160
164 public readonly int? Build => _build;
165
169 public readonly string? Optional => _optional;
170
172 public override readonly bool Equals(object? obj)
173 {
174 return obj is RuntimeVersion other && Equals(other);
175 }
176
182 public readonly bool Equals(RuntimeVersion other)
183 {
184 return
185 _version.SequenceEqual(other._version) &&
186 _pre == other._pre &&
187 _build == other._build;
188 }
189
191 public override readonly int GetHashCode()
192 {
193 var hc = new HashCode();
194
195 foreach (var i in _version)
196 hc.Add(i);
197
198 hc.Add(_pre);
199 hc.Add(_build);
200 hc.Add(_optional);
201
202 return hc.ToHashCode();
203 }
204
206 public int CompareTo(RuntimeVersion other)
207 {
208 return CompareTo(other, false);
209 }
210
217 readonly int CompareTo(RuntimeVersion other, bool ignoreOptional)
218 {
219 int ret = CompareVersion(other);
220 if (ret != 0)
221 return ret;
222
223 ret = ComparePre(other);
224 if (ret != 0)
225 return ret;
226
227 ret = CompareBuild(other);
228 if (ret != 0)
229 return ret;
230
231 if (ignoreOptional == false)
232 return CompareOptional(other);
233
234 return 0;
235 }
236
242 readonly int CompareVersion(RuntimeVersion other)
243 {
244 int size = _version.Length;
245 int oSize = other._version.Length;
246 int min = Math.Min(size, oSize);
247 for (int i = 0; i < min; i++)
248 {
249 int val = _version[i];
250 int oVal = other._version[i];
251 if (val != oVal)
252 return val - oVal;
253 }
254
255 return size - oSize;
256 }
257
263 readonly int ComparePre(RuntimeVersion other)
264 {
265 if (_pre is null)
266 {
267 if (other._pre is not null)
268 return 1;
269 }
270 else
271 {
272 if (other._pre is null)
273 return -1;
274
275 if (Regex.IsMatch(_pre, "\\d+"))
276 {
277 return Regex.IsMatch(other._pre, "\\d+")
278 ? (BigInteger.Parse(_pre)).CompareTo(BigInteger.Parse(other._pre))
279 : -1;
280 }
281 else
282 {
283 return Regex.IsMatch(other._pre, "\\d+")
284 ? 1
285 : _pre.CompareTo(other._pre);
286 }
287 }
288
289 return 0;
290 }
291
297 readonly int CompareBuild(RuntimeVersion obj)
298 {
299 if (obj._build is not null)
300 {
301 return _build is not null
302 ? _build.Value.CompareTo(obj._build.Value)
303 : -1;
304 }
305 else if (_build is not null)
306 {
307 return 1;
308 }
309
310 return 0;
311 }
312
318 readonly int CompareOptional(RuntimeVersion other)
319 {
320 if (_optional is null)
321 {
322 if (other._optional is not null)
323 return -1;
324 }
325 else
326 {
327 if (other._optional is null)
328 return 1;
329
330 return _optional.CompareTo(other._optional);
331 }
332
333 return 0;
334 }
335
337 public override readonly string? ToString()
338 {
339 var sb = new ValueStringBuilder(16);
340 sb.Append(string.Join(".", _version));
341
342 if (_pre is not null)
343 {
344 sb.Append("-");
345 sb.Append(_pre);
346 }
347
348 if (_build is not null)
349 {
350 sb.Append("+");
351 sb.Append(_build.ToString());
352
353 if (_optional is not null)
354 {
355 sb.Append("-");
356 sb.Append(_optional);
357 }
358 }
359 else
360 {
361 if (_optional is not null)
362 {
363 sb.Append(_pre is not null ? "-" : "+-");
364 sb.Append(_optional);
365 }
366 }
367
368 return sb.ToString();
369 }
370
371 }
372
373}
Various utilities for parsing versions.