hellbound/dll_src/crc/crc.cs

325 lines
8.8 KiB
C#

using System;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
/// <summary>
/// Implements a 32-bit CRC hash algorithm compatible with Zip etc.
/// </summary>
/// <remarks>
/// Crc32 should only be used for backward compatibility with older file formats
/// and algorithms. It is not secure enough for new applications.
/// If you need to call multiple times for the same data either use the HashAlgorithm
/// interface or remember that the result of one Compute call needs to be ~ (XOR) before
/// being passed in as the seed for the next Compute call.
/// </remarks>
public sealed class Crc32 : HashAlgorithm
{
public const UInt32 DefaultPolynomial = 0xedb88320u;
public const UInt32 DefaultSeed = 0xffffffffu;
private static UInt32[] defaultTable;
private readonly UInt32 seed;
private readonly UInt32[] table;
private UInt32 hash;
public Crc32()
: this(DefaultPolynomial, DefaultSeed)
{
}
public Crc32(UInt32 polynomial, UInt32 seed)
{
table = InitializeTable(polynomial);
this.seed = hash = seed;
}
public override void Initialize()
{
hash = seed;
}
protected override void HashCore(byte[] buffer, int start, int length)
{
hash = CalculateHash(table, hash, buffer, start, length);
}
protected override byte[] HashFinal()
{
var hashBuffer = UInt32ToBigEndianBytes(~hash);
HashValue = hashBuffer;
return hashBuffer;
}
public override int HashSize { get { return 32; } }
public static UInt32 Compute(byte[] buffer, int buffer_len)
{
return Compute(DefaultSeed, buffer, buffer_len);
}
public static UInt32 Compute(UInt32 seed, byte[] buffer, int buffer_len)
{
return Compute(DefaultPolynomial, seed, buffer, buffer_len);
}
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer, int buffer_len)
{
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer_len);
}
public static UInt32 Compute(Stream s)
{
return Compute(DefaultSeed, s);
}
public static UInt32 Compute(UInt32 seed, Stream s)
{
return Compute(DefaultPolynomial, seed, s);
}
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, Stream s)
{
return ~CalculateHash(InitializeTable(polynomial), seed, s);
}
private static UInt32[] InitializeTable(UInt32 polynomial)
{
if (polynomial == DefaultPolynomial && defaultTable != null)
return defaultTable;
var createTable = new UInt32[256];
for (var i = 0; i < 256; i++)
{
var entry = (UInt32)i;
for (var j = 0; j < 8; j++)
if ((entry & 1) == 1)
entry = (entry >> 1) ^ polynomial;
else
entry = entry >> 1;
createTable[i] = entry;
}
if (polynomial == DefaultPolynomial)
defaultTable = createTable;
return createTable;
}
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
{
var crc = seed;
for (var i = start; i < size - start; i++)
crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
return crc;
}
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, Stream s)
{
var crc = seed;
byte[] bytes = new byte[4 * 1024];
int bytes_to_read = (int)(s.Length - s.Position);
while(bytes_to_read > 0)
{
int n = s.Read(bytes, 0, bytes.Length);
bytes_to_read -= n;
crc = CalculateHash(table, crc, bytes, 0, n);
}
return crc;
}
public static UInt32 BeginHash()
{
var crc = DefaultSeed;
return crc;
}
public static UInt32 AddHash(UInt32 crc, byte data)
{
UInt32[] table = InitializeTable(DefaultPolynomial);
crc = (crc >> 8) ^ table[data ^ crc & 0xff];
return crc;
}
public static UInt32 AddHash(UInt32 crc, short data)
{
crc = AddHash(crc, (byte)(data & 0xFF));
crc = AddHash(crc, (byte)((data >> 8) & 0xFF));
return crc;
}
public static UInt32 AddHash(UInt32 crc, UInt32 data)
{
crc = AddHash(crc, (byte)(data & 0xFF));
crc = AddHash(crc, (byte)((data >> 8) & 0xFF));
crc = AddHash(crc, (byte)((data >> 16) & 0xFF));
crc = AddHash(crc, (byte)((data >> 24) & 0xFF));
return crc;
}
public static UInt32 FinalizeHash(UInt32 crc)
{
return ~crc;
}
//Convenience shortcut
public static uint CalcChecksum(uint data1, uint data2)
{
uint checksum = Crc32.BeginHash();
checksum = Crc32.AddHash(checksum, data1);
checksum = Crc32.AddHash(checksum, data2);
checksum = Crc32.FinalizeHash(checksum);
return checksum;
}
//Convenience shortcut
public static uint CalcChecksum(ulong data1, ulong data2)
{
uint checksum = Crc32.BeginHash();
checksum = Crc32.AddHash(checksum, (uint)(data1 >> 32));
checksum = Crc32.AddHash(checksum, (uint)(data1 & 0xFFFFFFFFL));
checksum = Crc32.AddHash(checksum, (uint)(data2 >> 32));
checksum = Crc32.AddHash(checksum, (uint)(data2 & 0xFFFFFFFFL));
checksum = Crc32.FinalizeHash(checksum);
return checksum;
}
//Convenience shortcut
public static uint CalcChecksum(ulong data)
{
uint checksum = Crc32.BeginHash();
checksum = Crc32.AddHash(checksum, (uint)(data >> 32));
checksum = Crc32.AddHash(checksum, (uint)(data & 0xFFFFFFFFL));
checksum = Crc32.FinalizeHash(checksum);
return checksum;
}
//Convenience shortcut
public static uint CalcChecksum(long data)
{
return CalcChecksum((ulong)data);
}
private static byte[] UInt32ToBigEndianBytes(UInt32 uint32)
{
var result = BitConverter.GetBytes(uint32);
if (BitConverter.IsLittleEndian)
Array.Reverse(result);
return result;
}
}
public sealed class Adler32
{
// largest prime smaller than 65536
private const int BASE = 65521;
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
private const int NMAX = 5552;
static public long Compute(long adler, byte[] buf, int index, int len)
{
if (buf == null)
{
return 1L;
}
long s1 = adler & 0xffff;
long s2 = (adler >> 16) & 0xffff;
int k;
while (len > 0)
{
k = len < NMAX?len:NMAX;
len -= k;
while (k >= 16)
{
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
s1 += (buf[index++] & 0xff); s2 += s1;
k -= 16;
}
if (k != 0)
{
do
{
s1 += (buf[index++] & 0xff); s2 += s1;
}
while (--k != 0);
}
s1 %= BASE;
s2 %= BASE;
}
return (s2 << 16) | s1;
}
static public long Compute(long adler, Stream buf)
{
if (buf == null)
{
return 1L;
}
long pos = buf.Position;
long s1 = adler & 0xffff;
long s2 = (adler >> 16) & 0xffff;
int k;
int len = (int)(buf.Length - pos);
while (len > 0)
{
k = len < NMAX ? len : NMAX;
len -= k;
while (k >= 16)
{
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
s1 += (buf.ReadByte() & 0xff); s2 += s1;
k -= 16;
}
if (k != 0)
{
do
{
s1 += (buf.ReadByte() & 0xff); s2 += s1;
}
while (--k != 0);
}
s1 %= BASE;
s2 %= BASE;
}
buf.Position = pos;
return (s2 << 16) | s1;
}
}