Skip to content

Commit fd26ea9

Browse files
AraHaanbartonjsvcsjonesstephentoub
authored
Added Adler32 to System.IO.Hashing. (#123601)
Signed-off-by: AraHaan <seandhunt_7@yahoo.com> Co-authored-by: Jeremy Barton <jbarton@microsoft.com> Co-authored-by: Kevin Jones <vcsjones@github.com> Co-authored-by: Stephen Toub <stoub@microsoft.com>
1 parent 55f6dc4 commit fd26ea9

File tree

5 files changed

+401
-0
lines changed

5 files changed

+401
-0
lines changed

src/libraries/System.IO.Hashing/ref/System.IO.Hashing.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66

77
namespace System.IO.Hashing
88
{
9+
public sealed partial class Adler32 : System.IO.Hashing.NonCryptographicHashAlgorithm
10+
{
11+
public Adler32() : base (default(int)) { }
12+
public override void Append(System.ReadOnlySpan<byte> source) { }
13+
public System.IO.Hashing.Adler32 Clone() { throw null; }
14+
[System.CLSCompliantAttribute(false)]
15+
public uint GetCurrentHashAsUInt32() { throw null; }
16+
protected override void GetCurrentHashCore(System.Span<byte> destination) { }
17+
protected override void GetHashAndResetCore(System.Span<byte> destination) { }
18+
public static byte[] Hash(byte[] source) { throw null; }
19+
public static byte[] Hash(System.ReadOnlySpan<byte> source) { throw null; }
20+
public static int Hash(System.ReadOnlySpan<byte> source, System.Span<byte> destination) { throw null; }
21+
[System.CLSCompliantAttribute(false)]
22+
public static uint HashToUInt32(System.ReadOnlySpan<byte> source) { throw null; }
23+
public override void Reset() { }
24+
public static bool TryHash(System.ReadOnlySpan<byte> source, System.Span<byte> destination, out int bytesWritten) { throw null; }
25+
}
926
public sealed partial class Crc32 : System.IO.Hashing.NonCryptographicHashAlgorithm
1027
{
1128
public Crc32() : base (default(int)) { }

src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ System.IO.Hashing.XxHash32</PackageDescription>
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16+
<Compile Include="System\IO\Hashing\Adler32.cs" />
1617
<Compile Include="System\IO\Hashing\Crc32.cs" />
1718
<Compile Include="System\IO\Hashing\Crc32.Table.cs" />
1819
<Compile Include="System\IO\Hashing\Crc64.cs" />
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Buffers.Binary;
5+
6+
namespace System.IO.Hashing
7+
{
8+
/// <summary>
9+
/// Provides an implementation of the Adler-32 algorithm, as used in
10+
/// RFC1950.
11+
/// </summary>
12+
/// <remarks>
13+
/// <para>
14+
/// The Adler-32 algorithm is designed for fast, lightweight integrity checking and is commonly used in
15+
/// data compression and transmission scenarios. This class is not suitable for cryptographic purposes.
16+
/// </para>
17+
/// <para>
18+
/// Adler-32 is not as robust as other checksum algorithms like CRC32, but it is faster to compute.
19+
/// </para>
20+
/// </remarks>
21+
public sealed partial class Adler32 : NonCryptographicHashAlgorithm
22+
{
23+
private const uint InitialState = 1u;
24+
private const int Size = sizeof(uint);
25+
private uint _adler = InitialState;
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="Adler32"/> class.
29+
/// </summary>
30+
public Adler32()
31+
: base(Size)
32+
{
33+
}
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="Adler32"/> class using the state from another instance.
37+
/// </summary>
38+
private Adler32(uint adler)
39+
: base(Size)
40+
=> _adler = adler;
41+
42+
/// <summary>
43+
/// Returns a clone of the current instance, with a copy of the current instance's internal state.
44+
/// </summary>
45+
/// <returns>
46+
/// A new instance that will produce the same sequence of values as the current instance.
47+
/// </returns>
48+
public Adler32 Clone()
49+
=> new(_adler);
50+
51+
/// <summary>
52+
/// Appends the contents of <paramref name="source"/> to the data already
53+
/// processed for the current hash computation.
54+
/// </summary>
55+
/// <param name="source">The data to process.</param>
56+
public override void Append(ReadOnlySpan<byte> source)
57+
=> _adler = Update(_adler, source);
58+
59+
/// <summary>
60+
/// Resets the hash computation to the initial state.
61+
/// </summary>
62+
public override void Reset()
63+
=> _adler = InitialState;
64+
65+
/// <summary>
66+
/// Writes the computed hash value to <paramref name="destination"/>
67+
/// without modifying accumulated state.
68+
/// </summary>
69+
/// <param name="destination">The buffer that receives the computed hash value.</param>
70+
protected override void GetCurrentHashCore(Span<byte> destination)
71+
=> BinaryPrimitives.WriteUInt32BigEndian(destination, _adler);
72+
73+
/// <summary>
74+
/// Writes the computed hash value to <paramref name="destination"/>
75+
/// then clears the accumulated state.
76+
/// </summary>
77+
protected override void GetHashAndResetCore(Span<byte> destination)
78+
{
79+
BinaryPrimitives.WriteUInt32BigEndian(destination, _adler);
80+
_adler = InitialState;
81+
}
82+
83+
/// <summary>
84+
/// Gets the current computed hash value without modifying accumulated state.
85+
/// </summary>
86+
/// <returns>
87+
/// The hash value for the data already provided.
88+
/// </returns>
89+
[CLSCompliant(false)]
90+
public uint GetCurrentHashAsUInt32()
91+
=> _adler;
92+
93+
/// <summary>
94+
/// Computes the Adler-32 hash of the provided data.
95+
/// </summary>
96+
/// <param name="source">The data to hash.</param>
97+
/// <returns>The Adler-32 hash of the provided data.</returns>
98+
/// <exception cref="ArgumentNullException">
99+
/// <paramref name="source"/> is <see langword="null"/>.
100+
/// </exception>
101+
public static byte[] Hash(byte[] source)
102+
{
103+
ArgumentNullException.ThrowIfNull(source);
104+
return Hash(new ReadOnlySpan<byte>(source));
105+
}
106+
107+
/// <summary>
108+
/// Computes the Adler-32 hash of the provided data.
109+
/// </summary>
110+
/// <param name="source">The data to hash.</param>
111+
/// <returns>The Adler-32 hash of the provided data.</returns>
112+
public static byte[] Hash(ReadOnlySpan<byte> source)
113+
{
114+
byte[] ret = new byte[Size];
115+
uint hash = HashToUInt32(source);
116+
BinaryPrimitives.WriteUInt32BigEndian(ret, hash);
117+
return ret;
118+
}
119+
120+
/// <summary>
121+
/// Attempts to compute the Adler-32 hash of the provided data into the provided destination.
122+
/// </summary>
123+
/// <param name="source">The data to hash.</param>
124+
/// <param name="destination">The buffer that receives the computed hash value.</param>
125+
/// <param name="bytesWritten">
126+
/// On success, receives the number of bytes written to <paramref name="destination"/>.
127+
/// </param>
128+
/// <returns>
129+
/// <see langword="true"/> if <paramref name="destination"/> is long enough to receive
130+
/// the computed hash value (4 bytes); otherwise, <see langword="false"/>.
131+
/// </returns>
132+
public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
133+
{
134+
if (destination.Length < Size)
135+
{
136+
bytesWritten = 0;
137+
return false;
138+
}
139+
140+
uint hash = HashToUInt32(source);
141+
BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
142+
bytesWritten = Size;
143+
return true;
144+
}
145+
146+
/// <summary>
147+
/// Computes the Adler-32 hash of the provided data into the provided destination.
148+
/// </summary>
149+
/// <param name="source">The data to hash.</param>
150+
/// <param name="destination">The buffer that receives the computed hash value.</param>
151+
/// <returns>
152+
/// The number of bytes written to <paramref name="destination"/>.
153+
/// </returns>
154+
public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination)
155+
{
156+
if (destination.Length < Size)
157+
{
158+
ThrowDestinationTooShort();
159+
}
160+
161+
uint hash = HashToUInt32(source);
162+
BinaryPrimitives.WriteUInt32BigEndian(destination, hash);
163+
return Size;
164+
}
165+
166+
/// <summary>
167+
/// Computes the Adler-32 hash of the provided data.
168+
/// </summary>
169+
/// <param name="source">The data to hash.</param>
170+
/// <returns>
171+
/// The computed Adler-32 hash.
172+
/// </returns>
173+
[CLSCompliant(false)]
174+
public static uint HashToUInt32(ReadOnlySpan<byte> source)
175+
=> Update(InitialState, source);
176+
177+
private static uint Update(uint adler, ReadOnlySpan<byte> buf)
178+
{
179+
if (buf.IsEmpty)
180+
{
181+
return adler;
182+
}
183+
184+
return UpdateScalar(adler, buf);
185+
}
186+
187+
private static uint UpdateScalar(uint adler, ReadOnlySpan<byte> buf)
188+
{
189+
const uint Base = 65521; // largest prime smaller than 65536
190+
const int NMax = 5552; // NMax is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
191+
192+
uint s1 = adler & 0xFFFF;
193+
uint s2 = (adler >> 16) & 0xFFFF;
194+
while (buf.Length > 0)
195+
{
196+
int k = buf.Length < NMax ? buf.Length : NMax;
197+
foreach (byte b in buf.Slice(0, k))
198+
{
199+
s1 += b;
200+
s2 += s1;
201+
}
202+
s1 %= Base;
203+
s2 %= Base;
204+
buf = buf.Slice(k);
205+
}
206+
207+
return (s2 << 16) | s1;
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)