Skip to content

Commit dc67995

Browse files
authored
Merge pull request #14 from rameel/alternate-lookup-support
Add support for `IAlternateEqualityComparer`
2 parents a1b6ac8 + fdf23cc commit dc67995

3 files changed

Lines changed: 200 additions & 4 deletions

File tree

Ramstack.Structures.Tests/Text/StringViewComparerTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,34 @@ public void Create_IgnoreCase()
136136
Assert.That(c1.Equals("test", "TEST"), Is.True);
137137
Assert.That(c2.Equals("test", "TEST"), Is.True);
138138
}
139+
140+
#if NET9_0_OR_GREATER
141+
[Test]
142+
public void AlternateComparer_Ordinal()
143+
{
144+
var dictionary = new Dictionary<StringView, int>(StringViewComparer.Ordinal)
145+
{
146+
["test"] = 1
147+
};
148+
149+
var lookup = dictionary.GetAlternateLookup<ReadOnlySpan<char>>();
150+
Assert.That(lookup.TryGetValue("TEST", out _), Is.False);
151+
Assert.That(lookup.TryGetValue("test", out var v), Is.True);
152+
Assert.That(v, Is.EqualTo(1));
153+
}
154+
155+
[Test]
156+
public void AlternateComparer_OrdinalIgnoreCase()
157+
{
158+
var dictionary = new Dictionary<StringView, int>(StringViewComparer.OrdinalIgnoreCase)
159+
{
160+
["TEST"] = 1
161+
};
162+
163+
var lookup = dictionary.GetAlternateLookup<ReadOnlySpan<char>>();
164+
Assert.That(lookup.TryGetValue("text", out _), Is.False);
165+
Assert.That(lookup.TryGetValue("test", out var v), Is.True);
166+
Assert.That(v, Is.EqualTo(1));
167+
}
168+
#endif
139169
}

Ramstack.Structures/Text/StringView.cs

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,24 @@ public bool Contains(ReadOnlySpan<char> value, StringComparison comparisonType)
651651
public int CompareTo(StringView other) =>
652652
Compare(this, other, StringComparison.CurrentCulture);
653653

654+
/// <summary>
655+
/// Compares one view with a readonly span, and returns an integer
656+
/// that indicates their relative position in the sort order.
657+
/// </summary>
658+
/// <param name="other">An object to compare with this instance.</param>
659+
/// <returns>
660+
/// A signed integer that indicates the relative order of view and other:
661+
/// - If less than 0, this instance precedes than <paramref name="other"/>.
662+
/// - If 0, this instance equals <paramref name="other"/>.
663+
/// - If greater than 0, this instance follows <paramref name="other"/>.
664+
/// </returns>
665+
public int CompareTo(ReadOnlySpan<char> other) =>
666+
Compare(this, other, StringComparison.CurrentCulture);
667+
668+
/// <inheritdoc />
669+
public int CompareTo(string? other) =>
670+
Compare(this, other, StringComparison.CurrentCulture);
671+
654672
/// <summary>
655673
/// Compares one view with another using a specified string comparison,
656674
/// and returns an integer that indicates their relative position in the sort order.
@@ -667,12 +685,24 @@ public int CompareTo(StringView other) =>
667685
public int CompareTo(StringView other, StringComparison comparisonType) =>
668686
Compare(this, other, comparisonType);
669687

670-
/// <inheritdoc />
671-
public int CompareTo(string? other) =>
672-
Compare(this, other, StringComparison.CurrentCulture);
688+
/// <summary>
689+
/// Compares one view with a readonly span using a specified string comparison,
690+
/// and returns an integer that indicates their relative position in the sort order.
691+
/// </summary>
692+
/// <param name="other">The view to compare with the current instance.</param>
693+
/// <param name="comparisonType">An enumeration value that determines
694+
/// how current view and other are compared.</param>
695+
/// <returns>
696+
/// A signed integer that indicates the relative order of view and other:
697+
/// - If less than 0, this instance precedes than <paramref name="other"/>.
698+
/// - If 0, this instance equals <paramref name="other"/>.
699+
/// - If greater than 0, this instance follows <paramref name="other"/>.
700+
/// </returns>
701+
public int CompareTo(ReadOnlySpan<char> other, StringComparison comparisonType) =>
702+
Compare(this, other, comparisonType);
673703

674704
/// <summary>
675-
/// Compares one view with another using a specified string comparison,
705+
/// Compares one view with a string using a specified string comparison,
676706
/// and returns an integer that indicates their relative position in the sort order.
677707
/// </summary>
678708
/// <param name="other">The view to compare with the current instance.</param>
@@ -703,6 +733,17 @@ public override bool Equals([NotNullWhen(true)] object? obj)
703733
public bool Equals(StringView other) =>
704734
Equals(this, other);
705735

736+
/// <summary>
737+
/// Indicates whether the current object is equal to a specified readonly span.
738+
/// </summary>
739+
/// <param name="other">An object to compare with this object.</param>
740+
/// <returns>
741+
/// <see langword="true" /> if the current object is equal to the <paramref name="other" /> parameter;
742+
/// otherwise, <see langword="false" />.
743+
/// </returns>
744+
public bool Equals(ReadOnlySpan<char> other) =>
745+
Equals(this, other);
746+
706747
/// <inheritdoc />
707748
public bool Equals(string? other) =>
708749
Equals(this, other);
@@ -720,6 +761,19 @@ public bool Equals(string? other) =>
720761
public bool Equals(StringView other, StringComparison comparisonType) =>
721762
Equals(this, other, comparisonType);
722763

764+
/// <summary>
765+
/// Determines whether this view and the specified readonly span have the same characters
766+
/// when compared using the specified <paramref name="comparisonType"/> option.
767+
/// </summary>
768+
/// <param name="other">The value to compare with the current instance.</param>
769+
/// <param name="comparisonType">An enumeration value that determines
770+
/// how current instance and <paramref name="other"/> are compared.</param>
771+
/// <returns>
772+
/// true if equal, false otherwise.
773+
/// </returns>
774+
public bool Equals(ReadOnlySpan<char> other, StringComparison comparisonType) =>
775+
Equals(this, other, comparisonType);
776+
723777
/// <summary>
724778
/// Determines whether this view and the specified other view have the same characters
725779
/// when compared using the specified <paramref name="comparisonType"/> option.
@@ -864,6 +918,18 @@ public bool TryCopyTo(Span<char> destination) =>
864918
public static bool Equals(StringView a, StringView b) =>
865919
a.AsSpan().SequenceEqual(b);
866920

921+
/// <summary>
922+
/// Determines whether two specified <see cref="StringView"/> objects have the same value.
923+
/// </summary>
924+
/// <param name="a">The first value to compare.</param>
925+
/// <param name="b">The second value to compare.</param>
926+
/// <returns>
927+
/// <see langword="true"/> if the value of <paramref name="a"/> is the same as the value
928+
/// of <paramref name="b"/>; otherwise, <see langword="false"/>.
929+
/// </returns>
930+
public static bool Equals(StringView a, ReadOnlySpan<char> b) =>
931+
a.AsSpan().SequenceEqual(b);
932+
867933
/// <summary>
868934
/// Determines whether two specified <see cref="StringView"/> objects have the same value.
869935
/// </summary>
@@ -891,6 +957,21 @@ public static bool Equals(StringView a, string? b) =>
891957
public static bool Equals(StringView a, StringView b, StringComparison comparison) =>
892958
a.AsSpan().Equals(b, comparison);
893959

960+
/// <summary>
961+
/// Determines whether two specified <see cref="StringView"/> objects have the same value
962+
/// with the specified comparison rules.
963+
/// </summary>
964+
/// <param name="a">The first value to compare.</param>
965+
/// <param name="b">The second value to compare.</param>
966+
/// <param name="comparison">An enumeration value that determines how values are compared.</param>
967+
/// <returns>
968+
/// <see langword="true"/> if the value of the <paramref name="a"/> parameter
969+
/// is equal to the value of <paramref name="b"/> parameter;
970+
/// otherwise, <see langword="false"/>.
971+
/// </returns>
972+
public static bool Equals(StringView a, ReadOnlySpan<char> b, StringComparison comparison) =>
973+
a.AsSpan().Equals(b, comparison);
974+
894975
/// <summary>
895976
/// Determines whether two specified <see cref="StringView"/> objects have the same value
896977
/// with the specified comparison rules.
@@ -922,6 +1003,22 @@ public static bool Equals(StringView a, string? b, StringComparison comparison)
9221003
public static int CompareOrdinal(StringView a, StringView b) =>
9231004
a.AsSpan().SequenceCompareTo(b);
9241005

1006+
/// <summary>
1007+
/// Compares two specified <see cref="StringView"/> objects and returns an integer
1008+
/// that indicates their relative position in the sort order.
1009+
/// </summary>
1010+
/// <param name="a">The first value to compare.</param>
1011+
/// <param name="b">The second value to compare.</param>
1012+
/// <returns>
1013+
/// A 32-bit signed integer that indicates the lexical relationship between the two comparands.
1014+
/// A signed integer that indicates the relative order of view and other:
1015+
/// - If less than 0, <paramref name="a"/>> precedes <paramref name="b"/> in the sort order.
1016+
/// - If 0, <paramref name="a"/>> equals <paramref name="b"/>.
1017+
/// - If greater than 0, <paramref name="a"/>> follows <paramref name="b"/> in the sort order.
1018+
/// </returns>
1019+
public static int CompareOrdinal(StringView a, ReadOnlySpan<char> b) =>
1020+
a.AsSpan().SequenceCompareTo(b);
1021+
9251022
/// <summary>
9261023
/// Compares two specified <see cref="StringView"/> objects and returns an integer
9271024
/// that indicates their relative position in the sort order.
@@ -956,6 +1053,24 @@ public static int CompareOrdinal(StringView a, string? b) =>
9561053
public static int Compare(StringView a, StringView b, StringComparison comparison) =>
9571054
a.AsSpan().CompareTo(b, comparison);
9581055

1056+
/// <summary>
1057+
/// Compares two specified <see cref="StringView"/> objects and returns an integer
1058+
/// that indicates their relative position in the sort order.
1059+
/// </summary>
1060+
/// <param name="a">The first value to compare.</param>
1061+
/// <param name="b">The second value to compare.</param>
1062+
/// <param name="comparison">One of the enumeration values
1063+
/// that specifies the rules to use in the comparison.</param>
1064+
/// <returns>
1065+
/// A 32-bit signed integer that indicates the lexical relationship between the two comparands.
1066+
/// A signed integer that indicates the relative order of view and other:
1067+
/// - If less than 0, <paramref name="a"/>> precedes <paramref name="b"/> in the sort order.
1068+
/// - If 0, <paramref name="a"/>> equals <paramref name="b"/>.
1069+
/// - If greater than 0, <paramref name="a"/>> follows <paramref name="b"/> in the sort order.
1070+
/// </returns>
1071+
public static int Compare(StringView a, ReadOnlySpan<char> b, StringComparison comparison) =>
1072+
a.AsSpan().CompareTo(b, comparison);
1073+
9591074
/// <summary>
9601075
/// Compares two specified <see cref="StringView"/> objects and returns an integer
9611076
/// that indicates their relative position in the sort order.

Ramstack.Structures/Text/StringViewComparer.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ static StringViewComparer Error(StringComparison comparisonType) =>
121121
/// <param name="options">A bitwise combination of <see cref="CompareOptions"/> values
122122
/// that specify how the comparison should be performed.</param>
123123
private sealed class CultureAwareComparer(CompareInfo info, CompareOptions options) : StringViewComparer
124+
#if NET9_0_OR_GREATER
125+
, IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>
126+
#endif
124127
{
125128
/// <summary>
126129
/// A singleton instance of the <see cref="T:CultureAwareComparer"/> class
@@ -147,6 +150,20 @@ public override int GetHashCode(StringView obj) =>
147150
/// <inheritdoc />
148151
public override int Compare(StringView x, StringView y) =>
149152
info.IndexOf(x, y, options);
153+
154+
#if NET9_0_OR_GREATER
155+
/// <inheritdoc />
156+
bool IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Equals(ReadOnlySpan<char> alternate, StringView other) =>
157+
info.IndexOf(alternate, other, options) == 0;
158+
159+
/// <inheritdoc />
160+
int IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.GetHashCode(ReadOnlySpan<char> alternate) =>
161+
info.GetHashCode(alternate, options);
162+
163+
/// <inheritdoc />
164+
StringView IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Create(ReadOnlySpan<char> alternate) =>
165+
alternate.ToString();
166+
#endif
150167
}
151168

152169
#endregion
@@ -158,6 +175,9 @@ public override int Compare(StringView x, StringView y) =>
158175
/// that performs a case-sensitive ordinal string comparison.
159176
/// </summary>
160177
private sealed class OrdinalComparer : StringViewComparer
178+
#if NET9_0_OR_GREATER
179+
, IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>
180+
#endif
161181
{
162182
/// <summary>
163183
/// A singleton instance of the <see cref="T:OrdinalComparer"/> class.
@@ -175,6 +195,20 @@ public override int Compare(StringView x, StringView y) =>
175195
/// <inheritdoc />
176196
public override int GetHashCode(StringView obj) =>
177197
obj.GetHashCode();
198+
199+
#if NET9_0_OR_GREATER
200+
/// <inheritdoc />
201+
bool IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Equals(ReadOnlySpan<char> alternate, StringView other) =>
202+
alternate.SequenceEqual(other);
203+
204+
/// <inheritdoc />
205+
int IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.GetHashCode(ReadOnlySpan<char> alternate) =>
206+
string.GetHashCode(alternate);
207+
208+
/// <inheritdoc />
209+
StringView IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Create(ReadOnlySpan<char> alternate) =>
210+
alternate.ToString();
211+
#endif
178212
}
179213

180214
#endregion
@@ -186,6 +220,9 @@ public override int GetHashCode(StringView obj) =>
186220
/// that performs a case-insensitive ordinal string comparison.
187221
/// </summary>
188222
private sealed class OrdinalIgnoreCaseComparer : StringViewComparer
223+
#if NET9_0_OR_GREATER
224+
, IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>
225+
#endif
189226
{
190227
/// <summary>
191228
/// A singleton instance of the <see cref="T:OrdinalIgnoreCaseComparer"/> class.
@@ -203,6 +240,20 @@ public override int Compare(StringView x, StringView y) =>
203240
/// <inheritdoc />
204241
public override int GetHashCode(StringView obj) =>
205242
obj.GetHashCode(StringComparison.OrdinalIgnoreCase);
243+
244+
#if NET9_0_OR_GREATER
245+
/// <inheritdoc />
246+
bool IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Equals(ReadOnlySpan<char> alternate, StringView other) =>
247+
alternate.Equals(other, StringComparison.OrdinalIgnoreCase);
248+
249+
/// <inheritdoc />
250+
int IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.GetHashCode(ReadOnlySpan<char> alternate) =>
251+
string.GetHashCode(alternate, StringComparison.OrdinalIgnoreCase);
252+
253+
/// <inheritdoc />
254+
StringView IAlternateEqualityComparer<ReadOnlySpan<char>, StringView>.Create(ReadOnlySpan<char> alternate) =>
255+
alternate.ToString();
256+
#endif
206257
}
207258

208259
#endregion

0 commit comments

Comments
 (0)