From 570bad0ff233b9be7d7aff3ff9b4480edf6c53a4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Sep 2019 19:27:45 +1000 Subject: [PATCH] Use shared source MathF and HashCode --- shared-infrastructure | 2 +- src/SixLabors.Core/HashCode.cs | 448 --------------------------------- src/SixLabors.Core/MathF.cs | 231 ----------------- 3 files changed, 1 insertion(+), 680 deletions(-) delete mode 100644 src/SixLabors.Core/HashCode.cs delete mode 100644 src/SixLabors.Core/MathF.cs diff --git a/shared-infrastructure b/shared-infrastructure index 9b5a5b7..faf84e4 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 9b5a5b70b46bc23b9d8d8645cd691d5bc5a2d84f +Subproject commit faf84e44ec90e8a42a7271bcd04fea76279efb08 diff --git a/src/SixLabors.Core/HashCode.cs b/src/SixLabors.Core/HashCode.cs deleted file mode 100644 index b93d4a9..0000000 --- a/src/SixLabors.Core/HashCode.cs +++ /dev/null @@ -1,448 +0,0 @@ -#pragma warning disable SA1636, SA1600, SA1503, SA1202, SA1101, SA1132, SA1309, SA1520, SA1108, SA1203, SA1028, SA1512, SA1308 - -// SOURCE: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/HashCode.cs - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/* -The xxHash32 implementation is based on the code published by Yann Collet: -https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c - xxHash - Fast Hash algorithm - Copyright (C) 2012-2016, Yann Collet - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - xxHash homepage: http://www.xxhash.com - - xxHash source repository : https://github.com/Cyan4973/xxHash -*/ - -#if SUPPORTS_HASHCODE -using System.Runtime.CompilerServices; - -[assembly: TypeForwardedTo(typeof(System.HashCode))] -#else -using System.Buffers.Binary; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; - -namespace System -{ - // xxHash32 is used for the hash code. - // https://github.com/Cyan4973/xxHash - internal struct HashCode - { -#pragma warning disable SA1311 // Static readonly fields should begin with upper-case letter - private static readonly uint s_seed = GenerateGlobalSeed(); -#pragma warning restore SA1311 // Static readonly fields should begin with upper-case letter - - private const uint Prime1 = 2654435761U; - private const uint Prime2 = 2246822519U; - private const uint Prime3 = 3266489917U; - private const uint Prime4 = 668265263U; - private const uint Prime5 = 374761393U; - - private uint _v1, _v2, _v3, _v4; - private uint _queue1, _queue2, _queue3; - private uint _length; - - private static uint GenerateGlobalSeed() - { - byte[] data = new byte[4]; - - using (var rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(data); - } - - return BinaryPrimitives.ReadUInt32LittleEndian(data); - } - - public static int Combine(T1 value1) - { - // Provide a way of diffusing bits from something with a limited - // input hash space. For example, many enums only have a few - // possible hashes, only using the bottom few bits of the code. Some - // collections are built on the assumption that hashes are spread - // over a larger space, so diffusing the bits may help the - // collection work more efficiently. - - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 4; - - hash = QueueRound(hash, hc1); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 8; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 12; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - hash = QueueRound(hash, hc3); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 16; - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 20; - - hash = QueueRound(hash, hc5); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 24; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - var hc7 = (uint)(value7?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 28; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - hash = QueueRound(hash, hc7); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - var hc7 = (uint)(value7?.GetHashCode() ?? 0); - var hc8 = (uint)(value8?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - v1 = Round(v1, hc5); - v2 = Round(v2, hc6); - v3 = Round(v3, hc7); - v4 = Round(v4, hc8); - - uint hash = MixState(v1, v2, v3, v4); - hash += 32; - - hash = MixFinal(hash); - return (int)hash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Rol(uint value, int count) - => (value << count) | (value >> (32 - count)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) - { - v1 = s_seed + Prime1 + Prime2; - v2 = s_seed + Prime2; - v3 = s_seed; - v4 = s_seed - Prime1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Round(uint hash, uint input) - { - hash += input * Prime2; - hash = Rol(hash, 13); - hash *= Prime1; - return hash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint QueueRound(uint hash, uint queuedValue) - { - hash += queuedValue * Prime3; - return Rol(hash, 17) * Prime4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixState(uint v1, uint v2, uint v3, uint v4) - { - return Rol(v1, 1) + Rol(v2, 7) + Rol(v3, 12) + Rol(v4, 18); - } - - private static uint MixEmptyState() - { - return s_seed + Prime5; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixFinal(uint hash) - { - hash ^= hash >> 15; - hash *= Prime2; - hash ^= hash >> 13; - hash *= Prime3; - hash ^= hash >> 16; - return hash; - } - - public void Add(T value) - { - Add(value?.GetHashCode() ?? 0); - } - - public void Add(T value, IEqualityComparer comparer) - { - Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0)); - } - - private void Add(int value) - { - // The original xxHash works as follows: - // 0. Initialize immediately. We can't do this in a struct (no - // default ctor). - // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. - // 2. Accumulate remaining blocks of length 4 (1 uint) into the - // hash. - // 3. Accumulate remaining blocks of length 1 into the hash. - - // There is no need for #3 as this type only accepts ints. _queue1, - // _queue2 and _queue3 are basically a buffer so that when - // ToHashCode is called we can execute #2 correctly. - - // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see - // #0) nd the last place that can be done if you look at the - // original code is just before the first block of 16 bytes is mixed - // in. The xxHash32 state is never used for streams containing fewer - // than 16 bytes. - - // To see what's really going on here, have a look at the Combine - // methods. - - var val = (uint)value; - - // Storing the value of _length locally shaves of quite a few bytes - // in the resulting machine code. - uint previousLength = _length++; - uint position = previousLength % 4; - - // Switch can't be inlined. - - if (position == 0) - _queue1 = val; - else if (position == 1) - _queue2 = val; - else if (position == 2) - _queue3 = val; - else // position == 3 - { - if (previousLength == 3) - Initialize(out _v1, out _v2, out _v3, out _v4); - - _v1 = Round(_v1, _queue1); - _v2 = Round(_v2, _queue2); - _v3 = Round(_v3, _queue3); - _v4 = Round(_v4, val); - } - } - - public int ToHashCode() - { - // Storing the value of _length locally shaves of quite a few bytes - // in the resulting machine code. - uint length = _length; - - // position refers to the *next* queue position in this method, so - // position == 1 means that _queue1 is populated; _queue2 would have - // been populated on the next call to Add. - uint position = length % 4; - - // If the length is less than 4, _v1 to _v4 don't contain anything - // yet. xxHash32 treats this differently. - - uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); - - // _length is incremented once per Add(Int32) and is therefore 4 - // times too small (xxHash length is in bytes, not ints). - - hash += length * 4; - - // Mix what remains in the queue - - // Switch can't be inlined right now, so use as few branches as - // possible by manually excluding impossible scenarios (position > 1 - // is always false if position is not > 0). - if (position > 0) - { - hash = QueueRound(hash, _queue1); - if (position > 1) - { - hash = QueueRound(hash, _queue2); - if (position > 2) - hash = QueueRound(hash, _queue3); - } - } - - hash = MixFinal(hash); - return (int)hash; - } - -#pragma warning disable 0809 - - // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. - // Disallowing GetHashCode and Equals is by design - - // * We decided to not override GetHashCode() to produce the hash code - // as this would be weird, both naming-wise as well as from a - // behavioral standpoint (GetHashCode() should return the object's - // hash code, not the one being computed). - - // * Even though ToHashCode() can be called safely multiple times on - // this implementation, it is not part of the contract. If the - // implementation has to change in the future we don't want to worry - // about people who might have incorrectly used this type. - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => throw new NotSupportedException("Equality not supported"); - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => throw new NotSupportedException("Equality not supported"); -#pragma warning restore 0809 - } -} -#endif - -#pragma warning restore SA1636, SA1600, SA1503, SA1202, SA1101, SA1132, SA1309, SA1520, SA1108, SA1203, SA1028, SA1512, SA1308 \ No newline at end of file diff --git a/src/SixLabors.Core/MathF.cs b/src/SixLabors.Core/MathF.cs deleted file mode 100644 index d4a493a..0000000 --- a/src/SixLabors.Core/MathF.cs +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -#if SUPPORTS_MATHF -[assembly: TypeForwardedTo(typeof(System.MathF))] -#else -namespace System -{ - /// - /// Provides single-precision floating point constants and static methods for trigonometric, logarithmic, and other common mathematical functions. - /// - /// MathF emulation on platforms that don't support it natively. - // ReSharper disable InconsistentNaming - internal static class MathF - { - /// - /// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π. - /// - public const float PI = (float)Math.PI; - - /// - /// Returns the absolute value of a single-precision floating-point number. - /// - /// - /// A number that is greater than or equal to , but less than or equal to . - /// - /// - /// A single-precision floating-point number, x, such that 0 ≤ x ≤. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Abs(float f) - { - return Math.Abs(f); - } - - /// - /// Returns the angle whose tangent is the quotient of two specified numbers. - /// - /// The y coordinate of a point. - /// The x coordinate of a point. - /// - /// An angle, θ, measured in radians, such that -π≤θ≤π, and tan(θ) = y / x, where - /// (x, y) is a point in the Cartesian plane. Observe the following: For (x, y) in - /// quadrant 1, 0 < θ < π/2.For (x, y) in quadrant 2, π/2 < θ≤π.For (x, y) in quadrant - /// 3, -π < θ < -π/2.For (x, y) in quadrant 4, -π/2 < θ < 0.For points on the boundaries - /// of the quadrants, the return value is the following:If y is 0 and x is not negative, - /// θ = 0.If y is 0 and x is negative, θ = π.If y is positive and x is 0, θ = π/2.If - /// y is negative and x is 0, θ = -π/2.If y is 0 and x is 0, θ = 0. If x or y is - /// , or if x and y are either or - /// , the method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Atan2(float y, float x) - { - return (float)Math.Atan2(y, x); - } - - /// - /// Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number. - /// - /// A single-precision floating-point number. - /// - /// The smallest integral value that is greater than or equal to . - /// If is equal to , , - /// or , that value is returned. - /// Note that this method returns a instead of an integral type. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Ceiling(float f) - { - return (float)Math.Ceiling(f); - } - - /// - /// Returns the cosine of the specified angle. - /// - /// An angle, measured in radians. - /// - /// The cosine of . If is equal to , , - /// or , this method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Cos(float f) - { - return (float)Math.Cos(f); - } - - /// - /// Returns e raised to the specified power. - /// - /// A number specifying a power. - /// - /// The number e raised to the power . - /// If equals or , that value is returned. - /// If equals , 0 is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Exp(float f) - { - return (float)Math.Exp(f); - } - - /// - /// Returns the largest integer less than or equal to the specified single-precision floating-point number. - /// - /// A single-precision floating-point number. - /// - /// The largest integer less than or equal to . - /// If is equal to , , - /// or , that value is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Floor(float f) - { - return (float)Math.Floor(f); - } - - /// - /// Returns the larger of two single-precision floating-point numbers. - /// - /// The first of two single-precision floating-point numbers to compare. - /// The second of two single-precision floating-point numbers to compare. - /// - /// Parameter or , whichever is larger. - /// If , or , or both and are - /// equal to , is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Max(float val1, float val2) - { - return Math.Max(val1, val2); - } - - /// - /// Returns the smaller of two single-precision floating-point numbers. - /// - /// The first of two single-precision floating-point numbers to compare. - /// The second of two single-precision floating-point numbers to compare. - /// - /// Parameter or , whichever is smaller. - /// If , , or both and are equal - /// to , is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Min(float val1, float val2) - { - return Math.Min(val1, val2); - } - - /// - /// Returns a specified number raised to the specified power. - /// - /// A single-precision floating-point number to be raised to a power. - /// A single-precision floating-point number that specifies a power. - /// The number raised to the power . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Pow(float x, float y) - { - return (float)Math.Pow(x, y); - } - - /// - /// Rounds a single-precision floating-point value to the nearest integral value. - /// - /// A single-precision floating-point number to be rounded. - /// - /// The integer nearest . - /// If the fractional component of is halfway between two integers, one of which is even and the other odd, then the even number is returned. - /// Note that this method returns a instead of an integral type. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Round(float f) - { - return (float)Math.Round(f); - } - - /// - /// Rounds a single-precision floating-point value to the nearest integer. - /// A parameter specifies how to round the value if it is midway between two numbers. - /// - /// A single-precision floating-point number to be rounded. - /// Specification for how to round if it is midway between two other numbers. - /// - /// The integer nearest . If is halfway between two integers, one of which is even - /// and the other odd, then determines which of the two is returned. - /// Note that this method returns a instead of an integral type. - /// - /// - /// is not a valid value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Round(float f, MidpointRounding mode) - { - return (float)Math.Round(f, mode); - } - - /// - /// Returns the sine of the specified angle. - /// - /// An angle, measured in radians. - /// - /// The sine of . - /// If is equal to , , - /// or , this method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sin(float f) - { - return (float)Math.Sin(f); - } - - /// - /// Returns the square root of a specified number. - /// - /// The number whose square root is to be found. - /// - /// One of the values in the following table. - /// parameter Return value Zero or positive The positive square root of . - /// Negative Equals - /// Equals - /// . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sqrt(float f) - { - return (float)Math.Sqrt(f); - } - } -} -#endif \ No newline at end of file