// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
namespace dnlib.DotNet {
///
/// Flags used by
///
[Flags]
public enum AssemblyNameComparerFlags {
///
/// Compare assembly simple name
///
Name = 1,
///
/// Compare assembly version
///
Version = 2,
///
/// Compare assembly public key token
///
PublicKeyToken = 4,
///
/// Compare assembly culture
///
Culture = 8,
///
/// Compare content type
///
ContentType = 0x10,
///
/// Compare assembly simple name, version, public key token, locale and content type
///
All = Name | Version | PublicKeyToken | Culture | ContentType,
}
///
/// Compares two assembly names
///
public readonly struct AssemblyNameComparer : IEqualityComparer {
///
/// Compares the name, version, public key token, culture and content type
///
public static readonly AssemblyNameComparer CompareAll = new AssemblyNameComparer(AssemblyNameComparerFlags.All);
///
/// Compares only the name and the public key token
///
public static readonly AssemblyNameComparer NameAndPublicKeyTokenOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name | AssemblyNameComparerFlags.PublicKeyToken);
///
/// Compares only the name
///
public static readonly AssemblyNameComparer NameOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name);
readonly AssemblyNameComparerFlags flags;
///
/// Gets the bit
///
public bool CompareName => (flags & AssemblyNameComparerFlags.Name) != 0;
///
/// Gets the bit
///
public bool CompareVersion => (flags & AssemblyNameComparerFlags.Version) != 0;
///
/// Gets the bit
///
public bool ComparePublicKeyToken => (flags & AssemblyNameComparerFlags.PublicKeyToken) != 0;
///
/// Gets the bit
///
public bool CompareCulture => (flags & AssemblyNameComparerFlags.Culture) != 0;
///
/// Gets the bit
///
public bool CompareContentType => (flags & AssemblyNameComparerFlags.ContentType) != 0;
///
/// Constructor
///
/// Comparison flags
public AssemblyNameComparer(AssemblyNameComparerFlags flags) => this.flags = flags;
///
/// Compares two assembly names
///
/// First
/// Second
/// < 0 if a < b, 0 if a == b, > 0 if a > b
public int CompareTo(IAssembly a, IAssembly b) {
if (a == b)
return 0;
if (a is null)
return -1;
if (b is null)
return 1;
int v;
if (CompareName && (v = UTF8String.CaseInsensitiveCompareTo(a.Name, b.Name)) != 0)
return v;
if (CompareVersion && (v = Utils.CompareTo(a.Version, b.Version)) != 0)
return v;
if (ComparePublicKeyToken && (v = PublicKeyBase.TokenCompareTo(a.PublicKeyOrToken, b.PublicKeyOrToken)) != 0)
return v;
if (CompareCulture && (v = Utils.LocaleCompareTo(a.Culture, b.Culture)) != 0)
return v;
if (CompareContentType && (v = a.ContentType.CompareTo(b.ContentType)) != 0)
return v;
return 0;
}
///
/// Compares two assembly names
///
/// First
/// Second
/// true if equal, false otherwise
public bool Equals(IAssembly a, IAssembly b) => CompareTo(a, b) == 0;
///
/// Figures out which of two assembly names is closer to another assembly name
///
/// Requested assembly name
/// First
/// Second
/// -1 if both are equally close, 0 if is closest, 1 if
/// is closest
public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) {
if (a == b)
return 0;
if (a is null)
return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b.Name) ? 1 : 0;
if (b is null)
return !CompareName ? 0 : UTF8String.CaseInsensitiveEquals(requested.Name, a.Name) ? 0 : 1;
// Compare the most important parts first:
// 1. Assembly simple name
// 2. Public key token
// 3. Version
// 4. Locale
// 5. Content type
if (CompareName) {
// If the name only matches one of a or b, return that one.
bool na = UTF8String.CaseInsensitiveEquals(requested.Name, a.Name);
bool nb = UTF8String.CaseInsensitiveEquals(requested.Name, b.Name);
if (na && !nb)
return 0;
if (!na && nb)
return 1;
if (!na && !nb)
return -1;
}
if (ComparePublicKeyToken) {
bool pa, pb;
if (PublicKeyBase.IsNullOrEmpty2(requested.PublicKeyOrToken)) {
// If one of them has a pkt but the other one hasn't, return the one with
// no pkt.
pa = PublicKeyBase.IsNullOrEmpty2(a.PublicKeyOrToken);
pb = PublicKeyBase.IsNullOrEmpty2(b.PublicKeyOrToken);
}
else {
// If one of them has the correct pkt, but the other one has an incorrect
// pkt, return the one with the correct pkt.
pa = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, a.PublicKeyOrToken);
pb = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, b.PublicKeyOrToken);
}
if (pa && !pb)
return 0;
if (!pa && pb)
return 1;
}
if (CompareVersion && !Utils.Equals(a.Version, b.Version)) {
var rv = Utils.CreateVersionWithNoUndefinedValues(requested.Version);
if (rv == new Version(0, 0, 0, 0))
rv = new Version(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue);
int va = Utils.CompareTo(a.Version, rv);
int vb = Utils.CompareTo(b.Version, rv);
if (va == 0)
return 0; // vb != 0 so return 0
if (vb == 0)
return 1; // va != 0 so return 1
if (va > 0 && vb < 0)
return 0;
if (va < 0 && vb > 0)
return 1;
// Now either both a and b's version > req version or both are < req version
if (va > 0) {
// a.Version and b.Version > req.Version. Pick the one that is closest.
return Utils.CompareTo(a.Version, b.Version) < 0 ? 0 : 1;
}
else {
// a.Version and b.Version < req.Version. Pick the one that is closest.
return Utils.CompareTo(a.Version, b.Version) > 0 ? 0 : 1;
}
}
if (CompareCulture) {
bool la = Utils.LocaleEquals(requested.Culture, a.Culture);
bool lb = Utils.LocaleEquals(requested.Culture, b.Culture);
if (la && !lb)
return 0;
if (!la && lb)
return 1;
}
if (CompareContentType) {
bool ca = requested.ContentType == a.ContentType;
bool cb = requested.ContentType == b.ContentType;
if (ca && !cb)
return 0;
if (!ca && cb)
return 1;
}
return -1;
}
///
/// Gets the hash code of an assembly name
///
/// Assembly name
/// The hash code
public int GetHashCode(IAssembly a) {
if (a is null)
return 0;
int hash = 0;
if (CompareName)
hash += UTF8String.GetHashCode(a.Name);
if (CompareVersion)
hash += Utils.CreateVersionWithNoUndefinedValues(a.Version).GetHashCode();
if (ComparePublicKeyToken)
hash += PublicKeyBase.GetHashCodeToken(a.PublicKeyOrToken);
if (CompareCulture)
hash += Utils.GetHashCodeLocale(a.Culture);
if (CompareContentType)
hash += (int)a.ContentType;
return hash;
}
}
}