/
Crc32.cs
170 lines (152 loc) · 6.31 KB
/
Crc32.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers.Binary;
namespace System.IO.Hashing
{
/// <summary>
/// Provides an implementation of the CRC-32 algorithm, as used in
/// ITU-T V.42 and IEEE 802.3.
/// </summary>
/// <remarks>
/// <para>
/// This implementation emits the answer in the Little Endian byte order so that
/// the CRC residue relationship (CRC(message concat CRC(message))) is a fixed value) holds.
/// For CRC-32 this stable output is the byte sequence <c>{ 0x1C, 0xDF, 0x44, 0x21 }</c>,
/// the Little Endian representation of <c>0x2144DF1C</c>.
/// </para>
/// <para>
/// There are multiple, incompatible, definitions of a 32-bit cyclic redundancy
/// check (CRC) algorithm. When interoperating with another system, ensure that you
/// are using the same definition. The definition used by this implementation is not
/// compatible with the cyclic redundancy check described in ITU-T I.363.5.
/// </para>
/// </remarks>
public sealed partial class Crc32 : NonCryptographicHashAlgorithm
{
private const uint InitialState = 0xFFFF_FFFFu;
private const int Size = sizeof(uint);
private uint _crc = InitialState;
/// <summary>
/// Initializes a new instance of the <see cref="Crc32"/> class.
/// </summary>
public Crc32()
: base(Size)
{
}
/// <summary>
/// Appends the contents of <paramref name="source"/> to the data already
/// processed for the current hash computation.
/// </summary>
/// <param name="source">The data to process.</param>
public override void Append(ReadOnlySpan<byte> source)
{
_crc = Update(_crc, source);
}
/// <summary>
/// Resets the hash computation to the initial state.
/// </summary>
public override void Reset()
{
_crc = InitialState;
}
/// <summary>
/// Writes the computed hash value to <paramref name="destination"/>
/// without modifying accumulated state.
/// </summary>
/// <param name="destination">The buffer that receives the computed hash value.</param>
protected override void GetCurrentHashCore(Span<byte> destination)
{
// The finalization step of the CRC is to perform the ones' complement.
BinaryPrimitives.WriteUInt32LittleEndian(destination, ~_crc);
}
/// <summary>
/// Writes the computed hash value to <paramref name="destination"/>
/// then clears the accumulated state.
/// </summary>
protected override void GetHashAndResetCore(Span<byte> destination)
{
BinaryPrimitives.WriteUInt32LittleEndian(destination, ~_crc);
_crc = InitialState;
}
/// <summary>
/// Computes the CRC-32 hash of the provided data.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <returns>The CRC-32 hash of the provided data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> is <see langword="null"/>.
/// </exception>
public static byte[] Hash(byte[] source)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
return Hash(new ReadOnlySpan<byte>(source));
}
/// <summary>
/// Computes the CRC-32 hash of the provided data.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <returns>The CRC-32 hash of the provided data.</returns>
public static byte[] Hash(ReadOnlySpan<byte> source)
{
byte[] ret = new byte[Size];
StaticHash(source, ret);
return ret;
}
/// <summary>
/// Attempts to compute the CRC-32 hash of the provided data into the provided destination.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <param name="destination">The buffer that receives the computed hash value.</param>
/// <param name="bytesWritten">
/// On success, receives the number of bytes written to <paramref name="destination"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if <paramref name="destination"/> is long enough to receive
/// the computed hash value (4 bytes); otherwise, <see langword="false"/>.
/// </returns>
public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
{
if (destination.Length < Size)
{
bytesWritten = 0;
return false;
}
bytesWritten = StaticHash(source, destination);
return true;
}
/// <summary>
/// Computes the CRC-32 hash of the provided data into the provided destination.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <param name="destination">The buffer that receives the computed hash value.</param>
/// <returns>
/// The number of bytes written to <paramref name="destination"/>.
/// </returns>
public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination)
{
if (destination.Length < Size)
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
return StaticHash(source, destination);
}
private static int StaticHash(ReadOnlySpan<byte> source, Span<byte> destination)
{
uint crc = InitialState;
crc = Update(crc, source);
BinaryPrimitives.WriteUInt32LittleEndian(destination, ~crc);
return Size;
}
private static uint Update(uint crc, ReadOnlySpan<byte> source)
{
for (int i = 0; i < source.Length; i++)
{
byte idx = (byte)crc;
idx ^= source[i];
crc = s_crcLookup[idx] ^ (crc >> 8);
}
return crc;
}
}
}