diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenDictionary.AlternateLookup.cs index 40425121c8c6f0..d09a9f132553a3 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenDictionary.AlternateLookup.cs @@ -10,7 +10,33 @@ namespace System.Collections.Frozen internal sealed partial class DefaultFrozenDictionary { /// - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (dictionary, key) + => ref ((DefaultFrozenDictionary)dictionary).GetValueRefOrNullRefCoreAlternate(key); + } + + /// + private ref readonly TValue GetValueRefOrNullRefCoreAlternate(TAlternateKey key) + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif { IAlternateEqualityComparer comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenSet.AlternateLookup.cs index 63586dcce70e04..6d45cf448c58a9 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/DefaultFrozenSet.AlternateLookup.cs @@ -7,8 +7,32 @@ namespace System.Collections.Frozen { internal sealed partial class DefaultFrozenSet { - /// - private protected override int FindItemIndex(TAlternate item) + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (set, item) + => ((DefaultFrozenSet)set).FindItemIndexAlternate(item); + } + + /// + private int FindItemIndexAlternate(TAlternate item) +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif { IAlternateEqualityComparer comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.AlternateLookup.cs index acdcc49835dd1c..45f0436deb5fee 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.AlternateLookup.cs @@ -53,7 +53,7 @@ public bool TryGetAlternateLookup(out AlternateLookup && (typeof(TKey) != typeof(string) || typeof(TAlternateKey) == typeof(ReadOnlySpan))) { - lookup = new AlternateLookup(this); + lookup = new AlternateLookup(this, GetAlternateLookupDelegate()); return true; } @@ -76,12 +76,16 @@ private protected IAlternateEqualityComparer GetAlternateEq /// The alternate type of a key for performing lookups. public readonly struct AlternateLookup where TAlternateKey : notnull, allows ref struct { + private readonly AlternateLookupDelegate _alternateLookupDelegate; + /// Initialize the instance. The dictionary must have already been verified to have a compatible comparer. - internal AlternateLookup(FrozenDictionary dictionary) + internal AlternateLookup(FrozenDictionary dictionary, AlternateLookupDelegate alternateLookupDelegate) { Debug.Assert(dictionary is not null); Debug.Assert(dictionary.Comparer is IAlternateEqualityComparer); + Debug.Assert(alternateLookupDelegate is not null); Dictionary = dictionary; + _alternateLookupDelegate = alternateLookupDelegate; } /// Gets the against which this instance performs operations. @@ -99,7 +103,7 @@ public TValue this[TAlternateKey key] { get { - ref readonly TValue valueRef = ref Dictionary.GetValueRefOrNullRefCore(key); + ref readonly TValue valueRef = ref _alternateLookupDelegate(Dictionary, key); if (Unsafe.IsNullRef(in valueRef)) { ThrowHelper.ThrowKeyNotFoundException(); @@ -114,7 +118,7 @@ public TValue this[TAlternateKey key] /// if the key is in the dictionary; otherwise, . /// is . public bool ContainsKey(TAlternateKey key) => - !Unsafe.IsNullRef(in Dictionary.GetValueRefOrNullRefCore(key)); + !Unsafe.IsNullRef(in _alternateLookupDelegate(Dictionary, key)); /// Gets the value associated with the specified alternate key. /// The alternate key of the value to get. @@ -126,7 +130,7 @@ public bool ContainsKey(TAlternateKey key) => /// is . public bool TryGetValue(TAlternateKey key, [MaybeNullWhen(false)] out TValue value) { - ref readonly TValue valueRef = ref Dictionary.GetValueRefOrNullRefCore(key); + ref readonly TValue valueRef = ref _alternateLookupDelegate(Dictionary, key); if (!Unsafe.IsNullRef(in valueRef)) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs index 0f757b9d1203eb..1772628aca0229 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs @@ -449,20 +449,20 @@ public ref readonly TValue GetValueRefOrNullRef(TKey key) /// private protected abstract ref readonly TValue GetValueRefOrNullRefCore(TKey key); - /// + /// + /// Retrieves a delegate which calls a method equivalent to + /// for the . + /// /// /// This is virtual rather than abstract because only some implementations need to support this, e.g. implementations that /// are only ever used with the default comparer won't ever hit code paths that use this, at least not /// until/if we make `EqualityComparer{string}.Default` implement `IAlternateEqualityComparer{ReadOnlySpan{char}, string}`. /// - /// This unfortunately needs to be a generic virtual method, but the only other known option involves having a dedicated - /// class instance such that the generic can be baked into that, where the methods on it are still virtual but don't have - /// extra generic methods. But for most implementations, either a) that class would need to be allocated as part of - /// TryGetAlternateLookup, which would be more expensive for use cases where someone needs a lookup for just a few operations, - /// or b) a dictionary of those instances would need to be maintained, which just replaces the runtime's dictionary for a GVM - /// with a custom one here. + /// Generic Virtual method invocation is slower than delegate invocation and could negate + /// much of the benefit of using Alternate Keys. By retrieving the delegate up-front when + /// the lookup is created, we only pay for generic virtual method invocation once. /// - private protected virtual ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) + private protected virtual AlternateLookupDelegate GetAlternateLookupDelegate() where TAlternateKey : notnull #if NET9_0_OR_GREATER #pragma warning disable SA1001 // Commas should be spaced correctly @@ -473,7 +473,34 @@ private protected virtual ref readonly TValue GetValueRefOrNullRefCore ref Unsafe.NullRef(); + => AlternateLookupDelegateHolder.ReturnsNullRef; + + /// + /// Invokes a method equivalent to + /// for the . + /// + internal delegate ref readonly TValue AlternateLookupDelegate(FrozenDictionary dictionary, TAlternateKey key) + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif + ; + + /// + /// Holds an implementation of which always returns a null ref. + /// + private static class AlternateLookupDelegateHolder + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif + { + public static readonly AlternateLookupDelegate ReturnsNullRef = (_, _) => ref Unsafe.NullRef(); + } /// Gets a reference to the value associated with the specified key. /// The key of the value to get. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.AlternateLookup.cs index 57584e8784fd51..71390182e23d81 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.AlternateLookup.cs @@ -53,7 +53,7 @@ public bool TryGetAlternateLookup(out AlternateLookup lo if (Comparer is IAlternateEqualityComparer && (typeof(T) != typeof(string) || typeof(TAlternate) == typeof(ReadOnlySpan))) { - lookup = new AlternateLookup(this); + lookup = new AlternateLookup(this, GetAlternateLookupDelegate()); return true; } @@ -63,10 +63,10 @@ public bool TryGetAlternateLookup(out AlternateLookup lo /// Gets the as an . /// This must only be used when it's already been proven that the comparer implements the target interface. - private protected IAlternateEqualityComparer GetAlternateEqualityComparer() where TAlternateKey : allows ref struct + private protected IAlternateEqualityComparer GetAlternateEqualityComparer() where TAlternate : allows ref struct { - Debug.Assert(Comparer is IAlternateEqualityComparer, "Must have already been verified"); - return Unsafe.As>(Comparer); + Debug.Assert(Comparer is IAlternateEqualityComparer, "Must have already been verified"); + return Unsafe.As>(Comparer); } /// @@ -76,12 +76,16 @@ private protected IAlternateEqualityComparer GetAlternateEqual /// The alternate type of a key for performing lookups. public readonly struct AlternateLookup where TAlternate : allows ref struct { + private readonly AlternateLookupDelegate _alternateLookupDelegate; + /// Initialize the instance. The set must have already been verified to have a compatible comparer. - internal AlternateLookup(FrozenSet set) + internal AlternateLookup(FrozenSet set, AlternateLookupDelegate alternateLookupDelegate) { Debug.Assert(set is not null); Debug.Assert(set.Comparer is IAlternateEqualityComparer); + Debug.Assert(alternateLookupDelegate is not null); Set = set; + _alternateLookupDelegate = alternateLookupDelegate; } /// Gets the against which this instance performs operations. @@ -90,7 +94,7 @@ internal AlternateLookup(FrozenSet set) /// Determines whether a set contains the specified element. /// The element to locate in the set. /// true if the set contains the specified element; otherwise, false. - public bool Contains(TAlternate item) => Set.FindItemIndex(item) >= 0; + public bool Contains(TAlternate item) => _alternateLookupDelegate(Set, item) >= 0; /// Searches the set for a given value and returns the equal value it finds, if any. /// The value to search for. @@ -98,7 +102,7 @@ internal AlternateLookup(FrozenSet set) /// A value indicating whether the search was successful. public bool TryGetValue(TAlternate equalValue, [MaybeNullWhen(false)] out T actualValue) { - int index = Set.FindItemIndex(equalValue); + int index = _alternateLookupDelegate(Set, equalValue); if (index >= 0) { actualValue = Set.Items[index]; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs index a867caa47ffbbd..2e7fb33b0e81c6 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs @@ -329,18 +329,56 @@ public bool TryGetValue(T equalValue, [MaybeNullWhen(false)] out T actualValue) /// The index of the value, or -1 if not found. private protected abstract int FindItemIndex(T item); - /// Finds the index of a specific value in a set. - /// The value to lookup. - /// The index of the value, or -1 if not found. - private protected virtual int FindItemIndex(TAlternate item) + /// + /// Retrieves a delegate which calls a method equivalent to + /// for the . + /// + /// + /// This is virtual rather than abstract because only some implementations need to support this, e.g. implementations that + /// are only ever used with the default comparer won't ever hit code paths that use this, at least not + /// until/if we make `EqualityComparer{string}.Default` implement `IAlternateEqualityComparer{ReadOnlySpan{char}, string}`. + /// + /// Generic Virtual method invocation is slower than delegate invocation and could negate + /// much of the benefit of using Alternate Keys. By retrieving the delegate up-front when + /// the lookup is created, we only pay for generic virtual method invocation once. + /// + private protected virtual AlternateLookupDelegate GetAlternateLookupDelegate() #if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly // This method will only ever be used on .NET 9+. However, because of how everything is structured, // and to avoid a proliferation of conditional files for many of the derived types (in particular // for the OrdinalString* implementations), we still build this method into all builds, even though // it'll be unused. But we can't use the allows ref struct constraint downlevel, hence the #if. where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif + => AlternateLookupDelegateHolder.ReturnsNullRef; + + /// + /// Invokes a method equivalent to + /// for the . + /// + internal delegate int AlternateLookupDelegate(FrozenSet set, TAlternate key) +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 #endif - => -1; + ; + + /// + /// Holds an implementation of which always returns -1. + /// + private static class AlternateLookupDelegateHolder +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif + { + public static readonly AlternateLookupDelegate ReturnsNullRef = (_, _) => -1; + } + /// Returns an enumerator that iterates through the set. /// An enumerator that iterates through the set. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenDictionary.AlternateLookup.cs index 4b43f2ad259e8f..a160a60f5c089c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenDictionary.AlternateLookup.cs @@ -11,7 +11,33 @@ namespace System.Collections.Frozen internal sealed partial class Int32FrozenDictionary { /// - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (dictionary, key) + => ref ((Int32FrozenDictionary)dictionary).GetValueRefOrNullRefCoreAlternate(key); + } + + /// + private ref readonly TValue GetValueRefOrNullRefCoreAlternate(TAlternateKey key) + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif { IAlternateEqualityComparer comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenSet.AlternateLookup.cs index 0c56deab4914b5..22c52227d5f78a 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenSet.AlternateLookup.cs @@ -9,8 +9,32 @@ namespace System.Collections.Frozen { internal sealed partial class Int32FrozenSet { - /// - private protected override int FindItemIndex(TAlternate item) + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (set, item) + => ((Int32FrozenSet)set).FindItemIndexAlternate(item); + } + + /// + private int FindItemIndexAlternate(TAlternate item) +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif { var comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenDictionary.AlternateLookup.cs index abf4a0d55c5c96..88fb2f1f3c7d5a 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenDictionary.AlternateLookup.cs @@ -9,7 +9,34 @@ namespace System.Collections.Frozen { internal sealed partial class SmallFrozenDictionary { - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (dictionary, key) + => ref ((SmallFrozenDictionary)dictionary).GetValueRefOrNullRefCoreAlternate(key); + } + + /// + private ref readonly TValue GetValueRefOrNullRefCoreAlternate(TAlternateKey key) + where TAlternateKey : notnull +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced correctly + , allows ref struct +#pragma warning restore SA1001 +#endif { IAlternateEqualityComparer comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenSet.AlternateLookup.cs index 7854e32e90f00e..98e10e7581bb01 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/SmallFrozenSet.AlternateLookup.cs @@ -7,8 +7,32 @@ namespace System.Collections.Frozen { internal sealed partial class SmallFrozenSet { - /// - private protected override int FindItemIndex(TAlternate item) + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + => AlternateLookupDelegateHolder.Instance; + + private static class AlternateLookupDelegateHolder +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced TAlternate + where TAlternateKey : allows ref struct +#pragma warning restore SA1001 +#endif + { + /// + /// Invokes + /// on instances known to be of type . + /// + public static readonly AlternateLookupDelegate Instance = (set, item) + => ((SmallFrozenSet)set).FindItemIndexAlternate(item); + } + + /// + private int FindItemIndexAlternate(TAlternate item) +#if NET9_0_OR_GREATER +#pragma warning disable SA1001 // Commas should be spaced TAlternate + where TAlternate : allows ref struct +#pragma warning restore SA1001 +#endif { IAlternateEqualityComparer comparer = GetAlternateEqualityComparer(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenDictionary.AlternateLookup.cs index 19b7df2df1dde5..2ed2a376196eb0 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenDictionary.AlternateLookup.cs @@ -9,12 +9,23 @@ namespace System.Collections.Frozen { internal sealed partial class LengthBucketsFrozenDictionary { - [MethodImpl(MethodImplOptions.NoInlining)] - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey alternate) + /// + /// Invokes + /// on instances known to be of type . + /// + private static readonly AlternateLookupDelegate> s_alternateLookup = (dictionary, key) + => ref ((LengthBucketsFrozenDictionary)dictionary).GetValueRefOrNullRefCoreAlternate(key); + + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() { Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan)); - ReadOnlySpan key = Unsafe.As>(ref alternate); + return (AlternateLookupDelegate)(object)s_alternateLookup; + } + /// + private ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) + { IAlternateEqualityComparer, string> comparer = GetAlternateEqualityComparer>(); // If the length doesn't have an associated bucket, the key isn't in the dictionary. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenSet.AlternateLookup.cs index 943a9a27a1f57b..a81ce57482df76 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/LengthBucketsFrozenSet.AlternateLookup.cs @@ -9,11 +9,23 @@ namespace System.Collections.Frozen { internal sealed partial class LengthBucketsFrozenSet { - private protected override int FindItemIndex(TAlternate alternate) + /// + /// Invokes + /// on instances known to be of type . + /// + private static readonly AlternateLookupDelegate> s_alternateLookup = (set, key) + => ((LengthBucketsFrozenSet)set).FindItemIndexAlternate(key); + + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() { - Debug.Assert(typeof(TAlternate) == typeof(ReadOnlySpan)); - ReadOnlySpan item = Unsafe.As>(ref alternate); + Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan)); + return (AlternateLookupDelegate)(object)s_alternateLookup; + } + /// + private int FindItemIndexAlternate(ReadOnlySpan item) + { IAlternateEqualityComparer, string> comparer = GetAlternateEqualityComparer>(); // If the length doesn't have an associated bucket, the key isn't in the dictionary. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary.AlternateLookup.cs index fb82fc7086936d..ee07acdc45a469 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary.AlternateLookup.cs @@ -9,19 +9,30 @@ namespace System.Collections.Frozen { internal abstract partial class OrdinalStringFrozenDictionary : FrozenDictionary { - // We want to avoid having to implement GetValueRefOrNullRefCore for each of the multiple types + /// + /// Invokes + /// on instances known to be of type . + /// + private static readonly AlternateLookupDelegate> s_alternateLookup = (dictionary, key) + => ref ((OrdinalStringFrozenDictionary)dictionary).GetValueRefOrNullRefCoreAlternate(key); + + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + { + Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan)); + return (AlternateLookupDelegate)(object)s_alternateLookup; + } + + // We want to avoid having to implement GetValueRefOrNullRefCoreAlternate for each of the multiple types // that derive from this one, but each of those needs to supply its own notion of Equals/GetHashCode. - // To avoid lots of virtual calls, we have every derived type override GetValueRefOrNullRefCore and + // To avoid lots of virtual calls, we have every derived type override GetValueRefOrNullRefCoreAlternate and // call to that span-based method that's aggressively inlined. That then exposes the implementation // to the sealed Equals/GetHashCodes on each derived type, allowing them to be devirtualized and inlined // into each unique copy of the code. - + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey alternate) + private protected virtual ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) { - Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan)); - ReadOnlySpan key = Unsafe.As>(ref alternate); - if ((uint)(key.Length - _minimumLength) <= (uint)_maximumLengthDiff) { if (CheckLengthQuick((uint)key.Length)) @@ -44,4 +55,61 @@ private protected override ref readonly TValue GetValueRefOrNullRefCore(); } } + + // See comment above for why these overrides exist. Do not remove. + + internal sealed partial class OrdinalStringFrozenDictionary_Full + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_FullCaseInsensitive + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedSingleChar + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedSingleChar + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } + + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedSubstring + { + private protected override ref readonly TValue GetValueRefOrNullRefCoreAlternate(ReadOnlySpan key) => ref base.GetValueRefOrNullRefCoreAlternate(key); + } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_Full.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_Full.cs index 54ccce9eacd22d..2c16af7027e28b 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_Full.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_Full.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_Full : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_Full : OrdinalStringFrozenDictionary { private readonly ulong _lengthFilter; @@ -23,7 +23,6 @@ internal OrdinalStringFrozenDictionary_Full( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => string.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.SequenceEqual(y.AsSpan()); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitive.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitive.cs index 4944319d17aec0..f0c71579ac43ab 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitive.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitive.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_FullCaseInsensitive : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_FullCaseInsensitive : OrdinalStringFrozenDictionary { private readonly ulong _lengthFilter; @@ -23,7 +23,6 @@ internal OrdinalStringFrozenDictionary_FullCaseInsensitive( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii.cs index 8f53193d77ae07..505160a52596d5 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii : OrdinalStringFrozenDictionary { private readonly ulong _lengthFilter; @@ -23,7 +23,6 @@ internal OrdinalStringFrozenDictionary_FullCaseInsensitiveAscii( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring.cs index b90a27e3327718..c2becafa6f5da0 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveAsciiSubstrin // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring.cs index a269e060052f89..80567fa9581d41 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_LeftJustifiedCaseInsensitiveSubstring( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSingleChar.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSingleChar.cs index 21e4ca402420ed..9be295125f8090 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSingleChar.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSingleChar.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_LeftJustifiedSingleChar : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedSingleChar : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_LeftJustifiedSingleChar( string[] keys, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenDictionary_LeftJustifiedSingleChar( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => string.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.SequenceEqual(y.AsSpan()); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSubstring.cs index 41fd111eed1f73..617b90f2cd21af 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_LeftJustifiedSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_LeftJustifiedSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_LeftJustifiedSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_LeftJustifiedSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_LeftJustifiedSubstring( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => string.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.SequenceEqual(y.AsSpan()); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring.cs index 725ba7f12f73d4..2a522bded07deb 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveAsciiSubstri // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring.cs index b037e674a349e7..daead4ac30bda1 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_RightJustifiedCaseInsensitiveSubstring( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.Equals(y.AsSpan(), StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSingleChar.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSingleChar.cs index 4514ae33c87c75..f68ca356789b4e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSingleChar.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSingleChar.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_RightJustifiedSingleChar : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedSingleChar : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_RightJustifiedSingleChar( string[] keys, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenDictionary_RightJustifiedSingleChar( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => string.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.SequenceEqual(y.AsSpan()); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSubstring.cs index dafd61dd33940c..81b40e6d1d759f 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary_RightJustifiedSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenDictionary_RightJustifiedSubstring : OrdinalStringFrozenDictionary + internal sealed partial class OrdinalStringFrozenDictionary_RightJustifiedSubstring : OrdinalStringFrozenDictionary { internal OrdinalStringFrozenDictionary_RightJustifiedSubstring( string[] keys, @@ -21,7 +21,6 @@ internal OrdinalStringFrozenDictionary_RightJustifiedSubstring( // See comment in OrdinalStringFrozenDictionary for why these overrides exist. Do not remove. private protected override ref readonly TValue GetValueRefOrNullRefCore(string key) => ref base.GetValueRefOrNullRefCore(key); - private protected override ref readonly TValue GetValueRefOrNullRefCore(TAlternateKey key) => ref base.GetValueRefOrNullRefCore(key); private protected override bool Equals(string? x, string? y) => string.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => x.SequenceEqual(y.AsSpan()); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet.AlternateLookup.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet.AlternateLookup.cs index e050dd3a24954a..a17826a8f2a0f6 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet.AlternateLookup.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet.AlternateLookup.cs @@ -8,19 +8,31 @@ namespace System.Collections.Frozen { internal abstract partial class OrdinalStringFrozenSet { + /// + /// Invokes + /// on instances known to be of type . + /// + private static readonly AlternateLookupDelegate> s_alternateLookup = (set, key) + => ((OrdinalStringFrozenSet)set).FindItemIndexAlternate(key); + + /// + private protected override AlternateLookupDelegate GetAlternateLookupDelegate() + { + Debug.Assert(typeof(TAlternateKey) == typeof(ReadOnlySpan)); + return (AlternateLookupDelegate)(object)s_alternateLookup; + } + // We want to avoid having to implement FindItemIndex for each of the multiple types // that derive from this one, but each of those needs to supply its own notion of Equals/GetHashCode. // To avoid lots of virtual calls, we have every derived type override FindItemIndex and // call to that span-based method that's aggressively inlined. That then exposes the implementation // to the sealed Equals/GetHashCodes on each derived type, allowing them to be devirtualized and inlined // into each unique copy of the code. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private protected override int FindItemIndex(TAlternate alternate) + private protected virtual int FindItemIndexAlternate(ReadOnlySpan item) { - Debug.Assert(typeof(TAlternate) == typeof(ReadOnlySpan)); - ReadOnlySpan item = Unsafe.As>(ref alternate); - if ((uint)(item.Length - _minimumLength) <= (uint)_maximumLengthDiff) { if (CheckLengthQuick((uint)item.Length)) @@ -43,4 +55,61 @@ private protected override int FindItemIndex(TAlternate alternate) return -1; } } + + // See comment above for why these overrides exist. Do not remove. + + internal sealed partial class OrdinalStringFrozenSet_Full + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_FullCaseInsensitive + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_FullCaseInsensitiveAscii + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedSingleChar + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedSingleChar + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } + + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedSubstring + { + private protected override int FindItemIndexAlternate(ReadOnlySpan item) => base.FindItemIndexAlternate(item); + } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_Full.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_Full.cs index 6a081ab6c3c96b..b2321888cf6751 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_Full.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_Full.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_Full : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_Full : OrdinalStringFrozenSet { private readonly ulong _lengthFilter; @@ -22,7 +22,6 @@ internal OrdinalStringFrozenSet_Full( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override int GetHashCode(string s) => Hashing.GetHashCodeOrdinal(s.AsSpan()); private protected override int GetHashCode(ReadOnlySpan s) => Hashing.GetHashCodeOrdinal(s); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitive.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitive.cs index 1989a0a17b9459..56d73a724547a4 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitive.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitive.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_FullCaseInsensitive : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_FullCaseInsensitive : OrdinalStringFrozenSet { private readonly ulong _lengthFilter; @@ -22,7 +22,6 @@ internal OrdinalStringFrozenSet_FullCaseInsensitive( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitiveAscii.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitiveAscii.cs index 66a7012620bc03..9d174d939946a7 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitiveAscii.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_FullCaseInsensitiveAscii.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_FullCaseInsensitiveAscii : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_FullCaseInsensitiveAscii : OrdinalStringFrozenSet { private readonly ulong _lengthFilter; @@ -22,7 +22,6 @@ internal OrdinalStringFrozenSet_FullCaseInsensitiveAscii( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring.cs index 20593bc6d50e46..bf2bf7767d79ef 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveAsciiSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring.cs index fad2f977871f1d..62ef2c377cf229 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_LeftJustifiedCaseInsensitiveSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSingleChar.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSingleChar.cs index 29fc87848f89d0..6c215b181ee457 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSingleChar.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSingleChar.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_LeftJustifiedSingleChar : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedSingleChar : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_LeftJustifiedSingleChar( string[] entries, @@ -19,7 +19,6 @@ internal OrdinalStringFrozenSet_LeftJustifiedSingleChar( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override int GetHashCode(string s) => s[HashIndex]; private protected override int GetHashCode(ReadOnlySpan s) => s[HashIndex]; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSubstring.cs index 0c6a8ce1b769b6..8721651a680c24 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_LeftJustifiedSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_LeftJustifiedSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_LeftJustifiedSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_LeftJustifiedSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_LeftJustifiedSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override int GetHashCode(string s) => Hashing.GetHashCodeOrdinal(s.AsSpan(HashIndex, HashCount)); private protected override int GetHashCode(ReadOnlySpan s) => Hashing.GetHashCodeOrdinal(s.Slice(HashIndex, HashCount)); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring.cs index 730185c8949e54..2c6647655222b7 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveAsciiSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring.cs index 53b01d93cd79fe..22c4334b6db5dd 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_RightJustifiedCaseInsensitiveSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override bool Equals(string? x, string? y) => StringComparer.OrdinalIgnoreCase.Equals(x, y); private protected override bool Equals(ReadOnlySpan x, string? y) => EqualsOrdinalIgnoreCase(x, y); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSingleChar.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSingleChar.cs index 9bfcdf9213837e..ed77949f348c5c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSingleChar.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSingleChar.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_RightJustifiedSingleChar : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedSingleChar : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_RightJustifiedSingleChar( string[] entries, @@ -19,7 +19,6 @@ internal OrdinalStringFrozenSet_RightJustifiedSingleChar( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override int GetHashCode(string s) => s[s.Length + HashIndex]; private protected override int GetHashCode(ReadOnlySpan s) => s[s.Length + HashIndex]; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSubstring.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSubstring.cs index 6d2f656b0b2f6b..ae1461e9f20691 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSubstring.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet_RightJustifiedSubstring.cs @@ -5,7 +5,7 @@ namespace System.Collections.Frozen { - internal sealed class OrdinalStringFrozenSet_RightJustifiedSubstring : OrdinalStringFrozenSet + internal sealed partial class OrdinalStringFrozenSet_RightJustifiedSubstring : OrdinalStringFrozenSet { internal OrdinalStringFrozenSet_RightJustifiedSubstring( string[] entries, @@ -20,7 +20,6 @@ internal OrdinalStringFrozenSet_RightJustifiedSubstring( // See comment in OrdinalStringFrozenSet for why these overrides exist. Do not remove. private protected override int FindItemIndex(string item) => base.FindItemIndex(item); - private protected override int FindItemIndex(TAlternate item) => base.FindItemIndex(item); private protected override int GetHashCode(string s) => Hashing.GetHashCodeOrdinal(s.AsSpan(s.Length + HashIndex, HashCount)); private protected override int GetHashCode(ReadOnlySpan s) => Hashing.GetHashCodeOrdinal(s.Slice(s.Length + HashIndex, HashCount));