diff --git a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
index bbf10fdca8..9a34eaf643 100644
--- a/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
+++ b/src/Avalonia.Base/Media/Fonts/FontCollectionBase.cs
@@ -132,7 +132,7 @@ namespace Avalonia.Media.Fonts
glyphTypeface = typeface;
return true;
- }
+ }
}
return false;
@@ -297,7 +297,7 @@ namespace Avalonia.Media.Fonts
var tokenizer = new StringTokenizer(familyName, ' ');
- tokenizer.ReadString();
+ tokenizer.ReadSpan();
while (tokenizer.TryReadString(out var weightString))
{
@@ -325,7 +325,7 @@ namespace Avalonia.Media.Fonts
var tokenizer = new StringTokenizer(familyName, ' ');
- tokenizer.ReadString();
+ tokenizer.ReadSpan();
while (tokenizer.TryReadString(out var styleString))
{
@@ -354,7 +354,7 @@ namespace Avalonia.Media.Fonts
var tokenizer = new StringTokenizer(familyName, ' ');
- tokenizer.ReadString();
+ tokenizer.ReadSpan();
while (tokenizer.TryReadString(out var stretchString))
{
diff --git a/src/Avalonia.Base/Media/TextDecorationCollection.cs b/src/Avalonia.Base/Media/TextDecorationCollection.cs
index 2d7bd17b20..b703eaba57 100644
--- a/src/Avalonia.Base/Media/TextDecorationCollection.cs
+++ b/src/Avalonia.Base/Media/TextDecorationCollection.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Media
using (var tokenizer = new StringTokenizer(s, ',', "Invalid text decoration."))
{
- while (tokenizer.TryReadString(out var name))
+ while (tokenizer.TryReadSpan(out var name))
{
var location = GetTextDecorationLocation(name);
@@ -59,9 +59,9 @@ namespace Avalonia.Media
///
/// The string.
/// The .
- private static TextDecorationLocation GetTextDecorationLocation(string s)
+ private static TextDecorationLocation GetTextDecorationLocation(ReadOnlySpan s)
{
- if (Enum.TryParse(s,true, out var location))
+ if (SpanHelpers.TryParseEnum(s,true, out var location))
{
return location;
}
diff --git a/src/Avalonia.Base/RelativeRect.cs b/src/Avalonia.Base/RelativeRect.cs
index 9f2010ea72..7109ec41e9 100644
--- a/src/Avalonia.Base/RelativeRect.cs
+++ b/src/Avalonia.Base/RelativeRect.cs
@@ -152,7 +152,7 @@ namespace Avalonia
Rect.Width * size.Width,
Rect.Height * size.Height);
}
-
+
///
/// Converts a into pixels.
///
@@ -178,18 +178,18 @@ namespace Avalonia
{
using (var tokenizer = new StringTokenizer(s, exceptionMessage: "Invalid RelativeRect."))
{
- var x = tokenizer.ReadString();
- var y = tokenizer.ReadString();
- var width = tokenizer.ReadString();
- var height = tokenizer.ReadString();
+ var x = tokenizer.ReadSpan();
+ var y = tokenizer.ReadSpan();
+ var width = tokenizer.ReadSpan();
+ var height = tokenizer.ReadSpan();
var unit = RelativeUnit.Absolute;
var scale = 1.0;
- var xRelative = x.EndsWith("%", StringComparison.Ordinal);
- var yRelative = y.EndsWith("%", StringComparison.Ordinal);
- var widthRelative = width.EndsWith("%", StringComparison.Ordinal);
- var heightRelative = height.EndsWith("%", StringComparison.Ordinal);
+ var xRelative = x.EndsWith(PercentChar, StringComparison.Ordinal);
+ var yRelative = y.EndsWith(PercentChar, StringComparison.Ordinal);
+ var widthRelative = width.EndsWith(PercentChar, StringComparison.Ordinal);
+ var heightRelative = height.EndsWith(PercentChar, StringComparison.Ordinal);
if (xRelative && yRelative && widthRelative && heightRelative)
{
@@ -207,10 +207,10 @@ namespace Avalonia
}
return new RelativeRect(
- double.Parse(x, CultureInfo.InvariantCulture) * scale,
- double.Parse(y, CultureInfo.InvariantCulture) * scale,
- double.Parse(width, CultureInfo.InvariantCulture) * scale,
- double.Parse(height, CultureInfo.InvariantCulture) * scale,
+ SpanHelpers.ParseDouble(x, CultureInfo.InvariantCulture) * scale,
+ SpanHelpers.ParseDouble(y, CultureInfo.InvariantCulture) * scale,
+ SpanHelpers.ParseDouble(width, CultureInfo.InvariantCulture) * scale,
+ SpanHelpers.ParseDouble(height, CultureInfo.InvariantCulture) * scale,
unit);
}
}
diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs
index f80ac7c046..8ca3206a57 100644
--- a/src/Avalonia.Base/Utilities/SpanHelpers.cs
+++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs
@@ -29,6 +29,16 @@ namespace Avalonia.Utilities
#endif
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseInt(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out int value)
+ {
+#if NETSTANDARD2_0
+ return int.TryParse(span.ToString(), style, provider, out value);
+#else
+ return int.TryParse(span, style, provider, out value);
+#endif
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseDouble(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out double value)
{
@@ -39,6 +49,16 @@ namespace Avalonia.Utilities
#endif
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double ParseDouble(this ReadOnlySpan span, IFormatProvider provider)
+ {
+#if NETSTANDARD2_0
+ return double.Parse(span.ToString(), provider);
+#else
+ return double.Parse(span, provider: provider);
+#endif
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseByte(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out byte value)
{
@@ -46,6 +66,16 @@ namespace Avalonia.Utilities
return byte.TryParse(span.ToString(), style, provider, out value);
#else
return byte.TryParse(span, style, provider, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseEnum(this ReadOnlySpan span, bool ignoreCase, out TEnum value) where TEnum : struct
+ {
+#if NETSTANDARD2_0
+ return Enum.TryParse(span.ToString(), ignoreCase, out value);
+#else
+ return Enum.TryParse(span, ignoreCase, out value);
#endif
}
}
diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs
index aad742e02b..027817be2c 100644
--- a/src/Avalonia.Base/Utilities/StringTokenizer.cs
+++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs
@@ -46,6 +46,8 @@ namespace Avalonia.Utilities
public string? CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength);
+ public ReadOnlySpan CurrentTokenSpan => _tokenIndex < 0 ? ReadOnlySpan.Empty : _s.AsSpan().Slice(_tokenIndex, _tokenLength);
+
public void Dispose()
{
if (_index != _length)
@@ -56,8 +58,8 @@ namespace Avalonia.Utilities
public bool TryReadInt32(out Int32 result, char? separator = null)
{
- if (TryReadString(out var stringResult, separator) &&
- int.TryParse(stringResult, NumberStyles.Integer, _formatProvider, out result))
+ if (TryReadSpan(out var stringResult, separator) &&
+ SpanHelpers.TryParseInt(stringResult, NumberStyles.Integer, _formatProvider, out result))
{
return true;
}
@@ -80,8 +82,8 @@ namespace Avalonia.Utilities
public bool TryReadDouble(out double result, char? separator = null)
{
- if (TryReadString(out var stringResult, separator) &&
- double.TryParse(stringResult, NumberStyles.Float, _formatProvider, out result))
+ if (TryReadSpan(out var stringResult, separator) &&
+ SpanHelpers.TryParseDouble(stringResult, NumberStyles.Float, _formatProvider, out result))
{
return true;
}
@@ -102,10 +104,10 @@ namespace Avalonia.Utilities
return result;
}
- public bool TryReadString([MaybeNullWhen(false)] out string result, char? separator = null)
+ public bool TryReadString([NotNull] out string result, char? separator = null)
{
var success = TryReadToken(separator ?? _separator);
- result = CurrentToken;
+ result = CurrentTokenSpan.ToString();
return success;
}
@@ -119,6 +121,23 @@ namespace Avalonia.Utilities
return result;
}
+ public bool TryReadSpan(out ReadOnlySpan result, char? separator = null)
+ {
+ var success = TryReadToken(separator ?? _separator);
+ result = CurrentTokenSpan;
+ return success;
+ }
+
+ public ReadOnlySpan ReadSpan(char? separator = null)
+ {
+ if (!TryReadSpan(out var result, separator))
+ {
+ throw GetFormatException();
+ }
+
+ return result;
+ }
+
private bool TryReadToken(char separator)
{
_tokenIndex = -1;
diff --git a/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
index 4f94b1ca80..42a4a406ef 100644
--- a/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Utilities/StringTokenizerTests.cs
@@ -65,5 +65,15 @@ namespace Avalonia.Base.UnitTests.Utilities
Assert.False(target.TryReadDouble(out var value));
}
+
+ [Fact]
+ public void ReadSpan_And_ReadString_Reads_Same()
+ {
+ var target1 = new StringTokenizer("abc,def");
+ var target2 = new StringTokenizer("abc,def");
+
+ Assert.Equal(target1.ReadString(), target2.ReadSpan().ToString());
+ Assert.True(target1.ReadSpan().SequenceEqual(target2.ReadString()));
+ }
}
}