Skip to content

Remove allocations on all base converters, improve TokenizerHelper #9364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
10731fe
Simplify CanConvertTo
h3xds1nz Oct 3, 2024
209820d
Simplify ConvertFrom
h3xds1nz Oct 3, 2024
1b2b68d
Simplify ConvertTo, use pattern matching
h3xds1nz Oct 3, 2024
6c384c2
Remove allocations on Tokenizer for standard converters
h3xds1nz Oct 3, 2024
0c8afb0
Remove allocations on Tokenizer for generated structs
h3xds1nz Oct 3, 2024
50835dd
Remove allocations on Tokenizer for generated collections
h3xds1nz Oct 3, 2024
ed46efe
Adjust MilCodeGen to generate code that corresponds to edits on gener…
h3xds1nz Oct 3, 2024
7b87ece
Simplify CanConvertTo in Corner/Cache
h3xds1nz Oct 3, 2024
fb3ba6f
Simplify ConvertFrom methods
h3xds1nz Oct 3, 2024
e7b2662
Simplify ConvertTo methods
h3xds1nz Oct 3, 2024
8c87862
Final formatting, clean up redundant usings
h3xds1nz Oct 3, 2024
3e94449
Stop double checking in IsWhiteSpace, convert fields that could be re…
h3xds1nz Oct 3, 2024
cb6fe8e
Add a ref struct implementation of TokenizerHelper, use it in Present…
h3xds1nz Oct 3, 2024
db159b5
Use ValueTokenizerHelper in generated structs/collections
h3xds1nz Oct 3, 2024
c578f70
Use ValueTokenizerHelper in KeySplineConverter, make it allocation free
h3xds1nz Oct 3, 2024
a239e7c
Remove double inclusion of ValueTokenizerHelper
h3xds1nz Oct 3, 2024
3d23f75
Comments/code style adjustments, some documentation, no code changes
h3xds1nz Oct 3, 2024
e65a468
Fix string comparisons
h3xds1nz Apr 1, 2025
3c551cb
Fix ValueTokenizerHelper IDE0073 and move to file-scoped namespace
h3xds1nz Apr 1, 2025
8b733a1
CR notes
h3xds1nz Apr 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,129 +1,125 @@
// 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
{
/// <summary>
/// PointConverter - Converter class for converting instances of other types to Point instances
/// Converter class for converting instances of <see cref="KeySpline"/> to <see cref="string"/> and vice versa.
/// </summary>
/// <ExternalAPI/>
public class KeySplineConverter : TypeConverter
{
/// <summary>
/// CanConvertFrom - Returns whether or not this class can convert from a given type
/// </summary>
/// <ExternalAPI/>
/// <returns>
/// <see langword="true"/> if the given <paramref name="sourceType"/> can be converted from, <see langword="false"/> otherwise.
/// </returns>
/// <param name="typeDescriptor">The <see cref="ITypeDescriptorContext"/> for this call.</param>
/// <param name="destinationType">The <see cref="Type"/> being queried for support.</param>
public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptor, Type destinationType)
{
if (destinationType == typeof(string))
{
return true;
}
else
{
return false;
}
return destinationType == typeof(string);
}

/// <summary>
/// TypeConverter method override.
/// </summary>
/// <param name="context">ITypeDescriptorContext</param>
/// <param name="destinationType">Type to convert to</param>
/// <returns>true if conversion is possible</returns>
/// <ExternalAPI/>
/// <returns>
/// <see langword="true"/> if this class can convert to <paramref name="destinationType"/>, <see langword="false"/> otherwise.
/// </returns>
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);
}

/// <summary>
/// ConvertFrom
/// Converts <paramref name="value"/> of <see langword="string"/> type to its <see cref="KeySpline"/> represensation.
/// </summary>
public override object ConvertFrom(
ITypeDescriptorContext context,
CultureInfo cultureInfo,
object value)
/// <param name="context">The <see cref="ITypeDescriptorContext"/> for this call.</param>
/// <param name="cultureInfo">The <see cref="CultureInfo"/> which is respected during conversion.</param>
/// <param name="value"> The object to convert to a <see cref="KeySpline"/>.</param>
/// <returns>A new instance of <see cref="KeySpline"/> class representing the data contained in <paramref name="value"/>.</returns>
/// <exception cref="NotSupportedException">Thrown in case the <paramref name="value"/> was not a <see cref="string"/>.</exception>
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));
double.Parse(tokenizer.NextTokenRequired(), cultureInfo),
double.Parse(tokenizer.NextTokenRequired(), cultureInfo),
double.Parse(tokenizer.NextTokenRequired(), cultureInfo),
double.Parse(tokenizer.NextTokenRequired(), cultureInfo));
}

/// <summary>
/// TypeConverter method implementation.
/// Attempt to convert a <see cref="KeySpline"/> class to the <paramref name="destinationType"/>.
/// </summary>
/// <param name="context">ITypeDescriptorContext</param>
/// <param name="cultureInfo">current culture (see CLR specs), null is a valid value</param>
/// <param name="value">value to convert from</param>
/// <param name="destinationType">Type to convert to</param>
/// <returns>converted value</returns>
/// <ExternalAPI/>
public override object ConvertTo(
ITypeDescriptorContext context,
CultureInfo cultureInfo,
object value,
Type destinationType)
/// <returns>
/// The formatted <paramref name="value"/> as <see cref="string"/> using the specified <paramref name="cultureInfo"/> or an <see cref="InstanceDescriptor"/>.
/// </returns>
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);
}

/// <summary>
/// Converts <paramref name="keySpline"/> to its <see cref="string"/> representation using the specified <paramref name="cultureInfo"/>.
/// </summary>
/// <param name="keySpline">The <see cref="KeySpline"/> to convert to string.</param>
/// <param name="cultureInfo">Culture to use when formatting doubles and choosing separator.</param>
/// <returns>The formatted <paramref name="keySpline"/> as <see cref="string"/> using the specified <paramref name="cultureInfo"/>.</returns>
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:
// 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]);
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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 = Convert.ToDouble(th.GetCurrentToken(), formatProvider);
value = double.Parse(tokenizer.GetCurrentToken(), formatProvider);

resource.Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Convert.ToDouble(th.NextTokenRequired(), formatProvider));
Parsers.ParseColor(tokenizer.GetCurrentToken().ToString(), formatProvider),
double.Parse(tokenizer.NextTokenRequired(), formatProvider));

resource.Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = Convert.ToInt32(th.GetCurrentToken(), formatProvider);
value = Int32.Parse(tokenizer.GetCurrentToken(), formatProvider);

resource.Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Convert.ToDouble(th.GetCurrentToken(), formatProvider),
Convert.ToDouble(th.NextTokenRequired(), formatProvider));
double.Parse(tokenizer.GetCurrentToken(), formatProvider),
double.Parse(tokenizer.NextTokenRequired(), formatProvider));

resource.Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Convert.ToDouble(th.GetCurrentToken(), formatProvider),
Convert.ToDouble(th.NextTokenRequired(), formatProvider));
double.Parse(tokenizer.GetCurrentToken(), formatProvider),
double.Parse(tokenizer.NextTokenRequired(), formatProvider));

resource.Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,41 +211,41 @@ 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;

String firstToken = th.NextTokenRequired();
ReadOnlySpan<char> firstToken = tokenizer.NextTokenRequired();

// 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(
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));
double.Parse(firstToken, 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,19 @@ 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;

String firstToken = th.NextTokenRequired();
ReadOnlySpan<char> firstToken = tokenizer.NextTokenRequired();

value = new Point3D(
Convert.ToDouble(firstToken, formatProvider),
Convert.ToDouble(th.NextTokenRequired(), formatProvider),
Convert.ToDouble(th.NextTokenRequired(), formatProvider));
double.Parse(firstToken, 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;
}
Expand Down
Loading