Skip to content

Commit 3615be2

Browse files
committed
Checksum Cleanup
* Bzip2Crc and Crc32 are actually the same CRC-32 formula with different starting points and end results * According to http://crcmod.sourceforge.net/crcmod.predefined.html: * Both use the same polynomial of 0x04C11DB7 * Both have an initial value of 0xFFFFFFFF * The "normal" CRC-32 has the data reversed with an unreversed output * The "BZip2" CRC-32 has the output reversed and uses unreversed data Many thanks to Francisco Javier Lana Romero and his [CRC Calculator](https://sourceforge.net/projects/crccalculator/) written in plain C for helping me to finally wrap my brain around CRCs. * Adler32 code was cleaned up a bit * Comments and exception handling was made consistent across all three * Tests added for both accuracy and exception handling
1 parent 82bebde commit 3615be2

10 files changed

Lines changed: 374 additions & 249 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using NUnit.Framework;
2+
using ICSharpCode.SharpZipLib.Checksum;
3+
using System;
4+
5+
namespace ICSharpCode.SharpZipLib.Tests.Checksum
6+
{
7+
[TestFixture]
8+
[Category("Checksum")]
9+
public class ChecksumTests
10+
{
11+
readonly
12+
// Represents ASCII string of "123456789"
13+
byte[] check = { 49, 50, 51, 52, 53, 54, 55, 56, 57 };
14+
15+
[Test]
16+
public void Adler_32()
17+
{
18+
var underTestAdler32 = new Adler32();
19+
Assert.AreEqual(0x00000001, underTestAdler32.Value);
20+
21+
underTestAdler32.Update(check);
22+
Assert.AreEqual(0x091E01DE, underTestAdler32.Value);
23+
24+
underTestAdler32.Reset();
25+
Assert.AreEqual(0x00000001, underTestAdler32.Value);
26+
27+
exceptionTesting(underTestAdler32);
28+
}
29+
30+
[Test]
31+
public void CRC_32_BZip2()
32+
{
33+
var underTestBZip2Crc = new BZip2Crc();
34+
Assert.AreEqual(0x0, underTestBZip2Crc.Value);
35+
36+
underTestBZip2Crc.Update(check);
37+
Assert.AreEqual(0xFC891918, underTestBZip2Crc.Value);
38+
39+
underTestBZip2Crc.Reset();
40+
Assert.AreEqual(0x0, underTestBZip2Crc.Value);
41+
42+
exceptionTesting(underTestBZip2Crc);
43+
}
44+
45+
[Test]
46+
public void CRC_32()
47+
{
48+
var underTestCrc32 = new Crc32();
49+
Assert.AreEqual(0x0, underTestCrc32.Value);
50+
51+
underTestCrc32.Update(check);
52+
Assert.AreEqual(0xCBF43926, underTestCrc32.Value);
53+
54+
underTestCrc32.Reset();
55+
Assert.AreEqual(0x0, underTestCrc32.Value);
56+
57+
exceptionTesting(underTestCrc32);
58+
}
59+
60+
private void exceptionTesting(IChecksum crcUnderTest) {
61+
62+
bool exception = false;
63+
64+
try {
65+
crcUnderTest.Update(null);
66+
} catch (ArgumentNullException) {
67+
exception = true;
68+
}
69+
Assert.IsTrue(exception, "Passing a null buffer should cause an ArgumentNullException");
70+
71+
// reset exception
72+
exception = false;
73+
try {
74+
crcUnderTest.Update(null, 0, 0);
75+
} catch (ArgumentNullException) {
76+
exception = true;
77+
}
78+
Assert.IsTrue(exception, "Passing a null buffer should cause an ArgumentNullException");
79+
80+
// reset exception
81+
exception = false;
82+
try {
83+
crcUnderTest.Update(check, -1, 9);
84+
} catch (ArgumentOutOfRangeException) {
85+
exception = true;
86+
}
87+
Assert.IsTrue(exception, "Passing a negative offset should cause an ArgumentOutOfRangeException");
88+
89+
// reset exception
90+
exception = false;
91+
try {
92+
crcUnderTest.Update(check, 9, 0);
93+
} catch (ArgumentOutOfRangeException) {
94+
exception = true;
95+
}
96+
Assert.IsTrue(exception, "Passing an offset greater than or equal to buffer.Length should cause an ArgumentOutOfRangeException");
97+
98+
// reset exception
99+
exception = false;
100+
try {
101+
crcUnderTest.Update(check, 0, -1);
102+
} catch (ArgumentOutOfRangeException) {
103+
exception = true;
104+
}
105+
Assert.IsTrue(exception, "Passing a negative count should cause an ArgumentOutOfRangeException");
106+
107+
// reset exception
108+
exception = false;
109+
try {
110+
crcUnderTest.Update(check, 0, 10);
111+
} catch (ArgumentOutOfRangeException) {
112+
exception = true;
113+
}
114+
Assert.IsTrue(exception, "Passing a count + offset greater than buffer.Length should cause an ArgumentOutOfRangeException");
115+
}
116+
}
117+
}

ICSharpCode.SharpZipLib.Tests/Checksum/Crc32Tests.cs

Lines changed: 0 additions & 34 deletions
This file was deleted.

ICSharpCode.SharpZipLib.Tests/ICSharpCode.SharpZipLib.Tests.csproj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
<Compile Include="AssemblyInfo.cs" />
8989
<Compile Include="Base\InflaterDeflaterTests.cs" />
9090
<Compile Include="BZip2\Bzip2Tests.cs" />
91-
<Compile Include="Checksum\Crc32Tests.cs" />
91+
<Compile Include="Checksum\ChecksumTests.cs" />
9292
<Compile Include="Core\Core.cs" />
9393
<Compile Include="GZip\GZipTests.cs" />
9494
<Compile Include="Lzw\LzwTests.cs" />
@@ -100,16 +100,16 @@
100100
<Compile Include="Zip\ZipTests.cs" />
101101
</ItemGroup>
102102
<ItemGroup>
103-
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
104-
<HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
103+
<Reference Include="nunit.framework, Version=3.2.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
104+
<HintPath>..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll</HintPath>
105105
<Private>True</Private>
106106
</Reference>
107-
<Reference Include="Ploeh.AutoFixture, Version=3.44.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
108-
<HintPath>..\packages\AutoFixture.3.44.0\lib\net40\Ploeh.AutoFixture.dll</HintPath>
107+
<Reference Include="Ploeh.AutoFixture, Version=3.45.1.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
108+
<HintPath>..\packages\AutoFixture.3.45.1\lib\net40\Ploeh.AutoFixture.dll</HintPath>
109109
<Private>True</Private>
110110
</Reference>
111-
<Reference Include="Ploeh.AutoFixture.NUnit3, Version=3.44.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
112-
<HintPath>..\packages\AutoFixture.NUnit3.3.44.0\lib\net40\Ploeh.AutoFixture.NUnit3.dll</HintPath>
111+
<Reference Include="Ploeh.AutoFixture.NUnit3, Version=3.45.1.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
112+
<HintPath>..\packages\AutoFixture.NUnit3.3.45.1\lib\net40\Ploeh.AutoFixture.NUnit3.dll</HintPath>
113113
<Private>True</Private>
114114
</Reference>
115115
<Reference Include="System" />

ICSharpCode.SharpZipLib.Tests/app.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
55
<dependentAssembly>
66
<assemblyIdentity name="nunit.framework" publicKeyToken="2638cd05610744eb" culture="neutral" />
7-
<bindingRedirect oldVersion="0.0.0.0-3.2.0.0" newVersion="3.2.0.0" />
7+
<bindingRedirect oldVersion="0.0.0.0-3.2.1.0" newVersion="3.2.1.0" />
88
</dependentAssembly>
99
</assemblyBinding>
1010
</runtime>
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="AutoFixture" version="3.44.0" targetFramework="net45" />
4-
<package id="AutoFixture.NUnit3" version="3.44.0" targetFramework="net45" />
5-
<package id="NUnit" version="3.2.0" targetFramework="net45" />
6-
<package id="NUnit.Console" version="3.2.0.1" targetFramework="net45" />
7-
<package id="NUnit.ConsoleRunner" version="3.2.0" targetFramework="net45" />
8-
<package id="NUnit.Extension.NUnitProjectLoader" version="3.2.0" targetFramework="net45" />
9-
<package id="NUnit.Extension.NUnitV2Driver" version="3.2.0" targetFramework="net45" />
10-
<package id="NUnit.Extension.NUnitV2ResultWriter" version="3.2.0" targetFramework="net45" />
11-
<package id="NUnit.Extension.VSProjectLoader" version="3.2.0" targetFramework="net45" />
3+
<package id="AutoFixture" version="3.45.1" targetFramework="net45" />
4+
<package id="AutoFixture.NUnit3" version="3.45.1" targetFramework="net45" />
5+
<package id="NUnit" version="3.2.1" targetFramework="net45" />
6+
<package id="NUnit.Console" version="3.2.1" targetFramework="net45" />
7+
<package id="NUnit.ConsoleRunner" version="3.2.1" targetFramework="net45" />
8+
<package id="NUnit.Extension.NUnitProjectLoader" version="3.2.1" targetFramework="net45" />
9+
<package id="NUnit.Extension.NUnitV2Driver" version="3.2.1" targetFramework="net45" />
10+
<package id="NUnit.Extension.NUnitV2ResultWriter" version="3.2.1" targetFramework="net45" />
11+
<package id="NUnit.Extension.VSProjectLoader" version="3.2.1" targetFramework="net45" />
1212
<package id="OpenCover" version="4.6.519" targetFramework="net45" />
13-
<package id="ReportGenerator" version="2.4.4.0" targetFramework="net45" />
13+
<package id="ReportGenerator" version="2.4.5.0" targetFramework="net45" />
1414
</packages>

ICSharpCode.SharpZipLib/Checksum/Adler32.cs

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -89,64 +89,67 @@ namespace ICSharpCode.SharpZipLib.Checksum
8989
/// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream"/>
9090
public sealed class Adler32 : IChecksum
9191
{
92+
#region Instance Fields
9293
/// <summary>
9394
/// largest prime smaller than 65536
9495
/// </summary>
95-
const uint BASE = 65521;
96+
readonly static uint BASE = 65521;
9697

9798
/// <summary>
98-
/// Returns the Adler32 data checksum computed so far.
99+
/// The CRC data checksum so far.
99100
/// </summary>
100-
public long Value
101-
{
102-
get
103-
{
104-
return checksum;
105-
}
106-
}
101+
uint checkValue;
102+
#endregion
107103

108104
/// <summary>
109-
/// Creates a new instance of the Adler32 class.
110-
/// The checksum starts off with a value of 1.
111-
/// </summary>
105+
/// Initialise a default instance of <see cref="Adler32"></see>
106+
/// </summary>
112107
public Adler32()
113108
{
114109
Reset();
115110
}
116111

117112
/// <summary>
118-
/// Resets the Adler32 checksum to the initial value.
113+
/// Resets the Adler32 data checksum as if no update was ever called.
119114
/// </summary>
120115
public void Reset()
121116
{
122-
checksum = 1;
117+
checkValue = 1;
118+
}
119+
120+
/// <summary>
121+
/// Returns the Adler32 data checksum computed so far.
122+
/// </summary>
123+
public long Value {
124+
get {
125+
return checkValue;
126+
}
123127
}
124128

125129
/// <summary>
126-
/// Updates the checksum with a byte value.
130+
/// Updates the checksum with the byte b.
127131
/// </summary>
128-
/// <param name="value">
132+
/// <param name="bval">
129133
/// The data value to add. The high byte of the int is ignored.
130134
/// </param>
131-
public void Update(int value)
135+
public void Update(int bval)
132136
{
133137
// We could make a length 1 byte array and call update again, but I
134138
// would rather not have that overhead
135-
uint s1 = checksum & 0xFFFF;
136-
uint s2 = checksum >> 16;
139+
uint s1 = checkValue & 0xFFFF;
140+
uint s2 = checkValue >> 16;
137141

138-
s1 = (s1 + ((uint)value & 0xFF)) % BASE;
142+
s1 = (s1 + ((uint)bval & 0xFF)) % BASE;
139143
s2 = (s1 + s2) % BASE;
140144

141-
checksum = (s2 << 16) + s1;
145+
checkValue = (s2 << 16) + s1;
142146
}
143147

144148
/// <summary>
145-
/// Updates the checksum with an array of bytes.
149+
/// Updates the Adler32 data checksum with the bytes taken from
150+
/// a block of data.
146151
/// </summary>
147-
/// <param name="buffer">
148-
/// The source of the data to update with.
149-
/// </param>
152+
/// <param name="buffer">Contains the data to update the checksum with.</param>
150153
public void Update(byte[] buffer)
151154
{
152155
if (buffer == null) {
@@ -157,42 +160,36 @@ public void Update(byte[] buffer)
157160
}
158161

159162
/// <summary>
160-
/// Updates the checksum with the bytes taken from the array.
163+
/// Update Adler32 data checksum based on a portion of a block of data
161164
/// </summary>
162-
/// <param name="buffer">
163-
/// an array of bytes
164-
/// </param>
165-
/// <param name="offset">
166-
/// the start of the data used for this update
167-
/// </param>
168-
/// <param name="count">
169-
/// the number of bytes to use for this update
170-
/// </param>
165+
/// <param name = "buffer">Contains the data to update the CRC with.</param>
166+
/// <param name = "offset">The offset into the buffer where the data starts</param>
167+
/// <param name = "count">The number of data bytes to update the CRC with.</param>
171168
public void Update(byte[] buffer, int offset, int count)
172169
{
173170
if (buffer == null) {
174171
throw new ArgumentNullException(nameof(buffer));
175172
}
176173

177174
if (offset < 0) {
178-
throw new ArgumentOutOfRangeException(nameof(offset), "cannot be negative");
179-
}
180-
181-
if (count < 0) {
182-
throw new ArgumentOutOfRangeException(nameof(count), "cannot be negative");
175+
throw new ArgumentOutOfRangeException(nameof(offset), "cannot be less than zero");
183176
}
184177

185178
if (offset >= buffer.Length) {
186179
throw new ArgumentOutOfRangeException(nameof(offset), "not a valid index into buffer");
187180
}
188181

182+
if (count < 0) {
183+
throw new ArgumentOutOfRangeException(nameof(count), "cannot be less than zero");
184+
}
185+
189186
if (offset + count > buffer.Length) {
190187
throw new ArgumentOutOfRangeException(nameof(count), "exceeds buffer size");
191188
}
192189

193190
//(By Per Bothner)
194-
uint s1 = checksum & 0xFFFF;
195-
uint s2 = checksum >> 16;
191+
uint s1 = checkValue & 0xFFFF;
192+
uint s2 = checkValue >> 16;
196193

197194
while (count > 0) {
198195
// We can defer the modulo operation:
@@ -211,11 +208,7 @@ public void Update(byte[] buffer, int offset, int count)
211208
s2 %= BASE;
212209
}
213210

214-
checksum = (s2 << 16) | s1;
211+
checkValue = (s2 << 16) | s1;
215212
}
216-
217-
#region Instance Fields
218-
uint checksum;
219-
#endregion
220213
}
221214
}

0 commit comments

Comments
 (0)