git-svn-id: https://duplicati.googlecode.com/svn/trunk@57 59da171f-624f-0410-aa54-27559c288bec
269 lines
8.5 KiB
C#
269 lines
8.5 KiB
C#
#region Disclaimer / License
|
|
// Copyright (C) 2008, Kenneth Skovhede
|
|
// http://www.hexad.dk, opensource@hexad.dk
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
#endregion
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Duplicati.Library.SharpRSync
|
|
{
|
|
/// <summary>
|
|
/// This is an implementation of the Adler-32 rolling checksum.
|
|
/// This implementation is converted to C# from C code in librsync.
|
|
/// </summary>
|
|
public class Adler32Checksum
|
|
{
|
|
/// <summary>
|
|
/// The number of bytes to include in each checksum
|
|
/// </summary>
|
|
public const int DEFAULT_BLOCK_SIZE = 2048;
|
|
private const uint CHAR_OFFSET = 31;
|
|
|
|
private System.IO.Stream m_source;
|
|
private RollingBuffer m_buffer;
|
|
private bool m_done;
|
|
|
|
private uint m_s1;
|
|
private uint m_s2;
|
|
private long m_count;
|
|
private long m_blockSize;
|
|
|
|
/// <summary>
|
|
/// Constructs a new adler32 instance, reading from a rolling buffer
|
|
/// </summary>
|
|
/// <param name="buffer">The buffer to read from</param>
|
|
/// <param name="blocksize">The size of a single block</param>
|
|
public Adler32Checksum(RollingBuffer buffer, int blocksize)
|
|
{
|
|
m_buffer = buffer;
|
|
m_done = false;
|
|
m_count = 0;
|
|
m_blockSize = blocksize;
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new adler32 instance, reading from a stream
|
|
/// </summary>
|
|
/// <param name="buffer">The buffer to read from</param>
|
|
/// <param name="blocksize">The size of a single block</param>
|
|
public Adler32Checksum(System.IO.Stream source, int blocksize)
|
|
{
|
|
m_source = source;
|
|
m_done = false;
|
|
m_count = 0;
|
|
m_blockSize = blocksize;
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new adler32 instance, reading from a stream
|
|
/// </summary>
|
|
/// <param name="sourc">The buffer to read from</param>
|
|
public Adler32Checksum(System.IO.Stream source)
|
|
: this(source, DEFAULT_BLOCK_SIZE)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating if the last data has been read from the source
|
|
/// </summary>
|
|
public bool Done { get { return m_done; } }
|
|
|
|
/// <summary>
|
|
/// Gets the current checksum
|
|
/// </summary>
|
|
public uint Checksum { get { return (uint)((m_s1 & 0xffff) + (m_s2 << 16)); } }
|
|
|
|
/// <summary>
|
|
/// Resets the checksum and calculates the checksum for a new block
|
|
/// </summary>
|
|
/// <returns>False if there were no new data</returns>
|
|
public bool Reset()
|
|
{
|
|
int i;
|
|
byte[] buf = new byte[4];
|
|
int a;
|
|
|
|
if (m_done)
|
|
return false;
|
|
|
|
m_s1 = m_s2 = 0;
|
|
m_count = 0;
|
|
|
|
for (i = 0; i < m_blockSize - (m_blockSize % 4); i += 4)
|
|
{
|
|
if (m_source != null)
|
|
a = Utility.ForceStreamRead(m_source, buf, buf.Length);
|
|
else
|
|
{
|
|
a = m_buffer.Advance(4);
|
|
m_buffer.GetHead(buf, 0, a);
|
|
}
|
|
|
|
if (a == 4)
|
|
{
|
|
m_s2 += (uint)(4 * (m_s1 + buf[0]) + 3 * buf[1] +
|
|
2 * buf[2] + buf[3] + 10 * CHAR_OFFSET);
|
|
m_s1 += (uint)(buf[0] + buf[1] + buf[2] + buf[3] +
|
|
4 * CHAR_OFFSET);
|
|
|
|
m_s1 %= ushort.MaxValue + 1;
|
|
m_s2 %= ushort.MaxValue + 1;
|
|
|
|
m_count += 4;
|
|
}
|
|
else
|
|
{
|
|
for(int j = 0; j < a; j++)
|
|
UpdateChecksum(buf[j]);
|
|
m_count += a;
|
|
m_done = true;
|
|
return m_count != 0;
|
|
}
|
|
}
|
|
|
|
if (m_source != null)
|
|
a = Utility.ForceStreamRead(m_source, buf, (int)m_blockSize % 4);
|
|
else
|
|
{
|
|
a = m_buffer.Advance((int)m_blockSize % 4);
|
|
m_buffer.GetHead(buf, 0, a);
|
|
}
|
|
|
|
for (i = 0; i < a; i++)
|
|
UpdateChecksum(buf[i]);
|
|
m_count += a;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rolls the checksum a number of bytes
|
|
/// </summary>
|
|
/// <param name="count">The number of bytes to roll</param>
|
|
/// <returns>True if there is new data, false otherwise</returns>
|
|
public bool AdvanceChecksum(int count)
|
|
{
|
|
if (m_done)
|
|
return false;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
if (!AdvanceChecksum())
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rolls the checksum a single byte
|
|
/// </summary>
|
|
/// <returns>True if a new byte was checksummed, false if there was no more data</returns>
|
|
public bool AdvanceChecksum()
|
|
{
|
|
if (m_done)
|
|
return false;
|
|
|
|
int b;
|
|
if (m_source != null)
|
|
{
|
|
b = m_source.ReadByte();
|
|
if (b == -1)
|
|
{
|
|
m_done = true;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_buffer.Advance(1) != 1)
|
|
{
|
|
m_done = true;
|
|
return false;
|
|
}
|
|
b = m_buffer.GetByteAt(m_buffer.Count - 1);
|
|
}
|
|
|
|
m_count += 1;
|
|
|
|
UpdateChecksum((byte)b);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the checksum with the read byte
|
|
/// </summary>
|
|
/// <param name="value">The byte read</param>
|
|
private void UpdateChecksum(byte value)
|
|
{
|
|
m_s1 += value + CHAR_OFFSET;
|
|
m_s2 += m_s1;
|
|
|
|
m_s1 %= ushort.MaxValue + 1;
|
|
m_s2 %= ushort.MaxValue + 1;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Rolls the buffer, so the checksum now fits a block that is shifted one byte from the previous one.
|
|
/// </summary>
|
|
/// <returns>True if new data was included, false otherwise</returns>
|
|
public bool Rollbuffer()
|
|
{
|
|
if (m_buffer == null)
|
|
throw new Exception("Cannot roll buffer, when the source is not a rolling buffer instance");
|
|
|
|
if (m_done)
|
|
return false;
|
|
|
|
if (m_buffer.Advance(1) != 1)
|
|
{
|
|
m_done = true;
|
|
return false;
|
|
}
|
|
|
|
byte out_byte = m_buffer.GetByteAt(m_buffer.Count - m_count - 1);
|
|
byte in_byte = m_buffer.GetByteAt(m_buffer.Count - 1);
|
|
|
|
//TODO: This can be done much nicer, but the .Net overflow detection
|
|
//prevents it.
|
|
|
|
/*
|
|
* m_s1 += in - out;
|
|
* m_s2 += m_s1 - (m_count *(out + CHAR_OFFSET));
|
|
*/
|
|
|
|
int diff = in_byte - out_byte;
|
|
if (diff < 0)
|
|
diff += ushort.MaxValue + 1;
|
|
|
|
m_s1 = (uint)((diff + m_s1) % (ushort.MaxValue + 1));
|
|
|
|
diff = (int)((m_count * (out_byte + CHAR_OFFSET)) % (ushort.MaxValue + 1));
|
|
diff = (int)m_s1 - diff;
|
|
if (diff < 0)
|
|
diff += ushort.MaxValue + 1;
|
|
|
|
m_s2 = (uint)((m_s2 + diff) % (ushort.MaxValue + 1));
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
}
|