Skip to content

Commit f646512

Browse files
Remove most boxing and allow custom enum converters
1 parent 3f0268e commit f646512

File tree

12 files changed

+172
-111
lines changed

12 files changed

+172
-111
lines changed

src/ImageSharp.Web/Commands/CommandParser.cs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,32 +39,40 @@ public CommandParser(IEnumerable<ICommandConverter> converters)
3939
/// <returns>The converted instance or the default.</returns>
4040
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4141
public T ParseValue<T>(string value, CultureInfo culture)
42-
=> (T)this.ParseValue(typeof(T), value, culture);
43-
44-
/// <summary>
45-
/// Parses the given string value converting it to the given type.
46-
/// </summary>
47-
/// <param name="type"> The type to convert the string to.</param>
48-
/// <param name="value">The string value to parse.</param>
49-
/// <param name="culture">The <see cref="CultureInfo"/> to use as the current culture.</param>
50-
/// <returns>The converted instance or the default value.</returns>
51-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52-
public object ParseValue(Type type, string value, CultureInfo culture)
5342
{
54-
DebugGuard.NotNull(type, nameof(type));
5543
DebugGuard.NotNull(culture, nameof(culture));
5644

57-
// This allows us to reuse the same converter for infinite enum types.
58-
Type matchType = type.IsEnum ? typeof(Enum) : type;
45+
Type type = typeof(T);
46+
ICommandConverter converter = Array.Find(this.converters, x => x.Type.Equals(type));
5947

60-
ICommandConverter converter = Array.Find(this.converters, x => x.Type.Equals(matchType));
48+
if (converter != null)
49+
{
50+
return ((ICommandConverter<T>)converter).ConvertFrom(
51+
this,
52+
culture,
53+
WebUtility.UrlDecode(value),
54+
type);
55+
}
6156

62-
if (converter is null)
57+
// This special case allows us to reuse the same converter for infinite enum types
58+
// if one has not already been configured.
59+
if (type.IsEnum)
6360
{
64-
ThrowNotSupported(type);
61+
converter = Array.Find(this.converters, x => x.Type.Equals(typeof(Enum)));
62+
if (converter != null)
63+
{
64+
return (T)((ICommandConverter<object>)converter).ConvertFrom(
65+
this,
66+
culture,
67+
WebUtility.UrlDecode(value),
68+
type);
69+
}
6570
}
6671

67-
return converter.ConvertFrom(this, culture, WebUtility.UrlDecode(value), type);
72+
// We don't actually return here.
73+
// The compiler just cannot see our exception.
74+
ThrowNotSupported(type);
75+
return default;
6876
}
6977

7078
[MethodImpl(MethodImplOptions.NoInlining)]

src/ImageSharp.Web/Commands/Converters/ArrayConverter{T}.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,51 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Globalization;
7+
using System.Linq;
8+
using System.Runtime.CompilerServices;
79

810
namespace SixLabors.ImageSharp.Web.Commands.Converters
911
{
1012
/// <summary>
11-
/// Converts the value of an string to a generic array.
13+
/// Converts the value of a string to a generic array.
1214
/// </summary>
1315
/// <typeparam name="T">The parameter type to convert to.</typeparam>
14-
internal sealed class ArrayConverter<T> : ListConverter<T>
16+
internal sealed class ArrayConverter<T> : ICommandConverter<T[]>
1517
{
1618
/// <inheritdoc/>
17-
public override Type Type => typeof(T[]);
19+
public Type Type => typeof(T[]);
1820

1921
/// <inheritdoc/>
20-
public override object ConvertFrom(
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
public T[] ConvertFrom(
2124
CommandParser parser,
2225
CultureInfo culture,
2326
string value,
2427
Type propertyType)
25-
=> ((List<T>)base.ConvertFrom(parser, culture, value, propertyType)).ToArray();
28+
{
29+
if (string.IsNullOrWhiteSpace(value))
30+
{
31+
return Array.Empty<T>();
32+
}
33+
34+
var result = new List<T>();
35+
foreach (string pill in GetStringArray(value, culture))
36+
{
37+
T item = parser.ParseValue<T>(pill, culture);
38+
if (item != null)
39+
{
40+
result.Add(item);
41+
}
42+
}
43+
44+
return result.ToArray();
45+
}
46+
47+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
48+
private static string[] GetStringArray(string input, CultureInfo culture)
49+
{
50+
char separator = culture.TextInfo.ListSeparator[0];
51+
return input.Split(separator).Select(s => s.Trim()).ToArray();
52+
}
2653
}
2754
}

src/ImageSharp.Web/Commands/Converters/ColorConverter.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.Reflection;
8+
using System.Runtime.CompilerServices;
89
using System.Text.RegularExpressions;
910

1011
namespace SixLabors.ImageSharp.Web.Commands.Converters
1112
{
1213
/// <summary>
1314
/// Allows the conversion of strings into rgba32 pixel colors.
1415
/// </summary>
15-
internal class ColorConverter : ICommandConverter
16+
internal class ColorConverter : ICommandConverter<Color>
1617
{
1718
/// <summary>
1819
/// The web color hexadecimal regex. Matches strings arranged
@@ -34,11 +35,12 @@ internal class ColorConverter : ICommandConverter
3435
public Type Type => typeof(Color);
3536

3637
/// <inheritdoc/>
37-
public object ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType)
38+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
39+
public Color ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType)
3840
{
3941
if (string.IsNullOrWhiteSpace(value))
4042
{
41-
return default(Color);
43+
return default;
4244
}
4345

4446
// Numeric r,g,b - r,g,b,a
@@ -81,10 +83,6 @@ public object ConvertFrom(CommandParser parser, CultureInfo culture, string valu
8183
return table.ContainsKey(value) ? table[value] : default;
8284
}
8385

84-
/// <summary>
85-
/// Initializes color table mapping color constants.
86-
/// </summary>
87-
/// <returns>The <see cref="IDictionary{String, Color}"/>.</returns>
8886
private static IDictionary<string, Color> InitializeColorConstantsTable()
8987
{
9088
IDictionary<string, Color> table = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase);

src/ImageSharp.Web/Commands/Converters/EnumConverter.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@
44
using System;
55
using System.Globalization;
66
using System.Linq;
7+
using System.Runtime.CompilerServices;
78

89
namespace SixLabors.ImageSharp.Web.Commands.Converters
910
{
1011
/// <summary>
1112
/// The enum converter. Allows conversion to enumerations.
1213
/// </summary>
13-
internal sealed class EnumConverter : ICommandConverter
14+
internal sealed class EnumConverter : ICommandConverter<object>
1415
{
1516
public Type Type => typeof(Enum);
1617

1718
/// <inheritdoc/>
1819
/// <remarks>
19-
/// Unlike other converters the type here does not match the passed type.
20+
/// Unlike other converters the <see cref="Type"/> property does not
21+
/// match the <paramref name="propertyType"/> value.
2022
/// This allows us to reuse the same converter for infinite enum types.
2123
/// </remarks>
24+
[MethodImpl(MethodImplOptions.NoInlining)]
2225
public object ConvertFrom(
2326
CommandParser parser,
2427
CultureInfo culture,
@@ -59,6 +62,7 @@ public object ConvertFrom(
5962
/// <param name="input">The input string to split.</param>
6063
/// <param name="separator">The separator to split string by.</param>
6164
/// <returns>The <see cref="T:String[]"/>.</returns>
65+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6266
private static string[] GetStringArray(string input, char separator)
6367
=> input.Split(separator).Select(s => s.Trim()).ToArray();
6468
}

src/ImageSharp.Web/Commands/Converters/ICommandConverter.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@ namespace SixLabors.ImageSharp.Web.Commands.Converters
88
{
99
/// <summary>
1010
/// Defines a contract for converting the value of a string into a different data type.
11-
/// Implementations of this interface should be stateless by design.
11+
/// Implementations should be stateless by design and also implement <see cref="ICommandConverter{T}"/>.
1212
/// </summary>
1313
public interface ICommandConverter
1414
{
1515
/// <summary>
16-
/// Gets the type of property this converter converts.
16+
/// Gets the type this converter returns.
1717
/// </summary>
1818
Type Type { get; }
19+
}
1920

21+
/// <inheritdoc/>
22+
/// <typeparam name="T">The type this converter returns.</typeparam>
23+
public interface ICommandConverter<T> : ICommandConverter
24+
{
2025
/// <summary>
2126
/// Converts the given string to the type of this converter, using the specified culture information.
2227
/// </summary>
@@ -30,6 +35,6 @@ public interface ICommandConverter
3035
/// <param name="value">The <see cref="string"/> to convert. </param>
3136
/// <param name="propertyType">The property type that the converter will convert to.</param>
3237
/// <exception cref="NotSupportedException">The conversion cannot be performed.</exception>
33-
object ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType);
38+
T ConvertFrom(CommandParser parser, CultureInfo culture, string value, Type propertyType);
3439
}
3540
}

src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter.cs renamed to src/ImageSharp.Web/Commands/Converters/IntegralNumberConverter{T}.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ namespace SixLabors.ImageSharp.Web.Commands.Converters
99
/// <summary>
1010
/// The generic converter for integral types.
1111
/// </summary>
12-
/// <typeparam name="T">The type of object to convert to.</typeparam>
13-
internal sealed class IntegralNumberConverter<T> : ICommandConverter
12+
/// <inheritdoc/>
13+
internal sealed class IntegralNumberConverter<T> : ICommandConverter<T>
1414
where T : struct, IConvertible, IComparable<T>
1515
{
1616
/// <inheritdoc/>
1717
public Type Type => typeof(T);
1818

1919
/// <inheritdoc/>
20-
public object ConvertFrom(
20+
public T ConvertFrom(
2121
CommandParser parser,
2222
CultureInfo culture,
2323
string value,
@@ -26,7 +26,7 @@ public object ConvertFrom(
2626
if (string.IsNullOrWhiteSpace(value)
2727
|| Array.IndexOf(TypeConstants.IntegralTypes, propertyType) < 0)
2828
{
29-
return default(T);
29+
return default;
3030
}
3131

3232
// Round the value to the nearest decimal value

src/ImageSharp.Web/Commands/Converters/ListConverter{T}.cs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.Linq;
8+
using System.Runtime.CompilerServices;
89

910
namespace SixLabors.ImageSharp.Web.Commands.Converters
1011
{
1112
/// <summary>
12-
/// Converts the value of an string to a generic list.
13+
/// Converts the value of a string to a generic list.
1314
/// </summary>
14-
/// <typeparam name="T">The type to convert from.</typeparam>
15-
internal class ListConverter<T> : ICommandConverter
15+
/// <typeparam name="T">The type of result to return.</typeparam>
16+
internal sealed class ListConverter<T> : ICommandConverter<List<T>>
1617
{
17-
public virtual Type Type => typeof(List<T>);
18+
public Type Type => typeof(List<T>);
1819

1920
/// <inheritdoc/>
20-
public virtual object ConvertFrom(
21+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
22+
public List<T> ConvertFrom(
2123
CommandParser parser,
2224
CultureInfo culture,
2325
string value,
@@ -29,26 +31,20 @@ public virtual object ConvertFrom(
2931
return result;
3032
}
3133

32-
Type type = typeof(T);
33-
foreach (string pill in this.GetStringArray(value, culture))
34+
foreach (string pill in GetStringArray(value, culture))
3435
{
35-
object item = parser.ParseValue(type, pill, culture);
36+
T item = parser.ParseValue<T>(pill, culture);
3637
if (item != null)
3738
{
38-
result.Add((T)item);
39+
result.Add(item);
3940
}
4041
}
4142

4243
return result;
4344
}
4445

45-
/// <summary>
46-
/// Splits a string by separator to return an array of string values.
47-
/// </summary>
48-
/// <param name="input">The input string to split.</param>
49-
/// <param name="culture">A <see cref="CultureInfo"/>. The current culture to split string by.</param>
50-
/// <returns>The <see cref="T:String[]"/>.</returns>
51-
protected string[] GetStringArray(string input, CultureInfo culture)
46+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
47+
private static string[] GetStringArray(string input, CultureInfo culture)
5248
{
5349
char separator = culture.TextInfo.ListSeparator[0];
5450
return input.Split(separator).Select(s => s.Trim()).ToArray();

src/ImageSharp.Web/Commands/Converters/SimpleCommandConverter{T}.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,31 @@
33

44
using System;
55
using System.Globalization;
6-
using System.Text;
6+
using System.Runtime.CompilerServices;
77

88
namespace SixLabors.ImageSharp.Web.Commands.Converters
99
{
1010
/// <summary>
1111
/// The generic converter for simple types that implement <see cref="IConvertible"/>.
1212
/// </summary>
1313
/// <typeparam name="T">The type of object to convert to.</typeparam>
14-
internal sealed class SimpleCommandConverter<T> : ICommandConverter
14+
internal sealed class SimpleCommandConverter<T> : ICommandConverter<T>
1515
where T : IConvertible
1616
{
1717
/// <inheritdoc/>
1818
public Type Type => typeof(T);
1919

2020
/// <inheritdoc/>
21-
public object ConvertFrom(
21+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
22+
public T ConvertFrom(
2223
CommandParser parser,
2324
CultureInfo culture,
2425
string value,
2526
Type propertyType)
2627
{
2728
if (string.IsNullOrWhiteSpace(value))
2829
{
29-
return default(T);
30+
return default;
3031
}
3132

3233
Type t = typeof(T);

src/ImageSharp.Web/Processors/ResizeWebProcessor.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,16 @@ private static IResampler GetSampler(IDictionary<string, string> commands)
168168
{
169169
switch (sampler.ToLowerInvariant())
170170
{
171-
case "nearest": return KnownResamplers.NearestNeighbor;
171+
case "nearest":
172+
case "nearestneighbor":
173+
return KnownResamplers.NearestNeighbor;
172174
case "box": return KnownResamplers.Box;
173-
case "mitchell": return KnownResamplers.MitchellNetravali;
174-
case "catmull": return KnownResamplers.CatmullRom;
175+
case "mitchell":
176+
case "mitchellnetravali":
177+
return KnownResamplers.MitchellNetravali;
178+
case "catmull":
179+
case "catmullrom":
180+
return KnownResamplers.CatmullRom;
175181
case "lanczos2": return KnownResamplers.Lanczos2;
176182
case "lanczos3": return KnownResamplers.Lanczos3;
177183
case "lanczos5": return KnownResamplers.Lanczos5;

0 commit comments

Comments
 (0)