From 529dae96589f9a2448c4d10bcdf7ab16e0c32250 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 20 Mar 2026 12:57:41 -0400 Subject: [PATCH] Add CRC-82/DARC --- Hasher/Features/MainFeature.cs | 4 + README.MD | 2 +- SabreTools.Hashing.Test/TestHelper.cs | 4 + SabreTools.Hashing/Checksum/BitOperations.cs | 4 + SabreTools.Hashing/Checksum/ChecksumBase.cs | 144 ++++++++++-------- SabreTools.Hashing/Checksum/Crc.cs | 4 + SabreTools.Hashing/Checksum/CrcTable.cs | 6 +- .../Checksum/StandardDefinitions.cs | 20 +++ SabreTools.Hashing/HashOperations.cs | 23 +++ SabreTools.Hashing/HashType.cs | 23 +++ SabreTools.Hashing/HashWrapper.cs | 4 + 11 files changed, 175 insertions(+), 63 deletions(-) diff --git a/Hasher/Features/MainFeature.cs b/Hasher/Features/MainFeature.cs index dd96903..e23ee34 100644 --- a/Hasher/Features/MainFeature.cs +++ b/Hasher/Features/MainFeature.cs @@ -268,6 +268,10 @@ namespace Hasher.Features "crc64_we" => HashType.CRC64_WE, "crc64_xz" => HashType.CRC64_XZ, +#if NET7_0_OR_GREATER + "crc82_darc" => HashType.CRC82_DARC, +#endif + "fletcher16" => HashType.Fletcher16, "fletcher32" => HashType.Fletcher32, "fletcher64" => HashType.Fletcher64, diff --git a/README.MD b/README.MD index a8e5838..de289e5 100644 --- a/README.MD +++ b/README.MD @@ -38,7 +38,7 @@ All hash and checksum types here have been written to ensure compatibility acros | Hash / Checksum Type | Notes | | --- | --- | | Adler-32 | Based on the [zlib source code](https://github.com/madler/zlib/blob/v1.2.11/adler32.c) | -| CRC | All CRC values documented [here](https://reveng.sourceforge.io/crc-catalogue/all.htm) except for CRC-82 due to bit-length restrictions | +| CRC | All CRC values documented [here](https://reveng.sourceforge.io/crc-catalogue/all.htm), with CRC-82 only supported in `net7.0` and above | | Fletcher | 16-, 32-, and 64-bit variants | | FNV | 32-, and 64-bit variants; 0, 1, and 1a algorithms | | Message Digest | MD2 and MD4 only | diff --git a/SabreTools.Hashing.Test/TestHelper.cs b/SabreTools.Hashing.Test/TestHelper.cs index 4712632..ca9e1f9 100644 --- a/SabreTools.Hashing.Test/TestHelper.cs +++ b/SabreTools.Hashing.Test/TestHelper.cs @@ -162,6 +162,10 @@ namespace SabreTools.Hashing.Test {HashType.CRC64_WE, "91812be748f941c4"}, {HashType.CRC64_XZ, "fb49044e8331f6e5"}, +#if NET7_0_OR_GREATER + { HashType.CRC82_DARC, "14a892baa6688e66f55f3"}, +#endif + {HashType.Fletcher16, "46c1"}, {HashType.Fletcher32, "073f2d94"}, {HashType.Fletcher64, "000b073400002d94"}, diff --git a/SabreTools.Hashing/Checksum/BitOperations.cs b/SabreTools.Hashing/Checksum/BitOperations.cs index edb14b2..4297a0f 100644 --- a/SabreTools.Hashing/Checksum/BitOperations.cs +++ b/SabreTools.Hashing/Checksum/BitOperations.cs @@ -11,7 +11,11 @@ namespace SabreTools.Hashing.Checksum public static byte[] ClampValueToBytes(ulong value, int bitWidth) #endif { +#if NET7_0_OR_GREATER + value &= System.UInt128.MaxValue >> (128 - bitWidth); +#else value &= ulong.MaxValue >> (64 - bitWidth); +#endif byte[] bytes = new byte[(bitWidth + 7) / 8]; for (int i = 0; i < bytes.Length; i++) diff --git a/SabreTools.Hashing/Checksum/ChecksumBase.cs b/SabreTools.Hashing/Checksum/ChecksumBase.cs index 53fa1d8..b4d9f90 100644 --- a/SabreTools.Hashing/Checksum/ChecksumBase.cs +++ b/SabreTools.Hashing/Checksum/ChecksumBase.cs @@ -7,7 +7,89 @@ namespace SabreTools.Hashing.Checksum /// public abstract class ChecksumBase : System.Security.Cryptography.HashAlgorithm { - // No common, untyped functionality + #if NET7_0_OR_GREATER + /// + /// Convert an Int64 to a byte array + /// + /// Reads in little-endian format + internal 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 + internal 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; + } + + /// + /// Convert a byte array at an offset to a UInt128 + /// + /// Reads in little-endian format + internal static UInt128 ToUInt128(byte[] value) + { + return value[0] + | ((UInt128)value[1] << 8) + | ((UInt128)value[2] << 16) + | ((UInt128)value[3] << 24) + | ((UInt128)value[4] << 32) + | ((UInt128)value[5] << 40) + | ((UInt128)value[6] << 48) + | ((UInt128)value[7] << 56) + | ((UInt128)value[8] << 64) + | ((UInt128)value[9] << 72) + | ((UInt128)value[10] << 80) + | ((UInt128)value[11] << 88) + | ((UInt128)value[12] << 96) + | ((UInt128)value[13] << 104) + | ((UInt128)value[14] << 112) + | ((UInt128)value[15] << 120); + } +#endif } /// @@ -51,65 +133,5 @@ namespace SabreTools.Hashing.Checksum 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 1f96019..9ea42de 100644 --- a/SabreTools.Hashing/Checksum/Crc.cs +++ b/SabreTools.Hashing/Checksum/Crc.cs @@ -32,7 +32,11 @@ namespace SabreTools.Hashing.Checksum public Crc(CrcDefinition def) { // Check for a valid bit width +#if NET7_0_OR_GREATER + if (def.Width < 0 || def.Width > 128) +#else if (def.Width < 0 || def.Width > 64) +#endif throw new ArgumentOutOfRangeException(nameof(def)); Def = def; diff --git a/SabreTools.Hashing/Checksum/CrcTable.cs b/SabreTools.Hashing/Checksum/CrcTable.cs index b911765..4d13ffb 100644 --- a/SabreTools.Hashing/Checksum/CrcTable.cs +++ b/SabreTools.Hashing/Checksum/CrcTable.cs @@ -24,7 +24,7 @@ namespace SabreTools.Hashing.Checksum /// Bit mask based on the CRC width /// #if NET7_0_OR_GREATER - private UInt128 BitMask => 1UL << (_definition.Width - 1); + private UInt128 BitMask => UInt128.Parse("1") << (_definition.Width - 1); #else private ulong BitMask => 1UL << (_definition.Width - 1); #endif @@ -87,7 +87,11 @@ namespace SabreTools.Hashing.Checksum point = ReverseBits(point, def.Width); // Shift back to account for storage +#if NET7_0_OR_GREATER + point &= UInt128.MaxValue >> (128 - def.Width); +#else point &= ulong.MaxValue >> (64 - def.Width); +#endif // Assign to the table _table[0, i] = point; diff --git a/SabreTools.Hashing/Checksum/StandardDefinitions.cs b/SabreTools.Hashing/Checksum/StandardDefinitions.cs index 7e8181b..1a7c680 100644 --- a/SabreTools.Hashing/Checksum/StandardDefinitions.cs +++ b/SabreTools.Hashing/Checksum/StandardDefinitions.cs @@ -1700,5 +1700,25 @@ namespace SabreTools.Hashing.Checksum }; #endregion + + #region CRC-82 + +#if NET7_0_OR_GREATER + /// + /// CRC-82/DARC + /// + public static readonly CrcDefinition CRC82_DARC = new() + { + Name = "CRC-82/DARC", + Width = 82, + Poly = ChecksumBase.ToUInt128([0x11, 0x04, 0x44, 0x01, 0x14, 0x01, 0x11, 0x01, 0x8c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + Init = ChecksumBase.ToUInt128([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + ReflectIn = true, + ReflectOut = true, + XorOut = ChecksumBase.ToUInt128([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + }; +#endif + + #endregion } } diff --git a/SabreTools.Hashing/HashOperations.cs b/SabreTools.Hashing/HashOperations.cs index 8cdf0dc..fa499f5 100644 --- a/SabreTools.Hashing/HashOperations.cs +++ b/SabreTools.Hashing/HashOperations.cs @@ -50,6 +50,29 @@ namespace SabreTools.Hashing return result; } +#if NET7_0_OR_GREATER + /// + /// Convert a byte array to a UInt64 + /// + /// Byte array to convert + /// UInt64 representing the byte array + /// https://stackoverflow.com/questions/66750224/how-to-convert-a-byte-array-of-any-size-to-ulong-in-c + public static UInt128 BytesToUInt128(byte[]? bytes) + { + // If we get null in, we send 0 out + if (bytes is null) + return default; + + UInt128 result = 0; + for (int i = 0; i < bytes.Length; i++) + { + result |= (UInt128)bytes[i] << (i * 8); + } + + return result; + } +#endif + #endregion #region Read Big-Endian diff --git a/SabreTools.Hashing/HashType.cs b/SabreTools.Hashing/HashType.cs index 7bcb1ef..d60077a 100644 --- a/SabreTools.Hashing/HashType.cs +++ b/SabreTools.Hashing/HashType.cs @@ -1116,6 +1116,20 @@ namespace SabreTools.Hashing #endregion + #region CRC-82 + +#if NET7_0_OR_GREATER + /// + /// CRC 82-bit checksum (CRC-82/DARC) + /// + public static readonly HashType CRC82_DARC = new("CRC-82/DARC", + "CRC 82-bit checksum (CRC-82/DARC)", + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + "000000000000000000000"); +#endif + + #endregion + #endregion #region Fletcher @@ -1652,6 +1666,10 @@ namespace SabreTools.Hashing CRC64_WE, CRC64_XZ, +#if NET7_0_OR_GREATER + CRC82_DARC, +#endif + Fletcher16, Fletcher32, Fletcher64, @@ -2000,6 +2018,11 @@ namespace SabreTools.Hashing else if (hashType == CRC64_XZ) return new Crc(StandardDefinitions.CRC64_XZ); +#if NET7_0_OR_GREATER + else if (hashType == CRC82_DARC) + return new Crc(StandardDefinitions.CRC82_DARC); +#endif + else if (hashType == Fletcher16) return new Fletcher16(); else if (hashType == Fletcher32) diff --git a/SabreTools.Hashing/HashWrapper.cs b/SabreTools.Hashing/HashWrapper.cs index c86b9d3..b53a6a1 100644 --- a/SabreTools.Hashing/HashWrapper.cs +++ b/SabreTools.Hashing/HashWrapper.cs @@ -147,7 +147,11 @@ namespace SabreTools.Hashing return null; // Get the total number of characters needed +#if NET7_0_OR_GREATER + UInt128 hash = BytesToUInt128(cr.Hash); +#else ulong hash = BytesToUInt64(cr.Hash); +#endif int length = (cr.Def.Width / 4) + (cr.Def.Width % 4 > 0 ? 1 : 0); return hash.ToString($"x{length}"); }