// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Text;
namespace dnlib.DotNet {
///
/// Compares byte arrays
///
sealed class ByteArrayEqualityComparer : IEqualityComparer {
///
/// Default instance
///
public static readonly ByteArrayEqualityComparer Instance = new ByteArrayEqualityComparer();
///
public bool Equals(byte[] x, byte[] y) => Utils.Equals(x, y);
///
public int GetHashCode(byte[] obj) => Utils.GetHashCode(obj);
}
static class Utils {
///
/// Convert a byte[] to a
///
/// All bytes
/// true if output should be in upper case hex
/// as a hex string
internal static string ToHex(byte[] bytes, bool upper) {
if (bytes is null)
return "";
var chars = new char[bytes.Length * 2];
for (int i = 0, j = 0; i < bytes.Length; i++) {
byte b = bytes[i];
chars[j++] = ToHexChar(b >> 4, upper);
chars[j++] = ToHexChar(b & 0x0F, upper);
}
return new string(chars);
}
static char ToHexChar(int val, bool upper) {
if (0 <= val && val <= 9)
return (char)(val + (int)'0');
return (char)(val - 10 + (upper ? (int)'A' : (int)'a'));
}
///
/// Converts a hex string to a byte[]
///
/// A string with an even number of hex characters
/// converted to a byte[] or null
/// if is invalid
internal static byte[] ParseBytes(string hexString) {
try {
if (hexString.Length % 2 != 0)
return null;
var bytes = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2) {
int upper = TryParseHexChar(hexString[i]);
int lower = TryParseHexChar(hexString[i + 1]);
if (upper < 0 || lower < 0)
return null;
bytes[i / 2] = (byte)((upper << 4) | lower);
}
return bytes;
}
catch {
return null;
}
}
///
/// Converts a character to a hex digit
///
/// Hex character
/// 0x00-0x0F if successful, -1 if is not
/// a valid hex digit
static int TryParseHexChar(char c) {
if ('0' <= c && c <= '9')
return (ushort)c - (ushort)'0';
if ('a' <= c && c <= 'f')
return 10 + (ushort)c - (ushort)'a';
if ('A' <= c && c <= 'F')
return 10 + (ushort)c - (ushort)'A';
return -1;
}
///
/// Compares two byte arrays
///
/// Byte array #1
/// Byte array #2
/// < 0 if a < b, 0 if a == b, > 0 if a > b
internal static int CompareTo(byte[] a, byte[] b) {
if (a == b)
return 0;
if (a is null)
return -1;
if (b is null)
return 1;
int count = Math.Min(a.Length, b.Length);
for (int i = 0; i < count; i++) {
var ai = a[i];
var bi = b[i];
if (ai < bi)
return -1;
if (ai > bi)
return 1;
}
return a.Length.CompareTo(b.Length);
}
///
/// Checks whether two byte arrays are equal
///
/// First
/// Second
/// true if same, false otherwise
internal static bool Equals(byte[] a, byte[] b) {
if (a == b)
return true;
if (a is null || b is null)
return false;
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; i++) {
if (a[i] != b[i])
return false;
}
return true;
}
///
/// Gets the hash code of a byte array
///
/// Byte array
/// The hash code
internal static int GetHashCode(byte[] a) {
if (a is null || a.Length == 0)
return 0;
int count = Math.Min(a.Length / 2, 20);
if (count == 0)
count = 1;
uint hash = 0;
for (int i = 0, j = a.Length - 1; i < count; i++, j--) {
hash ^= a[i] | ((uint)a[j] << 8);
hash = (hash << 13) | (hash >> 19);
}
return (int)hash;
}
///
/// Compares two versions
///
/// This differs from if the build
/// and/or revision numbers haven't been initialized or if one of the args is null.
///
/// Version #1 or null to be treated as v0.0.0.0
/// Version #2 or null to be treated as v0.0.0.0
/// < 0 if a < b, 0 if a == b, > 0 if a > b
internal static int CompareTo(Version a, Version b) {
if (a is null)
a = new Version();
if (b is null)
b = new Version();
if (a.Major != b.Major)
return a.Major.CompareTo(b.Major);
if (a.Minor != b.Minor)
return a.Minor.CompareTo(b.Minor);
if (GetDefaultVersionValue(a.Build) != GetDefaultVersionValue(b.Build))
return GetDefaultVersionValue(a.Build).CompareTo(GetDefaultVersionValue(b.Build));
return GetDefaultVersionValue(a.Revision).CompareTo(GetDefaultVersionValue(b.Revision));
}
///
/// Checks whether two versions are the same
///
/// This differs from if the build
/// and/or revision numbers haven't been initialized or if one of the args is null.
///
/// Version #1 or null to be treated as v0.0.0.0
/// Version #2 or null to be treated as v0.0.0.0
/// true if same, false otherwise
internal static bool Equals(Version a, Version b) => CompareTo(a, b) == 0;
///
/// Creates a new instance with no undefined version values (eg.
/// the build and revision values won't be -1).
///
/// A instance
/// A new instance
internal static Version CreateVersionWithNoUndefinedValues(Version a) {
if (a is null)
return new Version(0, 0, 0, 0);
return new Version(a.Major, a.Minor, GetDefaultVersionValue(a.Build), GetDefaultVersionValue(a.Revision));
}
static int GetDefaultVersionValue(int val) => val == -1 ? 0 : val;
///
/// Parses a version string
///
/// Version string
/// A new or null if
/// is an invalid version
internal static Version ParseVersion(string versionString) {
try {
return Utils.CreateVersionWithNoUndefinedValues(new Version(versionString));
}
catch {
return null;
}
}
///
/// Compares two locales (cultures)
///
/// First
/// Second
/// < 0 if a < b, 0 if a == b, > 0 if a > b
internal static int LocaleCompareTo(UTF8String a, UTF8String b) => GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b));
///
/// Compares two locales (cultures)
///
/// First
/// Second
/// true if same, false otherwise
internal static bool LocaleEquals(UTF8String a, UTF8String b) => LocaleCompareTo(a, b) == 0;
///
/// Compares two locales (cultures)
///
/// First
/// Second
/// < 0 if a < b, 0 if a == b, > 0 if a > b
internal static int LocaleCompareTo(UTF8String a, string b) => GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b));
///
/// Compares two locales (cultures)
///
/// First
/// Second
/// true if same, false otherwise
internal static bool LocaleEquals(UTF8String a, string b) => LocaleCompareTo(a, b) == 0;
///
/// Gets the hash code of a locale
///
/// Value
/// The hash code
internal static int GetHashCodeLocale(UTF8String a) => GetCanonicalLocale(a).GetHashCode();
static string GetCanonicalLocale(UTF8String locale) => GetCanonicalLocale(UTF8String.ToSystemStringOrEmpty(locale));
static string GetCanonicalLocale(string locale) {
var s = locale.ToUpperInvariant();
if (s == "NEUTRAL")
s = string.Empty;
return s;
}
///
/// Align up
///
/// Value
/// Alignment
public static uint AlignUp(uint v, uint alignment) => (v + alignment - 1) & ~(alignment - 1);
///
/// Align up
///
/// Value
/// Alignment
public static int AlignUp(int v, uint alignment) => (int)AlignUp((uint)v, alignment);
///
/// Rounds up the provided number to the next power of 2
///
/// The number to round
public static uint RoundToNextPowerOfTwo(uint num) {
num--;
num |= num >> 1;
num |= num >> 2;
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
return num + 1;
}
}
}