Skip to content

Commit

Permalink
Added Gapotchenko.FX module.
Browse files Browse the repository at this point in the history
  • Loading branch information
hrumhurum committed Mar 1, 2019
1 parent 271007e commit 8523076
Show file tree
Hide file tree
Showing 45 changed files with 5,864 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -333,3 +333,4 @@ ASALocalRun/
Publish/*
!Publish/README.md
*.snk
Source/Metadata/Optimization.proj
12 changes: 12 additions & 0 deletions Source/Gapotchenko.FX.sln
Expand Up @@ -3,7 +3,19 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gapotchenko.FX", "Gapotchenko.FX\Gapotchenko.FX.csproj", "{3188853F-ECB8-4002-9273-23F4A9B41823}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3188853F-ECB8-4002-9273-23F4A9B41823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3188853F-ECB8-4002-9273-23F4A9B41823}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3188853F-ECB8-4002-9273-23F4A9B41823}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3188853F-ECB8-4002-9273-23F4A9B41823}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
Expand Down
152 changes: 152 additions & 0 deletions Source/Gapotchenko.FX/ArrayEqualityComparer.Byte.cs
@@ -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();
}
}
}
139 changes: 139 additions & 0 deletions Source/Gapotchenko.FX/ArrayEqualityComparer.cs
@@ -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;
}
}

0 comments on commit 8523076

Please sign in to comment.