File: System\IO\Hashing\Crc32ParameterSet.WellKnown.cs
Web Access
Project: src\src\libraries\System.IO.Hashing\src\System.IO.Hashing.csproj (System.IO.Hashing)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
namespace System.IO.Hashing
{
    public partial class Crc32ParameterSet
    {
        /// <summary>
        ///   Gets the parameter set for the variant of CRC-32 as used in
        ///   ITU-T V.42 and IEEE 802.3.
        /// </summary>
        /// <value>
        ///   The parameter set for the variant of CRC-32 as used in
        ///   ITU-T V.42 and IEEE 802.3.
        /// </value>
        public static Crc32ParameterSet Crc32 =>
            field ??= new Ieee8023ParameterSet();
 
        /// <summary>
        ///   Gets the parameter set for the CRC-32C variant of CRC-32.
        /// </summary>
        /// <value>
        ///   The parameter set for the CRC-32C variant of CRC-32.
        /// </value>
        public static Crc32ParameterSet Crc32C =>
            field ??= MakeCrc32CParameterSet();
 
        private static Crc32ParameterSet MakeCrc32CParameterSet()
        {
#if NET
            if (System.Runtime.Intrinsics.X86.Sse42.IsSupported || System.Runtime.Intrinsics.Arm.Crc32.IsSupported)
            {
                return new Crc32CParameterSet();
            }
#endif
 
            return Create(
                polynomial: 0x1edc6f41,
                initialValue: 0xffffffff,
                finalXorValue: 0xffffffff,
                reflectValues: true);
        }
 
        private sealed class Ieee8023ParameterSet : Crc32ParameterSet
        {
            public Ieee8023ParameterSet()
                : base(0x04c11db7, 0xffffffff, 0xffffffff, reflectValues: true)
            {
            }
 
            internal override uint Update(uint value, ReadOnlySpan<byte> source) => Hashing.Crc32.Update(value, source);
        }
 
#if NET
        private sealed class Crc32CParameterSet : Crc32ParameterSet
        {
            public Crc32CParameterSet()
                : base(0x1edc6f41, 0xffffffff, 0xffffffff, reflectValues: true)
            {
            }
 
            internal override uint Update(uint value, ReadOnlySpan<byte> source) => UpdateIntrinsic(value, source);
 
            private static uint UpdateIntrinsic(uint crc, ReadOnlySpan<byte> source)
            {
                if (System.Runtime.Intrinsics.X86.Sse42.IsSupported)
                {
                    if (System.Runtime.Intrinsics.X86.Sse42.X64.IsSupported)
                    {
                        ReadOnlySpan<ulong> ulongData = MemoryMarshal.Cast<byte, ulong>(source);
                        ulong crc64 = crc;
 
                        foreach (ulong value in ulongData)
                        {
                            crc64 = System.Runtime.Intrinsics.X86.Sse42.X64.Crc32(crc64, value);
                        }
 
                        crc = (uint)crc64;
                        source = source.Slice(ulongData.Length * sizeof(ulong));
                    }
 
                    ReadOnlySpan<uint> uintData = MemoryMarshal.Cast<byte, uint>(source);
 
                    foreach (uint value in uintData)
                    {
                        crc = System.Runtime.Intrinsics.X86.Sse42.Crc32(crc, value);
                    }
 
                    // SSE 4.2 defines a ushort version as well, but that will only save us one byte,
                    // so not worth the branch and cast.
 
                    ReadOnlySpan<byte> remainingBytes = source.Slice(uintData.Length * sizeof(uint));
 
                    foreach (byte value in remainingBytes)
                    {
                        crc = System.Runtime.Intrinsics.X86.Sse42.Crc32(crc, value);
                    }
                }
                else
                {
                    Debug.Assert(System.Runtime.Intrinsics.Arm.Crc32.IsSupported);
                    ref byte ptr = ref MemoryMarshal.GetReference(source);
                    int offset = 0;
 
                    if (System.Runtime.Intrinsics.Arm.Crc32.Arm64.IsSupported)
                    {
                        int longLength = source.Length & ~0x7; // Exclude trailing bytes not a multiple of 8
 
                        for (; offset < longLength; offset += sizeof(ulong))
                        {
                            crc = System.Runtime.Intrinsics.Arm.Crc32.Arm64.ComputeCrc32C(
                                crc,
                                Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref ptr, offset)));
                        }
                    }
 
                    int intLength = source.Length & ~0x3; // Exclude trailing bytes not a multiple of 4
 
                    for (; offset < intLength; offset += sizeof(uint))
                    {
                        crc = System.Runtime.Intrinsics.Arm.Crc32.ComputeCrc32C(
                            crc,
                            Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref ptr, offset)));
                    }
 
                    ReadOnlySpan<byte> remainingBytes = source.Slice(offset);
 
                    foreach (byte value in remainingBytes)
                    {
                        crc = System.Runtime.Intrinsics.Arm.Crc32.ComputeCrc32C(crc, value);
                    }
                }
 
                return crc;
            }
        }
#endif
    }
}