Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Implementation]: Expose general purpose Crc32 APIs #61558

Merged
merged 60 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
66d11bb
Expose Crc32 Implementation and implement Crc32 software fallback
deeprobin Nov 14, 2021
d33918c
Fix signature of BitOperation Crc32 methods
deeprobin Nov 14, 2021
b8eb138
Implement common test cases
deeprobin Nov 14, 2021
f22c90d
Add X64 Intrinsic support for Crc32(uint crc, ulong data)
deeprobin Nov 14, 2021
3b0313c
Add documentation comments
deeprobin Nov 14, 2021
ece5c27
Remove Hwintrinsic doc-comment
deeprobin Nov 14, 2021
1474bab
Rename Crc32 to Crc32C
deeprobin Nov 14, 2021
a362cf9
Remove unnessecary heap allocation
deeprobin Nov 14, 2021
c42411b
Fix software fallback using table-based-CRC
deeprobin Nov 14, 2021
edff837
Usage of uint32_t __crc32ch (uint32_t a, uint32_t b)
deeprobin Nov 15, 2021
7ed1a09
Remove Crc.Sse42 implementation for Crc32C(uint crc, ulong data)
deeprobin Nov 15, 2021
56953c9
Add SSE 4.2 x64 implementation with byte truncation for Crc32C(uint c…
deeprobin Nov 15, 2021
e171227
Replace Unsafe.As with unchecked-uint cast
deeprobin Nov 15, 2021
bf95530
Usage of WriteUnaligned instead of As
deeprobin Nov 15, 2021
4b91869
Usage of WriteUnaligned instead of As
deeprobin Nov 15, 2021
1ef8591
Usage of WriteUnaligned instead of As
deeprobin Nov 15, 2021
b369a75
Usage of explicit types
deeprobin Nov 15, 2021
8b3afd2
Merge branch 'issue-2036' of https://github.com/deeprobin/runtime int…
deeprobin Nov 15, 2021
ca96fdf
Fix Software-Fallback Table to Castalogni equivalent
deeprobin Nov 16, 2021
3ba6819
Fix test signature
deeprobin Nov 17, 2021
ca35eaa
Remove mask for software fallback
deeprobin Nov 17, 2021
d74d96a
reuse reflected table generator
kasperk81 Nov 17, 2021
5bb5943
Fix test data
deeprobin Nov 17, 2021
d44a215
Usage of CRC Table Generator instead of constant values
deeprobin Nov 17, 2021
1e7a753
Fix solution file
deeprobin Nov 17, 2021
43704ec
Revert "Usage of CRC Table Generator instead of constant values"
deeprobin Nov 17, 2021
7e61dda
Merge pull request #1 from kasperk81/issue-2036
deeprobin Nov 17, 2021
d7f2156
Fix solution file
deeprobin Nov 17, 2021
e5d5563
Merge branch 'issue-2036' of https://github.com/deeprobin/runtime int…
deeprobin Nov 17, 2021
ee294c9
Make Crc32ReflectedTable static
deeprobin Nov 18, 2021
db9d590
Fix wrong intrinsic for Arm64/BitOperations.Crc32C(uint crc, ulong data)
deeprobin Nov 18, 2021
a125263
Merge branch 'issue-2036' of https://github.com/deeprobin/runtime int…
deeprobin Nov 18, 2021
385f528
Reduce amount of stackalloc statements and replace them with MemoryMa…
deeprobin Nov 19, 2021
4f8da5d
Loop unwinding and performance optimization
deeprobin Nov 19, 2021
6911e7d
Merge branch 'dotnet:main' into issue-2036
deeprobin Nov 19, 2021
6023bcd
Remove unnessecary cast
deeprobin Nov 20, 2021
d22566c
Use MemoryMarshal.GetArrayDataReference instead of direct array access
deeprobin Nov 20, 2021
0e4467f
Remove unnessecary newlines
deeprobin Nov 20, 2021
eea2874
Merge branch 'issue-2036' of https://github.com/deeprobin/runtime int…
deeprobin Nov 20, 2021
dfd7ed7
Merge branch 'dotnet:main' into issue-2036
deeprobin Nov 21, 2021
02319ea
Update src/libraries/System.Private.CoreLib/src/System/Numerics/BitOp…
deeprobin Nov 25, 2021
c5bc276
Add x64 SSE check
deeprobin Nov 27, 2021
cf974b1
Move Crc32 Software into own class
deeprobin Dec 2, 2021
5801d6e
Fix merge conflict
deeprobin Dec 2, 2021
c4092f2
Remove IsSupported check
deeprobin Dec 2, 2021
6b7e6c4
Fix parameter naming
deeprobin Dec 2, 2021
de1efd9
Fix fallback implementation method naming
deeprobin Dec 2, 2021
c0da910
Improve documentation
deeprobin Dec 3, 2021
2720d18
Move bswap into Crc32Fallback class
deeprobin Dec 3, 2021
0cee4ff
Implement efficient usage of SSE x86/x64
deeprobin Dec 12, 2021
ec74c04
Style Changes for BitOperations.cs
deeprobin Dec 12, 2021
12d7a1c
Remove unnessecary `bswap` of a single byte
deeprobin Dec 12, 2021
c8f5d7d
Merge branch 'main' into issue-2036
deeprobin Aug 1, 2022
ef65ff1
Merge branch 'main' into issue-2036
deeprobin Aug 14, 2022
f007128
Fix doc comments
deeprobin Aug 14, 2022
0057d5e
fix doc
deeprobin Aug 14, 2022
c6a69be
Fix
deeprobin Aug 14, 2022
230e5cc
Merge remote-tracking branch 'upstream/main' into issue-2036
deeprobin Sep 27, 2022
f60964b
Merge remote-tracking branch 'upstream/main' into issue-2036
deeprobin Sep 27, 2022
59fa0e2
Apply suggestions
deeprobin Sep 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/libraries/Common/src/System/Numerics/Crc32ReflectedTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Numerics
{
internal static class Crc32ReflectedTable
{
internal static uint[] Generate(uint reflectedPolynomial)
{
uint[] table = new uint[256];

for (int i = 0; i < 256; i++)
{
uint val = (uint)i;

for (int j = 0; j < 8; j++)
dakersnar marked this conversation as resolved.
Show resolved Hide resolved
{
if ((val & 0b0000_0001) == 0)
{
val >>= 1;
}
else
{
val = (val >> 1) ^ reflectedPolynomial;
}
}

table[i] = val;
}

return table;
}
}
}
3 changes: 3 additions & 0 deletions src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ System.IO.Hashing.XxHash32</PackageDescription>
<Compile Include="System\IO\Hashing\XxHash64.cs" />
<Compile Include="System\IO\Hashing\XxHash64.State.cs" />
<Compile Include="System\IO\Hashing\NonCryptographicHashAlgorithm.cs" />
<Compile Include="$(CommonPath)System\Numerics\Crc32ReflectedTable.cs">
<Link>Common\System\Numerics\Crc32ReflectedTable.cs</Link>
</Compile>
<Compile Include="System\IO\Hashing\BitOperations.cs"
Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;

namespace System.IO.Hashing
{
public sealed partial class Crc32 : NonCryptographicHashAlgorithm
Expand All @@ -9,32 +11,6 @@ public sealed partial class Crc32 : NonCryptographicHashAlgorithm
// While this implementation is based on the standard CRC-32 polynomial,
// x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x1 + x0,
// this version uses reflected bit ordering, so 0x04C11DB7 becomes 0xEDB88320
private static readonly uint[] s_crcLookup = GenerateReflectedTable(0xEDB88320u);

private static uint[] GenerateReflectedTable(uint reflectedPolynomial)
{
uint[] table = new uint[256];

for (int i = 0; i < 256; i++)
{
uint val = (uint)i;

for (int j = 0; j < 8; j++)
{
if ((val & 0b0000_0001) == 0)
{
val >>= 1;
}
else
{
val = (val >> 1) ^ reflectedPolynomial;
}
}

table[i] = val;
}

return table;
}
private static readonly uint[] s_crcLookup = Crc32ReflectedTable.Generate(0xEDB88320u);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,9 @@
<Compile Include="$(CommonPath)System\NotImplemented.cs">
<Link>Common\System\NotImplemented.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\Numerics\Crc32ReflectedTable.cs">
<Link>Common\System\Numerics\Crc32ReflectedTable.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\Obsoletions.cs">
<Link>Common\System\Obsoletions.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
Expand Down Expand Up @@ -51,7 +53,7 @@ public static class BitOperations
/// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static bool IsPow2(uint value) => (value & (value - 1)) == 0 && value != 0 ;
public static bool IsPow2(uint value) => (value & (value - 1)) == 0 && value != 0;
deeprobin marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Evaluate whether a given integral value is a power of 2.
Expand Down Expand Up @@ -730,6 +732,164 @@ public static nuint RotateRight(nuint value, int offset)
#endif
}

/// <summary>
/// Accumulates the CRC (Cyclic redundancy check) checksum.
/// </summary>
/// <param name="crc">The base value to calculate checksum on</param>
/// <param name="data">The data for which to compute the checksum</param>
/// <returns>The CRC-checksum</returns>
[CLSCompliant(false)]
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Crc32C(uint crc, byte data)
danmoseley marked this conversation as resolved.
Show resolved Hide resolved
{
if (Sse42.IsSupported)
{
return Sse42.Crc32(crc, data);
dakersnar marked this conversation as resolved.
Show resolved Hide resolved
}

if (Crc32.IsSupported)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
return Crc32.ComputeCrc32C(crc, data);
}

return Crc32Fallback.Crc32C(crc, data);
}

/// <summary>
/// Accumulates the CRC (Cyclic redundancy check) checksum.
/// </summary>
/// <param name="crc">The base value to calculate checksum on</param>
/// <param name="data">The data for which to compute the checksum</param>
/// <returns>The CRC-checksum</returns>
[CLSCompliant(false)]
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Crc32C(uint crc, ushort data)
{
if (Sse42.IsSupported)
{
return Sse42.Crc32(crc, data);
}

if (Crc32.IsSupported)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
return Crc32.ComputeCrc32C(crc, data);
}

return Crc32Fallback.Crc32C(crc, data);
}

/// <summary>
/// Accumulates the CRC (Cyclic redundancy check) checksum.
/// </summary>
/// <param name="crc">The base value to calculate checksum on</param>
/// <param name="data">The data for which to compute the checksum</param>
/// <returns>The CRC-checksum</returns>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Crc32C(uint crc, uint data)
{
if (Sse42.IsSupported)
{
return Sse42.Crc32(crc, data);
}

if (Crc32.IsSupported)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
return Crc32.ComputeCrc32C(crc, data);
}

return Crc32Fallback.Crc32C(crc, data);
}

/// <summary>
/// Accumulates the CRC (Cyclic redundancy check) checksum.
/// </summary>
/// <param name="crc">The base value to calculate checksum on</param>
/// <param name="data">The data for which to compute the checksum</param>
/// <returns>The CRC-checksum</returns>
[CLSCompliant(false)]
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Crc32C(uint crc, ulong data)
{
if (Sse42.X64.IsSupported)
{
// This intrinsic returns a 64-bit register with the upper 32-bits set to 0.
return (uint)Sse42.X64.Crc32(crc, data);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
}

if (Sse42.IsSupported)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
uint result = Sse42.Crc32(crc, (uint)(data));
return Sse42.Crc32(result, (uint)(data >> 32));
}

if (Crc32.Arm64.IsSupported)
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
{
return Crc32.Arm64.ComputeCrc32C(crc, data);
}

return Crc32Fallback.Crc32C(crc, data);
}

private static class Crc32Fallback
{
// Pre-computed CRC-32 transition table.
// While this implementation is based on the Castagnoli CRC-32 polynomial (CRC-32C),
// x32 + x28 + x27 + x26 + x25 + x23 + x22 + x20 + x19 + x18 + x14 + x13 + x11 + x10 + x9 + x8 + x6 + x0,
// this version uses reflected bit ordering, so 0x1EDC6F41 becomes 0x82F63B78u
private static readonly uint[] s_crcTable = Crc32ReflectedTable.Generate(0x82F63B78u);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved

internal static uint Crc32C(uint crc, byte data)
{
ref uint lookupTable = ref MemoryMarshal.GetArrayDataReference(s_crcTable);
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ data)) ^ (crc >> 8);

return crc;
}

internal static uint Crc32C(uint crc, ushort data)
{
ref uint lookupTable = ref MemoryMarshal.GetArrayDataReference(s_crcTable);

crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ (byte)data)) ^ (crc >> 8);
data >>= 8;
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ data)) ^ (crc >> 8);

return crc;
}

internal static uint Crc32C(uint crc, uint data)
{
ref uint lookupTable = ref MemoryMarshal.GetArrayDataReference(s_crcTable);
return Crc32CCore(ref lookupTable, crc, data);
}

internal static uint Crc32C(uint crc, ulong data)
{
ref uint lookupTable = ref MemoryMarshal.GetArrayDataReference(s_crcTable);

crc = Crc32CCore(ref lookupTable, crc, (uint)data);
data >>= 32;
crc = Crc32CCore(ref lookupTable, crc, (uint)data);

return crc;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Crc32CCore(ref uint lookupTable, uint crc, uint data)
{
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ (byte)data)) ^ (crc >> 8);
deeprobin marked this conversation as resolved.
Show resolved Hide resolved
data >>= 8;
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ (byte)data)) ^ (crc >> 8);
data >>= 8;
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ (byte)data)) ^ (crc >> 8);
data >>= 8;
crc = Unsafe.Add(ref lookupTable, (nint)(byte)(crc ^ data)) ^ (crc >> 8);

return crc;
}
}

/// <summary>
/// Reset the lowest significant bit in the given value
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -887,5 +887,49 @@ public static void BitOps_RoundUpToPow2_nuint_64(ulong value, ulong expected)
{
Assert.Equal(expected, BitOperations.RoundUpToPowerOf2((nuint) value));
}

[Theory]
[InlineData(0, 0, 0)]
[InlineData(0, 120, 4215344322)]
[InlineData(0, byte.MaxValue, 2910671697)]
[InlineData(123, byte.MaxValue, 1164749927)]
dakersnar marked this conversation as resolved.
Show resolved Hide resolved
public static void BitOps_Crc32C_byte(uint crc, byte data, uint expected)
{
uint obtained = BitOperations.Crc32C(crc, data);
Assert.Equal(expected, obtained);
}

[Theory]
[InlineData(0, 0, 0)]
[InlineData(0, 120, 575477567)]
[InlineData(0, ushort.MaxValue, 245266386)]
[InlineData(123, ushort.MaxValue, 406112372)]
public static void BitOps_Crc32C_ushort(uint crc, ushort data, uint expected)
{
uint obtained = BitOperations.Crc32C(crc, data);
Assert.Equal(expected, obtained);
}

[Theory]
[InlineData(0, 0, 0)]
[InlineData(0, 120, 1671666103)]
[InlineData(0, uint.MaxValue, 3080238136)]
[InlineData(123, uint.MaxValue, 3055133878)]
public static void BitOps_Crc32C_uint(uint crc, uint data, uint expected)
{
uint obtained = BitOperations.Crc32C(crc, data);
Assert.Equal(expected, obtained);
}

[Theory]
[InlineData(0, 0, 0)]
[InlineData(0, 120, 3511526341)]
[InlineData(0, ulong.MaxValue, 3293575501)]
[InlineData(123, ulong.MaxValue, 3460750817)]
public static void BitOps_Crc32C_ulong(uint crc, ulong data, uint expected)
{
uint obtained = BitOperations.Crc32C(crc, data);
Assert.Equal(expected, obtained);
}
}
}
8 changes: 8 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10217,6 +10217,14 @@ public static partial class BitOperations
public static int TrailingZeroCount(ulong value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static int TrailingZeroCount(nuint value) { throw null; }
[System.CLSCompliantAttribute(false)]
public static uint Crc32C(uint crc, byte data) { throw null; }
[System.CLSCompliantAttribute(false)]
public static uint Crc32C(uint crc, ushort data) { throw null; }
[System.CLSCompliantAttribute(false)]
public static uint Crc32C(uint crc, uint data) { throw null; }
[System.CLSCompliantAttribute(false)]
public static uint Crc32C(uint crc, ulong data) { throw null; }
}
public partial interface IAdditionOperators<TSelf, TOther, TResult> where TSelf : System.Numerics.IAdditionOperators<TSelf, TOther, TResult>?
{
Expand Down