From 2ef447b357ebc76ac4d923abff3df719bdb9e030 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 20 Mar 2026 12:08:38 -0400 Subject: [PATCH] Internally use 128-bit integers for CRCs on .NET 7 and above --- SabreTools.Hashing/Checksum/BitOperations.cs | 4 + SabreTools.Hashing/Checksum/ChecksumBase.cs | 65 ++++++++++++++ SabreTools.Hashing/Checksum/Crc.cs | 8 ++ SabreTools.Hashing/Checksum/CrcDefinition.cs | 12 +++ SabreTools.Hashing/Checksum/CrcTable.cs | 91 ++++++++++++++++++++ SabreTools.Hashing/HashOperations.cs | 8 ++ 6 files changed, 188 insertions(+) diff --git a/SabreTools.Hashing/Checksum/BitOperations.cs b/SabreTools.Hashing/Checksum/BitOperations.cs index 2405933..edb14b2 100644 --- a/SabreTools.Hashing/Checksum/BitOperations.cs +++ b/SabreTools.Hashing/Checksum/BitOperations.cs @@ -5,7 +5,11 @@ namespace SabreTools.Hashing.Checksum /// /// Clamp a value to a certain bit width and convert to a byte array /// +#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]; diff --git a/SabreTools.Hashing/Checksum/ChecksumBase.cs b/SabreTools.Hashing/Checksum/ChecksumBase.cs index 7c4b798..53fa1d8 100644 --- a/SabreTools.Hashing/Checksum/ChecksumBase.cs +++ b/SabreTools.Hashing/Checksum/ChecksumBase.cs @@ -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 + /// + /// Convert an Int64 to a byte array + /// + /// Reads in little-endian format + 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; + } + + /// + /// Convert a UInt64 to a byte array + /// + /// Reads in little-endian format + 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 } } diff --git a/SabreTools.Hashing/Checksum/Crc.cs b/SabreTools.Hashing/Checksum/Crc.cs index 560f138..1f96019 100644 --- a/SabreTools.Hashing/Checksum/Crc.cs +++ b/SabreTools.Hashing/Checksum/Crc.cs @@ -3,7 +3,11 @@ using static SabreTools.Hashing.HashOperations; namespace SabreTools.Hashing.Checksum { +#if NET7_0_OR_GREATER + public class Crc : ChecksumBase +#else public class Crc : ChecksumBase +#endif { /// 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) diff --git a/SabreTools.Hashing/Checksum/CrcDefinition.cs b/SabreTools.Hashing/Checksum/CrcDefinition.cs index 2f4b246..7d7518f 100644 --- a/SabreTools.Hashing/Checksum/CrcDefinition.cs +++ b/SabreTools.Hashing/Checksum/CrcDefinition.cs @@ -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. /// +#if NET7_0_OR_GREATER + public System.UInt128 Poly { get; set; } +#else public ulong Poly { get; set; } +#endif /// /// 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. /// +#if NET7_0_OR_GREATER + public System.UInt128 Init { get; set; } +#else public ulong Init { get; set; } +#endif /// /// 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. /// +#if NET7_0_OR_GREATER + public System.UInt128 XorOut { get; set; } +#else public ulong XorOut { get; set; } +#endif } } diff --git a/SabreTools.Hashing/Checksum/CrcTable.cs b/SabreTools.Hashing/Checksum/CrcTable.cs index 2a1bd2a..b911765 100644 --- a/SabreTools.Hashing/Checksum/CrcTable.cs +++ b/SabreTools.Hashing/Checksum/CrcTable.cs @@ -23,12 +23,20 @@ namespace SabreTools.Hashing.Checksum /// /// Bit mask based on the CRC width /// +#if NET7_0_OR_GREATER + private UInt128 BitMask => 1UL << (_definition.Width - 1); +#else private ulong BitMask => 1UL << (_definition.Width - 1); +#endif /// /// Mapping table /// +#if NET7_0_OR_GREATER + private readonly UInt128[,] _table; +#else private readonly ulong[,] _table; +#endif /// /// 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 + is greater /// than the data length. /// +#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 /// Current hash value, updated on run /// Byte array representing the data /// Offset in the data to process +#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 /// /// Perform an optimized transform step /// +#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 /// /// Reads 4 bytes at a time +#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 /// /// Reads 8 bytes at a time +#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 /// /// Reads 8 bytes at a time +#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] diff --git a/SabreTools.Hashing/HashOperations.cs b/SabreTools.Hashing/HashOperations.cs index 20b52c3..8cdf0dc 100644 --- a/SabreTools.Hashing/HashOperations.cs +++ b/SabreTools.Hashing/HashOperations.cs @@ -117,9 +117,17 @@ namespace SabreTools.Hashing /// /// Reverse the endianness of a value /// +#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;