diff --git a/Crc32.NET.Tests/ImplementationTest.cs b/Crc32.NET.Tests/ImplementationTest.cs index 7bfe6da..d543d90 100644 --- a/Crc32.NET.Tests/ImplementationTest.cs +++ b/Crc32.NET.Tests/ImplementationTest.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Text; @@ -30,7 +31,7 @@ public void ResultConsistency(string text, int offset) Assert.That(crc2, Is.EqualTo(crc1)); } #endif - + [Test] public void ResultConsistency2() { @@ -52,7 +53,7 @@ public void ResultConsistencyAsHashAlgorithm() Console.WriteLine(crc2.ToString("X8")); Assert.That(crc1, Is.EqualTo(crc2)); } -#endif +#endif [Test] public void PartIsWhole() @@ -119,5 +120,47 @@ public void Computation_With_Crc_End_Should_Be_Validated(int length) Assert.That(Crc32Algorithm.IsValidWithCrcAtEnd(buf, 1, length - 2 + 4), Is.False); } } + + [Test] + public void Compute_With_Stream_Should_Be_Consistent() + { + var buf = new byte[5000]; + var r = new Random(); + r.NextBytes(buf); + + using (var ms = new MemoryStream(buf)) + { + Assert.AreEqual(Crc32Algorithm.Compute(buf), Crc32Algorithm.Compute(ms)); + } + } + + [Test] + public void Compute_And_Write_To_End_With_Stream_Should_Be_Consistent() + { + var buf = new byte[5000]; + var r = new Random(); + r.NextBytes(buf); + + var copyA = new byte[buf.Length + 4]; + Array.Copy(buf, copyA, buf.Length); + + Crc32Algorithm.ComputeAndWriteToEnd(copyA); + + using (var ms = new MemoryStream()) + { + ms.Write(buf, 0, buf.Length); + ms.Seek(0, SeekOrigin.Begin); + + Crc32Algorithm.ComputeAndWriteToEnd(ms, buf.Length); + + ms.Seek(-4, SeekOrigin.End); + + for (int i = 0; i < 4; ++i) + { + var index = i + buf.Length; + Assert.AreEqual(copyA[index], ms.ReadByte()); + } + } + } } } diff --git a/Crc32.NET/Crc32Algorithm.cs b/Crc32.NET/Crc32Algorithm.cs index ccacd03..c911202 100644 --- a/Crc32.NET/Crc32Algorithm.cs +++ b/Crc32.NET/Crc32Algorithm.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Security.Cryptography; namespace Force.Crc32 @@ -107,10 +108,84 @@ public static uint ComputeAndWriteToEnd(byte[] input, int offset, int length) throw new ArgumentOutOfRangeException("length", "Length of data should be less than array length - 4 bytes of CRC data"); var crc = Append(0, input, offset, length); var r = offset + length; - input[r] = (byte)crc; - input[r + 1] = (byte)(crc >> 8); - input[r + 2] = (byte)(crc >> 16); - input[r + 3] = (byte)(crc >> 24); + SetCrcBytes(input, r, crc); + return crc; + } + + private static void SetCrcBytes(byte[] input, int offset, uint crc) + { + input[offset] = (byte) crc; + input[offset + 1] = (byte) (crc >> 8); + input[offset + 2] = (byte) (crc >> 16); + input[offset + 3] = (byte) (crc >> 24); + } + + /// + /// Computes CRC-32 from input stream. + /// + /// Input stream with data to be checksummed. + /// Length of the input data in the stream. + /// CRC-32 of the data in the stream. + public static uint Compute(Stream input, long length) + { + if (!input.CanRead) + { + throw new ArgumentException("input", "Input stream must be readable"); + } + + const int bufferLength = 4096; + var buffer = new byte[bufferLength]; + uint crc = 0; + var totalRemaining = length; + + while (true) + { + var lengthToRead = (int) Math.Min(totalRemaining, bufferLength); + + var bytesRead = input.Read(buffer, 0, lengthToRead); + if (bytesRead == 0) + { + break; + } + + crc = Append(crc, buffer, 0, bytesRead); + totalRemaining -= bytesRead; + } + + return crc; + } + + /// + /// Computes CRC-32 from entire input stream. + /// + /// Input stream with data to be checksummed. + /// CRC-32 of the data in the stream. + public static uint Compute(Stream input) + { + return Compute(input, long.MaxValue); + } + + /// + /// Computes CRC-32 from input stream and writes it at the end. + /// + /// Input stream with data to be checksummed. + /// Length of the input data in the stream. + /// CRC-32 of the data in the stream. + public static uint ComputeAndWriteToEnd(Stream input, long length) + { + if (!input.CanWrite) + { + throw new ArgumentException("input", "Input stream must be writable"); + } + + var crc = Compute(input, length); + + var writeBuffer = new byte[4]; + + SetCrcBytes(writeBuffer, 0, crc); + + input.Write(writeBuffer, 0, writeBuffer.Length); + return crc; }