Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion Benchmarks/CharAssumptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Open.Text.Benchmarks;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "For benchmarking.")]
public class CharAssumptionTests
{
const string TestString = "abcdefghijklmnopqrstuvwxyz0123456789";
Expand Down
1 change: 0 additions & 1 deletion Benchmarks/EnumAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Open.Text.Benchmarks;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>")]
public class EnumAttributeTests
{
public static IReadOnlyList<Attribute> GetAttribute(Greek value)
Expand Down
17 changes: 10 additions & 7 deletions Benchmarks/EnumParseTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using BenchmarkDotNet.Attributes;
using CommandLine;
using FastEnumUtility;
using System.Security;

namespace Open.Text.Benchmarks;

Expand Down Expand Up @@ -69,8 +69,9 @@ abstract class Tests

static readonly Dictionary<string, Greek> LookupD
= Enum
.GetValues<Greek>()
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.Ordinal);
.GetValues(typeof(Greek))
.Cast<Greek>()
.ToDictionary(e => e.GetName(), e => e, StringComparer.Ordinal);

protected virtual bool Lookup(string value, out Greek e)
=> LookupD.TryGetValue(value, out e);
Expand Down Expand Up @@ -262,8 +263,9 @@ public override Greek FastEnumParse()

static readonly Dictionary<string, Greek> LookupD
= Enum
.GetValues<Greek>()
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.OrdinalIgnoreCase);
.GetValues(typeof(Greek))
.Cast<Greek>()
.ToDictionary(e => e.GetName(), e => e, StringComparer.OrdinalIgnoreCase);

protected override bool Lookup(string value, out Greek e)
=> LookupD.TryGetValue(value, out e);
Expand Down Expand Up @@ -328,8 +330,9 @@ public override Greek FastEnumParse()

static readonly Dictionary<string, Greek> LookupD
= Enum
.GetValues<Greek>()
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.OrdinalIgnoreCase);
.GetValues(typeof(Greek))
.Cast<Greek>()
.ToDictionary(e => e.GetName(), e => e, StringComparer.OrdinalIgnoreCase);

protected override bool Lookup(string value, out Greek e)
=> LookupD.TryGetValue(value, out e);
Expand Down
1 change: 0 additions & 1 deletion Benchmarks/EnumToStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

namespace Open.Text.Benchmarks;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Benchmarking.")]
public class EnumToStringTests
{
static readonly IReadOnlyList<Greek> Values = Enum.GetValues(typeof(Greek)).Cast<Greek>().ToArray();
Expand Down
63 changes: 63 additions & 0 deletions Benchmarks/IndexOfTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace Open.Text.Benchmarks;

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net80)]
public class IndexOfTests
{
public const string Text = "The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog";
public static readonly string TextUpper = Text.ToUpper();
public const string Search = "fox";
public const string SearchCased = "Fox";

private StringComparison _comparison = StringComparison.Ordinal;
private StringComparison _comparisonCaseIgnored;

[Params(
StringComparison.Ordinal,
StringComparison.CurrentCulture,
StringComparison.InvariantCulture)]
public StringComparison Comparison
{
get => _comparison;
set
{
_comparison = value;
_comparisonCaseIgnored = _comparison switch
{
StringComparison.Ordinal => StringComparison.OrdinalIgnoreCase,
StringComparison.CurrentCulture => StringComparison.CurrentCultureIgnoreCase,
StringComparison.InvariantCulture => StringComparison.InvariantCultureIgnoreCase,
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
}

[Benchmark(Baseline = true)]
public int IndexOf()
=> Text.IndexOf(Search, _comparison);

[Benchmark]
public int IndexOfCaseIgnored()
=> Text.IndexOf(SearchCased, _comparisonCaseIgnored);

[Benchmark]
public int IndexOfSpan()
=> Text.IndexOf(Search.AsSpan(), _comparison);

[Benchmark]
public int IndexOfSpanCaseIgnored()
=> Text.IndexOf(SearchCased.AsSpan(), _comparisonCaseIgnored);

[Benchmark]
public int IndexOfSpanSlice()
=> Text.IndexOf(Text.AsSpan(16, 3), _comparison);

[Benchmark]
public int IndexOfSpanSliceCaseIgnored()
=> Text.IndexOf(TextUpper.AsSpan(16, 3), _comparisonCaseIgnored);
}
2 changes: 0 additions & 2 deletions Benchmarks/IsDefinedTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using BenchmarkDotNet.Attributes;
using System.Diagnostics.CodeAnalysis;

namespace Open.Text.Benchmarks;

[SuppressMessage("Performance", "CA1822:Mark members as static")]
public class IsDefinedTests
{
static readonly IReadOnlyList<int> Values = Enumerable.Range(-2, 30).ToArray();
Expand Down
10 changes: 6 additions & 4 deletions Benchmarks/Open.Text.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net472;net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<NoWarn>CA1822</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="FastEnum" Version="1.7.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
<PackageReference Include="FastEnum" Version="1.8.0" />
</ItemGroup>

<ItemGroup>
Expand Down
9 changes: 7 additions & 2 deletions Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
using BenchmarkDotNet.Running;
using Open.Text.Benchmarks;

//BenchmarkSwitcher
// .FromAssembly(typeof(Program).Assembly)
// .Run(args); // crucial to make it work

//BenchmarkRunner.Run<EnumParseTests>();
//enchmarkRunner.Run<EnumToStringTests>();
//BenchmarkRunner.Run<EnumToStringTests>();
//BenchmarkRunner.Run<CharAssumptionTests>();
//BenchmarkRunner.Run<EnumAttributeTests>();
//BenchmarkRunner.Run<IsDefinedTests>();
BenchmarkRunner.Run<StringConcatTests>();
//BenchmarkRunner.Run<StringConcatTests>();
BenchmarkRunner.Run<IndexOfTests>();
2 changes: 0 additions & 2 deletions Benchmarks/StringConcatTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.Primitives;
using System.Text;
using System.Diagnostics.CodeAnalysis;

namespace Open.Text.Benchmarks;

[MemoryDiagnoser]
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public class StringConcatTests
{
public static readonly string Phrase
Expand Down
36 changes: 13 additions & 23 deletions Source/EnumValue.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
// Ignore Spelling: Deconstruct

using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Threading;
using static System.Linq.Expressions.Expression;
//using static FastExpressionCompiler.LightExpression.Expression;
namespace Open.Text;
Expand Down Expand Up @@ -366,8 +356,8 @@ public static bool IsDefined<T>(T value)
/// Returns the <typeparamref name="TEnum"/> from the <paramref name="value"/> provided if it maps directly to the underlying value.
/// </summary>
public static bool TryGetValue<T>(T value, out TEnum e)
where T : notnull
=> Underlying<T>.Map.TryGetValue(value, out e!);
where T : notnull
=> Underlying<T>.Map.TryGetValue(value, out e!);

private string GetDebuggerDisplay()
{
Expand Down Expand Up @@ -533,7 +523,7 @@ public static bool TryParse<TEnum>(string value, out TEnum e)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TEnum Parse<TEnum>(StringSegment value)
where TEnum : notnull, Enum
=> TryParse<TEnum>(value, false, out var e) ? e
=> TryParse<TEnum>(value, false, out var e) ? e
: throw new ArgumentException(string.Format(NotFoundMessage, value), nameof(value));

/// <inheritdoc cref="TryParse{TEnum}(StringSegment, bool, out TEnum)"/>
Expand All @@ -549,7 +539,7 @@ public static TEnum Parse<TEnum>(string value, bool ignoreCase)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TEnum Parse<TEnum>(StringSegment value, bool ignoreCase)
where TEnum : notnull, Enum
{
{
var buffer = value.Buffer ?? throw new ArgumentNullException(nameof(value));
return value.Length == buffer.Length
? Parse<TEnum>(value.Buffer, ignoreCase)
Expand All @@ -561,13 +551,13 @@ public static TEnum Parse<TEnum>(StringSegment value, bool ignoreCase)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParse<TEnum>(StringSegment value, out TEnum e)
where TEnum : notnull, Enum
=> TryParse(value, false, out e);
=> TryParse(value, false, out e);

/// <inheritdoc cref="TryParse{TEnum}(StringSegment, bool, out TEnum)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParse<TEnum>(string name, bool ignoreCase, out TEnum e)
where TEnum : notnull, Enum
=> ignoreCase
=> ignoreCase
? EnumValue<TEnum>.IgnoreCaseLookup.TryGetValue(name, out e!)
: TryParse(name, out e);

Expand All @@ -576,7 +566,7 @@ public static bool TryParse<TEnum>(string name, bool ignoreCase, out TEnum e)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseIgnoreCase<TEnum>(string name, out TEnum e)
where TEnum : notnull, Enum
=> EnumValue<TEnum>.IgnoreCaseLookup.TryGetValue(name, out e!);
=> EnumValue<TEnum>.IgnoreCaseLookup.TryGetValue(name, out e!);

/// <summary>
/// Converts the string representation of the name of one or more enumerated constants to an equivalent enumerated object.
Expand All @@ -587,7 +577,7 @@ public static bool TryParseIgnoreCase<TEnum>(string name, out TEnum e)
/// <returns>true if the value was found; otherwise false.</returns>
public static bool TryParse<TEnum>(StringSegment name, bool ignoreCase, out TEnum e)
where TEnum : notnull, Enum
{
{
var len = name.Length;
if (len == 0) goto notFound;

Expand Down Expand Up @@ -626,8 +616,8 @@ public static bool TryParse<TEnum>(StringSegment name, bool ignoreCase, out TEnu
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue<TEnum, T>(T value, out TEnum e)
where TEnum : notnull, Enum
where T : notnull
=> EnumValue<TEnum>.TryGetValue(value, out e);
where T : notnull
=> EnumValue<TEnum>.TryGetValue(value, out e);

/// <summary>
/// Uses an expression tree to do an fast lookup the name of the enum value.
Expand All @@ -639,14 +629,14 @@ public static bool TryGetValue<TEnum, T>(T value, out TEnum e)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetName<TEnum>(this TEnum value)
where TEnum : notnull, Enum
=> EnumValue<TEnum>.NameLookup(value);
=> EnumValue<TEnum>.NameLookup(value);

/// <summary>
/// Retrieves the attributes for a given enum value.
/// </summary>
public static IReadOnlyList<Attribute> GetAttributes<TEnum>(this TEnum value)
where TEnum : notnull, Enum
{
{
return EnumValue<TEnum>.Attributes.GetOrAdd(value, GetAttributesCore);

static IReadOnlyList<Attribute> GetAttributesCore(TEnum value)
Expand Down
Loading