// // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Runtime.InteropServices; using System.Text; namespace DotZLib { #region ChecksumGeneratorBase /// <summary> /// Implements the common functionality needed for all <see cref="ChecksumGenerator"/>s /// </summary> /// <example></example> public abstract class ChecksumGeneratorBase : ChecksumGenerator { /// <summary> /// The value of the current checksum /// </summary> protected uint _current; /// <summary> /// Initializes a new instance of the checksum generator base - the current checksum is /// set to zero /// </summary> public ChecksumGeneratorBase() { _current = 0; } /// <summary> /// Initializes a new instance of the checksum generator base with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public ChecksumGeneratorBase(uint initialValue) { _current = initialValue; } /// <summary> /// Resets the current checksum to zero /// </summary> public void Reset() { _current = 0; } /// <summary> /// Gets the current checksum value /// </summary> public uint Value { get { return _current; } } /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> /// <remarks>All the other <c>Update</c> methods are implemented in terms of this one. /// This is therefore the only method a derived class has to implement</remarks> public abstract void Update(byte[] data, int offset, int count); /// <summary> /// Updates the current checksum with an array of bytes. /// </summary> /// <param name="data">The data to update the checksum with</param> public void Update(byte[] data) { Update(data, 0, data.Length); } /// <summary> /// Updates the current checksum with the data from a string /// </summary> /// <param name="data">The string to update the checksum with</param> /// <remarks>The characters in the string are converted by the UTF-8 encoding</remarks> public void Update(string data) { Update(Encoding.UTF8.GetBytes(data)); } /// <summary> /// Updates the current checksum with the data from a string, using a specific encoding /// </summary> /// <param name="data">The string to update the checksum with</param> /// <param name="encoding">The encoding to use</param> public void Update(string data, Encoding encoding) { Update(encoding.GetBytes(data)); } } #endregion #region CRC32 /// <summary> /// Implements a CRC32 checksum generator /// </summary> public sealed class CRC32Checksum : ChecksumGeneratorBase { #region DLL imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern uint crc32(uint crc, int data, uint length); #endregion /// <summary> /// Initializes a new instance of the CRC32 checksum generator /// </summary> public CRC32Checksum() : base() {} /// <summary> /// Initializes a new instance of the CRC32 checksum generator with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public CRC32Checksum(uint initialValue) : base(initialValue) {} /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> public override void Update(byte[] data, int offset, int count) { if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); try { _current = crc32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); } finally { hData.Free(); } } } #endregion #region Adler /// <summary> /// Implements a checksum generator that computes the Adler checksum on data /// </summary> public sealed class AdlerChecksum : ChecksumGeneratorBase { #region DLL imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern uint adler32(uint adler, int data, uint length); #endregion /// <summary> /// Initializes a new instance of the Adler checksum generator /// </summary> public AdlerChecksum() : base() {} /// <summary> /// Initializes a new instance of the Adler checksum generator with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public AdlerChecksum(uint initialValue) : base(initialValue) {} /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> public override void Update(byte[] data, int offset, int count) { if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); try { _current = adler32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); } finally { hData.Free(); } } } #endregion }