From ee0c1b47781fd0ea48ec174c00552735452e1844 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 17 Oct 2022 14:19:24 +0000 Subject: [PATCH 1/5] Use span directly when available --- src/Avalonia.Base/Media/Color.cs | 5 +- src/Avalonia.Base/Utilities/SpanHelpers.cs | 31 ++++++++++ src/Avalonia.Build.Tasks/SpanCompat.cs | 61 +++++++++++++++++++ .../Markup/Parsers/SelectorGrammar.cs | 18 +++--- 4 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 src/Avalonia.Base/Utilities/SpanHelpers.cs diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index cb90404f6d..aee048cc99 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -9,6 +9,7 @@ using System; using System.Globalization; #if !BUILDTASK using Avalonia.Animation.Animators; +using static Avalonia.Utilities.SpanHelpers; #endif namespace Avalonia.Media @@ -295,9 +296,7 @@ namespace Avalonia.Media return false; } - // TODO: (netstandard 2.1) Can use allocation free parsing. - if (!uint.TryParse(input.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out var parsed)) + if (!input.TryParseFromHexToUInt(out var parsed)) { return false; } diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs new file mode 100644 index 0000000000..e4fab5ac7f --- /dev/null +++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs @@ -0,0 +1,31 @@ +using System; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace Avalonia.Utilities +{ + public static class SpanHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseFromHexToUInt(this ReadOnlySpan span, out uint value) + { +#if NETSTANDARD2_0 + return uint.TryParse(span.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out value); +#else + return uint.TryParse(span, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out value); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseToInt(this ReadOnlySpan span, out int value) + { +#if NETSTANDARD2_0 + return int.TryParse(span.ToString(), out value); +#else + return int.TryParse(span, out value); +#endif + } + } +} diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index 25f8d0175a..6da1fbd07c 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -1,4 +1,7 @@ #if !NETCOREAPP3_1_OR_GREATER +using System.Globalization; +using System.Runtime.CompilerServices; + namespace System { // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory @@ -9,6 +12,8 @@ namespace System private int _length; public int Length => _length; + public static implicit operator ReadOnlySpan(string s) => new ReadOnlySpan(s); + public ReadOnlySpan(string s) : this(s, 0, s.Length) { @@ -63,8 +68,64 @@ namespace System return Slice(start); } + public ReadOnlySpan TrimEnd() + { + int end = Length - 1; + for (; end >= 0; end--) + { + if (!char.IsWhiteSpace(this[end])) + { + break; + } + } + return Slice(0, end + 1); + } + + public ReadOnlySpan Trim() + { + return TrimStart().TrimEnd(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryParseFromHexToUInt(out uint value) + { + return uint.TryParse(ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryParseToInt(out int value) + { + return int.TryParse(ToString(), out value); + } + public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); + internal int IndexOf(string v, StringComparison ordinal, int start = 0) + { + if(Length == 0 || string.IsNullOrEmpty(v)) + { + return -1; + } + + for (var c = start; c < _length; c++) + { + if (this[c] == v[0]) + { + for(var i = 0; i < v.Length; i++) + { + if (this[c + i] != v[i]) + { + break; + } + } + return c; + } + } + + return -1; + } + public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr)); } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index 16856e674d..a5e11cd233 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -376,28 +376,28 @@ namespace Avalonia.Markup.Parsers if (r.Peek == 'o') { - var constArg = r.TakeUntil(')').ToString().Trim(); - if (constArg.Equals("odd", StringComparison.Ordinal)) + var constArg = r.TakeUntil(')').Trim(); + if (constArg.SequenceEqual("odd".AsSpan())) { step = 2; offset = 1; } else { - throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg}'."); + throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg.ToString()}'."); } } else if (r.Peek == 'e') { - var constArg = r.TakeUntil(')').ToString().Trim(); - if (constArg.Equals("even", StringComparison.Ordinal)) + var constArg = r.TakeUntil(')').Trim(); + if (constArg.SequenceEqual("even".AsSpan())) { step = 2; offset = 0; } else { - throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg}'."); + throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg.ToString()}'."); } } else @@ -405,7 +405,7 @@ namespace Avalonia.Markup.Parsers r.SkipWhitespace(); var stepOrOffset = 0; - var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+').ToString(); + var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+'); if (stepOrOffsetStr.Length == 0 || (stepOrOffsetStr.Length == 1 && stepOrOffsetStr[0] == '+')) @@ -417,7 +417,7 @@ namespace Avalonia.Markup.Parsers { stepOrOffset = -1; } - else if (!int.TryParse(stepOrOffsetStr.ToString(), out stepOrOffset)) + else if (!stepOrOffsetStr.TryParseToInt(out stepOrOffset)) { throw new ExpressionParseException(r.Position, "Couldn't parse nth-child step or offset value. Integer was expected."); } @@ -462,7 +462,7 @@ namespace Avalonia.Markup.Parsers r.SkipWhitespace(); if (sign != 0 - && !int.TryParse(r.TakeUntil(')').ToString(), out offset)) + && !r.TakeUntil(')').TryParseToInt(out offset)) { throw new ExpressionParseException(r.Position, "Couldn't parse nth-child offset value. Integer was expected."); } From 868e5a5488e3e0d4a6954990fd61df5de08cbc2d Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 17 Oct 2022 14:45:37 +0000 Subject: [PATCH 2/5] switch to using helper in the color classes --- src/Avalonia.Base/Media/Color.cs | 44 ++++++++-------------- src/Avalonia.Base/Media/HslColor.cs | 30 ++++++--------- src/Avalonia.Base/Media/HsvColor.cs | 30 ++++++--------- src/Avalonia.Base/Utilities/SpanHelpers.cs | 24 ++++++++++++ src/Avalonia.Build.Tasks/SpanCompat.cs | 18 ++++++++- 5 files changed, 80 insertions(+), 66 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index aee048cc99..14ef357393 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -381,9 +381,9 @@ namespace Avalonia.Media if (components.Length == 3) // RGB { - if (InternalTryParseByte(components[0], out byte red) && - InternalTryParseByte(components[1], out byte green) && - InternalTryParseByte(components[2], out byte blue)) + if (InternalTryParseByte(components[0].AsSpan(), out byte red) && + InternalTryParseByte(components[1].AsSpan(), out byte green) && + InternalTryParseByte(components[2].AsSpan(), out byte blue)) { color = new Color(0xFF, red, green, blue); return true; @@ -391,10 +391,10 @@ namespace Avalonia.Media } else if (components.Length == 4) // RGBA { - if (InternalTryParseByte(components[0], out byte red) && - InternalTryParseByte(components[1], out byte green) && - InternalTryParseByte(components[2], out byte blue) && - InternalTryParseDouble(components[3], out double alpha)) + if (InternalTryParseByte(components[0].AsSpan(), out byte red) && + InternalTryParseByte(components[1].AsSpan(), out byte green) && + InternalTryParseByte(components[2].AsSpan(), out byte blue) && + InternalTryParseDouble(components[3].AsSpan(), out double alpha)) { color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue); return true; @@ -402,17 +402,14 @@ namespace Avalonia.Media } // Local function to specially parse a byte value with an optional percentage sign - bool InternalTryParseByte(string inString, out byte outByte) + bool InternalTryParseByte(ReadOnlySpan inString, out byte outByte) { // The percent sign, if it exists, must be at the end of the number - int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal); if (percentIndex >= 0) { - var result = double.TryParse( - inString.Substring(0, percentIndex), - NumberStyles.Number, - CultureInfo.InvariantCulture, + var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( out double percentage); outByte = (byte)Math.Round((percentage / 100.0) * 255.0); @@ -420,37 +417,28 @@ namespace Avalonia.Media } else { - return byte.TryParse( - inString, - NumberStyles.Number, - CultureInfo.InvariantCulture, + return inString.TryParseNumberToByte( out outByte); } } // Local function to specially parse a double value with an optional percentage sign - bool InternalTryParseDouble(string inString, out double outDouble) + bool InternalTryParseDouble(ReadOnlySpan inString, out double outDouble) { // The percent sign, if it exists, must be at the end of the number - int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal); if (percentIndex >= 0) { - var result = double.TryParse( - inString.Substring(0, percentIndex), - NumberStyles.Number, - CultureInfo.InvariantCulture, - out double percentage); + var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + out double percentage); outDouble = percentage / 100.0; return result; } else { - return double.TryParse( - inString, - NumberStyles.Number, - CultureInfo.InvariantCulture, + return inString.TryParseNumberToDouble( out outDouble); } } diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index 485bb1db16..5a9e5cd54b 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -302,9 +302,9 @@ namespace Avalonia.Media if (components.Length == 3) // HSL { - if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && - TryInternalParse(components[1], out double saturation) && - TryInternalParse(components[2], out double lightness)) + if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + TryInternalParse(components[1].AsSpan(), out double saturation) && + TryInternalParse(components[2].AsSpan(), out double lightness)) { hslColor = new HslColor(1.0, hue, saturation, lightness); return true; @@ -312,10 +312,10 @@ namespace Avalonia.Media } else if (components.Length == 4) // HSLA { - if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && - TryInternalParse(components[1], out double saturation) && - TryInternalParse(components[2], out double lightness) && - TryInternalParse(components[3], out double alpha)) + if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + TryInternalParse(components[1].AsSpan(), out double saturation) && + TryInternalParse(components[2].AsSpan(), out double lightness) && + TryInternalParse(components[3].AsSpan(), out double alpha)) { hslColor = new HslColor(alpha, hue, saturation, lightness); return true; @@ -323,28 +323,22 @@ namespace Avalonia.Media } // Local function to specially parse a double value with an optional percentage sign - bool TryInternalParse(string inString, out double outDouble) + bool TryInternalParse(ReadOnlySpan inString, out double outDouble) { // The percent sign, if it exists, must be at the end of the number - int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal); if (percentIndex >= 0) { - var result = double.TryParse( - inString.Substring(0, percentIndex), - NumberStyles.Number, - CultureInfo.InvariantCulture, - out double percentage); + var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + out double percentage); outDouble = percentage / 100.0; return result; } else { - return double.TryParse( - inString, - NumberStyles.Number, - CultureInfo.InvariantCulture, + return inString.TryParseNumberToDouble( out outDouble); } } diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 512e57ae07..30a6c7ef4e 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -302,9 +302,9 @@ namespace Avalonia.Media if (components.Length == 3) // HSV { - if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && - TryInternalParse(components[1], out double saturation) && - TryInternalParse(components[2], out double value)) + if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + TryInternalParse(components[1].AsSpan(), out double saturation) && + TryInternalParse(components[2].AsSpan(), out double value)) { hsvColor = new HsvColor(1.0, hue, saturation, value); return true; @@ -312,10 +312,10 @@ namespace Avalonia.Media } else if (components.Length == 4) // HSVA { - if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && - TryInternalParse(components[1], out double saturation) && - TryInternalParse(components[2], out double value) && - TryInternalParse(components[3], out double alpha)) + if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + TryInternalParse(components[1].AsSpan(), out double saturation) && + TryInternalParse(components[2].AsSpan(), out double value) && + TryInternalParse(components[3].AsSpan(), out double alpha)) { hsvColor = new HsvColor(alpha, hue, saturation, value); return true; @@ -323,28 +323,22 @@ namespace Avalonia.Media } // Local function to specially parse a double value with an optional percentage sign - bool TryInternalParse(string inString, out double outDouble) + bool TryInternalParse(ReadOnlySpan inString, out double outDouble) { // The percent sign, if it exists, must be at the end of the number - int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal); if (percentIndex >= 0) { - var result = double.TryParse( - inString.Substring(0, percentIndex), - NumberStyles.Number, - CultureInfo.InvariantCulture, - out double percentage); + var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + out double percentage); outDouble = percentage / 100.0; return result; } else { - return double.TryParse( - inString, - NumberStyles.Number, - CultureInfo.InvariantCulture, + return inString.TryParseNumberToDouble( out outDouble); } } diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs index e4fab5ac7f..6b779dfd87 100644 --- a/src/Avalonia.Base/Utilities/SpanHelpers.cs +++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs @@ -25,6 +25,30 @@ namespace Avalonia.Utilities return int.TryParse(span.ToString(), out value); #else return int.TryParse(span, out value); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseNumberToDouble(this ReadOnlySpan span, out double value) + { +#if NETSTANDARD2_0 + return double.TryParse(span.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, + out value); +#else + return double.TryParse(span, NumberStyles.Number, CultureInfo.InvariantCulture, + out value); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryParseNumberToByte(this ReadOnlySpan span, out byte value) + { +#if NETSTANDARD2_0 + return byte.TryParse(span.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, + out value); +#else + return byte.TryParse(span, NumberStyles.Number, CultureInfo.InvariantCulture, + out value); #endif } } diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index 6da1fbd07c..af78178b5e 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -99,11 +99,25 @@ namespace System return int.TryParse(ToString(), out value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryParseNumberToDouble(out double value) + { + return double.TryParse(ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, + out value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryParseNumberToByte(out byte value) + { + return byte.TryParse(ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, + out value); + } + public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); - internal int IndexOf(string v, StringComparison ordinal, int start = 0) + internal int IndexOf(ReadOnlySpan v, StringComparison ordinal, int start = 0) { - if(Length == 0 || string.IsNullOrEmpty(v)) + if(Length == 0 || v.IsEmpty) { return -1; } From 39bd2f074aefbc2efcb5f52e2fb4149493240995 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Tue, 18 Oct 2022 07:58:46 +0000 Subject: [PATCH 3/5] add parameters for number styles and provider to double parse --- src/Avalonia.Base/Media/Color.cs | 10 +++---- src/Avalonia.Base/Media/HslColor.cs | 8 +++--- src/Avalonia.Base/Media/HsvColor.cs | 8 +++--- src/Avalonia.Base/Utilities/SpanHelpers.cs | 26 +++++++------------ src/Avalonia.Build.Tasks/SpanCompat.cs | 17 +++++------- .../Markup/Parsers/SelectorGrammar.cs | 4 +-- 6 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 14ef357393..5470a735b3 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -296,7 +296,7 @@ namespace Avalonia.Media return false; } - if (!input.TryParseFromHexToUInt(out var parsed)) + if (!input.TryParseUInt(NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsed)) { return false; } @@ -409,7 +409,7 @@ namespace Avalonia.Media if (percentIndex >= 0) { - var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double percentage); outByte = (byte)Math.Round((percentage / 100.0) * 255.0); @@ -417,7 +417,7 @@ namespace Avalonia.Media } else { - return inString.TryParseNumberToByte( + return inString.TryParseByte(NumberStyles.Number, CultureInfo.InvariantCulture, out outByte); } } @@ -430,7 +430,7 @@ namespace Avalonia.Media if (percentIndex >= 0) { - var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double percentage); outDouble = percentage / 100.0; @@ -438,7 +438,7 @@ namespace Avalonia.Media } else { - return inString.TryParseNumberToDouble( + return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out outDouble); } } diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index 5a9e5cd54b..425a3138c3 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -302,7 +302,7 @@ namespace Avalonia.Media if (components.Length == 3) // HSL { - if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && TryInternalParse(components[1].AsSpan(), out double saturation) && TryInternalParse(components[2].AsSpan(), out double lightness)) { @@ -312,7 +312,7 @@ namespace Avalonia.Media } else if (components.Length == 4) // HSLA { - if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && TryInternalParse(components[1].AsSpan(), out double saturation) && TryInternalParse(components[2].AsSpan(), out double lightness) && TryInternalParse(components[3].AsSpan(), out double alpha)) @@ -330,7 +330,7 @@ namespace Avalonia.Media if (percentIndex >= 0) { - var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double percentage); outDouble = percentage / 100.0; @@ -338,7 +338,7 @@ namespace Avalonia.Media } else { - return inString.TryParseNumberToDouble( + return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out outDouble); } } diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 30a6c7ef4e..9f95b31518 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -302,7 +302,7 @@ namespace Avalonia.Media if (components.Length == 3) // HSV { - if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && TryInternalParse(components[1].AsSpan(), out double saturation) && TryInternalParse(components[2].AsSpan(), out double value)) { @@ -312,7 +312,7 @@ namespace Avalonia.Media } else if (components.Length == 4) // HSVA { - if (components[0].AsSpan().TryParseNumberToDouble(out double hue) && + if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) && TryInternalParse(components[1].AsSpan(), out double saturation) && TryInternalParse(components[2].AsSpan(), out double value) && TryInternalParse(components[3].AsSpan(), out double alpha)) @@ -330,7 +330,7 @@ namespace Avalonia.Media if (percentIndex >= 0) { - var result = inString.Slice(0, percentIndex).TryParseNumberToDouble( + var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double percentage); outDouble = percentage / 100.0; @@ -338,7 +338,7 @@ namespace Avalonia.Media } else { - return inString.TryParseNumberToDouble( + return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out outDouble); } } diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs index 6b779dfd87..9a5dce9798 100644 --- a/src/Avalonia.Base/Utilities/SpanHelpers.cs +++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs @@ -7,19 +7,17 @@ namespace Avalonia.Utilities public static class SpanHelpers { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParseFromHexToUInt(this ReadOnlySpan span, out uint value) + public static bool TryParseUInt(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out uint value) { #if NETSTANDARD2_0 - return uint.TryParse(span.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out value); + return uint.TryParse(span.ToString(), style, provider, out value); #else - return uint.TryParse(span, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out value); + return uint.TryParse(span, style, provider, out value); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParseToInt(this ReadOnlySpan span, out int value) + public static bool TryParseInt(this ReadOnlySpan span, out int value) { #if NETSTANDARD2_0 return int.TryParse(span.ToString(), out value); @@ -29,26 +27,22 @@ namespace Avalonia.Utilities } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParseNumberToDouble(this ReadOnlySpan span, out double value) + public static bool TryParseDouble(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out double value) { #if NETSTANDARD2_0 - return double.TryParse(span.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return double.TryParse(span.ToString(), style, provider, out value); #else - return double.TryParse(span, NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return double.TryParse(span, style, provider, out value); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParseNumberToByte(this ReadOnlySpan span, out byte value) + public static bool TryParseByte(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out byte value) { #if NETSTANDARD2_0 - return byte.TryParse(span.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return byte.TryParse(span.ToString(), style, provider, out value); #else - return byte.TryParse(span, NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return byte.TryParse(span, style, provider, out value); #endif } } diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index af78178b5e..be59ff8b6c 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -87,30 +87,27 @@ namespace System } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryParseFromHexToUInt(out uint value) + public bool TryParseUInt(NumberStyles style, IFormatProvider provider, out uint value) { - return uint.TryParse(ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out value); + return uint.TryParse(ToString(), style, provider, out value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryParseToInt(out int value) + public bool TryParseInt(out int value) { return int.TryParse(ToString(), out value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryParseNumberToDouble(out double value) + public bool TryParseDouble(NumberStyles style, IFormatProvider provider, out double value) { - return double.TryParse(ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return double.TryParse(ToString(), style, provider, out value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryParseNumberToByte(out byte value) + public bool TryParseByte(NumberStyles style, IFormatProvider provider, out byte value) { - return byte.TryParse(ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, - out value); + return byte.TryParse(ToString(), style, provider, out value); } public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index a5e11cd233..4d6d16a3ce 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -417,7 +417,7 @@ namespace Avalonia.Markup.Parsers { stepOrOffset = -1; } - else if (!stepOrOffsetStr.TryParseToInt(out stepOrOffset)) + else if (!stepOrOffsetStr.TryParseInt(out stepOrOffset)) { throw new ExpressionParseException(r.Position, "Couldn't parse nth-child step or offset value. Integer was expected."); } @@ -462,7 +462,7 @@ namespace Avalonia.Markup.Parsers r.SkipWhitespace(); if (sign != 0 - && !r.TakeUntil(')').TryParseToInt(out offset)) + && !r.TakeUntil(')').TryParseInt(out offset)) { throw new ExpressionParseException(r.Position, "Couldn't parse nth-child offset value. Integer was expected."); } From cb0993b5fc5615402fa17aa5e3fd447a83277687 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 18 Oct 2022 17:19:10 +0200 Subject: [PATCH 4/5] fix: double declaration OutputType --- tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj index 3f4978f544..754a1d6a24 100644 --- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj +++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj @@ -1,6 +1,5 @@  - Exe net6.0 Exe false From 48c91fbac7ebc1a1a96b599000ddc0eabb866232 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 18 Oct 2022 19:07:05 +0200 Subject: [PATCH 5/5] fix: disable NETAnalyzers for tests --- tests/Directory.Build.props | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/Directory.Build.props diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000000..52af0f5ab2 --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,6 @@ + + + + false + +