Internally use 128-bit integers for CRCs on .NET 7 and above

This commit is contained in:
Matt Nadareski
2026-03-20 12:08:38 -04:00
parent 87d9e65eb6
commit 2ef447b357
6 changed files with 188 additions and 0 deletions

View File

@@ -5,7 +5,11 @@ namespace SabreTools.Hashing.Checksum
/// <summary>
/// Clamp a value to a certain bit width and convert to a byte array
/// </summary>
#if NET7_0_OR_GREATER
public static byte[] ClampValueToBytes(System.UInt128 value, int bitWidth)
#else
public static byte[] ClampValueToBytes(ulong value, int bitWidth)
#endif
{
value &= ulong.MaxValue >> (64 - bitWidth);
byte[] bytes = new byte[(bitWidth + 7) / 8];

View File

@@ -40,11 +40,76 @@ namespace SabreTools.Hashing.Checksum
long l => BitConverter.GetBytes(l),
ulong l => BitConverter.GetBytes(l),
#if NET7_0_OR_GREATER
Int128 i => GetBytes(i),
UInt128 i => GetBytes(i),
#endif
_ => [],
};
Array.Reverse(hashArr);
return hashArr;
}
#if NET7_0_OR_GREATER
/// <summary>
/// Convert an Int64 to a byte array
/// </summary>
/// <remarks>Reads in little-endian format</remarks>
private static byte[] GetBytes(Int128 value)
{
byte[] output =
[
(byte)(value & 0xFF),
(byte)((value >> 8) & 0xFF),
(byte)((value >> 16) & 0xFF),
(byte)((value >> 24) & 0xFF),
(byte)((value >> 32) & 0xFF),
(byte)((value >> 40) & 0xFF),
(byte)((value >> 48) & 0xFF),
(byte)((value >> 56) & 0xFF),
(byte)((value >> 64) & 0xFF),
(byte)((value >> 72) & 0xFF),
(byte)((value >> 80) & 0xFF),
(byte)((value >> 88) & 0xFF),
(byte)((value >> 96) & 0xFF),
(byte)((value >> 104) & 0xFF),
(byte)((value >> 112) & 0xFF),
(byte)((value >> 120) & 0xFF),
];
return output;
}
/// <summary>
/// Convert a UInt64 to a byte array
/// </summary>
/// <remarks>Reads in little-endian format</remarks>
private static byte[] GetBytes(UInt128 value)
{
byte[] output =
[
(byte)(value & 0xFF),
(byte)((value >> 8) & 0xFF),
(byte)((value >> 16) & 0xFF),
(byte)((value >> 24) & 0xFF),
(byte)((value >> 32) & 0xFF),
(byte)((value >> 40) & 0xFF),
(byte)((value >> 48) & 0xFF),
(byte)((value >> 56) & 0xFF),
(byte)((value >> 64) & 0xFF),
(byte)((value >> 72) & 0xFF),
(byte)((value >> 80) & 0xFF),
(byte)((value >> 88) & 0xFF),
(byte)((value >> 96) & 0xFF),
(byte)((value >> 104) & 0xFF),
(byte)((value >> 112) & 0xFF),
(byte)((value >> 120) & 0xFF),
];
return output;
}
#endif
}
}

View File

@@ -3,7 +3,11 @@ using static SabreTools.Hashing.HashOperations;
namespace SabreTools.Hashing.Checksum
{
#if NET7_0_OR_GREATER
public class Crc : ChecksumBase<UInt128>
#else
public class Crc : ChecksumBase<ulong>
#endif
{
/// <inheritdoc/>
public override int HashSize => Def.Width;
@@ -50,7 +54,11 @@ namespace SabreTools.Hashing.Checksum
protected override byte[] HashFinal()
{
// Create a copy of the hash
#if NET7_0_OR_GREATER
UInt128 localHash = _hash;
#else
ulong localHash = _hash;
#endif
// Handle mutual reflection
if (Def.ReflectIn ^ Def.ReflectOut)

View File

@@ -21,7 +21,11 @@ namespace SabreTools.Hashing.Checksum
/// corresponds to the inward end of the shift register, and is always
/// set. The highest-order term is omitted.
/// </summary>
#if NET7_0_OR_GREATER
public System.UInt128 Poly { get; set; }
#else
public ulong Poly { get; set; }
#endif
/// <summary>
/// The settings of the bit cells at the start of each calculation,
@@ -29,7 +33,11 @@ namespace SabreTools.Hashing.Checksum
/// hexadecimal, direct notation found in MSB-first code. The least
/// significant bit corresponds to the inward end of the shift register.
/// </summary>
#if NET7_0_OR_GREATER
public System.UInt128 Init { get; set; }
#else
public ulong Init { get; set; }
#endif
/// <summary>
/// If equal to false, specifies that the characters of the message
@@ -60,6 +68,10 @@ namespace SabreTools.Hashing.Checksum
/// is written in hexadecimal notation, having the same endianness as
/// the CRC such that its true image appears in the characters of the CRC.
/// </summary>
#if NET7_0_OR_GREATER
public System.UInt128 XorOut { get; set; }
#else
public ulong XorOut { get; set; }
#endif
}
}

View File

@@ -23,12 +23,20 @@ namespace SabreTools.Hashing.Checksum
/// <summary>
/// Bit mask based on the CRC width
/// </summary>
#if NET7_0_OR_GREATER
private UInt128 BitMask => 1UL << (_definition.Width - 1);
#else
private ulong BitMask => 1UL << (_definition.Width - 1);
#endif
/// <summary>
/// Mapping table
/// </summary>
#if NET7_0_OR_GREATER
private readonly UInt128[,] _table;
#else
private readonly ulong[,] _table;
#endif
/// <summary>
/// Definition used to build the table
@@ -44,13 +52,21 @@ namespace SabreTools.Hashing.Checksum
{
// Initialize the internal
_definition = def;
#if NET7_0_OR_GREATER
_table = new UInt128[SliceCount, 1 << BitsPerStep];
#else
_table = new ulong[SliceCount, 1 << BitsPerStep];
#endif
// Build the standard table
for (uint i = 0; i < (1 << BitsPerStep); i++)
{
// Get the starting value for this index
#if NET7_0_OR_GREATER
UInt128 point = i;
#else
ulong point = i;
#endif
if (!Bitwise && def.ReflectIn)
point = ReverseBits(point, BitsPerStep);
@@ -87,7 +103,11 @@ namespace SabreTools.Hashing.Checksum
// Build each slice from the previous
for (int j = 0; j < 1 << BitsPerStep; j++)
{
#if NET7_0_OR_GREATER
UInt128 last = _table[i - 1, j];
#else
ulong last = _table[i - 1, j];
#endif
if (_definition.ReflectIn)
_table[i, j] = (last >> BitsPerStep) ^ _table[0, (byte)last];
else
@@ -108,7 +128,11 @@ namespace SabreTools.Hashing.Checksum
/// or when <paramref name="offset"/> + <paramref name="length"/> is greater
/// than the data length.
/// </exception>
#if NET7_0_OR_GREATER
public void TransformBlock(ref UInt128 hash, byte[] data, int offset, int length)
#else
public void TransformBlock(ref ulong hash, byte[] data, int offset, int length)
#endif
{
// Empty data just returns
if (data.Length == 0)
@@ -137,7 +161,11 @@ namespace SabreTools.Hashing.Checksum
/// <param name="hash">Current hash value, updated on run</param>
/// <param name="data">Byte array representing the data</param>
/// <param name="offset">Offset in the data to process</param>
#if NET7_0_OR_GREATER
private void PerformChecksumStep(ref UInt128 hash, byte[] data, int offset)
#else
private void PerformChecksumStep(ref ulong hash, byte[] data, int offset)
#endif
{
// Per-bit processing
if (Bitwise)
@@ -164,7 +192,11 @@ namespace SabreTools.Hashing.Checksum
/// <summary>
/// Perform an optimized transform step
/// </summary>
#if NET7_0_OR_GREATER
private bool TransformBlockFast(ref UInt128 hash, byte[] data, int offset, int length)
#else
private bool TransformBlockFast(ref ulong hash, byte[] data, int offset, int length)
#endif
{
// Bitwise transformations are not optimized
if (Bitwise)
@@ -196,10 +228,18 @@ namespace SabreTools.Hashing.Checksum
/// Optimized transformation for CRC with reflection
/// </summary>
/// <remarks>Reads 4 bytes at a time</remarks>
#if NET7_0_OR_GREATER
private void TransformBlockFast4Reflect(ref UInt128 hash, byte[] data, int offset, int length)
#else
private void TransformBlockFast4Reflect(ref ulong hash, byte[] data, int offset, int length)
#endif
{
// Process on a copy of the hash
#if NET7_0_OR_GREATER
UInt128 local = hash;
#else
ulong local = hash;
#endif
// Process aligned data
if (length > 4)
@@ -209,11 +249,20 @@ namespace SabreTools.Hashing.Checksum
while (offset < end)
{
#if NET7_0_OR_GREATER
UInt128 low = local ^ (uint)(
data[offset + 0]
+ (data[offset + 1] << 8)
+ (data[offset + 2] << 16)
+ (data[offset + 3] << 24));
#else
ulong low = local ^ (uint)(
data[offset + 0]
+ (data[offset + 1] << 8)
+ (data[offset + 2] << 16)
+ (data[offset + 3] << 24));
#endif
offset += 4;
local = _table[3, (byte)low]
@@ -238,10 +287,18 @@ namespace SabreTools.Hashing.Checksum
/// Optimized transformation for CRC with reflection
/// </summary>
/// <remarks>Reads 8 bytes at a time</remarks>
#if NET7_0_OR_GREATER
private void TransformBlockFast8Reflect(ref UInt128 hash, byte[] data, int offset, int length)
#else
private void TransformBlockFast8Reflect(ref ulong hash, byte[] data, int offset, int length)
#endif
{
// Process on a copy of the hash
#if NET7_0_OR_GREATER
UInt128 local = hash;
#else
ulong local = hash;
#endif
// Process aligned data
if (length > 8)
@@ -251,6 +308,18 @@ namespace SabreTools.Hashing.Checksum
while (offset < end)
{
#if NET7_0_OR_GREATER
UInt128 low = local ^ (uint)(
data[offset + 0]
+ (data[offset + 1] << 8)
+ (data[offset + 2] << 16)
+ (data[offset + 3] << 24));
UInt128 high = (uint)(
data[offset + 4]
+ (data[offset + 5] << 8)
+ (data[offset + 6] << 16)
+ (data[offset + 7] << 24));
#else
ulong low = local ^ (uint)(
data[offset + 0]
+ (data[offset + 1] << 8)
@@ -261,6 +330,7 @@ namespace SabreTools.Hashing.Checksum
+ (data[offset + 5] << 8)
+ (data[offset + 6] << 16)
+ (data[offset + 7] << 24));
#endif
offset += 8;
local = _table[7, (byte)low]
@@ -289,10 +359,18 @@ namespace SabreTools.Hashing.Checksum
/// Optimized transformation for 32-bit CRC with no reflection
/// </summary>
/// <remarks>Reads 8 bytes at a time</remarks>
#if NET7_0_OR_GREATER
private void TransformBlockFast8NoReflect(ref UInt128 hash, byte[] data, int offset, int length)
#else
private void TransformBlockFast8NoReflect(ref ulong hash, byte[] data, int offset, int length)
#endif
{
// Process on a copy of the hash
#if NET7_0_OR_GREATER
UInt128 local = hash;
#else
ulong local = hash;
#endif
// Process aligned data
if (length > 8)
@@ -302,6 +380,18 @@ namespace SabreTools.Hashing.Checksum
while (offset < end)
{
#if NET7_0_OR_GREATER
UInt128 low = local ^ (uint)(
data[offset + 3]
+ (data[offset + 2] << 8)
+ (data[offset + 1] << 16)
+ (data[offset + 0] << 24));
UInt128 high = (uint)(
data[offset + 7]
+ (data[offset + 6] << 8)
+ (data[offset + 5] << 16)
+ (data[offset + 4] << 24));
#else
ulong low = local ^ (uint)(
data[offset + 3]
+ (data[offset + 2] << 8)
@@ -312,6 +402,7 @@ namespace SabreTools.Hashing.Checksum
+ (data[offset + 6] << 8)
+ (data[offset + 5] << 16)
+ (data[offset + 4] << 24));
#endif
offset += 8;
local = _table[4, (byte)low]

View File

@@ -117,9 +117,17 @@ namespace SabreTools.Hashing
/// <summary>
/// Reverse the endianness of a value
/// </summary>
#if NET7_0_OR_GREATER
public static UInt128 ReverseBits(UInt128 value, int bitWidth)
#else
public static ulong ReverseBits(ulong value, int bitWidth)
#endif
{
#if NET7_0_OR_GREATER
UInt128 reverse = 0;
#else
ulong reverse = 0;
#endif
for (int i = 0; i < bitWidth; i++)
{
reverse <<= 1;