|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
namespace System {
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
[Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>{
private static readonly StringComparer _invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);
private static readonly StringComparer _invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);
private static readonly StringComparer _ordinal = new OrdinalComparer(false);
private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true);
public static StringComparer InvariantCulture {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return _invariantCulture;
}
}
public static StringComparer InvariantCultureIgnoreCase {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return _invariantCultureIgnoreCase;
}
}
public static StringComparer CurrentCulture {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return new CultureAwareComparer(CultureInfo.CurrentCulture, false);
}
}
public static StringComparer CurrentCultureIgnoreCase {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return new CultureAwareComparer(CultureInfo.CurrentCulture, true);
}
}
public static StringComparer Ordinal {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return _ordinal;
}
}
public static StringComparer OrdinalIgnoreCase {
get {
Contract.Ensures(Contract.Result<StringComparer>() != null);
return _ordinalIgnoreCase;
}
}
public static StringComparer Create(CultureInfo culture, bool ignoreCase) {
if( culture == null) {
throw new ArgumentNullException("culture");
}
Contract.Ensures(Contract.Result<StringComparer>() != null);
Contract.EndContractBlock();
return new CultureAwareComparer(culture, ignoreCase);
}
public int Compare(object x, object y) {
if (x == y) return 0;
if (x == null) return -1;
if (y == null) return 1;
String sa = x as String;
if (sa != null) {
String sb = y as String;
if( sb != null) {
return Compare(sa, sb);
}
}
IComparable ia = x as IComparable;
if (ia != null) {
return ia.CompareTo(y);
}
throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
}
public new bool Equals(Object x, Object y) {
if (x == y) return true;
if (x == null || y == null) return false;
String sa = x as String;
if (sa != null) {
String sb = y as String;
if( sb != null) {
return Equals(sa, sb);
}
}
return x.Equals(y);
}
public int GetHashCode(object obj) {
if( obj == null) {
throw new ArgumentNullException("obj");
}
Contract.EndContractBlock();
string s = obj as string;
if( s != null) {
return GetHashCode(s);
}
return obj.GetHashCode();
}
public abstract int Compare(String x, String y);
public abstract bool Equals(String x, String y);
public abstract int GetHashCode(string obj);
}
[Serializable]
internal sealed class CultureAwareComparer : StringComparer
#if FEATURE_RANDOMIZED_STRING_HASHING
, IWellKnownStringEqualityComparer
#endif
{
private CompareInfo _compareInfo;
private bool _ignoreCase;
[OptionalField] private CompareOptions _options;
[NonSerialized] private bool _initializing;
internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) {
_compareInfo = culture.CompareInfo;
_ignoreCase = ignoreCase;
_options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
}
internal CultureAwareComparer(CompareInfo compareInfo, bool ignoreCase) {
_compareInfo = compareInfo;
_ignoreCase = ignoreCase;
_options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
}
internal CultureAwareComparer(CompareInfo compareInfo, CompareOptions options) {
_compareInfo = compareInfo;
_options = options;
// set the _ignoreCase flag to preserve compat in case this type is serialized on a
// newer version of the framework and deserialized on an older version of the framework
_ignoreCase = ((options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase ||
(options & CompareOptions.OrdinalIgnoreCase ) == CompareOptions.OrdinalIgnoreCase);
}
public override int Compare(string x, string y) {
EnsureInitialization();
if (Object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
return _compareInfo.Compare(x, y, _options);
}
public override bool Equals(string x, string y) {
EnsureInitialization();
if (Object.ReferenceEquals(x ,y)) return true;
if (x == null || y == null) return false;
return (_compareInfo.Compare(x, y, _options) == 0);
}
public override int GetHashCode(string obj) {
EnsureInitialization();
if( obj == null) {
throw new ArgumentNullException("obj");
}
Contract.EndContractBlock();
return _compareInfo.GetHashCodeOfString(obj, _options);
}
// Equals method for the comparer itself.
public override bool Equals(Object obj){
EnsureInitialization();
CultureAwareComparer comparer = obj as CultureAwareComparer;
if( comparer == null) {
return false;
}
return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo)
&& this._options == comparer._options);
}
public override int GetHashCode() {
EnsureInitialization();
int hashCode = _compareInfo.GetHashCode() ;
return _ignoreCase ? (~hashCode) : hashCode;
}
// During the deserialization we want to ensure the _options field is initialized as we can deserialize from old object which
// didn't include the _options field. We use OnDeserialized method to ensure this initialization.
// There are some classes (e.g. ConcurrentDictionary) which include CultureAwareComparer object and provide OnDeserialized method too
// and as the order of calling the OnDeserialized methods is not guaranteed, the OnDeserialized method of ConcurrentDictionary can be called
// before calling OnDeserialized method of CultureAwareComparer and in same time it uses the CultureAwareComparer object there which didn't
// initialize _options yet. To avoid this problem we implement OnDeserializing method and mark the object as not fully initialized there and then
// in every operation we perform on CultureAwareComparer, we'll check to ensure the initialization by the method EnsureInitialization()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureInitialization()
{
if (_initializing)
{
// fix up the _options value in case we are getting old serialized object not having _options
_options |= _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
_initializing = false;
}
}
[OnDeserializing]
private void OnDeserializing(StreamingContext ctx)
{
_initializing = true;
}
[OnDeserialized]
private void OnDeserialized(StreamingContext ctx)
{
EnsureInitialization();
}
#if FEATURE_RANDOMIZED_STRING_HASHING
IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
}
IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
return this;
}
#endif
}
#if FEATURE_RANDOMIZED_STRING_HASHING
internal sealed class CultureAwareRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
private CompareInfo _compareInfo;
private bool _ignoreCase;
private long _entropy;
internal CultureAwareRandomizedComparer(CompareInfo compareInfo, bool ignoreCase) {
_compareInfo = compareInfo;
_ignoreCase = ignoreCase;
_entropy = HashHelpers.GetEntropy();
}
public override int Compare(string x, string y) {
if (Object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
return _compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None);
}
public override bool Equals(string x, string y) {
if (Object.ReferenceEquals(x ,y)) return true;
if (x == null || y == null) return false;
return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None) == 0);
}
public override int GetHashCode(string obj) {
if( obj == null) {
throw new ArgumentNullException("obj");
}
Contract.EndContractBlock();
CompareOptions options = CompareOptions.None;
if( _ignoreCase) {
options |= CompareOptions.IgnoreCase;
}
return _compareInfo.GetHashCodeOfString(obj, options, true, _entropy);
}
// Equals method for the comparer itself.
public override bool Equals(Object obj){
CultureAwareRandomizedComparer comparer = obj as CultureAwareRandomizedComparer;
if( comparer == null) {
return false;
}
return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo)) && (this._entropy == comparer._entropy);
}
public override int GetHashCode() {
int hashCode = _compareInfo.GetHashCode() ;
return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
}
IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
}
// We want to serialize the old comparer.
IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
return new CultureAwareComparer(_compareInfo, _ignoreCase);
}
}
#endif
// Provide x more optimal implementation of ordinal comparison.
[Serializable]
internal sealed class OrdinalComparer : StringComparer
#if FEATURE_RANDOMIZED_STRING_HASHING
, IWellKnownStringEqualityComparer
#endif
{
private bool _ignoreCase;
internal OrdinalComparer(bool ignoreCase) {
_ignoreCase = ignoreCase;
}
public override int Compare(string x, string y) {
if (Object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
if( _ignoreCase) {
return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
return String.CompareOrdinal(x, y);
}
public override bool Equals(string x, string y) {
if (Object.ReferenceEquals(x ,y)) return true;
if (x == null || y == null) return false;
if( _ignoreCase) {
if( x.Length != y.Length) {
return false;
}
return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
}
return x.Equals(y);
}
public override int GetHashCode(string obj) {
if( obj == null) {
throw new ArgumentNullException("obj");
}
Contract.EndContractBlock();
if( _ignoreCase) {
return TextInfo.GetHashCodeOrdinalIgnoreCase(obj);
}
return obj.GetHashCode();
}
// Equals method for the comparer itself.
public override bool Equals(Object obj){
OrdinalComparer comparer = obj as OrdinalComparer;
if( comparer == null) {
return false;
}
return (this._ignoreCase == comparer._ignoreCase);
}
public override int GetHashCode() {
string name = "OrdinalComparer";
int hashCode = name.GetHashCode();
return _ignoreCase ? (~hashCode) : hashCode;
}
#if FEATURE_RANDOMIZED_STRING_HASHING
IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
return new OrdinalRandomizedComparer(_ignoreCase);
}
IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
return this;
}
#endif
}
#if FEATURE_RANDOMIZED_STRING_HASHING
internal sealed class OrdinalRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
private bool _ignoreCase;
private long _entropy;
internal OrdinalRandomizedComparer(bool ignoreCase) {
_ignoreCase = ignoreCase;
_entropy = HashHelpers.GetEntropy();
}
public override int Compare(string x, string y) {
if (Object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
if( _ignoreCase) {
return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
return String.CompareOrdinal(x, y);
}
public override bool Equals(string x, string y) {
if (Object.ReferenceEquals(x ,y)) return true;
if (x == null || y == null) return false;
if( _ignoreCase) {
if( x.Length != y.Length) {
return false;
}
return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
}
return x.Equals(y);
}
[System.Security.SecuritySafeCritical]
public override int GetHashCode(string obj) {
if( obj == null) {
throw new ArgumentNullException("obj");
}
Contract.EndContractBlock();
if( _ignoreCase) {
return TextInfo.GetHashCodeOrdinalIgnoreCase(obj, true, _entropy);
}
return String.InternalMarvin32HashString(obj, obj.Length, _entropy);
}
// Equals method for the comparer itself.
public override bool Equals(Object obj){
OrdinalRandomizedComparer comparer = obj as OrdinalRandomizedComparer;
if( comparer == null) {
return false;
}
return (this._ignoreCase == comparer._ignoreCase) && (this._entropy == comparer._entropy);
}
public override int GetHashCode() {
string name = "OrdinalRandomizedComparer";
int hashCode = name.GetHashCode();
return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
}
IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
return new OrdinalRandomizedComparer(_ignoreCase);
}
// We want to serialize the old comparer.
IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
return new OrdinalComparer(_ignoreCase);
}
}
// This interface is implemented by string comparers in the framework that can opt into
// randomized hashing behaviors.
internal interface IWellKnownStringEqualityComparer {
// Get an IEqualityComparer that has the same equality comparision rules as "this" but uses Randomized Hashing.
IEqualityComparer GetRandomizedEqualityComparer();
// Get an IEqaulityComparer that can be serailzied (e.g., it exists in older versions).
IEqualityComparer GetEqualityComparerForSerialization();
}
#endif
}
|