Merge CRC32 impls and clean up
This commit is contained in:
parent
7953152d51
commit
b0e1458137
|
@ -1,46 +1,134 @@
|
|||
using System;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
// we could get a little list of crcs from here and make it clear which crc this class was for, and expose others
|
||||
// http://www.ross.net/crc/download/crc_v3.txt
|
||||
// TODO - why am I here? put me alongside hash_md5 and such
|
||||
public static class CRC32
|
||||
{
|
||||
// Lookup table for speed.
|
||||
private static readonly uint[] Crc32Table;
|
||||
|
||||
static CRC32()
|
||||
{
|
||||
Crc32Table = new uint[256];
|
||||
for (uint i = 0; i < 256; ++i)
|
||||
{
|
||||
uint crc = i;
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
if ((crc & 1) == 1)
|
||||
{
|
||||
crc = (crc >> 1) ^ 0xEDB88320;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
Crc32Table[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
public static uint Calculate(ReadOnlySpan<byte> data)
|
||||
{
|
||||
uint result = 0xFFFFFFFF;
|
||||
foreach (var b in data)
|
||||
{
|
||||
result = (result >> 8) ^ Crc32Table[b ^ (result & 0xFF)];
|
||||
}
|
||||
|
||||
return ~result;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <remarks>Implementation of CRC-32 (i.e. POSIX cksum), intended for comparing discs against the Redump.org database</remarks>
|
||||
public sealed class CRC32
|
||||
{
|
||||
/// <remarks>coefficients of the polynomial, in the format Wikipedia calls "reversed"</remarks>
|
||||
public const uint POLYNOMIAL_CONST = 0xEDB88320U;
|
||||
|
||||
private static readonly uint[] COMBINER_INIT_STATE;
|
||||
|
||||
private static readonly uint[] CRC32Table;
|
||||
|
||||
static CRC32()
|
||||
{
|
||||
// for Add (CRC32 computation):
|
||||
CRC32Table = new uint[256];
|
||||
for (var i = 0U; i < 256U; i++)
|
||||
{
|
||||
var crc = i;
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
var xor = (crc & 1U) == 1U;
|
||||
crc >>= 1;
|
||||
if (xor) crc ^= POLYNOMIAL_CONST;
|
||||
}
|
||||
CRC32Table[i] = crc;
|
||||
}
|
||||
|
||||
// for Incorporate:
|
||||
var combinerState = (COMBINER_INIT_STATE = new uint[64]).AsSpan();
|
||||
var even = combinerState.Slice(start: 0, length: 32); // even-power-of-two zeros operator
|
||||
var odd = combinerState.Slice(start: 32, length: 32); // odd-power-of-two zeros operator
|
||||
// put operator for one zero bit in odd
|
||||
odd[0] = POLYNOMIAL_CONST;
|
||||
var oddTail = odd.Slice(1);
|
||||
for (var n = 0; n < oddTail.Length; n++) oddTail[n] = 1U << n;
|
||||
// put operator for two zero bits in even
|
||||
gf2_matrix_square(even, odd);
|
||||
// put operator for four zero bits in odd
|
||||
gf2_matrix_square(odd, even);
|
||||
}
|
||||
|
||||
public static uint Calculate(ReadOnlySpan<byte> data)
|
||||
{
|
||||
CRC32 crc32 = new();
|
||||
crc32.Add(data);
|
||||
return crc32.Result;
|
||||
}
|
||||
|
||||
private static void gf2_matrix_square(Span<uint> square, ReadOnlySpan<uint> mat)
|
||||
{
|
||||
if (mat.Length != square.Length) throw new ArgumentException();
|
||||
for (var n = 0; n < square.Length; n++) square[n] = gf2_matrix_times(mat, mat[n]);
|
||||
}
|
||||
|
||||
private static uint gf2_matrix_times(ReadOnlySpan<uint> mat, uint vec)
|
||||
{
|
||||
var matIdx = 0;
|
||||
uint sum = 0U;
|
||||
while (vec != 0U)
|
||||
{
|
||||
if ((vec & 1U) != 0U) sum ^= mat[matIdx];
|
||||
vec >>= 1;
|
||||
matIdx++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private uint _current = 0xFFFFFFFFU;
|
||||
|
||||
/// <summary>The raw non-negated output</summary>
|
||||
public uint Current
|
||||
{
|
||||
get => _current;
|
||||
set => _current = value;
|
||||
}
|
||||
|
||||
/// <summary>The negated output (the typical result of the CRC calculation)</summary>
|
||||
public uint Result => ~_current;
|
||||
|
||||
public void Add(byte datum)
|
||||
{
|
||||
_current = CRC32Table[(_current ^ datum) & 0xFF] ^ (_current >> 8);
|
||||
}
|
||||
|
||||
public void Add(ReadOnlySpan<byte> data)
|
||||
{
|
||||
foreach (var b in data)
|
||||
{
|
||||
// Add(b); // I assume this would be slower
|
||||
_current = CRC32Table[(_current ^ b) & 0xFF] ^ (_current >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Incorporates a pre-calculated CRC with the given length by combining crcs<br/>
|
||||
/// It's a bit flaky, so be careful, but it works
|
||||
/// </summary>
|
||||
/// <remarks>algorithm from zlib's crc32_combine. read http://www.leapsecond.com/tools/crcomb.c for more</remarks>
|
||||
public void Incorporate(uint crc, int len)
|
||||
{
|
||||
if (len == 0) return; // degenerate case
|
||||
|
||||
Span<uint> combinerState = stackalloc uint[64];
|
||||
COMBINER_INIT_STATE.CopyTo(combinerState);
|
||||
var even = combinerState.Slice(start: 0, length: 32);
|
||||
var odd = combinerState.Slice(start: 32, length: 32);
|
||||
|
||||
// apply len zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even)
|
||||
do
|
||||
{
|
||||
// apply zeros operator for this bit of len
|
||||
gf2_matrix_square(even, odd);
|
||||
if ((len & 1U) != 0U) _current = gf2_matrix_times(even, _current);
|
||||
len >>= 1;
|
||||
|
||||
// if no more bits set, then done
|
||||
if (len == 0U) break;
|
||||
|
||||
// another iteration of the loop with odd and even swapped
|
||||
gf2_matrix_square(odd, even);
|
||||
if ((len & 1U) != 0U) _current = gf2_matrix_times(odd, _current);
|
||||
len >>= 1;
|
||||
|
||||
// if no more bits set, then done
|
||||
} while (len != 0U);
|
||||
|
||||
// finally, combine and return
|
||||
_current ^= crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,138 +0,0 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// A stateful special CRC32 calculator
|
||||
/// This may be absolutely standard and not special at all. I don't know, there were some differences between it and other CRC code I found in bizhawk
|
||||
/// </summary>
|
||||
public class SpecialCRC32
|
||||
{
|
||||
private static readonly uint[] CRC32Table;
|
||||
|
||||
static SpecialCRC32()
|
||||
{
|
||||
CRC32Table = new uint[256];
|
||||
for (uint i = 0; i < 256; ++i)
|
||||
{
|
||||
uint crc = i;
|
||||
for (int j = 8; j > 0; --j)
|
||||
{
|
||||
if ((crc & 1) == 1)
|
||||
crc = ((crc >> 1) ^ 0xEDB88320);
|
||||
else
|
||||
crc >>= 1;
|
||||
}
|
||||
CRC32Table[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
private uint current = 0xFFFFFFFF;
|
||||
|
||||
public void Add(ReadOnlySpan<byte> data)
|
||||
{
|
||||
foreach (var b in data) current = CRC32Table[(current ^ b) & 0xFF] ^ (current >> 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The negated output (the typical result of the CRC calculation)
|
||||
/// </summary>
|
||||
public uint Result => current ^ 0xFFFFFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// The raw non-negated output
|
||||
/// </summary>
|
||||
public uint Current
|
||||
{
|
||||
get => current;
|
||||
set => current = value;
|
||||
}
|
||||
|
||||
private uint gf2_matrix_times(uint[] mat, uint vec)
|
||||
{
|
||||
int matIdx = 0;
|
||||
uint sum = 0;
|
||||
while (vec != 0)
|
||||
{
|
||||
if ((vec & 1) != 0)
|
||||
sum ^= mat[matIdx];
|
||||
vec >>= 1;
|
||||
matIdx++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private void gf2_matrix_square(uint[] square, uint[] mat)
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 32; n++)
|
||||
square[n] = gf2_matrix_times(mat, mat[n]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Incorporates a pre-calculated CRC with the given length by combining crcs
|
||||
/// It's a bit flaky, so be careful, but it works
|
||||
/// </summary>
|
||||
public void Incorporate(uint crc, int len)
|
||||
{
|
||||
current = crc32_combine(current, crc, len);
|
||||
}
|
||||
|
||||
//tables used by crc32_combine
|
||||
private uint[] even, odd;
|
||||
|
||||
//algorithm from zlib's crc32_combine. read http://www.leapsecond.com/tools/crcomb.c for more
|
||||
private uint crc32_combine(uint crc1, uint crc2, int len2)
|
||||
{
|
||||
even ??= new uint[32]; // even-power-of-two zeros operator
|
||||
odd ??= new uint[32]; // odd-power-of-two zeros operator
|
||||
|
||||
// degenerate case
|
||||
if (len2 == 0)
|
||||
return crc1;
|
||||
|
||||
// put operator for one zero bit in odd
|
||||
odd[0] = 0xedb88320; //CRC-32 polynomial
|
||||
uint row = 1;
|
||||
for (int n = 1; n < 32; n++)
|
||||
{
|
||||
odd[n] = row;
|
||||
row <<= 1;
|
||||
}
|
||||
|
||||
//put operator for two zero bits in even
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
//put operator for four zero bits in odd
|
||||
gf2_matrix_square(odd, even);
|
||||
|
||||
//apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even)
|
||||
do
|
||||
{
|
||||
//apply zeros operator for this bit of len2
|
||||
gf2_matrix_square(even, odd);
|
||||
if ((len2 & 1) != 0)
|
||||
crc1 = gf2_matrix_times(even, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
//if no more bits set, then done
|
||||
if (len2 == 0)
|
||||
break;
|
||||
|
||||
//another iteration of the loop with odd and even swapped
|
||||
gf2_matrix_square(odd, even);
|
||||
if ((len2 & 1) != 0)
|
||||
crc1 = gf2_matrix_times(odd, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
//if no more bits set, then done
|
||||
} while (len2 != 0);
|
||||
|
||||
//return combined crc
|
||||
crc1 ^= crc2;
|
||||
return crc1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,10 +24,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//Tekken 3 (Europe) (Alt) and Tekken 3 (Europe) conflict in track 2 and 3 unfortunately, not sure what to do about this yet
|
||||
//the TOC isn't needed!
|
||||
//but it will help detect dumps with mangled TOCs which are all too common
|
||||
//
|
||||
//a possibly special CRC32 is used to help us match redump's DB elsewhere
|
||||
|
||||
SpecialCRC32 crc = new SpecialCRC32();
|
||||
CRC32 crc = new();
|
||||
byte[] buffer2352 = new byte[2352];
|
||||
|
||||
var dsr = new DiscSectorReader(disc)
|
||||
|
@ -36,7 +34,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
};
|
||||
|
||||
//hash the TOC
|
||||
static void AddAsBytesTo(SpecialCRC32 crc32, int i)
|
||||
static void AddAsBytesTo(CRC32 crc32, int i)
|
||||
=> crc32.Add(BitConverter.GetBytes(i));
|
||||
AddAsBytesTo(crc, (int)disc.TOC.Session1Format);
|
||||
AddAsBytesTo(crc, disc.TOC.FirstRecordedTrackNumber);
|
||||
|
@ -64,8 +62,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// </summary>
|
||||
public uint Calculate_PSX_RedumpHash()
|
||||
{
|
||||
//a special CRC32 is used to help us match redump's DB
|
||||
SpecialCRC32 crc = new SpecialCRC32();
|
||||
CRC32 crc = new();
|
||||
byte[] buffer2352 = new byte[2352];
|
||||
|
||||
var dsr = new DiscSectorReader(disc)
|
||||
|
|
|
@ -33,12 +33,12 @@ namespace BizHawk.Tests.Common.checksums
|
|||
Assert.AreEqual(EXPECTED, CRC32.Calculate(data));
|
||||
|
||||
data = InitialiseArray();
|
||||
SpecialCRC32 crc32 = new();
|
||||
CRC32 crc32 = new();
|
||||
crc32.Add(data);
|
||||
Assert.AreEqual(EXPECTED, crc32.Result);
|
||||
|
||||
var dataExtra = InitialiseArrayExtra();
|
||||
SpecialCRC32 crc32Extra = new();
|
||||
CRC32 crc32Extra = new();
|
||||
crc32Extra.Add(dataExtra);
|
||||
Assert.AreEqual(EXPECTED_EXTRA, crc32Extra.Result);
|
||||
crc32.Incorporate(crc32Extra.Result, dataExtra.Length);
|
||||
|
|
Loading…
Reference in New Issue