/
Crc64.cs
193 lines (172 loc) · 7.14 KB
/
Crc64.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// 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-64 algorithm as described in ECMA-182, Annex B.
/// </summary>
/// <remarks>
/// <para>
/// For methods that return byte arrays or that write into spans of bytes,
/// this implementation emits the answer in the Big Endian byte order so that
/// the CRC residue relationship (CRC(message concat CRC(message))) is a fixed value) holds.
/// For CRC-64 this stable output is the byte sequence
/// <c>{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }</c>.
/// </para>
/// <para>
/// There are multiple, incompatible, definitions of a 64-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 ISO 3309.
/// </para>
/// </remarks>
public sealed partial class Crc64 : NonCryptographicHashAlgorithm
{
private const ulong InitialState = 0UL;
private const int Size = sizeof(ulong);
private ulong _crc = InitialState;
/// <summary>
/// Initializes a new instance of the <see cref="Crc64"/> class.
/// </summary>
public Crc64()
: 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)
{
BinaryPrimitives.WriteUInt64BigEndian(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.WriteUInt64BigEndian(destination, _crc);
_crc = InitialState;
}
/// <summary>Gets the current computed hash value without modifying accumulated state.</summary>
/// <returns>The hash value for the data already provided.</returns>
[CLSCompliant(false)]
public ulong GetCurrentHashAsUInt64() => _crc;
/// <summary>
/// Computes the CRC-64 hash of the provided data.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <returns>The CRC-64 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-64 hash of the provided data.
/// </summary>
/// <param name="source">The data to hash.</param>
/// <returns>The CRC-64 hash of the provided data.</returns>
public static byte[] Hash(ReadOnlySpan<byte> source)
{
byte[] ret = new byte[Size];
ulong hash = HashToUInt64(source);
BinaryPrimitives.WriteUInt64BigEndian(ret, hash);
return ret;
}
/// <summary>
/// Attempts to compute the CRC-64 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 (8 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;
}
ulong hash = HashToUInt64(source);
BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
bytesWritten = Size;
return true;
}
/// <summary>
/// Computes the CRC-64 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)
{
ThrowDestinationTooShort();
}
ulong hash = HashToUInt64(source);
BinaryPrimitives.WriteUInt64BigEndian(destination, hash);
return Size;
}
/// <summary>Computes the CRC-64 hash of the provided data.</summary>
/// <param name="source">The data to hash.</param>
/// <returns>The computed CRC-64 hash.</returns>
[CLSCompliant(false)]
public static ulong HashToUInt64(ReadOnlySpan<byte> source) =>
Update(InitialState, source);
private static ulong Update(ulong crc, ReadOnlySpan<byte> source)
{
#if NET7_0_OR_GREATER
if (CanBeVectorized(source))
{
return UpdateVectorized(crc, source);
}
#endif
return UpdateScalar(crc, source);
}
private static ulong UpdateScalar(ulong crc, ReadOnlySpan<byte> source)
{
ReadOnlySpan<ulong> crcLookup = CrcLookup;
for (int i = 0; i < source.Length; i++)
{
ulong idx = (crc >> 56);
idx ^= source[i];
crc = crcLookup[(int)idx] ^ (crc << 8);
}
return crc;
}
}
}