-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Background and motivation
The NuGet package System.IO.Hashing implements a variety of hash code algorithms, for example CRC-32, CRC64 and so on. For a current task, I need to hash data with CRC-32 and CRC-8, with the first already implemented by System.IO.Hashing. For CRC-8 I had to create an own implementation of the algorithm, which currently is only available in my employer's git repository.
To avoid reimplementing the CRC-8 algorithm, I would like to propose a new class within the System.IO.Hashing namespace, which follows the patterns and structures laid out by the CRC32 class. So the CRC-8 implementation becomes a part of the System.IO.Hashing package and is available to all users of this package.
With the base class NonCryptographicHashAlgorithm System.IO.Hashing provides already a skeleton to implement such algorithm in a standardized way. So the new class CRC8 will also be implementing this base class and its functionalities.
This proposal enhances the capability of the System.IO.Hashing package and provides an out-of-the-box implementation of the missing CRC-8 algorithm, which benefits all users of the System.IO.Hashing NuGet package.
I'm willing to implement the CRC-8 algorithm by submitting a Pull-Request to the dotnet runtime repo. If other algorithms as CRC-16 are also requested, I would also implement these algorithms in separate Pull-Requests and Issues.
API Proposal
namespace System.IO.Hashing
{
public class Crc8 : NonCryptographicHashAlgorithm
{
private const int SIZE = 1;
private const byte ZERO = 0x00;
private readonly byte[] lookupTable;
private byte crc;
public Crc8()
: this(GenerateTable(0xD5, ZERO) // Generates the table for the DVB-S2 polynom.
{
}
private Crc8(IEnumerable<byte> lookupTable, byte initialState)
: base(SIZE)
{
this.lookupTable = lookupTable.ToArray();
this.InitialState = initialState;
this.crc = this.InitialState;
}
/// <summary>
/// Creates a new instance of the <see cref="Crc8"/> class with a specified polynomial
/// and an optional initial state.
/// </summary>
/// <param name="polynomial">The polynomial used to generate the CRC-8 lookup table.</param>
/// <returns>A new instance of the <see cref="Crc8"/> class configured with the specified polynomial.</returns>
public static Crc8 Create(byte polynomial) => Create(polynomial, ZERO);
/// <summary>
/// Creates an instance of the <see cref="Crc8"/> class using a specified polynomial and an initial state.
/// </summary>
/// <param name="polynomial">The polynomial to be used for the CRC-8 calculation.</param>
/// <param name="initialState">The initial state value for the CRC-8 calculation.</param>
/// <returns>A new instance of the <see cref="Crc8"/> class configured with the specified polynomial and initial state.</returns>
public static Crc8 Create(byte polynomial, byte initialState) => new(GenerateTable(polynomial), initialState);
/// <summary>
/// Generates a CRC-8 lookup table based on the specified polynomial.
/// </summary>
/// <param name="polynomial">The polynomial value used in the computation of the lookup table.</param>
/// <returns>An array of bytes representing the CRC-8 lookup table generated using the provided polynomial.</returns>
public static byte[] GenerateTable(byte polynomial)
{
const int tableSize = 256;
const int bitsPerByte = 8;
const byte highestBitMask = 0x80;
var table = new byte[tableSize];
for (var i = 0; i < table.Length; i++)
{
var crc = (byte)i;
for (var bitPosition = 0; bitPosition < bitsPerByte; bitPosition++)
{
if ((crc & highestBitMask) != 0)
{
crc = (byte)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
table[i] = crc;
}
return table;
}
/// <inheritdoc />
public override void Append(ReadOnlySpan<byte> source)
{
foreach (var item in source)
{
this.crc = this.lookupTable[this.crc ^ item];
}
}
/// <inheritdoc />
public override void Reset() => this.crc = this.InitialState;
/// <inheritdoc />
protected override void GetCurrentHashCore(Span<byte> destination) => destination[0] = this.crc;
}API Usage
var text = "Hello World! :)";
var bytes = Encoding.UTF8.GetBytes(text); // byte[] { 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x21 0x20 0x3A 0x29 }
var crc8 = new Crc8(); // DVB-S2 polynom
crc8.Append(bytes);
var hash = crc8.GetCurrentHash(); // 0x76
// Create instance of Crc8 with generated lookup table for CRC-8/AUTOSAR.
crc8 = Crc8.Create(0x2F, 0x00);
crc8.Append(bytes);
hash = crc8.GetCurrentHash(); // 0x40Alternative Designs
None, because this adds a new class and algorithm to the System.IO.Hashing namespace which is currently not implemented.
Risks
None, because the implementation follows the structure of the CRC32 class, and also computes the lookup table upfront to avoid bitwise calculation.