Skip to content

Commit f80ecb4

Browse files
Merge pull request #7 from Open-NET-Libraries/vNext
Accurate parity of IndexOf and related with full code coverage for multiple targets.
2 parents 38c4c2d + 323a4bf commit f80ecb4

44 files changed

Lines changed: 1320 additions & 810 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Benchmarks/CharAssumptionTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Open.Text.Benchmarks;
44

5-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "For benchmarking.")]
65
public class CharAssumptionTests
76
{
87
const string TestString = "abcdefghijklmnopqrstuvwxyz0123456789";

Benchmarks/EnumAttributeTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Open.Text.Benchmarks;
44

5-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "<Pending>")]
65
public class EnumAttributeTests
76
{
87
public static IReadOnlyList<Attribute> GetAttribute(Greek value)

Benchmarks/EnumParseTests.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using BenchmarkDotNet.Attributes;
2+
using CommandLine;
23
using FastEnumUtility;
3-
using System.Security;
44

55
namespace Open.Text.Benchmarks;
66

@@ -69,8 +69,9 @@ abstract class Tests
6969

7070
static readonly Dictionary<string, Greek> LookupD
7171
= Enum
72-
.GetValues<Greek>()
73-
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.Ordinal);
72+
.GetValues(typeof(Greek))
73+
.Cast<Greek>()
74+
.ToDictionary(e => e.GetName(), e => e, StringComparer.Ordinal);
7475

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

263264
static readonly Dictionary<string, Greek> LookupD
264265
= Enum
265-
.GetValues<Greek>()
266-
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.OrdinalIgnoreCase);
266+
.GetValues(typeof(Greek))
267+
.Cast<Greek>()
268+
.ToDictionary(e => e.GetName(), e => e, StringComparer.OrdinalIgnoreCase);
267269

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

329331
static readonly Dictionary<string, Greek> LookupD
330332
= Enum
331-
.GetValues<Greek>()
332-
.ToDictionary(e => Enum.GetName(e)!, e => e, StringComparer.OrdinalIgnoreCase);
333+
.GetValues(typeof(Greek))
334+
.Cast<Greek>()
335+
.ToDictionary(e => e.GetName(), e => e, StringComparer.OrdinalIgnoreCase);
333336

334337
protected override bool Lookup(string value, out Greek e)
335338
=> LookupD.TryGetValue(value, out e);

Benchmarks/EnumToStringTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
namespace Open.Text.Benchmarks;
55

6-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Benchmarking.")]
76
public class EnumToStringTests
87
{
98
static readonly IReadOnlyList<Greek> Values = Enum.GetValues(typeof(Greek)).Cast<Greek>().ToArray();

Benchmarks/IndexOfTests.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Jobs;
3+
4+
namespace Open.Text.Benchmarks;
5+
6+
[MemoryDiagnoser]
7+
[SimpleJob(RuntimeMoniker.Net472, baseline: true)]
8+
[SimpleJob(RuntimeMoniker.Net60)]
9+
[SimpleJob(RuntimeMoniker.Net80)]
10+
public class IndexOfTests
11+
{
12+
public const string Text = "The quick brown fox jumps over the lazy dog The quick brown fox jumps over the lazy dog";
13+
public static readonly string TextUpper = Text.ToUpper();
14+
public const string Search = "fox";
15+
public const string SearchCased = "Fox";
16+
17+
private StringComparison _comparison = StringComparison.Ordinal;
18+
private StringComparison _comparisonCaseIgnored;
19+
20+
[Params(
21+
StringComparison.Ordinal,
22+
StringComparison.CurrentCulture,
23+
StringComparison.InvariantCulture)]
24+
public StringComparison Comparison
25+
{
26+
get => _comparison;
27+
set
28+
{
29+
_comparison = value;
30+
_comparisonCaseIgnored = _comparison switch
31+
{
32+
StringComparison.Ordinal => StringComparison.OrdinalIgnoreCase,
33+
StringComparison.CurrentCulture => StringComparison.CurrentCultureIgnoreCase,
34+
StringComparison.InvariantCulture => StringComparison.InvariantCultureIgnoreCase,
35+
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
36+
};
37+
}
38+
}
39+
40+
[Benchmark(Baseline = true)]
41+
public int IndexOf()
42+
=> Text.IndexOf(Search, _comparison);
43+
44+
[Benchmark]
45+
public int IndexOfCaseIgnored()
46+
=> Text.IndexOf(SearchCased, _comparisonCaseIgnored);
47+
48+
[Benchmark]
49+
public int IndexOfSpan()
50+
=> Text.IndexOf(Search.AsSpan(), _comparison);
51+
52+
[Benchmark]
53+
public int IndexOfSpanCaseIgnored()
54+
=> Text.IndexOf(SearchCased.AsSpan(), _comparisonCaseIgnored);
55+
56+
[Benchmark]
57+
public int IndexOfSpanSlice()
58+
=> Text.IndexOf(Text.AsSpan(16, 3), _comparison);
59+
60+
[Benchmark]
61+
public int IndexOfSpanSliceCaseIgnored()
62+
=> Text.IndexOf(TextUpper.AsSpan(16, 3), _comparisonCaseIgnored);
63+
}

Benchmarks/IsDefinedTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using BenchmarkDotNet.Attributes;
2-
using System.Diagnostics.CodeAnalysis;
32

43
namespace Open.Text.Benchmarks;
54

6-
[SuppressMessage("Performance", "CA1822:Mark members as static")]
75
public class IsDefinedTests
86
{
97
static readonly IReadOnlyList<int> Values = Enumerable.Range(-2, 30).ToArray();

Benchmarks/Open.Text.Benchmarks.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFrameworks>net472;net6.0;net8.0</TargetFrameworks>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
8+
<LangVersion>latest</LangVersion>
9+
<NoWarn>CA1822</NoWarn>
810
</PropertyGroup>
911

1012
<ItemGroup>
11-
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
12-
<PackageReference Include="FastEnum" Version="1.7.0" />
13+
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
14+
<PackageReference Include="FastEnum" Version="1.8.0" />
1315
</ItemGroup>
1416

1517
<ItemGroup>

Benchmarks/Program.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
using BenchmarkDotNet.Running;
22
using Open.Text.Benchmarks;
33

4+
//BenchmarkSwitcher
5+
// .FromAssembly(typeof(Program).Assembly)
6+
// .Run(args); // crucial to make it work
7+
48
//BenchmarkRunner.Run<EnumParseTests>();
5-
//enchmarkRunner.Run<EnumToStringTests>();
9+
//BenchmarkRunner.Run<EnumToStringTests>();
610
//BenchmarkRunner.Run<CharAssumptionTests>();
711
//BenchmarkRunner.Run<EnumAttributeTests>();
812
//BenchmarkRunner.Run<IsDefinedTests>();
9-
BenchmarkRunner.Run<StringConcatTests>();
13+
//BenchmarkRunner.Run<StringConcatTests>();
14+
BenchmarkRunner.Run<IndexOfTests>();

Benchmarks/StringConcatTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
using BenchmarkDotNet.Attributes;
22
using Microsoft.Extensions.Primitives;
33
using System.Text;
4-
using System.Diagnostics.CodeAnalysis;
54

65
namespace Open.Text.Benchmarks;
76

87
[MemoryDiagnoser]
9-
[SuppressMessage("Performance", "CA1822:Mark members as static")]
108
public class StringConcatTests
119
{
1210
public static readonly string Phrase

Source/EnumValue.cs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
// Ignore Spelling: Deconstruct
2-
3-
using Microsoft.Extensions.Primitives;
4-
using System;
5-
using System.Collections.Concurrent;
6-
using System.Collections.Generic;
7-
using System.Diagnostics;
8-
using System.Diagnostics.CodeAnalysis;
9-
using System.Linq;
1+
using System.Collections.Concurrent;
102
using System.Linq.Expressions;
11-
using System.Runtime.CompilerServices;
12-
using System.Threading;
133
using static System.Linq.Expressions.Expression;
144
//using static FastExpressionCompiler.LightExpression.Expression;
155
namespace Open.Text;
@@ -366,8 +356,8 @@ public static bool IsDefined<T>(T value)
366356
/// Returns the <typeparamref name="TEnum"/> from the <paramref name="value"/> provided if it maps directly to the underlying value.
367357
/// </summary>
368358
public static bool TryGetValue<T>(T value, out TEnum e)
369-
where T : notnull
370-
=> Underlying<T>.Map.TryGetValue(value, out e!);
359+
where T : notnull
360+
=> Underlying<T>.Map.TryGetValue(value, out e!);
371361

372362
private string GetDebuggerDisplay()
373363
{
@@ -533,7 +523,7 @@ public static bool TryParse<TEnum>(string value, out TEnum e)
533523
[MethodImpl(MethodImplOptions.AggressiveInlining)]
534524
public static TEnum Parse<TEnum>(StringSegment value)
535525
where TEnum : notnull, Enum
536-
=> TryParse<TEnum>(value, false, out var e) ? e
526+
=> TryParse<TEnum>(value, false, out var e) ? e
537527
: throw new ArgumentException(string.Format(NotFoundMessage, value), nameof(value));
538528

539529
/// <inheritdoc cref="TryParse{TEnum}(StringSegment, bool, out TEnum)"/>
@@ -549,7 +539,7 @@ public static TEnum Parse<TEnum>(string value, bool ignoreCase)
549539
[MethodImpl(MethodImplOptions.AggressiveInlining)]
550540
public static TEnum Parse<TEnum>(StringSegment value, bool ignoreCase)
551541
where TEnum : notnull, Enum
552-
{
542+
{
553543
var buffer = value.Buffer ?? throw new ArgumentNullException(nameof(value));
554544
return value.Length == buffer.Length
555545
? Parse<TEnum>(value.Buffer, ignoreCase)
@@ -561,13 +551,13 @@ public static TEnum Parse<TEnum>(StringSegment value, bool ignoreCase)
561551
[MethodImpl(MethodImplOptions.AggressiveInlining)]
562552
public static bool TryParse<TEnum>(StringSegment value, out TEnum e)
563553
where TEnum : notnull, Enum
564-
=> TryParse(value, false, out e);
554+
=> TryParse(value, false, out e);
565555

566556
/// <inheritdoc cref="TryParse{TEnum}(StringSegment, bool, out TEnum)"/>
567557
[MethodImpl(MethodImplOptions.AggressiveInlining)]
568558
public static bool TryParse<TEnum>(string name, bool ignoreCase, out TEnum e)
569559
where TEnum : notnull, Enum
570-
=> ignoreCase
560+
=> ignoreCase
571561
? EnumValue<TEnum>.IgnoreCaseLookup.TryGetValue(name, out e!)
572562
: TryParse(name, out e);
573563

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

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

@@ -626,8 +616,8 @@ public static bool TryParse<TEnum>(StringSegment name, bool ignoreCase, out TEnu
626616
[MethodImpl(MethodImplOptions.AggressiveInlining)]
627617
public static bool TryGetValue<TEnum, T>(T value, out TEnum e)
628618
where TEnum : notnull, Enum
629-
where T : notnull
630-
=> EnumValue<TEnum>.TryGetValue(value, out e);
619+
where T : notnull
620+
=> EnumValue<TEnum>.TryGetValue(value, out e);
631621

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

644634
/// <summary>
645635
/// Retrieves the attributes for a given enum value.
646636
/// </summary>
647637
public static IReadOnlyList<Attribute> GetAttributes<TEnum>(this TEnum value)
648638
where TEnum : notnull, Enum
649-
{
639+
{
650640
return EnumValue<TEnum>.Attributes.GetOrAdd(value, GetAttributesCore);
651641

652642
static IReadOnlyList<Attribute> GetAttributesCore(TEnum value)

0 commit comments

Comments
 (0)