diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
commit | a56e30c8d33048216567753d9d3fefc2152af8ac (patch) | |
tree | 7e5d979695fc4a431740982eb1cfecc2898b23a5 /src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs | |
parent | 4b11dc566a5bbfa1378d6266525c281b028abcc8 (diff) | |
download | coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.gz coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.bz2 coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.zip |
Imported Upstream version 2.0.0.11353upstream/2.0.0.11353
Diffstat (limited to 'src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs')
-rw-r--r-- | src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs b/src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs new file mode 100644 index 0000000000..3e428413d4 --- /dev/null +++ b/src/mscorlib/src/System/Collections/Generic/ComparerHelpers.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using static System.RuntimeTypeHandle; + +namespace System.Collections.Generic +{ + /// <summary> + /// Helper class for creating the default <see cref="Comparer{T}"/> and <see cref="EqualityComparer{T}"/>. + /// </summary> + /// <remarks> + /// This class is intentionally type-unsafe and non-generic to minimize the generic instantiation overhead of creating + /// the default comparer/equality comparer for a new type parameter. Efficiency of the methods in here does not matter too + /// much since they will only be run once per type parameter, but generic code involved in creating the comparers needs to be + /// kept to a minimum. + /// </remarks> + internal static class ComparerHelpers + { + /// <summary> + /// Creates the default <see cref="Comparer{T}"/>. + /// </summary> + /// <param name="type">The type to create the default comparer for.</param> + /// <remarks> + /// The logic in this method is replicated in vm/compile.cpp to ensure that NGen saves the right instantiations. + /// </remarks> + internal static object CreateDefaultComparer(Type type) + { + Debug.Assert(type != null && type is RuntimeType); + + object result = null; + var runtimeType = (RuntimeType)type; + + // If T implements IComparable<T> return a GenericComparer<T> + if (typeof(IComparable<>).MakeGenericType(type).IsAssignableFrom(type)) + { + result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), runtimeType); + } + // Nullable does not implement IComparable<T?> directly because that would add an extra interface call per comparison. + // Instead, it relies on Comparer<T?>.Default to specialize for nullables and do the lifted comparisons if T implements IComparable. + else if (type.IsGenericType) + { + if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + result = TryCreateNullableComparer(runtimeType); + } + } + // The comparer for enums is specialized to avoid boxing. + else if (type.IsEnum) + { + result = TryCreateEnumComparer(runtimeType); + } + + return result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectComparer<object>), runtimeType); + } + + /// <summary> + /// Creates the default <see cref="Comparer{T}"/> for a nullable type. + /// </summary> + /// <param name="nullableType">The nullable type to create the default comparer for.</param> + private static object TryCreateNullableComparer(RuntimeType nullableType) + { + Debug.Assert(nullableType != null); + Debug.Assert(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>)); + + var embeddedType = (RuntimeType)nullableType.GetGenericArguments()[0]; + + if (typeof(IComparable<>).MakeGenericType(embeddedType).IsAssignableFrom(embeddedType)) + { + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), embeddedType); + } + + return null; + } + + /// <summary> + /// Creates the default <see cref="Comparer{T}"/> for an enum type. + /// </summary> + /// <param name="enumType">The enum type to create the default comparer for.</param> + private static object TryCreateEnumComparer(RuntimeType enumType) + { + Debug.Assert(enumType != null); + Debug.Assert(enumType.IsEnum); + + // Explicitly call Enum.GetUnderlyingType here. Although GetTypeCode + // ends up doing this anyway, we end up avoiding an unnecessary P/Invoke + // and virtual method call. + TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(enumType)); + + // Depending on the enum type, we need to special case the comparers so that we avoid boxing. + // Specialize differently for signed/unsigned types so we avoid problems with large numbers. + switch (underlyingTypeCode) + { + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(Int32EnumComparer<int>), enumType); + case TypeCode.Byte: + case TypeCode.UInt16: + case TypeCode.UInt32: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(UInt32EnumComparer<uint>), enumType); + // 64-bit enums: Use `UnsafeEnumCastLong` + case TypeCode.Int64: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(Int64EnumComparer<long>), enumType); + case TypeCode.UInt64: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(UInt64EnumComparer<ulong>), enumType); + } + + return null; + } + + /// <summary> + /// Creates the default <see cref="EqualityComparer{T}"/>. + /// </summary> + /// <param name="type">The type to create the default equality comparer for.</param> + /// <remarks> + /// The logic in this method is replicated in vm/compile.cpp to ensure that NGen saves the right instantiations. + /// </remarks> + internal static object CreateDefaultEqualityComparer(Type type) + { + Debug.Assert(type != null && type is RuntimeType); + + object result = null; + var runtimeType = (RuntimeType)type; + + // Specialize for byte so Array.IndexOf is faster. + if (type == typeof(byte)) + { + result = new ByteEqualityComparer(); + } + // If T implements IEquatable<T> return a GenericEqualityComparer<T> + else if (typeof(IEquatable<>).MakeGenericType(type).IsAssignableFrom(type)) + { + result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), runtimeType); + } + // Nullable does not implement IEquatable<T?> directly because that would add an extra interface call per comparison. + // Instead, it relies on EqualityComparer<T?>.Default to specialize for nullables and do the lifted comparisons if T implements IEquatable. + else if (type.IsGenericType) + { + if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + result = TryCreateNullableEqualityComparer(runtimeType); + } + } + // The equality comparer for enums is specialized to avoid boxing. + else if (type.IsEnum) + { + result = TryCreateEnumEqualityComparer(runtimeType); + } + + return result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectEqualityComparer<object>), runtimeType); + } + + /// <summary> + /// Creates the default <see cref="EqualityComparer{T}"/> for a nullable type. + /// </summary> + /// <param name="nullableType">The nullable type to create the default equality comparer for.</param> + private static object TryCreateNullableEqualityComparer(RuntimeType nullableType) + { + Debug.Assert(nullableType != null); + Debug.Assert(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>)); + + var embeddedType = (RuntimeType)nullableType.GetGenericArguments()[0]; + + if (typeof(IEquatable<>).MakeGenericType(embeddedType).IsAssignableFrom(embeddedType)) + { + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), embeddedType); + } + + return null; + } + + /// <summary> + /// Creates the default <see cref="EqualityComparer{T}"/> for an enum type. + /// </summary> + /// <param name="enumType">The enum type to create the default equality comparer for.</param> + private static object TryCreateEnumEqualityComparer(RuntimeType enumType) + { + Debug.Assert(enumType != null); + Debug.Assert(enumType.IsEnum); + + // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation + // for how we cast the enum types to integral values in the comparer without boxing. + + TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(enumType)); + + // Depending on the enum type, we need to special case the comparers so that we avoid boxing. + // Note: We have different comparers for short and sbyte, since for those types GetHashCode does not simply return the value. + // We need to preserve what they would return. + switch (underlyingTypeCode) + { + case TypeCode.Int16: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer<short>), enumType); + case TypeCode.SByte: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer<sbyte>), enumType); + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Byte: + case TypeCode.UInt16: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), enumType); + case TypeCode.Int64: + case TypeCode.UInt64: + return RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer<long>), enumType); + } + + return null; + } + } +} |