From 10731fe4db8ef48b50a6f9aeeb860689f3711d50 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 13:54:42 +0200 Subject: [PATCH 01/20] Simplify CanConvertTo --- .../System/Windows/LengthConverter.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index da2652e9062..844842b7300 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -72,15 +72,7 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. - if (destinationType == typeof(InstanceDescriptor) || - destinationType == typeof(string)) - { - return true; - } - else - { - return false; - } + return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// From 209820db4c597b922dad87d7eb1a086ed71c813e Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 13:56:08 +0200 Subject: [PATCH 02/20] Simplify ConvertFrom --- .../System/Windows/LengthConverter.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 844842b7300..19559d6e24e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -91,17 +91,17 @@ public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, /// The ITypeDescriptorContext for this call. /// The CultureInfo which is respected when converting. /// The object to convert to a double. - public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, + public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source) CultureInfo cultureInfo, object source) { - if (source != null) - { - if (source is string) { return FromString((string)source, cultureInfo); } - else { return (double)(Convert.ToDouble(source, cultureInfo)); } - } + if (source is null) + throw GetConvertFromException(source); - throw GetConvertFromException(source); + if (source is string sourceString) + return FromString(sourceString, cultureInfo); + + return Convert.ToDouble(source, cultureInfo); } /// From 1b2b68dd32ef971e79ed74947a8847f8a9918ebf Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 14:00:17 +0200 Subject: [PATCH 03/20] Simplify ConvertTo, use pattern matching --- .../System/Windows/LengthConverter.cs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 19559d6e24e..189ad83aad4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -121,31 +121,29 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, /// The CultureInfo which is respected when converting. /// The double to convert. /// The type to which to convert the double. - public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, - CultureInfo cultureInfo, - object value, - Type destinationType) + public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(destinationType); - if ( value != null - && value is double ) + if (value is not double doubleValue) + throw GetConvertToException(value, destinationType); + + if (destinationType == typeof(string)) + { + if (double.IsNaN(doubleValue)) + return "Auto"; + + return Convert.ToString(doubleValue, cultureInfo); + } + + if (destinationType == typeof(InstanceDescriptor)) { - double l = (double)value; - if (destinationType == typeof(string)) - { - if(double.IsNaN(l)) - return "Auto"; - else - return Convert.ToString(l, cultureInfo); - } - else if (destinationType == typeof(InstanceDescriptor)) - { - ConstructorInfo ci = typeof(double).GetConstructor(new Type[] { typeof(double) }); - return new InstanceDescriptor(ci, new object[] { l }); - } + ConstructorInfo ci = typeof(double).GetConstructor(new Type[] { typeof(double) }); + return new InstanceDescriptor(ci, new object[] { doubleValue }); } - throw GetConvertToException(value, destinationType); + + // This will just throw an exception but it is a pattern + return base.ConvertTo(typeDescriptorContext, cultureInfo, value, destinationType); } #endregion From 6c384c2bbbf06e655adc64697a66810ed0b4ca15 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 14:38:00 +0200 Subject: [PATCH 04/20] Remove allocations on Tokenizer for standard converters --- .../VirtualizationCacheLengthConverter.cs | 28 ++++++------------- .../System/Windows/CornerRadiusConverter.cs | 25 ++++++----------- .../System/Windows/LengthConverter.cs | 25 ++++++++--------- .../System/Windows/ThicknessConverter.cs | 2 +- .../src/Shared/MS/Internal/TokenizerHelper.cs | 20 +++++++++++-- 5 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index fc78d576106..836a7f1671a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -180,36 +180,26 @@ internal static string ToString(VirtualizationCacheLength cacheLength, CultureIn /// Newly created VirtualizationCacheLength instance. internal static VirtualizationCacheLength FromString(string s, CultureInfo cultureInfo) { - TokenizerHelper th = new TokenizerHelper(s, cultureInfo); - double[] lengths = new double[2]; + TokenizerHelper th = new (s, cultureInfo); + Span lengths = stackalloc double[2]; int i = 0; // Peel off each double in the delimited list. while (th.NextToken()) { if (i >= 2) - { - i = 3; // Set i to a bad value. - break; - } + throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, s)); - lengths[i] = Double.Parse(th.GetCurrentToken(), cultureInfo); + lengths[i] = double.Parse(th.GetCurrentTokenAsSpan(), cultureInfo); i++; } - // We have a reasonable interpreation for one value (all four edges), two values (horizontal, vertical), - // and four values (left, top, right, bottom). - switch (i) + return i switch { - case 1: - return new VirtualizationCacheLength(lengths[0]); - - case 2: - // Should allowInfinity be false ??? (Rob) - return new VirtualizationCacheLength(lengths[0], lengths[1]); - } - - throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, s)); + 1 => new VirtualizationCacheLength(lengths[0]), + 2 => new VirtualizationCacheLength(lengths[0], lengths[1]), + _ => throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, s)), + }; } #endregion diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index 24f7d3401ff..8c46bf049ff 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -153,35 +153,28 @@ internal static string ToString(CornerRadius cr, CultureInfo cultureInfo) internal static CornerRadius FromString(string s, CultureInfo cultureInfo) { - TokenizerHelper th = new TokenizerHelper(s, cultureInfo); - double[] radii = new double[4]; + TokenizerHelper th = new (s, cultureInfo); + Span radii = stackalloc double[4]; int i = 0; // Peel off each Length in the delimited list. while (th.NextToken()) { if (i >= 4) - { - i = 5; // Set i to a bad value. - break; - } + throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, s)); - radii[i] = double.Parse(th.GetCurrentToken(), cultureInfo); + radii[i] = double.Parse(th.GetCurrentTokenAsSpan(), cultureInfo); i++; } // We have a reasonable interpreation for one value (all four edges) // and four values (left, top, right, bottom). - switch (i) + return i switch { - case 1: - return (new CornerRadius(radii[0])); - - case 4: - return (new CornerRadius(radii[0], radii[1], radii[2], radii[3])); - } - - throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, s)); + 1 => new CornerRadius(radii[0]), + 4 => new CornerRadius(radii[0], radii[1], radii[2], radii[3]), + _ => throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, s)), + }; } #endregion } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 189ad83aad4..88471d8c7db 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -174,22 +174,21 @@ internal static void FormatLengthAsString(double value, ref DefaultInterpolatedS // [value] is a double // [unit] is a string specifying the unit, like 'in' or 'px', or nothing (means pixels) // NOTE - This code is called from FontSizeConverter, so changes will affect both. - internal static double FromString(string s, CultureInfo cultureInfo) + internal static double FromString(ReadOnlySpan value, CultureInfo cultureInfo) { ReadOnlySpan valueSpan = s.AsSpan().Trim(); double unitFactor = 1.0; - //Auto is represented and Double.NaN - //properties that do not want Auto and NaN to be in their ligit values, - //should disallow NaN in validation callbacks (same goes for negative values) - if (valueSpan.Equals("auto", StringComparison.OrdinalIgnoreCase)) - return Double.NaN; - - PixelUnit pixelUnit; - if (PixelUnit.TryParsePixel(valueSpan, out pixelUnit) - || PixelUnit.TryParsePixelPerInch(valueSpan, out pixelUnit) - || PixelUnit.TryParsePixelPerCentimeter(valueSpan, out pixelUnit) - || PixelUnit.TryParsePixelPerPoint(valueSpan, out pixelUnit)) + // Auto is represented as Double.NaN + // Properties that do not want Auto and NaN to be in their ligit values, + // should disallow NaN in validation callbacks (same goes for negative values) + if (valueSpan.Equals("Auto", StringComparison.OrdinalIgnoreCase)) + return double.NaN; + + if (PixelUnit.TryParsePixel(valueSpan, out PixelUnit pixelUnit) || + PixelUnit.TryParsePixelPerInch(valueSpan, out pixelUnit) || + PixelUnit.TryParsePixelPerCentimeter(valueSpan, out pixelUnit) || + PixelUnit.TryParsePixelPerPoint(valueSpan, out pixelUnit)) { valueSpan = valueSpan.Slice(0, valueSpan.Length - pixelUnit.Name.Length); unitFactor = pixelUnit.Factor; @@ -205,7 +204,7 @@ private static double ParseDouble(ReadOnlySpan span, CultureInfo cultureIn { // FormatException errors thrown by double.Parse are pretty uninformative. // Throw a more meaningful error in this case that tells that we were attempting - // to create a Length instance from a string. This addresses windows bug 968884 + // to create a Length instance from a string. This addresses windows bug 968884 try { return double.Parse(span, cultureInfo); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs index 8acc47e454a..0555583e1e0 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs @@ -195,7 +195,7 @@ internal static Thickness FromString(string s, CultureInfo cultureInfo) if (i >= 4) // In case we've got more than 4 doubles, we throw throw new FormatException(SR.Format(SR.InvalidStringThickness, s)); - lengths[i] = LengthConverter.FromString(th.GetCurrentToken(), cultureInfo); + lengths[i] = LengthConverter.FromString(th.GetCurrentTokenAsSpan(), cultureInfo); i++; } diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs index c402f8ba7ce..04bd0b4e557 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs @@ -18,7 +18,7 @@ namespace MS.Internal.Markup namespace MS.Internal #endif { - internal class TokenizerHelper + internal sealed class TokenizerHelper { /// /// Constructor for TokenizerHelper which accepts an IFormatProvider. @@ -91,7 +91,23 @@ internal string GetCurrentToken() return _str.Substring(_currentTokenIndex,_currentTokenLength); } - + + /// + /// Sibling to with a minor difference; if there's no token, it will return + /// instead of . However, if used with , this edge-case is never hit. + /// + /// + internal ReadOnlySpan GetCurrentTokenAsSpan() + { + // If there's no current token, return empty span + if (_currentTokenIndex < 0) + { + return ReadOnlySpan.Empty; + } + + return _str.AsSpan().Slice(_currentTokenIndex, _currentTokenLength); + } + /// /// Throws an exception if there is any non-whitespace left un-parsed. /// From 0c8afb001c23fed213673568c15097f6b0f35882 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 14:44:53 +0200 Subject: [PATCH 05/20] Remove allocations on Tokenizer for generated structs --- .../Windows/Media3D/Generated/Matrix3D.cs | 35 +++++++++---------- .../Windows/Media3D/Generated/Point3D.cs | 9 +++-- .../Windows/Media3D/Generated/Point4D.cs | 11 +++--- .../Windows/Media3D/Generated/Quaternion.cs | 11 +++--- .../Windows/Media3D/Generated/Rect3D.cs | 15 ++++---- .../Windows/Media3D/Generated/Size3D.cs | 9 +++-- .../Windows/Media3D/Generated/Vector3D.cs | 9 +++-- .../src/Shared/MS/Internal/TokenizerHelper.cs | 14 ++++++++ .../System/Windows/Generated/Int32Rect.cs | 11 +++--- .../System/Windows/Generated/Point.cs | 7 ++-- .../System/Windows/Generated/Rect.cs | 11 +++--- .../System/Windows/Generated/Size.cs | 7 ++-- .../System/Windows/Generated/Vector.cs | 7 ++-- .../System/Windows/Media/Generated/Matrix.cs | 15 ++++---- 14 files changed, 86 insertions(+), 85 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs index 6b9662b751e..c74cf829f7b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs @@ -215,7 +215,7 @@ public static Matrix3D Parse(string source) Matrix3D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -225,23 +225,22 @@ public static Matrix3D Parse(string source) } else { - value = new Matrix3D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Matrix3D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs index 84b1d3950a1..7684bfd1ca8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs @@ -155,12 +155,11 @@ public static Point3D Parse(string source) Point3D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); - value = new Point3D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Point3D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); // There should be no more tokens in this string. th.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs index f5cdf6e27ed..2ac84c95f8a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs @@ -158,13 +158,12 @@ public static Point4D Parse(string source) Point4D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); - value = new Point4D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Point4D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); // There should be no more tokens in this string. th.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs index df034c6c5f5..54cf9fcf4b9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs @@ -179,7 +179,7 @@ public static Quaternion Parse(string source) Quaternion value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -189,11 +189,10 @@ public static Quaternion Parse(string source) } else { - value = new Quaternion( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Quaternion(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs index d7089f8f815..195914cc1cf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs @@ -178,7 +178,7 @@ public static Rect3D Parse(string source) Rect3D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -188,13 +188,12 @@ public static Rect3D Parse(string source) } else { - value = new Rect3D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Rect3D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs index 8be93cda490..1159390bd01 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs @@ -169,7 +169,7 @@ public static Size3D Parse(string source) Size3D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -179,10 +179,9 @@ public static Size3D Parse(string source) } else { - value = new Size3D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Size3D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs index 31e735152cc..a6d7992352c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs @@ -155,12 +155,11 @@ public static Vector3D Parse(string source) Vector3D value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); - value = new Vector3D( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Vector3D(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); // There should be no more tokens in this string. th.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs index 04bd0b4e557..7fefc4ed9d4 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs @@ -142,6 +142,20 @@ internal string NextTokenRequired() return GetCurrentToken(); } + /// + /// Sibling to ; advances to the next token, throws an if not present. + /// + /// The next token found + internal ReadOnlySpan NextTokenRequiredAsSpan() + { + if (!NextToken(false)) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); + } + + return GetCurrentTokenAsSpan(); + } + /// /// Advances to the NextToken, throwing an exception if not present /// diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs index 1855f9172f9..a7b8382ee82 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs @@ -166,7 +166,7 @@ public static Int32Rect Parse(string source) Int32Rect value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -176,11 +176,10 @@ public static Int32Rect Parse(string source) } else { - value = new Int32Rect( - Convert.ToInt32(firstToken, formatProvider), - Convert.ToInt32(th.NextTokenRequired(), formatProvider), - Convert.ToInt32(th.NextTokenRequired(), formatProvider), - Convert.ToInt32(th.NextTokenRequired(), formatProvider)); + value = new Int32Rect(Int32.Parse(firstToken, formatProvider), + Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs index 9fe73b62a6f..b57142ef219 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs @@ -146,11 +146,10 @@ public static Point Parse(string source) Point value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); - value = new Point( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Point(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); // There should be no more tokens in this string. th.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs index 69e2c18dc1d..dc56d3dd980 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs @@ -166,7 +166,7 @@ public static Rect Parse(string source) Rect value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -176,11 +176,10 @@ public static Rect Parse(string source) } else { - value = new Rect( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Rect(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs index 01fc36c0936..e84768ad003 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs @@ -160,7 +160,7 @@ public static Size Parse(string source) Size value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -170,9 +170,8 @@ public static Size Parse(string source) } else { - value = new Size( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Size(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs index 15452bf480d..3a42ec913e7 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs @@ -146,11 +146,10 @@ public static Vector Parse(string source) Vector value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); - value = new Vector( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Vector(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); // There should be no more tokens in this string. th.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs index 5d1259f6510..e3ff23ac76a 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs @@ -179,7 +179,7 @@ public static Matrix Parse(string source) Matrix value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -189,13 +189,12 @@ public static Matrix Parse(string source) } else { - value = new Matrix( - Convert.ToDouble(firstToken, formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + value = new Matrix(double.Parse(firstToken, formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); } // There should be no more tokens in this string. From 50835ddc6739c0141184745846303f796c5bd5b9 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 14:46:36 +0200 Subject: [PATCH 06/20] Remove allocations on Tokenizer for generated collections --- .../System/Windows/Media/Generated/DoubleCollection.cs | 2 +- .../Windows/Media/Generated/GradientStopCollection.cs | 2 +- .../System/Windows/Media/Generated/Int32Collection.cs | 2 +- .../System/Windows/Media/Generated/PointCollection.cs | 4 ++-- .../System/Windows/Media/Generated/VectorCollection.cs | 4 ++-- .../System/Windows/Media3D/Generated/Point3DCollection.cs | 6 +++--- .../System/Windows/Media3D/Generated/Vector3DCollection.cs | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs index 4190fb47727..a1f5c0a442c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs @@ -720,7 +720,7 @@ public static DoubleCollection Parse(string source) while (th.NextToken()) { - value = Convert.ToDouble(th.GetCurrentToken(), formatProvider); + value = double.Parse(th.GetCurrentTokenAsSpan(), formatProvider); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs index a438fb4514c..dadb5a93da3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs @@ -805,7 +805,7 @@ public static GradientStopCollection Parse(string source) { value = new GradientStop( Parsers.ParseColor(th.GetCurrentToken(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs index 6cf347c2c3d..d230d3ac8df 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs @@ -720,7 +720,7 @@ public static Int32Collection Parse(string source) while (th.NextToken()) { - value = Convert.ToInt32(th.GetCurrentToken(), formatProvider); + value = Int32.Parse(th.GetCurrentTokenAsSpan(), formatProvider); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs index 60659216a64..05c76f767b7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs @@ -721,8 +721,8 @@ public static PointCollection Parse(string source) while (th.NextToken()) { value = new Point( - Convert.ToDouble(th.GetCurrentToken(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs index 4b51cfe5924..6ac067e58ef 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs @@ -721,8 +721,8 @@ public static VectorCollection Parse(string source) while (th.NextToken()) { value = new Vector( - Convert.ToDouble(th.GetCurrentToken(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs index 5439f64bde9..06e6723325e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs @@ -719,9 +719,9 @@ public static Point3DCollection Parse(string source) while (th.NextToken()) { value = new Point3D( - Convert.ToDouble(th.GetCurrentToken(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs index 32cd5910d9c..dac63e3ffd4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs @@ -719,9 +719,9 @@ public static Vector3DCollection Parse(string source) while (th.NextToken()) { value = new Vector3D( - Convert.ToDouble(th.GetCurrentToken(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider), - Convert.ToDouble(th.NextTokenRequired(), formatProvider)); + double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), + double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); resource.Add(value); } From ed46efe24f0ff7f59893441b877f96e2debea13c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 14:50:03 +0200 Subject: [PATCH 07/20] Adjust MilCodeGen to generate code that corresponds to edits on generated files --- .../src/WpfGfx/codegen/mcg/generators/ManagedResource.cs | 2 +- .../src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs | 6 +++--- .../src/WpfGfx/codegen/mcg/xml/Resource.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs index 5c5834f8061..0a07a999129 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs @@ -2827,7 +2827,7 @@ private static string WriteParse(McgResource resource) while (th.NextToken()) { - [[WriteParseBody(resource.CollectionType, "th.GetCurrentToken()")]] + [[WriteParseBody(resource.CollectionType, "th.GetCurrentTokenAsSpan()")]] resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs index b7dc23cc0b6..e90b81ea21e 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs @@ -510,8 +510,8 @@ private static string WriteParseBody(McgType type, [[Helpers.CodeGenHelpers.WriteFieldStatementsFirstLastWithSeparator( resource.LocalFields, "{parseMethod}(" + firstToken + ", formatProvider)", - "{parseMethod}(th.NextTokenRequired(), formatProvider)", - "{parseMethod}(th.NextTokenRequired(), formatProvider)", + "{parseMethod}(th.NextTokenRequiredAsSpan(), formatProvider)", + "{parseMethod}(th.NextTokenRequiredAsSpan(), formatProvider)", ",\n")]]); [[/inline]]; @@ -560,7 +560,7 @@ public static [[resource.Name]] Parse(string source) [[resource.Name]] value; - String firstToken = th.NextTokenRequired(); + ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); [[WriteParseBody(resource, "firstToken")]] diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/xml/Resource.xml b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/xml/Resource.xml index 8b9f3569d23..408cd6ee084 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/xml/Resource.xml +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/xml/Resource.xml @@ -1638,10 +1638,10 @@ - + - + From 7b87eced9ab23bc9279089d14a3fdc041b047e4c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 15:14:10 +0200 Subject: [PATCH 08/20] Simplify CanConvertTo in Corner/Cache --- .../Controls/VirtualizationCacheLengthConverter.cs | 10 +--------- .../System/Windows/CornerRadiusConverter.cs | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index 836a7f1671a..2ed000e4a4b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -63,15 +63,7 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. - if ( destinationType == typeof(InstanceDescriptor) - || destinationType == typeof(string)) - { - return true; - } - else - { - return false; - } + return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index 8c46bf049ff..db53acf7d09 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -58,15 +58,7 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. - if ( destinationType == typeof(InstanceDescriptor) - || destinationType == typeof(string)) - { - return true; - } - else - { - return false; - } + return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// From fb3ba6ffec85f2437432c3fa00b20ccbe55081bb Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 15:14:50 +0200 Subject: [PATCH 09/20] Simplify ConvertFrom methods --- .../VirtualizationCacheLengthConverter.cs | 22 +++++++------------ .../System/Windows/CornerRadiusConverter.cs | 14 +++++++----- .../System/Windows/LengthConverter.cs | 5 ++--- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index 2ed000e4a4b..1cd0d28ba1a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -84,20 +84,14 @@ public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, /// The object to convert to a VirtualizationCacheLength. public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source) { - if (source != null) - { - if (source is string) - { - return (FromString((string)source, cultureInfo)); - } - else - { - // conversion from numeric type - double value = Convert.ToDouble(source, cultureInfo); - return new VirtualizationCacheLength(value); - } - } - throw GetConvertFromException(source); + if (source is null) + throw GetConvertFromException(source); + + if (source is string stringValue) + return FromString(stringValue, cultureInfo); + + // Conversion from a numeric type + return new VirtualizationCacheLength(Convert.ToDouble(source, cultureInfo)); } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index db53acf7d09..452b50e7f9b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -79,12 +79,14 @@ public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, /// The object to convert to a CornerRadius. public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source) { - if (source != null) - { - if (source is string) { return FromString((string)source, cultureInfo); } - else { return new CornerRadius(Convert.ToDouble(source, cultureInfo)); } - } - throw GetConvertFromException(source); + if (source is null) + throw GetConvertFromException(source); + + if (source is string stringValue) + return FromString(stringValue, cultureInfo); + + // Conversion from a numeric type + return new CornerRadius(Convert.ToDouble(source, cultureInfo)); } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 88471d8c7db..177d1f0e66f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -92,15 +92,14 @@ public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, /// The CultureInfo which is respected when converting. /// The object to convert to a double. public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source) - CultureInfo cultureInfo, - object source) { if (source is null) throw GetConvertFromException(source); if (source is string sourceString) return FromString(sourceString, cultureInfo); - + + // Conversion from a numeric type return Convert.ToDouble(source, cultureInfo); } From e7b26629210576ab98eec97cc8b2fda28ab4a6a5 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 15:16:20 +0200 Subject: [PATCH 10/20] Simplify ConvertTo methods --- .../VirtualizationCacheLengthConverter.cs | 27 +++++++++---------- .../System/Windows/CornerRadiusConverter.cs | 12 ++++----- .../System/Windows/LengthConverter.cs | 3 +-- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index 1cd0d28ba1a..a9c8fcb82f8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -115,23 +115,20 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C { ArgumentNullException.ThrowIfNull(destinationType); - if (value != null - && value is VirtualizationCacheLength) + if (value is not VirtualizationCacheLength cacheLength) + throw GetConvertToException(value, destinationType); + + if (destinationType == typeof(string)) + return ToString(cacheLength, cultureInfo); + + if (destinationType == typeof(InstanceDescriptor)) { - VirtualizationCacheLength gl = (VirtualizationCacheLength)value; - - if (destinationType == typeof(string)) - { - return (ToString(gl, cultureInfo)); - } - - if (destinationType == typeof(InstanceDescriptor)) - { - ConstructorInfo ci = typeof(VirtualizationCacheLength).GetConstructor(new Type[] { typeof(double), typeof(VirtualizationCacheLengthUnit) }); - return (new InstanceDescriptor(ci, new object[] { gl.CacheBeforeViewport, gl.CacheAfterViewport })); - } + ConstructorInfo ci = typeof(VirtualizationCacheLength).GetConstructor(new Type[] { typeof(double), typeof(VirtualizationCacheLengthUnit) }); + return new InstanceDescriptor(ci, new object[] { cacheLength.CacheBeforeViewport, cacheLength.CacheAfterViewport }); } - throw GetConvertToException(value, destinationType); + + // This will just throw an exception but it is a pattern + return base.ConvertTo(typeDescriptorContext, cultureInfo, value, destinationType); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index 452b50e7f9b..1973fe95a12 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -109,20 +109,18 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(value); - ArgumentNullException.ThrowIfNull(destinationType); - if (!(value is CornerRadius)) - { + if (value is not CornerRadius cornerRadius) throw new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(CornerRadius)), nameof(value)); - } - CornerRadius cr = (CornerRadius)value; - if (destinationType == typeof(string)) { return ToString(cr, cultureInfo); } + if (destinationType == typeof(string)) + return ToString(cornerRadius, cultureInfo); + if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo ci = typeof(CornerRadius).GetConstructor(new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) }); - return new InstanceDescriptor(ci, new object[] { cr.TopLeft, cr.TopRight, cr.BottomRight, cr.BottomLeft }); + return new InstanceDescriptor(ci, new object[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); } throw new ArgumentException(SR.Format(SR.CannotConvertType, typeof(CornerRadius), destinationType.FullName)); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 177d1f0e66f..05de326ae68 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -175,7 +175,7 @@ internal static void FormatLengthAsString(double value, ref DefaultInterpolatedS // NOTE - This code is called from FontSizeConverter, so changes will affect both. internal static double FromString(ReadOnlySpan value, CultureInfo cultureInfo) { - ReadOnlySpan valueSpan = s.AsSpan().Trim(); + ReadOnlySpan valueSpan = value.Trim(); double unitFactor = 1.0; // Auto is represented as Double.NaN @@ -214,7 +214,6 @@ private static double ParseDouble(ReadOnlySpan span, CultureInfo cultureIn } } - #endregion } From 8c87862b0f3e3fcfe6eaec2af0e93f2aa1d0a44c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 15:29:09 +0200 Subject: [PATCH 11/20] Final formatting, clean up redundant usings --- .../VirtualizationCacheLengthConverter.cs | 17 ++---- .../System/Windows/CornerRadiusConverter.cs | 4 +- .../System/Windows/LengthConverter.cs | 40 ++++++------- .../System/Windows/ThicknessConverter.cs | 56 +++++++++---------- 4 files changed, 54 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index a9c8fcb82f8..6475677cdc5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -1,15 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// Description: Virtualization cache length converter implementation -// - -using MS.Internal; -using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.ComponentModel; using System.Globalization; using System.Reflection; +using MS.Internal; namespace System.Windows.Controls { @@ -86,7 +82,7 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, { if (source is null) throw GetConvertFromException(source); - + if (source is string stringValue) return FromString(stringValue, cultureInfo); @@ -152,8 +148,7 @@ internal static string ToString(VirtualizationCacheLength cacheLength, CultureIn { char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); - return string.Create(cultureInfo, stackalloc char[128], - $"{cacheLength.CacheBeforeViewport}{listSeparator}{cacheLength.CacheAfterViewport}"); + return string.Create(cultureInfo, stackalloc char[128], $"{cacheLength.CacheBeforeViewport}{listSeparator}{cacheLength.CacheAfterViewport}"); } /// /// Parses a VirtualizationCacheLength from a string given the CultureInfo. @@ -163,7 +158,7 @@ internal static string ToString(VirtualizationCacheLength cacheLength, CultureIn /// Newly created VirtualizationCacheLength instance. internal static VirtualizationCacheLength FromString(string s, CultureInfo cultureInfo) { - TokenizerHelper th = new (s, cultureInfo); + TokenizerHelper th = new(s, cultureInfo); Span lengths = stackalloc double[2]; int i = 0; @@ -185,7 +180,7 @@ internal static VirtualizationCacheLength FromString(string s, CultureInfo cultu }; } - #endregion + #endregion } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index 1973fe95a12..b2e697e1ecd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.ComponentModel; using System.Globalization; using System.Reflection; using MS.Internal; @@ -145,7 +145,7 @@ internal static string ToString(CornerRadius cr, CultureInfo cultureInfo) internal static CornerRadius FromString(string s, CultureInfo cultureInfo) { - TokenizerHelper th = new (s, cultureInfo); + TokenizerHelper th = new(s, cultureInfo); Span radii = stackalloc double[4]; int i = 0; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs index 05de326ae68..a70a2c3aac6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/LengthConverter.cs @@ -1,27 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Contains the LengthConverter: TypeConverter for the Length class. -// -// - -using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Runtime.CompilerServices; +using System.ComponentModel; using System.Globalization; using System.Reflection; using MS.Internal; namespace System.Windows { - /// /// LengthConverter - Converter class for converting instances of other types to and from double representing length. /// - public class LengthConverter: TypeConverter + public class LengthConverter : TypeConverter { //------------------------------------------------------------------- // @@ -56,7 +48,7 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext case TypeCode.UInt32: case TypeCode.UInt64: return true; - default: + default: return false; } } @@ -69,7 +61,7 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext /// /// The ITypeDescriptorContext for this call. /// The Type being queried for support. - public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) + public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) { // We can convert to an InstanceDescriptor or to a string. return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); @@ -100,7 +92,7 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, return FromString(sourceString, cultureInfo); // Conversion from a numeric type - return Convert.ToDouble(source, cultureInfo); + return Convert.ToDouble(source, cultureInfo); } /// @@ -128,12 +120,7 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C throw GetConvertToException(value, destinationType); if (destinationType == typeof(string)) - { - if (double.IsNaN(doubleValue)) - return "Auto"; - - return Convert.ToString(doubleValue, cultureInfo); - } + return ToString(doubleValue, cultureInfo); if (destinationType == typeof(InstanceDescriptor)) { @@ -154,6 +141,21 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C #region Internal Methods + /// + /// Formats a single to length representation. + /// For values, "Auto" is returned instead. + /// + /// The value to format as string. + /// The handler specifying culture used for conversion. + /// Formatted length representation of the . + internal static string ToString(double value, CultureInfo cultureInfo) + { + if (double.IsNaN(value)) + return "Auto"; + + return Convert.ToString(value, cultureInfo); + } + /// Format into using specified /// in .

/// Special representation applies for values, emitted as "Auto" string instead.
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs index 0555583e1e0..df468773220 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs @@ -1,16 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Contains the ThicknessConverter: TypeConverter for the Thickness struct. -// -// - -using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Runtime.CompilerServices; +using System.ComponentModel; using System.Globalization; using System.Reflection; using MS.Internal; @@ -92,10 +85,12 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, if (source is string sourceString) return FromString(sourceString, cultureInfo); - else if (source is double sourceValue) + + if (source is double sourceValue) return new Thickness(sourceValue); - else - return new Thickness(Convert.ToDouble(source, cultureInfo)); + + // Conversion from a numeric type + return new Thickness(Convert.ToDouble(source, cultureInfo)); } /// @@ -125,7 +120,8 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C if (destinationType == typeof(string)) return ToString(thickness, cultureInfo); - else if (destinationType == typeof(InstanceDescriptor)) + + if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo ci = typeof(Thickness).GetConstructor(new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) }); return new InstanceDescriptor(ci, new object[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); @@ -134,7 +130,6 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C throw new ArgumentException(SR.Format(SR.CannotConvertType, typeof(Thickness), destinationType.FullName)); } - #endregion Public Methods //------------------------------------------------------------------- @@ -146,12 +141,12 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C #region Internal Methods /// - /// Converts to its string representation using the specified . + /// Converts to its string representation using the specified . /// - /// The to convert to string. + /// The to convert to string. /// Culture to use when formatting doubles and choosing separator. - /// The formatted as string using the specified . - internal static string ToString(Thickness th, CultureInfo cultureInfo) + /// The formatted as string using the specified . + internal static string ToString(Thickness thickness, CultureInfo cultureInfo) { char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); @@ -162,30 +157,30 @@ internal static string ToString(Thickness th, CultureInfo cultureInfo) // 1 = 1x scratch space for alignment DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]); - LengthConverter.FormatLengthAsString(th.Left, ref handler); + LengthConverter.FormatLengthAsString(thickness.Left, ref handler); handler.AppendFormatted(listSeparator); - LengthConverter.FormatLengthAsString(th.Top, ref handler); + LengthConverter.FormatLengthAsString(thickness.Top, ref handler); handler.AppendFormatted(listSeparator); - LengthConverter.FormatLengthAsString(th.Right, ref handler); + LengthConverter.FormatLengthAsString(thickness.Right, ref handler); handler.AppendFormatted(listSeparator); - LengthConverter.FormatLengthAsString(th.Bottom, ref handler); + LengthConverter.FormatLengthAsString(thickness.Bottom, ref handler); return handler.ToStringAndClear(); } /// - /// Constructs a struct out of string representation supplied by and the specified . + /// Constructs a struct out of string representation supplied by and the specified . /// - /// The string representation of a struct. + /// The string representation of a struct. /// The which was used to format this string. - /// A new instance of struct representing the data contained in . - /// Thrown when contains invalid string representation. - internal static Thickness FromString(string s, CultureInfo cultureInfo) + /// A new instance of struct representing the data contained in . + /// Thrown when contains invalid string representation. + internal static Thickness FromString(string input, CultureInfo cultureInfo) { - TokenizerHelper th = new(s, cultureInfo); + TokenizerHelper th = new(input, cultureInfo); Span lengths = stackalloc double[4]; int i = 0; @@ -193,7 +188,7 @@ internal static Thickness FromString(string s, CultureInfo cultureInfo) while (th.NextToken()) { if (i >= 4) // In case we've got more than 4 doubles, we throw - throw new FormatException(SR.Format(SR.InvalidStringThickness, s)); + throw new FormatException(SR.Format(SR.InvalidStringThickness, input)); lengths[i] = LengthConverter.FromString(th.GetCurrentTokenAsSpan(), cultureInfo); i++; @@ -207,11 +202,10 @@ internal static Thickness FromString(string s, CultureInfo cultureInfo) 1 => new Thickness(lengths[0]), 2 => new Thickness(lengths[0], lengths[1], lengths[0], lengths[1]), 4 => new Thickness(lengths[0], lengths[1], lengths[2], lengths[3]), - _ => throw new FormatException(SR.Format(SR.InvalidStringThickness, s)), + _ => throw new FormatException(SR.Format(SR.InvalidStringThickness, input)), }; } - #endregion - + #endregion } } From 3e9444924504ffca352539bcc997faeee9fa943f Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 16:50:17 +0200 Subject: [PATCH 12/20] Stop double checking in IsWhiteSpace, convert fields that could be readonly --- .../src/Shared/MS/Internal/TokenizerHelper.cs | 49 ++++++------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs index 7fefc4ed9d4..cf18784b5f7 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs @@ -29,26 +29,7 @@ internal sealed class TokenizerHelper /// /// The string which will be tokenized. /// The IFormatProvider which controls this tokenization. - internal TokenizerHelper(string str, IFormatProvider formatProvider) - { - char numberSeparator = GetNumericListSeparator(formatProvider); - - Initialize(str, '\'', numberSeparator); - } - - /// - /// Initialize the TokenizerHelper with the string to tokenize, - /// the char which represents quotes and the list separator. - /// - /// The string to tokenize. - /// The quote char. - /// The list separator. - internal TokenizerHelper(string str, - char quoteChar, - char separator) - { - Initialize(str, quoteChar, separator); - } + internal TokenizerHelper(string str, IFormatProvider formatProvider) : this(str, '\'', GetNumericListSeparator(formatProvider)) { } /// /// Initialize the TokenizerHelper with the string to tokenize, @@ -57,9 +38,7 @@ internal TokenizerHelper(string str, /// The string to tokenize. /// The quote char. /// The list separator. - private void Initialize(string str, - char quoteChar, - char separator) + internal TokenizerHelper(string str, char quoteChar, char separator) { _str = str; _strLen = str == null ? 0 : str.Length; @@ -72,7 +51,7 @@ private void Initialize(string str, // character of the next token. while (_charIndex < _strLen) { - if (!Char.IsWhiteSpace(_str, _charIndex)) + if (!char.IsWhiteSpace(_str[_charIndex])) { break; } @@ -198,7 +177,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) char currentChar = _str[_charIndex]; - Debug.Assert(!Char.IsWhiteSpace(currentChar),"Token started on Whitespace"); + Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); // setup the quoteCount int quoteCount = 0; @@ -239,7 +218,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) } } } - else if ((Char.IsWhiteSpace(currentChar)) || (currentChar == separator)) + else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) { if (currentChar == separator) { @@ -284,8 +263,7 @@ private void ScanToNextToken(char separator) // check that the currentChar is a space or the separator. If not // we have an error. this can happen in the quote case // that the char after the quotes string isn't a char. - if (!(currentChar == separator) && - !Char.IsWhiteSpace(currentChar)) + if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) { throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _str)); } @@ -308,7 +286,7 @@ private void ScanToNextToken(char separator) throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); } } - else if (Char.IsWhiteSpace(currentChar)) + else if (char.IsWhiteSpace(currentChar)) { ++_charIndex; } @@ -360,13 +338,16 @@ internal bool FoundSeparator } } - private char _quoteChar; - private char _argSeparator; - private string _str; - private int _strLen; + // Readonly fields + private readonly char _quoteChar; + private readonly char _argSeparator; + private readonly string _str; + private readonly int _strLen; + + // State fields private int _charIndex; + private bool _foundSeparator; internal int _currentTokenIndex; internal int _currentTokenLength; - private bool _foundSeparator; } } From cb6fe8e14f24d008bdf5de4e8acbe19d6170e6ae Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 17:41:45 +0200 Subject: [PATCH 13/20] Add a ref struct implementation of TokenizerHelper, use it in PresentationFramework --- .../VirtualizationCacheLengthConverter.cs | 21 +- .../System/Windows/CornerRadiusConverter.cs | 18 +- .../System/Windows/ThicknessConverter.cs | 6 +- .../MS/Internal/ValueTokenizerHelper.cs | 316 ++++++++++++++++++ .../src/WindowsBase/ValueTokenizerHelper.cs | 311 +++++++++++++++++ .../src/WindowsBase/WindowsBase.csproj | 3 +- 6 files changed, 655 insertions(+), 20 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index 6475677cdc5..2511765c684 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -150,25 +150,26 @@ internal static string ToString(VirtualizationCacheLength cacheLength, CultureIn return string.Create(cultureInfo, stackalloc char[128], $"{cacheLength.CacheBeforeViewport}{listSeparator}{cacheLength.CacheAfterViewport}"); } + /// - /// Parses a VirtualizationCacheLength from a string given the CultureInfo. + /// Parses a from a given the . /// - /// String to parse from. - /// Culture Info. - /// Newly created VirtualizationCacheLength instance. - internal static VirtualizationCacheLength FromString(string s, CultureInfo cultureInfo) + /// to parse from. + /// The that is respected during parsing. + /// A new instance of . + internal static VirtualizationCacheLength FromString(string input, CultureInfo cultureInfo) { - TokenizerHelper th = new(s, cultureInfo); + ValueTokenizerHelper tokenizer = new(input, cultureInfo); Span lengths = stackalloc double[2]; int i = 0; // Peel off each double in the delimited list. - while (th.NextToken()) + while (tokenizer.NextToken()) { if (i >= 2) - throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, s)); + throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, input)); - lengths[i] = double.Parse(th.GetCurrentTokenAsSpan(), cultureInfo); + lengths[i] = double.Parse(tokenizer.GetCurrentToken(), cultureInfo); i++; } @@ -176,7 +177,7 @@ internal static VirtualizationCacheLength FromString(string s, CultureInfo cultu { 1 => new VirtualizationCacheLength(lengths[0]), 2 => new VirtualizationCacheLength(lengths[0], lengths[1]), - _ => throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, s)), + _ => throw new FormatException(SR.Format(SR.InvalidStringVirtualizationCacheLength, input)), }; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index b2e697e1ecd..e15b5b4ba1d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -143,19 +143,25 @@ internal static string ToString(CornerRadius cr, CultureInfo cultureInfo) return string.Create(cultureInfo, stackalloc char[64], $"{cr.TopLeft}{listSeparator}{cr.TopRight}{listSeparator}{cr.BottomRight}{listSeparator}{cr.BottomLeft}"); } - internal static CornerRadius FromString(string s, CultureInfo cultureInfo) + /// + /// Parses a from a given the . + /// + /// to parse from. + /// The that is respected during parsing. + /// A new instance of . + internal static CornerRadius FromString(string input, CultureInfo cultureInfo) { - TokenizerHelper th = new(s, cultureInfo); + ValueTokenizerHelper tokenizer = new(input, cultureInfo); Span radii = stackalloc double[4]; int i = 0; // Peel off each Length in the delimited list. - while (th.NextToken()) + while (tokenizer.NextToken()) { if (i >= 4) - throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, s)); + throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, input)); - radii[i] = double.Parse(th.GetCurrentTokenAsSpan(), cultureInfo); + radii[i] = double.Parse(tokenizer.GetCurrentToken(), cultureInfo); i++; } @@ -165,7 +171,7 @@ internal static CornerRadius FromString(string s, CultureInfo cultureInfo) { 1 => new CornerRadius(radii[0]), 4 => new CornerRadius(radii[0], radii[1], radii[2], radii[3]), - _ => throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, s)), + _ => throw new FormatException(SR.Format(SR.InvalidStringCornerRadius, input)), }; } #endregion diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs index df468773220..10c43fc173b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs @@ -180,17 +180,17 @@ internal static string ToString(Thickness thickness, CultureInfo cultureInfo) /// Thrown when contains invalid string representation. internal static Thickness FromString(string input, CultureInfo cultureInfo) { - TokenizerHelper th = new(input, cultureInfo); + ValueTokenizerHelper tokenizer = new(input, cultureInfo); Span lengths = stackalloc double[4]; int i = 0; // Peel off each double in the delimited list. - while (th.NextToken()) + while (tokenizer.NextToken()) { if (i >= 4) // In case we've got more than 4 doubles, we throw throw new FormatException(SR.Format(SR.InvalidStringThickness, input)); - lengths[i] = LengthConverter.FromString(th.GetCurrentTokenAsSpan(), cultureInfo); + lengths[i] = LengthConverter.FromString(tokenizer.GetCurrentToken(), cultureInfo); i++; } diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs new file mode 100644 index 00000000000..a90aa1fa4d0 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs @@ -0,0 +1,316 @@ +// 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; +using System.Diagnostics; +using System.Globalization; +using MS.Internal.WindowsBase; + +namespace MS.Internal +{ + /// + /// Represents a implementation of operating over . + /// + internal ref struct ValueTokenizerHelper + { + /// + /// Constructor for which accepts an . + /// If the is null, we use the thread's info. + /// We will use ',' as the list separator, unless it's the same as the + /// decimal separator. If it *is*, then we can't determine if, say, "23,5" is one + /// number or two. In this case, we will use ";" as the separator. + /// + /// The string which will be tokenized. + /// The which controls this tokenization. + internal ValueTokenizerHelper(ReadOnlySpan input, IFormatProvider formatProvider) : this(input, '\'', GetNumericListSeparator(formatProvider)) { } + + /// + /// Initialize the with the string to tokenize, + /// the char which represents quotes and the list separator. + /// + /// The string to tokenize. + /// The quote char. + /// The list separator. + internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char separator) + { + _input = input; + _currentTokenIndex = -1; + _quoteChar = quoteChar; + _argSeparator = separator; + + // immediately forward past any whitespace so + // NextToken() logic always starts on the first + // character of the next token. + while (_charIndex < _input.Length) + { + if (!char.IsWhiteSpace(_input[_charIndex])) + { + break; + } + + ++_charIndex; + } + } + + /// + /// Returns the next available token or if there's none ready. + /// + /// A slice of the next token or . + internal readonly ReadOnlySpan GetCurrentToken() + { + // If there's no current token, return an empty span + if (_currentTokenIndex < 0) + { + return ReadOnlySpan.Empty; + } + + return _input.Slice(_currentTokenIndex, _currentTokenLength); + } + + /// + /// Throws an exception if there is any non-whitespace left un-parsed. + /// + internal readonly void LastTokenRequired() + { + if (_charIndex != _input.Length) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); + } + } + + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken() + { + return NextToken(false); + } + + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired() + { + if (!NextToken(false)) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); + } + + return GetCurrentToken(); + } + + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired(bool allowQuotedToken) + { + if (!NextToken(allowQuotedToken)) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); + } + + return GetCurrentToken(); + } + + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken) + { + // use the currently-set separator character. + return NextToken(allowQuotedToken, _argSeparator); + } + + /// + /// Advances to the next token. A separator character can be specified which overrides the one previously set. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken, char separator) + { + _currentTokenIndex = -1; // reset the currentTokenIndex + _foundSeparator = false; // reset + + // If we're at end of the string, just return false. + if (_charIndex >= _input.Length) + { + return false; + } + + char currentChar = _input[_charIndex]; + + Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); + + // setup the quoteCount + int quoteCount = 0; + + // If we are allowing a quoted token and this token begins with a quote, + // set up the quote count and skip the initial quote + if (allowQuotedToken && + currentChar == _quoteChar) + { + quoteCount++; // increment quote count + ++_charIndex; // move to next character + } + + int newTokenIndex = _charIndex; + int newTokenLength = 0; + + // loop until hit end of string or hit a , or whitespace + // if at end of string ust return false. + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; + + // if have a QuoteCount and this is a quote + // decrement the quoteCount + if (quoteCount > 0) + { + // if anything but a quoteChar we move on + if (currentChar == _quoteChar) + { + --quoteCount; + + // if at zero which it always should for now + // break out of the loop + if (0 == quoteCount) + { + ++_charIndex; // move past the quote + break; + } + } + } + else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) + { + if (currentChar == separator) + { + _foundSeparator = true; + } + break; + } + + ++_charIndex; + ++newTokenLength; + } + + // if quoteCount isn't zero we hit the end of the string + // before the ending quote + if (quoteCount > 0) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperMissingEndQuote, _input.ToString())); + } + + ScanToNextToken(separator); // move so at the start of the nextToken for next call + + // finally made it, update the _currentToken values + _currentTokenIndex = newTokenIndex; + _currentTokenLength = newTokenLength; + + if (_currentTokenLength < 1) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); + } + + return true; + } + + // helper to move the _charIndex to the next token or to the end of the string + void ScanToNextToken(char separator) + { + // if already at end of the string don't bother + if (_charIndex < _input.Length) + { + char currentChar = _input[_charIndex]; + + // check that the currentChar is a space or the separator. If not + // we have an error. this can happen in the quote case + // that the char after the quotes string isn't a char. + if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); + } + + // loop until hit a character that isn't + // an argument separator or whitespace. + int argSepCount = 0; + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; + + if (currentChar == separator) + { + _foundSeparator = true; + ++argSepCount; + _charIndex++; + + if (argSepCount > 1) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); + } + } + else if (char.IsWhiteSpace(currentChar)) + { + ++_charIndex; + } + else + { + break; + } + } + + // if there was a separatorChar then we shouldn't be + // at the end of string or means there was a separator + // but there isn't an arg + + if (argSepCount > 0 && _charIndex >= _input.Length) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); + } + } + } + + // Helper to get the numeric list separator for a given IFormatProvider. + // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. + static internal char GetNumericListSeparator(IFormatProvider provider) + { + char numericSeparator = ','; + + // Get the NumberFormatInfo out of the provider, if possible + // If the IFormatProvider doesn't not contain a NumberFormatInfo, then + // this method returns the current culture's NumberFormatInfo. + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + + Debug.Assert(numberFormat != null); + + // Is the decimal separator is the same as the list separator? + // If so, we use the ";". + if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) + { + numericSeparator = ';'; + } + + return numericSeparator; + } + + internal readonly bool FoundSeparator + { + get + { + return _foundSeparator; + } + } + + private readonly char _quoteChar; + private readonly char _argSeparator; + private readonly ReadOnlySpan _input; + + private int _charIndex; + internal int _currentTokenIndex; + internal int _currentTokenLength; + private bool _foundSeparator; + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs new file mode 100644 index 00000000000..96afaf5fda5 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs @@ -0,0 +1,311 @@ +// 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.Diagnostics; +using System.Globalization; + +namespace MS.Internal +{ + internal ref struct ValueTokenizerHelper + { + /// + /// Constructor for which accepts an . + /// If the is null, we use the thread's info. + /// We will use ',' as the list separator, unless it's the same as the + /// decimal separator. If it *is*, then we can't determine if, say, "23,5" is one + /// number or two. In this case, we will use ";" as the separator. + /// + /// The string which will be tokenized. + /// The which controls this tokenization. + internal ValueTokenizerHelper(ReadOnlySpan input, IFormatProvider formatProvider) : this(input, '\'', GetNumericListSeparator(formatProvider)) { } + + /// + /// Initialize the with the string to tokenize, + /// the char which represents quotes and the list separator. + /// + /// The string to tokenize. + /// The quote char. + /// The list separator. + internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char separator) + { + _input = input; + _currentTokenIndex = -1; + _quoteChar = quoteChar; + _argSeparator = separator; + + // immediately forward past any whitespace so + // NextToken() logic always starts on the first + // character of the next token. + while (_charIndex < _input.Length) + { + if (!char.IsWhiteSpace(_input[_charIndex])) + { + break; + } + + ++_charIndex; + } + } + + /// + /// Returns the next available token or if there's none ready. + /// + /// A slice of the next token or . + internal readonly ReadOnlySpan GetCurrentToken() + { + // if no current token, return null + if (_currentTokenIndex < 0) + { + return ReadOnlySpan.Empty; + } + + return _input.Slice(_currentTokenIndex, _currentTokenLength); + } + + /// + /// Throws an exception if there is any non-whitespace left un-parsed. + /// + internal readonly void LastTokenRequired() + { + if (_charIndex != _input.Length) + { + throw new InvalidOperationException("_str"); + } + } + + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken() + { + return NextToken(false); + } + + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired() + { + if (!NextToken(false)) + { + throw new InvalidOperationException("_str"); + } + + return GetCurrentToken(); + } + + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired(bool allowQuotedToken) + { + if (!NextToken(allowQuotedToken)) + { + throw new InvalidOperationException("_str"); + } + + return GetCurrentToken(); + } + + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken) + { + // use the currently-set separator character. + return NextToken(allowQuotedToken, _argSeparator); + } + + /// + /// Advances to the next token. A separator character can be specified which overrides the one previously set. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken, char separator) + { + _currentTokenIndex = -1; // reset the currentTokenIndex + _foundSeparator = false; // reset + + // If we're at end of the string, just return false. + if (_charIndex >= _input.Length) + { + return false; + } + + char currentChar = _input[_charIndex]; + + Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); + + // setup the quoteCount + int quoteCount = 0; + + // If we are allowing a quoted token and this token begins with a quote, + // set up the quote count and skip the initial quote + if (allowQuotedToken && + currentChar == _quoteChar) + { + quoteCount++; // increment quote count + ++_charIndex; // move to next character + } + + int newTokenIndex = _charIndex; + int newTokenLength = 0; + + // loop until hit end of string or hit a , or whitespace + // if at end of string ust return false. + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; + + // if have a QuoteCount and this is a quote + // decrement the quoteCount + if (quoteCount > 0) + { + // if anything but a quoteChar we move on + if (currentChar == _quoteChar) + { + --quoteCount; + + // if at zero which it always should for now + // break out of the loop + if (0 == quoteCount) + { + ++_charIndex; // move past the quote + break; + } + } + } + else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) + { + if (currentChar == separator) + { + _foundSeparator = true; + } + break; + } + + ++_charIndex; + ++newTokenLength; + } + + // if quoteCount isn't zero we hit the end of the string + // before the ending quote + if (quoteCount > 0) + { + throw new InvalidOperationException("_str"); + } + + ScanToNextToken(separator); // move so at the start of the nextToken for next call + + // finally made it, update the _currentToken values + _currentTokenIndex = newTokenIndex; + _currentTokenLength = newTokenLength; + + if (_currentTokenLength < 1) + { + throw new InvalidOperationException("_str"); + } + + return true; + } + + // helper to move the _charIndex to the next token or to the end of the string + void ScanToNextToken(char separator) + { + // if already at end of the string don't bother + if (_charIndex < _input.Length) + { + char currentChar = _input[_charIndex]; + + // check that the currentChar is a space or the separator. If not + // we have an error. this can happen in the quote case + // that the char after the quotes string isn't a char. + if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) + { + throw new InvalidOperationException("_str"); + } + + // loop until hit a character that isn't + // an argument separator or whitespace. + int argSepCount = 0; + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; + + if (currentChar == separator) + { + _foundSeparator = true; + ++argSepCount; + _charIndex++; + + if (argSepCount > 1) + { + throw new InvalidOperationException("_str"); + } + } + else if (char.IsWhiteSpace(currentChar)) + { + ++_charIndex; + } + else + { + break; + } + } + + // if there was a separatorChar then we shouldn't be + // at the end of string or means there was a separator + // but there isn't an arg + + if (argSepCount > 0 && _charIndex >= _input.Length) + { + throw new InvalidOperationException("_str"); + } + } + } + + // Helper to get the numeric list separator for a given IFormatProvider. + // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. + static internal char GetNumericListSeparator(IFormatProvider provider) + { + char numericSeparator = ','; + + // Get the NumberFormatInfo out of the provider, if possible + // If the IFormatProvider doesn't not contain a NumberFormatInfo, then + // this method returns the current culture's NumberFormatInfo. + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + + Debug.Assert(numberFormat != null); + + // Is the decimal separator is the same as the list separator? + // If so, we use the ";". + if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) + { + numericSeparator = ';'; + } + + return numericSeparator; + } + + internal readonly bool FoundSeparator + { + get + { + return _foundSeparator; + } + } + + private readonly char _quoteChar; + private readonly char _argSeparator; + private readonly ReadOnlySpan _input; + + private int _charIndex; + internal int _currentTokenIndex; + internal int _currentTokenLength; + private bool _foundSeparator; + } + } \ No newline at end of file diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 38aa9a81d81..c25f1baea26 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -43,7 +43,8 @@ - + + From db159b5b895b10b9f08d459e94571797651770d9 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 18:29:13 +0200 Subject: [PATCH 14/20] Use ValueTokenizerHelper in generated structs/collections --- .../Media/Generated/DoubleCollection.cs | 6 ++-- .../Media/Generated/GradientStopCollection.cs | 8 ++--- .../Media/Generated/Int32Collection.cs | 6 ++-- .../Media/Generated/PointCollection.cs | 8 ++--- .../Media/Generated/VectorCollection.cs | 8 ++--- .../Windows/Media3D/Generated/Matrix3D.cs | 36 +++++++++---------- .../Windows/Media3D/Generated/Point3D.cs | 10 +++--- .../Media3D/Generated/Point3DCollection.cs | 10 +++--- .../Windows/Media3D/Generated/Point4D.cs | 12 +++---- .../Windows/Media3D/Generated/Quaternion.cs | 12 +++---- .../Windows/Media3D/Generated/Rect3D.cs | 16 ++++----- .../Windows/Media3D/Generated/Size3D.cs | 10 +++--- .../Windows/Media3D/Generated/Vector3D.cs | 10 +++--- .../Media3D/Generated/Vector3DCollection.cs | 10 +++--- .../System/Windows/Generated/Int32Rect.cs | 12 +++---- .../System/Windows/Generated/Point.cs | 8 ++--- .../System/Windows/Generated/Rect.cs | 12 +++---- .../System/Windows/Generated/Size.cs | 8 ++--- .../System/Windows/Generated/Vector.cs | 8 ++--- .../System/Windows/Media/Generated/Matrix.cs | 16 ++++----- .../codegen/mcg/generators/ManagedResource.cs | 6 ++-- .../codegen/mcg/generators/ManagedStruct.cs | 16 ++++----- 22 files changed, 124 insertions(+), 124 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs index a1f5c0a442c..01fa0639317 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DoubleCollection.cs @@ -713,14 +713,14 @@ public static DoubleCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); DoubleCollection resource = new DoubleCollection(); double value; - while (th.NextToken()) + while (tokenizer.NextToken()) { - value = double.Parse(th.GetCurrentTokenAsSpan(), formatProvider); + value = double.Parse(tokenizer.GetCurrentToken(), formatProvider); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs index dadb5a93da3..5439faf8d2e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GradientStopCollection.cs @@ -796,16 +796,16 @@ public static GradientStopCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); GradientStopCollection resource = new GradientStopCollection(); GradientStop value; - while (th.NextToken()) + while (tokenizer.NextToken()) { value = new GradientStop( - Parsers.ParseColor(th.GetCurrentToken(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + Parsers.ParseColor(tokenizer.GetCurrentToken().ToString(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs index d230d3ac8df..f955c45bdc4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/Int32Collection.cs @@ -713,14 +713,14 @@ public static Int32Collection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Int32Collection resource = new Int32Collection(); int value; - while (th.NextToken()) + while (tokenizer.NextToken()) { - value = Int32.Parse(th.GetCurrentTokenAsSpan(), formatProvider); + value = Int32.Parse(tokenizer.GetCurrentToken(), formatProvider); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs index 05c76f767b7..33a91d28bce 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs @@ -713,16 +713,16 @@ public static PointCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); PointCollection resource = new PointCollection(); Point value; - while (th.NextToken()) + while (tokenizer.NextToken()) { value = new Point( - double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.GetCurrentToken(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs index 6ac067e58ef..07d098f3746 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/VectorCollection.cs @@ -713,16 +713,16 @@ public static VectorCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); VectorCollection resource = new VectorCollection(); Vector value; - while (th.NextToken()) + while (tokenizer.NextToken()) { value = new Vector( - double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.GetCurrentToken(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs index c74cf829f7b..ff5aa2d2854 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs @@ -211,11 +211,11 @@ public static Matrix3D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Matrix3D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -226,25 +226,25 @@ public static Matrix3D Parse(string source) else { value = new Matrix3D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs index 7684bfd1ca8..4f097520fdf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs @@ -151,18 +151,18 @@ public static Point3D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Point3D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); value = new Point3D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs index 06e6723325e..3f0890efe8d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3DCollection.cs @@ -711,17 +711,17 @@ public static Point3DCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Point3DCollection resource = new Point3DCollection(); Point3D value; - while (th.NextToken()) + while (tokenizer.NextToken()) { value = new Point3D( - double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.GetCurrentToken(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs index 2ac84c95f8a..a004c981282 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs @@ -154,19 +154,19 @@ public static Point4D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Point4D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); value = new Point4D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs index 54cf9fcf4b9..2bca90b47c3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs @@ -175,11 +175,11 @@ public static Quaternion Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Quaternion value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -190,13 +190,13 @@ public static Quaternion Parse(string source) else { value = new Quaternion(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs index 195914cc1cf..f5d20ddc82d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs @@ -174,11 +174,11 @@ public static Rect3D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Rect3D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -189,15 +189,15 @@ public static Rect3D Parse(string source) else { value = new Rect3D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs index 1159390bd01..cd6db4edcdd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs @@ -165,11 +165,11 @@ public static Size3D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Size3D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -180,12 +180,12 @@ public static Size3D Parse(string source) else { value = new Size3D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs index a6d7992352c..040a8c2c768 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs @@ -151,18 +151,18 @@ public static Vector3D Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Vector3D value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); value = new Vector3D(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs index dac63e3ffd4..9fc556342de 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3DCollection.cs @@ -711,17 +711,17 @@ public static Vector3DCollection Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Vector3DCollection resource = new Vector3DCollection(); Vector3D value; - while (th.NextToken()) + while (tokenizer.NextToken()) { value = new Vector3D( - double.Parse(th.GetCurrentTokenAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.GetCurrentToken(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs index a7b8382ee82..0aafa05c20a 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs @@ -162,11 +162,11 @@ public static Int32Rect Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Int32Rect value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -177,13 +177,13 @@ public static Int32Rect Parse(string source) else { value = new Int32Rect(Int32.Parse(firstToken, formatProvider), - Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - Int32.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + Int32.Parse(tokenizer.NextTokenRequired(), formatProvider), + Int32.Parse(tokenizer.NextTokenRequired(), formatProvider), + Int32.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs index b57142ef219..5ee49b2be9d 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs @@ -142,17 +142,17 @@ public static Point Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Point value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); value = new Point(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs index dc56d3dd980..1bce4882a34 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs @@ -162,11 +162,11 @@ public static Rect Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Rect value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -177,13 +177,13 @@ public static Rect Parse(string source) else { value = new Rect(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs index e84768ad003..e6d54204d79 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs @@ -156,11 +156,11 @@ public static Size Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Size value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -171,11 +171,11 @@ public static Size Parse(string source) else { value = new Size(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs index 3a42ec913e7..b79830b323e 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs @@ -142,17 +142,17 @@ public static Vector Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Vector value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); value = new Vector(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs index e3ff23ac76a..ca0fa943284 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs @@ -175,11 +175,11 @@ public static Matrix Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); Matrix value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); // The token will already have had whitespace trimmed so we can do a // simple string compare. @@ -190,15 +190,15 @@ public static Matrix Parse(string source) else { value = new Matrix(double.Parse(firstToken, formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider), - double.Parse(th.NextTokenRequiredAsSpan(), formatProvider)); + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs index 0a07a999129..606ae0b82f2 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedResource.cs @@ -2820,14 +2820,14 @@ private static string WriteParse(McgResource resource) { parseBody = [[inline]] - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); [[resource.ManagedName]] resource = new [[resource.ManagedName]](); [[resource.CollectionType.ManagedName]] value; - while (th.NextToken()) + while (tokenizer.NextToken()) { - [[WriteParseBody(resource.CollectionType, "th.GetCurrentTokenAsSpan()")]] + [[WriteParseBody(resource.CollectionType, "tokenizer.GetCurrentToken()")]] resource.Add(value); } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs index e90b81ea21e..3aa4cf856c0 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs @@ -477,11 +477,11 @@ internal[[modifiers]] string ConvertToString(string format, IFormatProvider prov /// This method returns a string which will parse an instance of the type referred to /// by McgType. It assumes that there is a variable called "value" to which it can /// assign the value it produces, as well as assuming that there's a TokenizerHelper - /// called "th" available and in the correct state for parsing. If there's ever a need + /// called "tokenizer" available and in the correct state for parsing. If there's ever a need /// to alter these assumptions, we can add parameters to WriteParseBody to control these. /// It assumes that the first token is present in the string referred to by firstToken, - /// (typically a local string var or "th.GetCurrentToken()", and that all subsequent - /// tokens are required. It is left to the caller to call .LastTokenRequired() if it + /// (typically a local string var or "tokenizer.GetCurrentToken()", and that all subsequent + /// tokens are required. It is left to the caller to call .LastTokenRequired() if it /// wished to ensure that there are no trailing tokens. /// private static string WriteParseBody(McgType type, @@ -510,8 +510,8 @@ private static string WriteParseBody(McgType type, [[Helpers.CodeGenHelpers.WriteFieldStatementsFirstLastWithSeparator( resource.LocalFields, "{parseMethod}(" + firstToken + ", formatProvider)", - "{parseMethod}(th.NextTokenRequiredAsSpan(), formatProvider)", - "{parseMethod}(th.NextTokenRequiredAsSpan(), formatProvider)", + "{parseMethod}(tokenizer.NextTokenRequired(), formatProvider)", + "{parseMethod}(tokenizer.NextTokenRequired(), formatProvider)", ",\n")]]); [[/inline]]; @@ -556,16 +556,16 @@ public static [[resource.Name]] Parse(string source) { IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS; - TokenizerHelper th = new TokenizerHelper(source, formatProvider); + ValueTokenizerHelper tokenizer = new(source, formatProvider); [[resource.Name]] value; - ReadOnlySpan firstToken = th.NextTokenRequiredAsSpan(); + ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); [[WriteParseBody(resource, "firstToken")]] // There should be no more tokens in this string. - th.LastTokenRequired(); + tokenizer.LastTokenRequired(); return value; } From c578f70870c274cc6bf60d0978b056d0acc38eff Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 18:45:43 +0200 Subject: [PATCH 15/20] Use ValueTokenizerHelper in KeySplineConverter, make it allocation free --- .../Media/Animation/KeySplineConverter.cs | 118 ++++++++---------- 1 file changed, 51 insertions(+), 67 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs index d167bc0e271..c1f80679dca 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs @@ -1,17 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using MS.Internal; -using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.Runtime.CompilerServices; +using System.Windows.Media.Animation; +using System.ComponentModel; using System.Globalization; using System.Reflection; -using System.Windows.Media.Animation; +using MS.Internal; namespace System.Windows { /// - /// PointConverter - Converter class for converting instances of other types to Point instances + /// Converter class for converting instances of to and vice versa. /// /// public class KeySplineConverter : TypeConverter @@ -22,14 +23,7 @@ public class KeySplineConverter : TypeConverter /// public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptor, Type destinationType) { - if (destinationType == typeof(string)) - { - return true; - } - else - { - return false; - } + return destinationType == typeof(string); } /// @@ -41,39 +35,23 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptor, Type /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { - if ( destinationType == typeof(InstanceDescriptor) - || destinationType == typeof(string)) - { - return true; - } - else - { - return false; - } + return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// /// ConvertFrom /// - public override object ConvertFrom( - ITypeDescriptorContext context, - CultureInfo cultureInfo, - object value) + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo cultureInfo, object value) { - string stringValue = value as string; - - if (value == null) - { + if (value is not string stringValue) throw new NotSupportedException(SR.Converter_ConvertFromNotSupported); - } - TokenizerHelper th = new TokenizerHelper(stringValue, cultureInfo); + ValueTokenizerHelper tokenizer = new(stringValue, cultureInfo); - return new KeySpline( - Convert.ToDouble(th.NextTokenRequired(), cultureInfo), - Convert.ToDouble(th.NextTokenRequired(), cultureInfo), - Convert.ToDouble(th.NextTokenRequired(), cultureInfo), - Convert.ToDouble(th.NextTokenRequired(), cultureInfo)); + return new KeySpline(double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo)); } /// @@ -85,45 +63,51 @@ public override object ConvertFrom( /// Type to convert to /// converted value /// - public override object ConvertTo( - ITypeDescriptorContext context, - CultureInfo cultureInfo, - object value, - Type destinationType) + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cultureInfo, object value, Type destinationType) { - KeySpline keySpline = value as KeySpline; - - if (keySpline != null && destinationType != null) + if (value is KeySpline keySpline && destinationType is not null) { - if (destinationType == typeof(InstanceDescriptor)) - { - ConstructorInfo ci = typeof(KeySpline).GetConstructor(new Type[] - { - typeof(double), typeof(double), - typeof(double), typeof(double) - }); + if (destinationType == typeof(string)) + return ToString(keySpline, cultureInfo); - return new InstanceDescriptor(ci, new object[] - { - keySpline.ControlPoint1.X, keySpline.ControlPoint1.Y, - keySpline.ControlPoint2.X, keySpline.ControlPoint2.Y - }); - } - else if (destinationType == typeof(string)) + if (destinationType == typeof(InstanceDescriptor)) { - return String.Format( - cultureInfo, - "{0}{4}{1}{4}{2}{4}{3}", - keySpline.ControlPoint1.X, - keySpline.ControlPoint1.Y, - keySpline.ControlPoint2.X, - keySpline.ControlPoint2.Y, - cultureInfo != null ? cultureInfo.TextInfo.ListSeparator : CultureInfo.InvariantCulture.TextInfo.ListSeparator); + ConstructorInfo ci = typeof(KeySpline).GetConstructor(new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) }); + return new InstanceDescriptor(ci, new object[] { keySpline.ControlPoint1.X, keySpline.ControlPoint1.Y, keySpline.ControlPoint2.X, keySpline.ControlPoint2.Y }); } } - // Pass unhandled cases to base class (which will throw exceptions for null value or destinationType.) + // Pass unhandled cases to base class (which will throw exceptions for null value or destinationType) return base.ConvertTo(context, cultureInfo, value, destinationType); } + + /// + /// Converts to its string representation using the specified . + /// + /// The to convert to string. + /// Culture to use when formatting doubles and choosing separator. + /// The formatted as string using the specified . + private static string ToString(KeySpline keySpline, CultureInfo cultureInfo) + { + string listSeparator = cultureInfo != null ? cultureInfo.TextInfo.ListSeparator : CultureInfo.InvariantCulture.TextInfo.ListSeparator; + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (fourteen digits is generous for the range of values likely) + // 3 = 3x separator characters + // 1 = 1x scratch space for alignment + DefaultInterpolatedStringHandler handler = new(3, 4, cultureInfo, stackalloc char[64]); + handler.AppendFormatted(keySpline.ControlPoint1.X); + handler.AppendLiteral(listSeparator); + + handler.AppendFormatted(keySpline.ControlPoint1.Y); + handler.AppendLiteral(listSeparator); + + handler.AppendFormatted(keySpline.ControlPoint2.X); + handler.AppendLiteral(listSeparator); + + handler.AppendFormatted(keySpline.ControlPoint2.Y); + + return handler.ToStringAndClear(); + } } } From a239e7ca7eed961c398432f72962d38eeba1a1f7 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 19:20:33 +0200 Subject: [PATCH 16/20] Remove double inclusion of ValueTokenizerHelper --- .../src/WindowsBase/ValueTokenizerHelper.cs | 311 ------------------ 1 file changed, 311 deletions(-) delete mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs deleted file mode 100644 index 96afaf5fda5..00000000000 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ValueTokenizerHelper.cs +++ /dev/null @@ -1,311 +0,0 @@ -// 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.Diagnostics; -using System.Globalization; - -namespace MS.Internal -{ - internal ref struct ValueTokenizerHelper - { - /// - /// Constructor for which accepts an . - /// If the is null, we use the thread's info. - /// We will use ',' as the list separator, unless it's the same as the - /// decimal separator. If it *is*, then we can't determine if, say, "23,5" is one - /// number or two. In this case, we will use ";" as the separator. - /// - /// The string which will be tokenized. - /// The which controls this tokenization. - internal ValueTokenizerHelper(ReadOnlySpan input, IFormatProvider formatProvider) : this(input, '\'', GetNumericListSeparator(formatProvider)) { } - - /// - /// Initialize the with the string to tokenize, - /// the char which represents quotes and the list separator. - /// - /// The string to tokenize. - /// The quote char. - /// The list separator. - internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char separator) - { - _input = input; - _currentTokenIndex = -1; - _quoteChar = quoteChar; - _argSeparator = separator; - - // immediately forward past any whitespace so - // NextToken() logic always starts on the first - // character of the next token. - while (_charIndex < _input.Length) - { - if (!char.IsWhiteSpace(_input[_charIndex])) - { - break; - } - - ++_charIndex; - } - } - - /// - /// Returns the next available token or if there's none ready. - /// - /// A slice of the next token or . - internal readonly ReadOnlySpan GetCurrentToken() - { - // if no current token, return null - if (_currentTokenIndex < 0) - { - return ReadOnlySpan.Empty; - } - - return _input.Slice(_currentTokenIndex, _currentTokenLength); - } - - /// - /// Throws an exception if there is any non-whitespace left un-parsed. - /// - internal readonly void LastTokenRequired() - { - if (_charIndex != _input.Length) - { - throw new InvalidOperationException("_str"); - } - } - - /// - /// Advances to the next token. - /// - /// if next token was found, if at end of string. - internal bool NextToken() - { - return NextToken(false); - } - - /// - /// Advances to the next token, throwing an exception if not present. - /// - /// A slice of the next next token. - internal ReadOnlySpan NextTokenRequired() - { - if (!NextToken(false)) - { - throw new InvalidOperationException("_str"); - } - - return GetCurrentToken(); - } - - /// - /// Advances to the next token, throwing an exception if not present. - /// - /// A slice of the next next token. - internal ReadOnlySpan NextTokenRequired(bool allowQuotedToken) - { - if (!NextToken(allowQuotedToken)) - { - throw new InvalidOperationException("_str"); - } - - return GetCurrentToken(); - } - - /// - /// Advances to the next token. - /// - /// if next token was found, if at end of string. - internal bool NextToken(bool allowQuotedToken) - { - // use the currently-set separator character. - return NextToken(allowQuotedToken, _argSeparator); - } - - /// - /// Advances to the next token. A separator character can be specified which overrides the one previously set. - /// - /// if next token was found, if at end of string. - internal bool NextToken(bool allowQuotedToken, char separator) - { - _currentTokenIndex = -1; // reset the currentTokenIndex - _foundSeparator = false; // reset - - // If we're at end of the string, just return false. - if (_charIndex >= _input.Length) - { - return false; - } - - char currentChar = _input[_charIndex]; - - Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); - - // setup the quoteCount - int quoteCount = 0; - - // If we are allowing a quoted token and this token begins with a quote, - // set up the quote count and skip the initial quote - if (allowQuotedToken && - currentChar == _quoteChar) - { - quoteCount++; // increment quote count - ++_charIndex; // move to next character - } - - int newTokenIndex = _charIndex; - int newTokenLength = 0; - - // loop until hit end of string or hit a , or whitespace - // if at end of string ust return false. - while (_charIndex < _input.Length) - { - currentChar = _input[_charIndex]; - - // if have a QuoteCount and this is a quote - // decrement the quoteCount - if (quoteCount > 0) - { - // if anything but a quoteChar we move on - if (currentChar == _quoteChar) - { - --quoteCount; - - // if at zero which it always should for now - // break out of the loop - if (0 == quoteCount) - { - ++_charIndex; // move past the quote - break; - } - } - } - else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) - { - if (currentChar == separator) - { - _foundSeparator = true; - } - break; - } - - ++_charIndex; - ++newTokenLength; - } - - // if quoteCount isn't zero we hit the end of the string - // before the ending quote - if (quoteCount > 0) - { - throw new InvalidOperationException("_str"); - } - - ScanToNextToken(separator); // move so at the start of the nextToken for next call - - // finally made it, update the _currentToken values - _currentTokenIndex = newTokenIndex; - _currentTokenLength = newTokenLength; - - if (_currentTokenLength < 1) - { - throw new InvalidOperationException("_str"); - } - - return true; - } - - // helper to move the _charIndex to the next token or to the end of the string - void ScanToNextToken(char separator) - { - // if already at end of the string don't bother - if (_charIndex < _input.Length) - { - char currentChar = _input[_charIndex]; - - // check that the currentChar is a space or the separator. If not - // we have an error. this can happen in the quote case - // that the char after the quotes string isn't a char. - if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) - { - throw new InvalidOperationException("_str"); - } - - // loop until hit a character that isn't - // an argument separator or whitespace. - int argSepCount = 0; - while (_charIndex < _input.Length) - { - currentChar = _input[_charIndex]; - - if (currentChar == separator) - { - _foundSeparator = true; - ++argSepCount; - _charIndex++; - - if (argSepCount > 1) - { - throw new InvalidOperationException("_str"); - } - } - else if (char.IsWhiteSpace(currentChar)) - { - ++_charIndex; - } - else - { - break; - } - } - - // if there was a separatorChar then we shouldn't be - // at the end of string or means there was a separator - // but there isn't an arg - - if (argSepCount > 0 && _charIndex >= _input.Length) - { - throw new InvalidOperationException("_str"); - } - } - } - - // Helper to get the numeric list separator for a given IFormatProvider. - // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. - static internal char GetNumericListSeparator(IFormatProvider provider) - { - char numericSeparator = ','; - - // Get the NumberFormatInfo out of the provider, if possible - // If the IFormatProvider doesn't not contain a NumberFormatInfo, then - // this method returns the current culture's NumberFormatInfo. - NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); - - Debug.Assert(numberFormat != null); - - // Is the decimal separator is the same as the list separator? - // If so, we use the ";". - if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) - { - numericSeparator = ';'; - } - - return numericSeparator; - } - - internal readonly bool FoundSeparator - { - get - { - return _foundSeparator; - } - } - - private readonly char _quoteChar; - private readonly char _argSeparator; - private readonly ReadOnlySpan _input; - - private int _charIndex; - internal int _currentTokenIndex; - internal int _currentTokenLength; - private bool _foundSeparator; - } - } \ No newline at end of file From 3d23f75af80f726bd7ead8717d34fb700aa1a79a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 3 Oct 2024 20:21:19 +0200 Subject: [PATCH 17/20] Comments/code style adjustments, some documentation, no code changes --- .../Media/Animation/KeySplineConverter.cs | 31 ++++++++----- .../VirtualizationCacheLengthConverter.cs | 11 +++-- .../System/Windows/CornerRadiusConverter.cs | 8 ++-- .../System/Windows/ThicknessConverter.cs | 26 +++++------ .../src/Shared/MS/Internal/TokenizerHelper.cs | 43 ++++++++++--------- .../MS/Internal/ValueTokenizerHelper.cs | 27 ++++++------ 6 files changed, 79 insertions(+), 67 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs index c1f80679dca..f0004fb0ad9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs @@ -20,7 +20,11 @@ public class KeySplineConverter : TypeConverter /// /// CanConvertFrom - Returns whether or not this class can convert from a given type /// - /// + /// + /// if the given can be converted from, otherwise. + /// + /// The for this call. + /// The being queried for support. public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptor, Type destinationType) { return destinationType == typeof(string); @@ -31,16 +35,22 @@ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptor, Type /// /// ITypeDescriptorContext /// Type to convert to - /// true if conversion is possible - /// + /// + /// if this class can convert to , otherwise. + /// public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string); } /// - /// ConvertFrom + /// Converts of type to its represensation. /// + /// The for this call. + /// The which is respected during conversion. + /// The object to convert to a . + /// A new instance of class representing the data contained in . + /// Thrown in case the was not a . public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo cultureInfo, object value) { if (value is not string stringValue) @@ -55,14 +65,15 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c } /// - /// TypeConverter method implementation. + /// Attempt to convert a class to the . /// /// ITypeDescriptorContext /// current culture (see CLR specs), null is a valid value /// value to convert from /// Type to convert to - /// converted value - /// + /// + /// The formatted as using the specified or an . + /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cultureInfo, object value, Type destinationType) { if (value is KeySpline keySpline && destinationType is not null) @@ -82,17 +93,17 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } /// - /// Converts to its string representation using the specified . + /// Converts to its representation using the specified . /// /// The to convert to string. /// Culture to use when formatting doubles and choosing separator. - /// The formatted as string using the specified . + /// The formatted as using the specified . private static string ToString(KeySpline keySpline, CultureInfo cultureInfo) { string listSeparator = cultureInfo != null ? cultureInfo.TextInfo.ListSeparator : CultureInfo.InvariantCulture.TextInfo.ListSeparator; // Initial capacity [64] is an estimate based on a sum of: - // 48 = 4x double (fourteen digits is generous for the range of values likely) + // 60 = 4x double (fifteen digits is generous for the range of values) // 3 = 3x separator characters // 1 = 1x scratch space for alignment DefaultInterpolatedStringHandler handler = new(3, 4, cultureInfo, stackalloc char[64]); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs index 2511765c684..57bcc7b4328 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/VirtualizationCacheLengthConverter.cs @@ -10,9 +10,8 @@ namespace System.Windows.Controls { /// - /// VirtualizationCacheLengthConverter - Converter class for converting - /// instances of other types to and from VirtualizationCacheLength instances. - /// + /// Converter class for converting instances of other types to and from instances. + /// public class VirtualizationCacheLengthConverter : TypeConverter { #region Public Methods @@ -21,10 +20,10 @@ public class VirtualizationCacheLengthConverter : TypeConverter /// CanConvertFrom - Returns whether or not this class can convert from a given type. ///
/// - /// bool - True if thie converter can convert from the provided type, false if not. + /// if the given can be converted from, otherwise. /// - /// The ITypeDescriptorContext for this call. - /// The Type being queried for support. + /// The for this call. + /// The being queried for support. public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType) { // We can only handle strings, integral and floating types diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs index e15b5b4ba1d..a4ef35ce582 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/CornerRadiusConverter.cs @@ -10,7 +10,7 @@ namespace System.Windows { /// - /// CornerRadiusConverter - Converter class for converting instances of other types to and from CornerRadius instances. + /// Converter class for converting instances of other types to and from instances. /// public class CornerRadiusConverter : TypeConverter { @@ -20,10 +20,10 @@ public class CornerRadiusConverter : TypeConverter /// CanConvertFrom - Returns whether or not this class can convert from a given type. ///
/// - /// bool - True if thie converter can convert from the provided type, false if not. + /// if the given can be converted from, otherwise. /// - /// The ITypeDescriptorContext for this call. - /// The Type being queried for support. + /// The for this call. + /// The being queried for support. public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType) { // We can only handle strings, integral and floating types diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs index 10c43fc173b..afb84a5ed2b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ThicknessConverter.cs @@ -11,20 +11,20 @@ namespace System.Windows { /// - /// ThicknessConverter - Converter class for converting instances of other types to and from Thickness instances. + /// Converter class for converting instances of other types to and from instances. /// public class ThicknessConverter : TypeConverter { #region Public Methods /// - /// CanConvertFrom - Returns whether or not this class can convert from a given type. + /// Returns whether this class can convert specific into . /// /// - /// bool - True if thie converter can convert from the provided type, false if not. + /// if the given can be converted from, otherwise. /// - /// The ITypeDescriptorContext for this call. - /// The Type being queried for support. + /// The for this call. + /// The being queried for support. public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType) { // We can only handle strings, integral and floating types @@ -94,11 +94,15 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, } /// - /// ConvertTo - Attempt to convert a Thickness to the given type + /// Attempt to convert a struct to the . /// /// - /// The object which was constructed. + /// The formatted as using the specified or an . /// + /// The ITypeDescriptorContext for this call. + /// The CultureInfo which is respected when converting. + /// The Thickness to convert. + /// The type to which to convert the Thickness instance. /// /// An ArgumentNullException is thrown if the example object is null. /// @@ -106,10 +110,6 @@ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, /// An ArgumentException is thrown if the object is not null and is not a Thickness, /// or if the destinationType isn't one of the valid destination types. /// - /// The ITypeDescriptorContext for this call. - /// The CultureInfo which is respected when converting. - /// The Thickness to convert. - /// The type to which to convert the Thickness instance. public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType) { ArgumentNullException.ThrowIfNull(value); @@ -143,9 +143,9 @@ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, C /// /// Converts to its string representation using the specified . /// - /// The to convert to string. + /// The to convert to . /// Culture to use when formatting doubles and choosing separator. - /// The formatted as string using the specified . + /// The formatted as using the specified . internal static string ToString(Thickness thickness, CultureInfo cultureInfo) { char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs index cf18784b5f7..13fe97f6cf1 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs @@ -21,18 +21,17 @@ namespace MS.Internal internal sealed class TokenizerHelper { /// - /// Constructor for TokenizerHelper which accepts an IFormatProvider. - /// If the IFormatProvider is null, we use the thread's IFormatProvider info. - /// We will use ',' as the list separator, unless it's the same as the - /// decimal separator. If it *is*, then we can't determine if, say, "23,5" is one - /// number or two. In this case, we will use ";" as the separator. + /// Constructor for which accepts an . + /// If the is , we use the thread's info. + /// We will use ',' as the list separator, unless it's the same as the decimal separator. + /// If it *is*, then we can't determine if, say, "23,5" is one number or two. In this case, we will use ";" as the separator. /// /// The string which will be tokenized. - /// The IFormatProvider which controls this tokenization. + /// The which controls this tokenization. internal TokenizerHelper(string str, IFormatProvider formatProvider) : this(str, '\'', GetNumericListSeparator(formatProvider)) { } /// - /// Initialize the TokenizerHelper with the string to tokenize, + /// Initialize the with the string to tokenize, /// the char which represents quotes and the list separator. /// /// The string to tokenize. @@ -52,9 +51,7 @@ internal TokenizerHelper(string str, char quoteChar, char separator) while (_charIndex < _strLen) { if (!char.IsWhiteSpace(_str[_charIndex])) - { break; - } ++_charIndex; } @@ -94,7 +91,7 @@ internal void LastTokenRequired() { if (_charIndex != _strLen) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _str)); } } @@ -115,7 +112,7 @@ internal string NextTokenRequired() { if (!NextToken(false)) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); } return GetCurrentToken(); @@ -143,7 +140,7 @@ internal string NextTokenRequired(bool allowQuotedToken) { if (!NextToken(allowQuotedToken)) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); } return GetCurrentToken(); @@ -184,8 +181,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) // If we are allowing a quoted token and this token begins with a quote, // set up the quote count and skip the initial quote - if (allowQuotedToken && - currentChar == _quoteChar) + if (allowQuotedToken && _quoteChar == currentChar) { quoteCount++; // increment quote count ++_charIndex; // move to next character @@ -235,7 +231,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) // before the ending quote if (quoteCount > 0) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperMissingEndQuote, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperMissingEndQuote, _str)); } ScanToNextToken(separator); // move so at the start of the nextToken for next call @@ -246,13 +242,17 @@ internal bool NextToken(bool allowQuotedToken, char separator) if (_currentTokenLength < 1) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); } return true; } - // helper to move the _charIndex to the next token or to the end of the string + /// + /// Helper function to move the _charIndex to the next token or to the end of the string. + /// + /// + /// private void ScanToNextToken(char separator) { // if already at end of the string don't bother @@ -265,7 +265,7 @@ private void ScanToNextToken(char separator) // that the char after the quotes string isn't a char. if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _str)); } // loop until hit a character that isn't @@ -283,7 +283,7 @@ private void ScanToNextToken(char separator) if (argSepCount > 1) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); } } else if (char.IsWhiteSpace(currentChar)) @@ -302,7 +302,7 @@ private void ScanToNextToken(char separator) if (argSepCount > 0 && _charIndex >= _strLen) { - throw new System.InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _str)); } } } @@ -318,7 +318,7 @@ internal static char GetNumericListSeparator(IFormatProvider provider) // this method returns the current culture's NumberFormatInfo. NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); - Debug.Assert(null != numberFormat); + Debug.Assert(numberFormat != null); // Is the decimal separator is the same as the list separator? // If so, we use the ";". @@ -347,6 +347,7 @@ internal bool FoundSeparator // State fields private int _charIndex; private bool _foundSeparator; + internal int _currentTokenIndex; internal int _currentTokenLength; } diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs index a90aa1fa4d0..5d4f01b5cca 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs @@ -16,10 +16,9 @@ internal ref struct ValueTokenizerHelper { /// /// Constructor for which accepts an . - /// If the is null, we use the thread's info. - /// We will use ',' as the list separator, unless it's the same as the - /// decimal separator. If it *is*, then we can't determine if, say, "23,5" is one - /// number or two. In this case, we will use ";" as the separator. + /// If the is , we use the thread's info. + /// We will use ',' as the list separator, unless it's the same as the decimal separator. + /// If it *is*, then we can't determine if, say, "23,5" is one number or two. In this case, we will use ";" as the separator. /// /// The string which will be tokenized. /// The which controls this tokenization. @@ -45,9 +44,7 @@ internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char sep while (_charIndex < _input.Length) { if (!char.IsWhiteSpace(_input[_charIndex])) - { break; - } ++_charIndex; } @@ -150,8 +147,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) // If we are allowing a quoted token and this token begins with a quote, // set up the quote count and skip the initial quote - if (allowQuotedToken && - currentChar == _quoteChar) + if (allowQuotedToken && _quoteChar == currentChar) { quoteCount++; // increment quote count ++_charIndex; // move to next character @@ -171,7 +167,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) if (quoteCount > 0) { // if anything but a quoteChar we move on - if (currentChar == _quoteChar) + if (_quoteChar == currentChar) { --quoteCount; @@ -218,8 +214,12 @@ internal bool NextToken(bool allowQuotedToken, char separator) return true; } - // helper to move the _charIndex to the next token or to the end of the string - void ScanToNextToken(char separator) + /// + /// Helper function to move the _charIndex to the next token or to the end of the string. + /// + /// + /// + private void ScanToNextToken(char separator) { // if already at end of the string don't bother if (_charIndex < _input.Length) @@ -275,7 +275,7 @@ void ScanToNextToken(char separator) // Helper to get the numeric list separator for a given IFormatProvider. // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. - static internal char GetNumericListSeparator(IFormatProvider provider) + internal static char GetNumericListSeparator(IFormatProvider provider) { char numericSeparator = ','; @@ -309,8 +309,9 @@ internal readonly bool FoundSeparator private readonly ReadOnlySpan _input; private int _charIndex; + private bool _foundSeparator; + internal int _currentTokenIndex; internal int _currentTokenLength; - private bool _foundSeparator; } } From e65a4683427c1ba3c90d1488195b778fdc06d0fa Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 1 Apr 2025 11:27:44 +0200 Subject: [PATCH 18/20] Fix string comparisons --- .../System/Windows/Media3D/Generated/Matrix3D.cs | 5 +++-- .../System/Windows/Media3D/Generated/Point3D.cs | 3 ++- .../System/Windows/Media3D/Generated/Point4D.cs | 3 ++- .../System/Windows/Media3D/Generated/Quaternion.cs | 5 +++-- .../System/Windows/Media3D/Generated/Rect3D.cs | 5 +++-- .../System/Windows/Media3D/Generated/Size3D.cs | 5 +++-- .../System/Windows/Media3D/Generated/Vector3D.cs | 3 ++- .../src/WindowsBase/System/Windows/Generated/Int32Rect.cs | 5 +++-- .../src/WindowsBase/System/Windows/Generated/Point.cs | 5 +++-- .../src/WindowsBase/System/Windows/Generated/Rect.cs | 5 +++-- .../src/WindowsBase/System/Windows/Generated/Size.cs | 5 +++-- .../src/WindowsBase/System/Windows/Generated/Vector.cs | 3 ++- .../src/WindowsBase/System/Windows/Media/Generated/Matrix.cs | 5 +++-- .../src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs | 2 +- 14 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs index ff5aa2d2854..33b8ac9b0bf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Matrix3D.cs @@ -219,13 +219,14 @@ public static Matrix3D Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Identity") + if (firstToken.Equals("Identity", StringComparison.Ordinal)) { value = Identity; } else { - value = new Matrix3D(double.Parse(firstToken, formatProvider), + value = new Matrix3D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs index 4f097520fdf..ff293a54fd6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point3D.cs @@ -157,7 +157,8 @@ public static Point3D Parse(string source) ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); - value = new Point3D(double.Parse(firstToken, formatProvider), + value = new Point3D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs index a004c981282..e70f2bbc21b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Point4D.cs @@ -160,7 +160,8 @@ public static Point4D Parse(string source) ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); - value = new Point4D(double.Parse(firstToken, formatProvider), + value = new Point4D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs index 2bca90b47c3..9dcf6a45a1a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Quaternion.cs @@ -183,13 +183,14 @@ public static Quaternion Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Identity") + if (firstToken.Equals("Identity", StringComparison.Ordinal)) { value = Identity; } else { - value = new Quaternion(double.Parse(firstToken, formatProvider), + value = new Quaternion( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs index f5d20ddc82d..97845f15650 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Rect3D.cs @@ -182,13 +182,14 @@ public static Rect3D Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Empty") + if (firstToken.Equals("Empty", StringComparison.Ordinal)) { value = Empty; } else { - value = new Rect3D(double.Parse(firstToken, formatProvider), + value = new Rect3D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs index cd6db4edcdd..fd0f19fc1e3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Size3D.cs @@ -173,13 +173,14 @@ public static Size3D Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Empty") + if (firstToken.Equals("Empty", StringComparison.Ordinal)) { value = Empty; } else { - value = new Size3D(double.Parse(firstToken, formatProvider), + value = new Size3D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs index 040a8c2c768..dd27124631f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Generated/Vector3D.cs @@ -157,7 +157,8 @@ public static Vector3D Parse(string source) ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); - value = new Vector3D(double.Parse(firstToken, formatProvider), + value = new Vector3D( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs index 0aafa05c20a..29918ca498f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Int32Rect.cs @@ -170,13 +170,14 @@ public static Int32Rect Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Empty") + if (firstToken.Equals("Empty", StringComparison.Ordinal)) { value = Empty; } else { - value = new Int32Rect(Int32.Parse(firstToken, formatProvider), + value = new Int32Rect( + Int32.Parse(firstToken, formatProvider), Int32.Parse(tokenizer.NextTokenRequired(), formatProvider), Int32.Parse(tokenizer.NextTokenRequired(), formatProvider), Int32.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs index 5ee49b2be9d..ff7bcc9cb5f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Point.cs @@ -148,8 +148,9 @@ public static Point Parse(string source) ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); - value = new Point(double.Parse(firstToken, formatProvider), - double.Parse(tokenizer.NextTokenRequired(), formatProvider)); + value = new Point( + double.Parse(firstToken, formatProvider), + double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. tokenizer.LastTokenRequired(); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs index 1bce4882a34..fd7333ab28b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Rect.cs @@ -170,13 +170,14 @@ public static Rect Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Empty") + if (firstToken.Equals("Empty", StringComparison.Ordinal)) { value = Empty; } else { - value = new Rect(double.Parse(firstToken, formatProvider), + value = new Rect( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs index e6d54204d79..b336fe8991f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Size.cs @@ -164,13 +164,14 @@ public static Size Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Empty") + if (firstToken.Equals("Empty", StringComparison.Ordinal)) { value = Empty; } else { - value = new Size(double.Parse(firstToken, formatProvider), + value = new Size( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs index b79830b323e..ff98d3c11cb 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Generated/Vector.cs @@ -148,7 +148,8 @@ public static Vector Parse(string source) ReadOnlySpan firstToken = tokenizer.NextTokenRequired(); - value = new Vector(double.Parse(firstToken, formatProvider), + value = new Vector( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider)); // There should be no more tokens in this string. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs index ca0fa943284..b3e2ac161c2 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Media/Generated/Matrix.cs @@ -183,13 +183,14 @@ public static Matrix Parse(string source) // The token will already have had whitespace trimmed so we can do a // simple string compare. - if (firstToken == "Identity") + if (firstToken.Equals("Identity", StringComparison.Ordinal)) { value = Identity; } else { - value = new Matrix(double.Parse(firstToken, formatProvider), + value = new Matrix( + double.Parse(firstToken, formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), double.Parse(tokenizer.NextTokenRequired(), formatProvider), diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs index 3aa4cf856c0..cb7ce4f4a21 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/generators/ManagedStruct.cs @@ -523,7 +523,7 @@ private static string WriteParseBody(McgType type, [[inline]] // The token will already have had whitespace trimmed so we can do a // simple string compare. - if ([[firstToken]] == "[[resource.EmptyField.Name]]") + if ([[firstToken]].Equals("[[resource.EmptyField.Name]]", StringComparison.Ordinal)) { value = [[resource.EmptyField.Name]]; } From 3c551cb8f47e952bf68c0dfaf80314d2848dc3ca Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 1 Apr 2025 11:51:24 +0200 Subject: [PATCH 19/20] Fix ValueTokenizerHelper IDE0073 and move to file-scoped namespace --- .../MS/Internal/ValueTokenizerHelper.cs | 479 +++++++++--------- 1 file changed, 237 insertions(+), 242 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs index 5d4f01b5cca..447370a6842 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs @@ -1,317 +1,312 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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; -using System.Diagnostics; using System.Globalization; -using MS.Internal.WindowsBase; -namespace MS.Internal +namespace MS.Internal; + +/// +/// Represents a implementation of operating over . +/// +internal ref struct ValueTokenizerHelper { /// - /// Represents a implementation of operating over . + /// Constructor for which accepts an . + /// If the is , we use the thread's info. + /// We will use ',' as the list separator, unless it's the same as the decimal separator. + /// If it *is*, then we can't determine if, say, "23,5" is one number or two. In this case, we will use ";" as the separator. + /// + /// The string which will be tokenized. + /// The which controls this tokenization. + internal ValueTokenizerHelper(ReadOnlySpan input, IFormatProvider formatProvider) : this(input, '\'', GetNumericListSeparator(formatProvider)) { } + + /// + /// Initialize the with the string to tokenize, + /// the char which represents quotes and the list separator. /// - internal ref struct ValueTokenizerHelper + /// The string to tokenize. + /// The quote char. + /// The list separator. + internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char separator) { - /// - /// Constructor for which accepts an . - /// If the is , we use the thread's info. - /// We will use ',' as the list separator, unless it's the same as the decimal separator. - /// If it *is*, then we can't determine if, say, "23,5" is one number or two. In this case, we will use ";" as the separator. - /// - /// The string which will be tokenized. - /// The which controls this tokenization. - internal ValueTokenizerHelper(ReadOnlySpan input, IFormatProvider formatProvider) : this(input, '\'', GetNumericListSeparator(formatProvider)) { } - - /// - /// Initialize the with the string to tokenize, - /// the char which represents quotes and the list separator. - /// - /// The string to tokenize. - /// The quote char. - /// The list separator. - internal ValueTokenizerHelper(ReadOnlySpan input, char quoteChar, char separator) + _input = input; + _currentTokenIndex = -1; + _quoteChar = quoteChar; + _argSeparator = separator; + + // immediately forward past any whitespace so + // NextToken() logic always starts on the first + // character of the next token. + while (_charIndex < _input.Length) { - _input = input; - _currentTokenIndex = -1; - _quoteChar = quoteChar; - _argSeparator = separator; - - // immediately forward past any whitespace so - // NextToken() logic always starts on the first - // character of the next token. - while (_charIndex < _input.Length) - { - if (!char.IsWhiteSpace(_input[_charIndex])) - break; + if (!char.IsWhiteSpace(_input[_charIndex])) + break; - ++_charIndex; - } + ++_charIndex; } + } - /// - /// Returns the next available token or if there's none ready. - /// - /// A slice of the next token or . - internal readonly ReadOnlySpan GetCurrentToken() + /// + /// Returns the next available token or if there's none ready. + /// + /// A slice of the next token or . + internal readonly ReadOnlySpan GetCurrentToken() + { + // If there's no current token, return an empty span + if (_currentTokenIndex < 0) { - // If there's no current token, return an empty span - if (_currentTokenIndex < 0) - { - return ReadOnlySpan.Empty; - } - - return _input.Slice(_currentTokenIndex, _currentTokenLength); + return ReadOnlySpan.Empty; } - /// - /// Throws an exception if there is any non-whitespace left un-parsed. - /// - internal readonly void LastTokenRequired() + return _input.Slice(_currentTokenIndex, _currentTokenLength); + } + + /// + /// Throws an exception if there is any non-whitespace left un-parsed. + /// + internal readonly void LastTokenRequired() + { + if (_charIndex != _input.Length) { - if (_charIndex != _input.Length) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); - } + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); } + } - /// - /// Advances to the next token. - /// - /// if next token was found, if at end of string. - internal bool NextToken() + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken() + { + return NextToken(false); + } + + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired() + { + if (!NextToken(false)) { - return NextToken(false); + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); } - /// - /// Advances to the next token, throwing an exception if not present. - /// - /// A slice of the next next token. - internal ReadOnlySpan NextTokenRequired() - { - if (!NextToken(false)) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); - } + return GetCurrentToken(); + } - return GetCurrentToken(); + /// + /// Advances to the next token, throwing an exception if not present. + /// + /// A slice of the next next token. + internal ReadOnlySpan NextTokenRequired(bool allowQuotedToken) + { + if (!NextToken(allowQuotedToken)) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); } - /// - /// Advances to the next token, throwing an exception if not present. - /// - /// A slice of the next next token. - internal ReadOnlySpan NextTokenRequired(bool allowQuotedToken) - { - if (!NextToken(allowQuotedToken)) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _input.ToString())); - } + return GetCurrentToken(); + } - return GetCurrentToken(); - } + /// + /// Advances to the next token. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken) + { + // use the currently-set separator character. + return NextToken(allowQuotedToken, _argSeparator); + } - /// - /// Advances to the next token. - /// - /// if next token was found, if at end of string. - internal bool NextToken(bool allowQuotedToken) - { - // use the currently-set separator character. - return NextToken(allowQuotedToken, _argSeparator); - } + /// + /// Advances to the next token. A separator character can be specified which overrides the one previously set. + /// + /// if next token was found, if at end of string. + internal bool NextToken(bool allowQuotedToken, char separator) + { + _currentTokenIndex = -1; // reset the currentTokenIndex + _foundSeparator = false; // reset - /// - /// Advances to the next token. A separator character can be specified which overrides the one previously set. - /// - /// if next token was found, if at end of string. - internal bool NextToken(bool allowQuotedToken, char separator) + // If we're at end of the string, just return false. + if (_charIndex >= _input.Length) { - _currentTokenIndex = -1; // reset the currentTokenIndex - _foundSeparator = false; // reset + return false; + } - // If we're at end of the string, just return false. - if (_charIndex >= _input.Length) - { - return false; - } + char currentChar = _input[_charIndex]; - char currentChar = _input[_charIndex]; + Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); - Debug.Assert(!char.IsWhiteSpace(currentChar), "Token started on Whitespace"); + // setup the quoteCount + int quoteCount = 0; - // setup the quoteCount - int quoteCount = 0; + // If we are allowing a quoted token and this token begins with a quote, + // set up the quote count and skip the initial quote + if (allowQuotedToken && _quoteChar == currentChar) + { + quoteCount++; // increment quote count + ++_charIndex; // move to next character + } - // If we are allowing a quoted token and this token begins with a quote, - // set up the quote count and skip the initial quote - if (allowQuotedToken && _quoteChar == currentChar) - { - quoteCount++; // increment quote count - ++_charIndex; // move to next character - } + int newTokenIndex = _charIndex; + int newTokenLength = 0; - int newTokenIndex = _charIndex; - int newTokenLength = 0; + // loop until hit end of string or hit a , or whitespace + // if at end of string ust return false. + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; - // loop until hit end of string or hit a , or whitespace - // if at end of string ust return false. - while (_charIndex < _input.Length) + // if have a QuoteCount and this is a quote + // decrement the quoteCount + if (quoteCount > 0) { - currentChar = _input[_charIndex]; - - // if have a QuoteCount and this is a quote - // decrement the quoteCount - if (quoteCount > 0) + // if anything but a quoteChar we move on + if (_quoteChar == currentChar) { - // if anything but a quoteChar we move on - if (_quoteChar == currentChar) + --quoteCount; + + // if at zero which it always should for now + // break out of the loop + if (0 == quoteCount) { - --quoteCount; - - // if at zero which it always should for now - // break out of the loop - if (0 == quoteCount) - { - ++_charIndex; // move past the quote - break; - } + ++_charIndex; // move past the quote + break; } } - else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) + } + else if (char.IsWhiteSpace(currentChar) || (currentChar == separator)) + { + if (currentChar == separator) { - if (currentChar == separator) - { - _foundSeparator = true; - } - break; + _foundSeparator = true; } - - ++_charIndex; - ++newTokenLength; + break; } - // if quoteCount isn't zero we hit the end of the string - // before the ending quote - if (quoteCount > 0) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperMissingEndQuote, _input.ToString())); - } + ++_charIndex; + ++newTokenLength; + } - ScanToNextToken(separator); // move so at the start of the nextToken for next call + // if quoteCount isn't zero we hit the end of the string + // before the ending quote + if (quoteCount > 0) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperMissingEndQuote, _input.ToString())); + } - // finally made it, update the _currentToken values - _currentTokenIndex = newTokenIndex; - _currentTokenLength = newTokenLength; + ScanToNextToken(separator); // move so at the start of the nextToken for next call - if (_currentTokenLength < 1) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); - } + // finally made it, update the _currentToken values + _currentTokenIndex = newTokenIndex; + _currentTokenLength = newTokenLength; - return true; + if (_currentTokenLength < 1) + { + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); } - /// - /// Helper function to move the _charIndex to the next token or to the end of the string. - /// - /// - /// - private void ScanToNextToken(char separator) + return true; + } + + /// + /// Helper function to move the _charIndex to the next token or to the end of the string. + /// + /// + /// + private void ScanToNextToken(char separator) + { + // if already at end of the string don't bother + if (_charIndex < _input.Length) { - // if already at end of the string don't bother - if (_charIndex < _input.Length) + char currentChar = _input[_charIndex]; + + // check that the currentChar is a space or the separator. If not + // we have an error. this can happen in the quote case + // that the char after the quotes string isn't a char. + if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) { - char currentChar = _input[_charIndex]; + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); + } - // check that the currentChar is a space or the separator. If not - // we have an error. this can happen in the quote case - // that the char after the quotes string isn't a char. - if (!(char.IsWhiteSpace(currentChar) || (currentChar == separator))) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperExtraDataEncountered, _charIndex, _input.ToString())); - } + // loop until hit a character that isn't + // an argument separator or whitespace. + int argSepCount = 0; + while (_charIndex < _input.Length) + { + currentChar = _input[_charIndex]; - // loop until hit a character that isn't - // an argument separator or whitespace. - int argSepCount = 0; - while (_charIndex < _input.Length) + if (currentChar == separator) { - currentChar = _input[_charIndex]; + _foundSeparator = true; + ++argSepCount; + _charIndex++; - if (currentChar == separator) - { - _foundSeparator = true; - ++argSepCount; - _charIndex++; - - if (argSepCount > 1) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); - } - } - else if (char.IsWhiteSpace(currentChar)) - { - ++_charIndex; - } - else + if (argSepCount > 1) { - break; + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); } } - - // if there was a separatorChar then we shouldn't be - // at the end of string or means there was a separator - // but there isn't an arg - - if (argSepCount > 0 && _charIndex >= _input.Length) + else if (char.IsWhiteSpace(currentChar)) + { + ++_charIndex; + } + else { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); + break; } } - } - - // Helper to get the numeric list separator for a given IFormatProvider. - // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. - internal static char GetNumericListSeparator(IFormatProvider provider) - { - char numericSeparator = ','; - // Get the NumberFormatInfo out of the provider, if possible - // If the IFormatProvider doesn't not contain a NumberFormatInfo, then - // this method returns the current culture's NumberFormatInfo. - NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + // if there was a separatorChar then we shouldn't be + // at the end of string or means there was a separator + // but there isn't an arg - Debug.Assert(numberFormat != null); - - // Is the decimal separator is the same as the list separator? - // If so, we use the ";". - if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) + if (argSepCount > 0 && _charIndex >= _input.Length) { - numericSeparator = ';'; + throw new InvalidOperationException(SR.Format(SR.TokenizerHelperEmptyToken, _charIndex, _input.ToString())); } + } + } + + // Helper to get the numeric list separator for a given IFormatProvider. + // Separator is a comma [,] if the decimal separator is not a comma, or a semicolon [;] otherwise. + internal static char GetNumericListSeparator(IFormatProvider provider) + { + char numericSeparator = ','; + + // Get the NumberFormatInfo out of the provider, if possible + // If the IFormatProvider doesn't not contain a NumberFormatInfo, then + // this method returns the current culture's NumberFormatInfo. + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + + Debug.Assert(numberFormat != null); - return numericSeparator; + // Is the decimal separator is the same as the list separator? + // If so, we use the ";". + if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0])) + { + numericSeparator = ';'; } - internal readonly bool FoundSeparator + return numericSeparator; + } + + internal readonly bool FoundSeparator + { + get { - get - { - return _foundSeparator; - } + return _foundSeparator; } + } - private readonly char _quoteChar; - private readonly char _argSeparator; - private readonly ReadOnlySpan _input; + private readonly char _quoteChar; + private readonly char _argSeparator; + private readonly ReadOnlySpan _input; - private int _charIndex; - private bool _foundSeparator; + private int _charIndex; + private bool _foundSeparator; - internal int _currentTokenIndex; - internal int _currentTokenLength; - } + internal int _currentTokenIndex; + internal int _currentTokenLength; } From 8b733a1484aa224bee136065372f9f896ccfb818 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 9 Apr 2025 23:25:31 +0200 Subject: [PATCH 20/20] CR notes --- .../Media/Animation/KeySplineConverter.cs | 9 +++--- .../src/Shared/MS/Internal/TokenizerHelper.cs | 32 +------------------ .../MS/Internal/ValueTokenizerHelper.cs | 2 +- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs index f0004fb0ad9..f452645decc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySplineConverter.cs @@ -58,10 +58,11 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c ValueTokenizerHelper tokenizer = new(stringValue, cultureInfo); - return new KeySpline(double.Parse(tokenizer.NextTokenRequired(), cultureInfo), - double.Parse(tokenizer.NextTokenRequired(), cultureInfo), - double.Parse(tokenizer.NextTokenRequired(), cultureInfo), - double.Parse(tokenizer.NextTokenRequired(), cultureInfo)); + return new KeySpline( + double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo), + double.Parse(tokenizer.NextTokenRequired(), cultureInfo)); } /// diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs index 13fe97f6cf1..478067cf776 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/TokenizerHelper.cs @@ -68,22 +68,6 @@ internal string GetCurrentToken() return _str.Substring(_currentTokenIndex,_currentTokenLength); } - /// - /// Sibling to with a minor difference; if there's no token, it will return - /// instead of . However, if used with , this edge-case is never hit. - /// - /// - internal ReadOnlySpan GetCurrentTokenAsSpan() - { - // If there's no current token, return empty span - if (_currentTokenIndex < 0) - { - return ReadOnlySpan.Empty; - } - - return _str.AsSpan().Slice(_currentTokenIndex, _currentTokenLength); - } - /// /// Throws an exception if there is any non-whitespace left un-parsed. /// @@ -118,20 +102,6 @@ internal string NextTokenRequired() return GetCurrentToken(); } - /// - /// Sibling to ; advances to the next token, throws an if not present. - /// - /// The next token found - internal ReadOnlySpan NextTokenRequiredAsSpan() - { - if (!NextToken(false)) - { - throw new InvalidOperationException(SR.Format(SR.TokenizerHelperPrematureStringTermination, _str)); - } - - return GetCurrentTokenAsSpan(); - } - /// /// Advances to the NextToken, throwing an exception if not present /// @@ -207,7 +177,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) // if at zero which it always should for now // break out of the loop - if (0 == quoteCount) + if (quoteCount == 0) { ++_charIndex; // move past the quote break; diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs index 447370a6842..d2e235937cc 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ValueTokenizerHelper.cs @@ -169,7 +169,7 @@ internal bool NextToken(bool allowQuotedToken, char separator) // if at zero which it always should for now // break out of the loop - if (0 == quoteCount) + if (quoteCount == 0) { ++_charIndex; // move past the quote break;