Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
45 changed files
with
5,864 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -333,3 +333,4 @@ ASALocalRun/ | |
Publish/* | ||
!Publish/README.md | ||
*.snk | ||
Source/Metadata/Optimization.proj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
using Gapotchenko.FX.Runtime.InteropServices; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace Gapotchenko.FX | ||
{ | ||
partial class ArrayEqualityComparer | ||
{ | ||
internal sealed class ByteRank1Comparer : EqualityComparer<byte[]> | ||
{ | ||
public override bool Equals(byte[] x, byte[] y) | ||
{ | ||
if (x == y) | ||
return true; | ||
|
||
if (x == null || y == null) | ||
return false; | ||
|
||
if (x.Length != y.Length) | ||
return false; | ||
|
||
if (CodeSafetyConfiguration.UnsafeCodeRecommended) | ||
return _EqualsUnsafeCore(x, y); | ||
else | ||
return _EqualsSafeCore(x, y); | ||
} | ||
|
||
static bool _EqualsSafeCore(byte[] x, byte[] y) | ||
{ | ||
for (int i = 0; i < x.Length; i++) | ||
if (x[i] != y[i]) | ||
return false; | ||
return true; | ||
} | ||
|
||
static unsafe bool _EqualsUnsafeCore(byte[] x, byte[] y) | ||
{ | ||
var n = (uint)x.Length; | ||
if (n == 0) | ||
return true; | ||
|
||
fixed (byte* p1 = x, p2 = y) | ||
{ | ||
byte* ix1 = p1, ix2 = p2; | ||
|
||
int wordSize = IntPtr.Size; | ||
|
||
if (n < wordSize) | ||
{ | ||
for (uint i = 0; i < n; i++, ix1++, ix2++) | ||
{ | ||
if (*ix1 != *ix2) | ||
return false; | ||
} | ||
} | ||
else | ||
{ | ||
if (wordSize == 4) | ||
{ | ||
// 32-bit architecture. | ||
uint n4 = n >> 2; | ||
for (uint i = 0; i < n4; i++, ix1 += 4, ix2 += 4) | ||
{ | ||
if (*((uint*)ix1) != *((uint*)ix2)) | ||
return false; | ||
} | ||
} | ||
else | ||
{ | ||
// 64-bit architecture. | ||
uint n8 = n >> 3; | ||
for (uint i = 0; i < n8; i++, ix1 += 8, ix2 += 8) | ||
{ | ||
if (*((ulong*)ix1) != *((ulong*)ix2)) | ||
return false; | ||
} | ||
|
||
if ((n & 4) != 0) | ||
{ | ||
if (*((uint*)ix1) != *((uint*)ix2)) | ||
return false; | ||
ix1 += 4; | ||
ix2 += 4; | ||
} | ||
} | ||
|
||
if ((n & 2) != 0) | ||
{ | ||
if (*((ushort*)ix1) != *((ushort*)ix2)) | ||
return false; | ||
ix1 += 2; | ||
ix2 += 2; | ||
} | ||
|
||
if ((n & 1) != 0) | ||
{ | ||
if (*ix1 != *ix2) | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public override int GetHashCode(byte[] obj) | ||
{ | ||
if (obj == null) | ||
return 0; | ||
|
||
// FNV-1a | ||
// The fastest hash function for byte arrays with lowest collision rate so far (10/2014). | ||
// http://isthe.com/chongo/tech/comp/fnv/ | ||
|
||
if (CodeSafetyConfiguration.UnsafeCodeRecommended) | ||
return _GetHashCodeUnsafeCore(obj); | ||
else | ||
return _GetHashCodeSafeCore(obj); | ||
} | ||
|
||
static int _GetHashCodeSafeCore(byte[] obj) | ||
{ | ||
uint hash = 2166136261; | ||
foreach (var i in obj) | ||
hash = (hash ^ i) * 16777619; | ||
return (int)hash; | ||
} | ||
|
||
static unsafe int _GetHashCodeUnsafeCore(byte[] obj) | ||
{ | ||
uint hash = 2166136261; | ||
fixed (byte* buffer = obj) | ||
{ | ||
byte* p = buffer; | ||
byte* p_end = p + obj.Length; | ||
while (p < p_end) | ||
{ | ||
hash = (hash ^ *p) * 16777619; | ||
++p; | ||
} | ||
} | ||
return (int)hash; | ||
} | ||
|
||
public override bool Equals(object obj) => obj is ByteRank1Comparer; | ||
|
||
public override int GetHashCode() => GetType().Name.GetHashCode(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
using Gapotchenko.FX.Properties; | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
|
||
namespace Gapotchenko.FX | ||
{ | ||
/// <summary> | ||
/// Optimized and fast equality comparer for one-dimensional arrays. | ||
/// </summary> | ||
public static partial class ArrayEqualityComparer | ||
{ | ||
/// <summary> | ||
/// Determines whether the specified arrays are equal. | ||
/// </summary> | ||
/// <typeparam name="T">The array element type.</typeparam> | ||
/// <param name="x">The first array to compare.</param> | ||
/// <param name="y">The second array to compare.</param> | ||
/// <returns><c>true</c> if the specified arrays are equal; otherwise, <c>false</c>.</returns> | ||
public static bool Equals<T>(T[] x, T[] y) => ArrayEqualityComparer<T>.Default.Equals(x, y); | ||
|
||
/// <summary> | ||
/// Returns a hash code for the specified array. | ||
/// </summary> | ||
/// <typeparam name="T">The array element type.</typeparam> | ||
/// <param name="array">The array.</param> | ||
/// <returns>A hash code for the specified array.</returns> | ||
public static int GetHashCode<T>(T[] array) => ArrayEqualityComparer<T>.Default.GetHashCode(array); | ||
|
||
static bool _TypedEquals<T>(T[] x, object y) => Equals(x, y as T[]); | ||
|
||
/// <summary> | ||
/// Determines whether the specified arrays are equal. | ||
/// </summary> | ||
/// <remarks>This method overshadows <see cref="Object.Equals(object, object)"/> to avoid a comparison by reference pitfall.</remarks> | ||
/// <param name="x">The first array to compare.</param> | ||
/// <param name="y">The second array to compare.</param> | ||
/// <returns><c>true</c> if the specified arrays are equal; otherwise, <c>false</c>.</returns> | ||
public new static bool Equals(object x, object y) | ||
{ | ||
if (x is null && y is null) | ||
return true; | ||
|
||
var arrayX = x as Array; | ||
var arrayY = y as Array; | ||
|
||
if (arrayX == null && arrayY == null) | ||
throw new ArgumentException(Resources.InvalidArgumentsForComparison); | ||
|
||
if (arrayX == null || arrayY == null) | ||
return false; | ||
|
||
if (arrayX.Rank != 1 && arrayY.Rank != 1) | ||
throw new ArgumentException(Resources.InvalidArgumentsForComparison); | ||
|
||
if (arrayX.Rank != arrayY.Rank) | ||
return false; | ||
|
||
int n = arrayX.Length; | ||
if (arrayY.Length != n) | ||
return false; | ||
|
||
var elementType = arrayX.GetType().GetElementType(); | ||
if (arrayY.GetType().GetElementType() != elementType) | ||
return false; | ||
|
||
if (n == 0) | ||
return true; | ||
|
||
// Signed element types are covered by implicit cast to an array of corresponding unsigned element types. | ||
// | ||
// E.g. the following is possible: | ||
// short[] x = new { 1, 2, 3 }; | ||
// var arrayX = (Array)x; | ||
// var tx = (ushort[])arrayX; // implicit cast | ||
|
||
switch (arrayX) | ||
{ | ||
case Boolean[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case Char[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case Byte[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case UInt16[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case UInt32[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case UInt64[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case Single[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case Double[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case Decimal[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case DateTime[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
case String[] tx: | ||
return _TypedEquals(tx, arrayY); | ||
} | ||
|
||
var elementEqualityComparer = EqualityComparer<object>.Default; | ||
for (int i = 0; i != n; ++i) | ||
if (!elementEqualityComparer.Equals(arrayX.GetValue(i), arrayY.GetValue(i))) | ||
return false; | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new equality comparer for one-dimensional array with a specified comparer for elements. | ||
/// </summary> | ||
/// <typeparam name="T">The type of array elements.</typeparam> | ||
/// <param name="elementEqualityComparer">The element equality comparer.</param> | ||
/// <returns>A new equality comparer for one-dimensional array with elements of type <typeparamref name="T"/>.</returns> | ||
public static IEqualityComparer<T[]> Create<T>(IEqualityComparer<T> elementEqualityComparer) | ||
{ | ||
var elementType = typeof(T); | ||
var elementTypeCode = Type.GetTypeCode(elementType); | ||
|
||
switch (elementTypeCode) | ||
{ | ||
case TypeCode.Byte when _IsDefaultEqualityComparer(elementEqualityComparer): | ||
return (IEqualityComparer<T[]>)(object)new ArrayEqualityComparer.ByteRank1Comparer(); | ||
} | ||
|
||
return new ArrayEqualityComparer<T>(elementEqualityComparer); | ||
} | ||
|
||
static bool _IsDefaultEqualityComparer<T>(IEqualityComparer<T> equalityComparer) => | ||
equalityComparer == null || | ||
equalityComparer == EqualityComparer<T>.Default; | ||
} | ||
} |
Oops, something went wrong.