Add CRC-82/DARC

This commit is contained in:
Matt Nadareski
2026-03-20 12:57:41 -04:00
parent 2ef447b357
commit 529dae9658
11 changed files with 175 additions and 63 deletions

View File

@@ -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,

View File

@@ -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 |

View File

@@ -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"},

View File

@@ -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++)

View File

@@ -7,7 +7,89 @@ namespace SabreTools.Hashing.Checksum
/// </summary>
public abstract class ChecksumBase : System.Security.Cryptography.HashAlgorithm
{
// No common, untyped functionality
#if NET7_0_OR_GREATER
/// <summary>
/// Convert an Int64 to a byte array
/// </summary>
/// <remarks>Reads in little-endian format</remarks>
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;
}
/// <summary>
/// Convert a UInt64 to a byte array
/// </summary>
/// <remarks>Reads in little-endian format</remarks>
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;
}
/// <summary>
/// Convert a byte array at an offset to a UInt128
/// </summary>
/// <remarks>Reads in little-endian format</remarks>
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
}
/// <summary>
@@ -51,65 +133,5 @@ namespace SabreTools.Hashing.Checksum
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

@@ -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;

View File

@@ -24,7 +24,7 @@ namespace SabreTools.Hashing.Checksum
/// Bit mask based on the CRC width
/// </summary>
#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;

View File

@@ -1700,5 +1700,25 @@ namespace SabreTools.Hashing.Checksum
};
#endregion
#region CRC-82
#if NET7_0_OR_GREATER
/// <summary>
/// CRC-82/DARC
/// </summary>
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
}
}

View File

@@ -50,6 +50,29 @@ namespace SabreTools.Hashing
return result;
}
#if NET7_0_OR_GREATER
/// <summary>
/// Convert a byte array to a UInt64
/// </summary>
/// <param name="bytes">Byte array to convert</param>
/// <returns>UInt64 representing the byte array</returns>
/// <link>https://stackoverflow.com/questions/66750224/how-to-convert-a-byte-array-of-any-size-to-ulong-in-c</link>
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

View File

@@ -1116,6 +1116,20 @@ namespace SabreTools.Hashing
#endregion
#region CRC-82
#if NET7_0_OR_GREATER
/// <summary>
/// CRC 82-bit checksum (CRC-82/DARC)
/// </summary>
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)

View File

@@ -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}");
}