Add native checksums.

This commit is contained in:
2021-10-14 01:38:59 +01:00
parent c398a3ef39
commit 3145e0a2f9
8 changed files with 858 additions and 78 deletions

View File

@@ -65,6 +65,7 @@
<Compile Include="CRC32\arm_simd.cs" />
<Compile Include="CRC32\clmul.cs" />
<Compile Include="CRC64\clmul.cs" />
<Compile Include="Native.cs" />
<Compile Include="Register.cs" />
<Compile Include="SpamSumContext.cs" />
<Compile Include="Adler32Context.cs" />
@@ -100,6 +101,7 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aaru.Checksums.Native" Version="6.0.0-alpha6" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1" PrivateAssets="all" />
</ItemGroup>
<ProjectExtensions>

View File

@@ -36,7 +36,9 @@
// Copyright (C) Jean-loup Gailly
// ****************************************************************************/
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Text;
using Aaru.Checksums.Adler32;
@@ -52,6 +54,8 @@ namespace Aaru.Checksums
{
internal const ushort ADLER_MODULE = 65521;
internal const uint NMAX = 5552;
readonly IntPtr _nativeContext;
readonly bool _useNative;
ushort _sum1, _sum2;
/// <summary>Initializes the Adler-32 sums</summary>
@@ -59,13 +63,19 @@ namespace Aaru.Checksums
{
_sum1 = 1;
_sum2 = 0;
if(!Native.IsSupported)
return;
_nativeContext = adler32_init();
_useNative = _nativeContext != IntPtr.Zero;
}
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
/// <param name="data">Data buffer.</param>
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len);
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len, _useNative, _nativeContext);
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
@@ -78,6 +88,12 @@ namespace Aaru.Checksums
{
uint finalSum = (uint)((_sum2 << 16) | _sum1);
if(!_useNative)
return BigEndianBitConverter.GetBytes(finalSum);
adler32_final(_nativeContext, ref finalSum);
adler32_free(_nativeContext);
return BigEndianBitConverter.GetBytes(finalSum);
}
@@ -85,8 +101,15 @@ namespace Aaru.Checksums
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
uint finalSum = (uint)((_sum2 << 16) | _sum1);
var adlerOutput = new StringBuilder();
uint finalSum = (uint)((_sum2 << 16) | _sum1);
if(_useNative)
{
adler32_final(_nativeContext, ref finalSum);
adler32_free(_nativeContext);
}
var adlerOutput = new StringBuilder();
for(int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++)
adlerOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2"));
@@ -94,8 +117,28 @@ namespace Aaru.Checksums
return adlerOutput.ToString();
}
static void Step(ref ushort preSum1, ref ushort preSum2, byte[] data, uint len)
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr adler32_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int adler32_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int adler32_final(IntPtr ctx, ref uint checksum);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void adler32_free(IntPtr ctx);
static void Step(ref ushort preSum1, ref ushort preSum2, byte[] data, uint len, bool useNative,
IntPtr nativeContext)
{
if(useNative)
{
adler32_update(nativeContext, data, len);
return;
}
if(Ssse3.IsSupported)
{
Adler32.Ssse3.Step(ref preSum1, ref preSum2, data, len);
@@ -273,6 +316,17 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string File(string filename, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = adler32_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
var fileStream = new FileStream(filename, FileMode.Open);
ushort localSum1 = 1;
@@ -283,12 +337,18 @@ namespace Aaru.Checksums
while(read > 0)
{
Step(ref localSum1, ref localSum2, buffer, (uint)read);
Step(ref localSum1, ref localSum2, buffer, (uint)read, useNative, nativeContext);
read = fileStream.Read(buffer, 0, 65536);
}
uint finalSum = (uint)((localSum2 << 16) | localSum1);
if(useNative)
{
adler32_final(nativeContext, ref finalSum);
adler32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var adlerOutput = new StringBuilder();
@@ -307,13 +367,30 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string Data(byte[] data, uint len, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = adler32_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
ushort localSum1 = 1;
ushort localSum2 = 0;
Step(ref localSum1, ref localSum2, data, len);
Step(ref localSum1, ref localSum2, data, len, useNative, nativeContext);
uint finalSum = (uint)((localSum2 << 16) | localSum1);
if(useNative)
{
adler32_final(nativeContext, ref finalSum);
adler32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var adlerOutput = new StringBuilder();

View File

@@ -32,6 +32,7 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
@@ -44,7 +45,11 @@ namespace Aaru.Checksums
{
readonly ushort _finalSeed;
readonly bool _inverse;
readonly IntPtr _nativeContext;
readonly ushort[][] _table;
readonly bool _useCcitt;
readonly bool _useIbm;
readonly bool _useNative;
ushort _hashInt;
/// <summary>Initializes the CRC16 table with a custom polynomial and seed</summary>
@@ -54,7 +59,29 @@ namespace Aaru.Checksums
_finalSeed = seed;
_inverse = inverse;
_table = table ?? GenerateTable(polynomial, inverse);
_useNative = Native.IsSupported;
_useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY &&
seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse;
_useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED &&
!inverse;
if(_useCcitt && _useNative)
{
_nativeContext = crc16_ccitt_init();
_useNative = _nativeContext != IntPtr.Zero;
}
else if(_useIbm && _useNative)
{
_nativeContext = crc16_init();
_useNative = _nativeContext != IntPtr.Zero;
}
else
_useNative = false;
if(!_useNative)
_table = table ?? GenerateTable(polynomial, inverse);
}
/// <inheritdoc />
@@ -63,10 +90,26 @@ namespace Aaru.Checksums
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len)
{
if(_inverse)
StepInverse(ref _hashInt, _table, data, len);
else
Step(ref _hashInt, _table, data, len);
switch(_useNative)
{
case true when _useCcitt:
crc16_ccitt_update(_nativeContext, data, len);
break;
case true when _useIbm:
crc16_update(_nativeContext, data, len);
break;
default:
{
if(_inverse)
StepInverse(ref _hashInt, _table, data, len);
else
Step(ref _hashInt, _table, data, len);
break;
}
}
}
/// <inheritdoc />
@@ -76,19 +119,65 @@ namespace Aaru.Checksums
/// <inheritdoc />
/// <summary>Returns a byte array of the hash value.</summary>
public byte[] Final() => !_inverse ? BigEndianBitConverter.GetBytes((ushort)(_hashInt ^ _finalSeed))
: BigEndianBitConverter.GetBytes((ushort)~(_hashInt ^ _finalSeed));
public byte[] Final()
{
ushort crc = 0;
switch(_useNative)
{
case true when _useCcitt:
crc16_ccitt_final(_nativeContext, ref crc);
crc16_ccitt_free(_nativeContext);
break;
case true when _useIbm:
crc16_final(_nativeContext, ref crc);
crc16_free(_nativeContext);
break;
default:
{
if(_inverse)
crc = (ushort)~(_hashInt ^ _finalSeed);
else
crc = (ushort)(_hashInt ^ _finalSeed);
break;
}
}
return BigEndianBitConverter.GetBytes(crc);
}
/// <inheritdoc />
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
var crc16Output = new StringBuilder();
var crc16Output = new StringBuilder();
ushort final = 0;
ushort final = (ushort)(_hashInt ^ _finalSeed);
switch(_useNative)
{
case true when _useCcitt:
crc16_ccitt_final(_nativeContext, ref final);
crc16_ccitt_free(_nativeContext);
if(_inverse)
final = (ushort)~final;
break;
case true when _useIbm:
crc16_final(_nativeContext, ref final);
crc16_free(_nativeContext);
break;
default:
{
if(_inverse)
final = (ushort)~(_hashInt ^ _finalSeed);
else
final = (ushort)(_hashInt ^ _finalSeed);
break;
}
}
byte[] finalBytes = BigEndianBitConverter.GetBytes(final);
@@ -98,6 +187,30 @@ namespace Aaru.Checksums
return crc16Output.ToString();
}
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr crc16_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc16_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc16_final(IntPtr ctx, ref ushort crc);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void crc16_free(IntPtr ctx);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr crc16_ccitt_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc16_ccitt_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc16_ccitt_final(IntPtr ctx, ref ushort crc);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void crc16_ccitt_free(IntPtr ctx);
static void Step(ref ushort previousCrc, ushort[][] table, byte[] data, uint len)
{
// Unroll according to Intel slicing by uint8_t
@@ -234,10 +347,34 @@ namespace Aaru.Checksums
public static string File(string filename, out byte[] hash, ushort polynomial, ushort seed, ushort[][] table,
bool inverse)
{
bool useNative = Native.IsSupported;
bool useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY &&
seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse;
bool useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED &&
!inverse;
IntPtr nativeContext = IntPtr.Zero;
var fileStream = new FileStream(filename, FileMode.Open);
ushort localHashInt = seed;
switch(useNative)
{
case true when useCcitt:
nativeContext = crc16_ccitt_init();
useNative = nativeContext != IntPtr.Zero;
break;
case true when useIbm:
nativeContext = crc16_init();
useNative = nativeContext != IntPtr.Zero;
break;
}
ushort[][] localTable = table ?? GenerateTable(polynomial, inverse);
byte[] buffer = new byte[65536];
@@ -245,18 +382,52 @@ namespace Aaru.Checksums
while(read > 0)
{
if(inverse)
StepInverse(ref localHashInt, localTable, buffer, (uint)read);
else
Step(ref localHashInt, localTable, buffer, (uint)read);
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_update(nativeContext, buffer, (uint)read);
break;
case true when useIbm:
crc16_update(nativeContext, buffer, (uint)read);
break;
default:
{
if(inverse)
StepInverse(ref localHashInt, localTable, buffer, (uint)read);
else
Step(ref localHashInt, localTable, buffer, (uint)read);
break;
}
}
read = fileStream.Read(buffer, 0, 65536);
}
localHashInt ^= seed;
if(inverse)
localHashInt = (ushort)~localHashInt;
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_final(nativeContext, ref localHashInt);
crc16_ccitt_free(nativeContext);
break;
case true when useIbm:
crc16_final(nativeContext, ref localHashInt);
crc16_free(nativeContext);
break;
default:
{
if(inverse)
localHashInt = (ushort)~localHashInt;
break;
}
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
@@ -281,19 +452,77 @@ namespace Aaru.Checksums
public static string Data(byte[] data, uint len, out byte[] hash, ushort polynomial, ushort seed,
ushort[][] table, bool inverse)
{
bool useNative = Native.IsSupported;
bool useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY &&
seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse;
bool useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED &&
!inverse;
IntPtr nativeContext = IntPtr.Zero;
ushort localHashInt = seed;
switch(useNative)
{
case true when useCcitt:
nativeContext = crc16_ccitt_init();
useNative = nativeContext != IntPtr.Zero;
break;
case true when useIbm:
nativeContext = crc16_init();
useNative = nativeContext != IntPtr.Zero;
break;
}
ushort[][] localTable = table ?? GenerateTable(polynomial, inverse);
if(inverse)
StepInverse(ref localHashInt, localTable, data, len);
else
Step(ref localHashInt, localTable, data, len);
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_update(nativeContext, data, len);
break;
case true when useIbm:
crc16_update(nativeContext, data, len);
break;
default:
{
if(inverse)
StepInverse(ref localHashInt, localTable, data, len);
else
Step(ref localHashInt, localTable, data, len);
break;
}
}
localHashInt ^= seed;
if(inverse)
localHashInt = (ushort)~localHashInt;
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_final(nativeContext, ref localHashInt);
crc16_ccitt_free(nativeContext);
break;
case true when useIbm:
crc16_final(nativeContext, ref localHashInt);
crc16_free(nativeContext);
break;
default:
{
if(inverse)
localHashInt = (ushort)~localHashInt;
break;
}
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
@@ -314,19 +543,77 @@ namespace Aaru.Checksums
/// <returns>CRC16</returns>
public static ushort Calculate(byte[] buffer, ushort polynomial, ushort seed, ushort[][] table, bool inverse)
{
bool useNative = Native.IsSupported;
bool useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY &&
seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse;
bool useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED &&
!inverse;
IntPtr nativeContext = IntPtr.Zero;
ushort localHashInt = seed;
switch(useNative)
{
case true when useCcitt:
nativeContext = crc16_ccitt_init();
useNative = nativeContext != IntPtr.Zero;
break;
case true when useIbm:
nativeContext = crc16_init();
useNative = nativeContext != IntPtr.Zero;
break;
}
ushort[][] localTable = table ?? GenerateTable(polynomial, inverse);
if(inverse)
StepInverse(ref localHashInt, localTable, buffer, (uint)buffer.Length);
else
Step(ref localHashInt, localTable, buffer, (uint)buffer.Length);
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_update(nativeContext, buffer, (uint)buffer.Length);
break;
case true when useIbm:
crc16_update(nativeContext, buffer, (uint)buffer.Length);
break;
default:
{
if(inverse)
StepInverse(ref localHashInt, localTable, buffer, (uint)buffer.Length);
else
Step(ref localHashInt, localTable, buffer, (uint)buffer.Length);
break;
}
}
localHashInt ^= seed;
if(inverse)
localHashInt = (ushort)~localHashInt;
switch(useNative)
{
case true when useCcitt:
crc16_ccitt_final(nativeContext, ref localHashInt);
crc16_ccitt_free(nativeContext);
break;
case true when useIbm:
crc16_final(nativeContext, ref localHashInt);
crc16_free(nativeContext);
break;
default:
{
if(inverse)
localHashInt = (ushort)~localHashInt;
break;
}
}
return localHashInt;
}

View File

@@ -36,8 +36,8 @@ namespace Aaru.Checksums
/// <summary>Implements the CRC16 algorithm with IBM polynomial and seed</summary>
public sealed class CRC16IBMContext : Crc16Context
{
const ushort CRC16_IBM_POLY = 0xA001;
const ushort CRC16_IBM_SEED = 0x0000;
internal const ushort CRC16_IBM_POLY = 0xA001;
internal const ushort CRC16_IBM_SEED = 0x0000;
static readonly ushort[][] _ibmCrc16Table =
{

View File

@@ -32,6 +32,7 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using System.Text;
@@ -333,8 +334,10 @@ namespace Aaru.Checksums
};
readonly uint _finalSeed;
readonly IntPtr _nativeContext;
readonly uint[][] _table;
readonly bool _useIso;
readonly bool _useNative;
uint _hashInt;
/// <summary>Initializes the CRC32 table and seed as CRC32-ISO</summary>
@@ -344,6 +347,12 @@ namespace Aaru.Checksums
_finalSeed = CRC32_ISO_SEED;
_table = _isoCrc32Table;
_useIso = true;
if(!Native.IsSupported)
return;
_nativeContext = crc32_init();
_useNative = _nativeContext != IntPtr.Zero;
}
/// <summary>Initializes the CRC32 table with a custom polynomial and seed</summary>
@@ -353,14 +362,21 @@ namespace Aaru.Checksums
_finalSeed = seed;
_useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
_table = GenerateTable(polynomial);
if(Native.IsSupported && _useIso)
{
_nativeContext = crc32_init();
_useNative = _nativeContext != IntPtr.Zero;
}
else
_table = GenerateTable(polynomial);
}
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
/// <param name="data">Data buffer.</param>
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len) => Step(ref _hashInt, _table, data, len, _useIso);
public void Update(byte[] data, uint len) =>
Step(ref _hashInt, _table, data, len, _useIso, _useNative, _nativeContext);
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
@@ -369,20 +385,52 @@ namespace Aaru.Checksums
/// <inheritdoc />
/// <summary>Returns a byte array of the hash value.</summary>
public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed);
public byte[] Final()
{
uint crc = _hashInt ^ _finalSeed;
if(!_useNative ||
!_useIso)
return BigEndianBitConverter.GetBytes(crc);
crc32_final(_nativeContext, ref crc);
crc32_free(_nativeContext);
return BigEndianBitConverter.GetBytes(crc);
}
/// <inheritdoc />
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
uint crc = _hashInt ^ _finalSeed;
var crc32Output = new StringBuilder();
for(int i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed).Length; i++)
crc32Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed)[i].ToString("x2"));
if(_useNative && _useIso)
{
crc32_final(_nativeContext, ref crc);
crc32_free(_nativeContext);
}
for(int i = 0; i < BigEndianBitConverter.GetBytes(crc).Length; i++)
crc32Output.Append(BigEndianBitConverter.GetBytes(crc)[i].ToString("x2"));
return crc32Output.ToString();
}
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr crc32_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc32_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc32_final(IntPtr ctx, ref uint crc);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void crc32_free(IntPtr ctx);
static uint[][] GenerateTable(uint polynomial)
{
uint[][] table = new uint[8][];
@@ -410,8 +458,16 @@ namespace Aaru.Checksums
return table;
}
static void Step(ref uint previousCrc, uint[][] table, byte[] data, uint len, bool useIso)
static void Step(ref uint previousCrc, uint[][] table, byte[] data, uint len, bool useIso, bool useNative,
IntPtr nativeContext)
{
if(useNative && useIso)
{
crc32_update(nativeContext, data, len);
return;
}
int currentPos = 0;
if(useIso)
@@ -426,7 +482,7 @@ namespace Aaru.Checksums
if(blocks > 0)
{
previousCrc = ~Clmul.Step(data, blocks * 64, ~previousCrc);
previousCrc = ~CRC32.Clmul.Step(data, blocks * 64, ~previousCrc);
currentPos = (int)(blocks * 64);
len -= blocks * 64;
@@ -505,6 +561,16 @@ namespace Aaru.Checksums
/// <param name="seed">CRC seed</param>
public static string File(string filename, out byte[] hash, uint polynomial, uint seed)
{
bool useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative && useIso)
{
nativeContext = crc32_init();
useNative = nativeContext != IntPtr.Zero;
}
var fileStream = new FileStream(filename, FileMode.Open);
uint localHashInt = seed;
@@ -516,14 +582,20 @@ namespace Aaru.Checksums
while(read > 0)
{
Step(ref localHashInt, localTable, buffer, (uint)read,
polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED);
Step(ref localHashInt, localTable, buffer, (uint)read, useIso, useNative, nativeContext);
read = fileStream.Read(buffer, 0, 65536);
}
localHashInt ^= seed;
hash = BigEndianBitConverter.GetBytes(localHashInt);
if(useNative && useIso)
{
crc32_final(nativeContext, ref localHashInt);
crc32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
var crc32Output = new StringBuilder();
@@ -550,14 +622,31 @@ namespace Aaru.Checksums
/// <param name="seed">CRC seed</param>
public static string Data(byte[] data, uint len, out byte[] hash, uint polynomial, uint seed)
{
bool useIso = polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED;
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative && useIso)
{
nativeContext = crc32_init();
useNative = nativeContext != IntPtr.Zero;
}
uint localHashInt = seed;
uint[][] localTable = GenerateTable(polynomial);
Step(ref localHashInt, localTable, data, len, polynomial == CRC32_ISO_POLY && seed == CRC32_ISO_SEED);
Step(ref localHashInt, localTable, data, len, useIso, useNative, nativeContext);
localHashInt ^= seed;
hash = BigEndianBitConverter.GetBytes(localHashInt);
if(useNative && useIso)
{
crc32_final(nativeContext, ref localHashInt);
crc32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
var crc32Output = new StringBuilder();

View File

@@ -32,9 +32,9 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Text;
using Aaru.Checksums.CRC64;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
@@ -274,8 +274,10 @@ namespace Aaru.Checksums
};
readonly ulong _finalSeed;
readonly IntPtr _nativeContext;
readonly ulong[][] _table;
readonly bool _useEcma;
readonly bool _useNative;
ulong _hashInt;
/// <summary>Initializes the CRC64 table and seed as CRC64-ECMA</summary>
@@ -285,22 +287,36 @@ namespace Aaru.Checksums
_table = _ecmaCrc64Table;
_finalSeed = CRC64_ECMA_SEED;
_useEcma = true;
if(!Native.IsSupported)
return;
_nativeContext = crc64_init();
_useNative = _nativeContext != IntPtr.Zero;
}
/// <summary>Initializes the CRC16 table with a custom polynomial and seed</summary>
public Crc64Context(ulong polynomial, ulong seed)
{
_hashInt = seed;
_table = GenerateTable(polynomial);
_finalSeed = seed;
_useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
if(Native.IsSupported && _useEcma)
{
_nativeContext = crc64_init();
_useNative = _nativeContext != IntPtr.Zero;
}
else
_table = GenerateTable(polynomial);
}
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
/// <param name="data">Data buffer.</param>
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len) => Step(ref _hashInt, _table, data, len, _useEcma);
public void Update(byte[] data, uint len) =>
Step(ref _hashInt, _table, data, len, _useEcma, _useNative, _nativeContext);
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
@@ -309,20 +325,52 @@ namespace Aaru.Checksums
/// <inheritdoc />
/// <summary>Returns a byte array of the hash value.</summary>
public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed);
public byte[] Final()
{
ulong crc = _hashInt ^ _finalSeed;
if(!_useNative ||
!_useEcma)
return BigEndianBitConverter.GetBytes(crc);
crc64_final(_nativeContext, ref crc);
crc64_free(_nativeContext);
return BigEndianBitConverter.GetBytes(crc);
}
/// <inheritdoc />
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
ulong crc = _hashInt ^ _finalSeed;
var crc64Output = new StringBuilder();
for(int i = 0; i < BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed).Length; i++)
crc64Output.Append(BigEndianBitConverter.GetBytes(_hashInt ^= _finalSeed)[i].ToString("x2"));
if(_useNative && _useEcma)
{
crc64_final(_nativeContext, ref crc);
crc64_free(_nativeContext);
}
for(int i = 0; i < BigEndianBitConverter.GetBytes(crc).Length; i++)
crc64Output.Append(BigEndianBitConverter.GetBytes(crc)[i].ToString("x2"));
return crc64Output.ToString();
}
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr crc64_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc64_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int crc64_final(IntPtr ctx, ref ulong crc);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void crc64_free(IntPtr ctx);
static ulong[][] GenerateTable(ulong polynomial)
{
ulong[][] table = new ulong[8][];
@@ -350,8 +398,16 @@ namespace Aaru.Checksums
return table;
}
static void Step(ref ulong previousCrc, ulong[][] table, byte[] data, uint len, bool useEcma)
static void Step(ref ulong previousCrc, ulong[][] table, byte[] data, uint len, bool useEcma, bool useNative,
IntPtr nativeContext)
{
if(useNative && useEcma)
{
crc64_update(nativeContext, data, len);
return;
}
int dataOff = 0;
if(useEcma &&
@@ -365,7 +421,7 @@ namespace Aaru.Checksums
if(blocks > 0)
{
previousCrc = ~Clmul.Step(~previousCrc, data, blocks * 32);
previousCrc = ~CRC64.Clmul.Step(~previousCrc, data, blocks * 32);
dataOff = (int)(blocks * 32);
len -= blocks * 32;
@@ -424,6 +480,16 @@ namespace Aaru.Checksums
/// <param name="seed">CRC seed</param>
public static string File(string filename, out byte[] hash, ulong polynomial, ulong seed)
{
bool useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative && useEcma)
{
nativeContext = crc64_init();
useNative = nativeContext != IntPtr.Zero;
}
var fileStream = new FileStream(filename, FileMode.Open);
ulong localHashInt = seed;
@@ -435,14 +501,20 @@ namespace Aaru.Checksums
while(read > 0)
{
Step(ref localHashInt, localTable, buffer, (uint)read,
polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED);
Step(ref localHashInt, localTable, buffer, (uint)read, useEcma, useNative, nativeContext);
read = fileStream.Read(buffer, 0, 65536);
}
localHashInt ^= seed;
hash = BigEndianBitConverter.GetBytes(localHashInt);
if(useNative && useEcma)
{
crc64_final(nativeContext, ref localHashInt);
crc64_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
var crc64Output = new StringBuilder();
@@ -469,14 +541,31 @@ namespace Aaru.Checksums
/// <param name="seed">CRC seed</param>
public static string Data(byte[] data, uint len, out byte[] hash, ulong polynomial, ulong seed)
{
bool useEcma = polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED;
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative && useEcma)
{
nativeContext = crc64_init();
useNative = nativeContext != IntPtr.Zero;
}
ulong localHashInt = seed;
ulong[][] localTable = GenerateTable(polynomial);
Step(ref localHashInt, localTable, data, len, polynomial == CRC64_ECMA_POLY && seed == CRC64_ECMA_SEED);
Step(ref localHashInt, localTable, data, len, useEcma, useNative, nativeContext);
localHashInt ^= seed;
hash = BigEndianBitConverter.GetBytes(localHashInt);
if(useNative && useEcma)
{
crc64_final(nativeContext, ref localHashInt);
crc64_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(localHashInt);
var crc64Output = new StringBuilder();

View File

@@ -32,7 +32,9 @@
// Disabled because the speed is abnormally slow
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
@@ -42,22 +44,30 @@ namespace Aaru.Checksums
/// <summary>Implements the Fletcher-32 algorithm</summary>
public sealed class Fletcher32Context : IChecksum
{
const ushort FLETCHER_MODULE = 0xFFFF;
const uint NMAX = 5552;
ushort _sum1, _sum2;
const ushort FLETCHER_MODULE = 0xFFFF;
const uint NMAX = 5552;
readonly IntPtr _nativeContext;
readonly bool _useNative;
ushort _sum1, _sum2;
/// <summary>Initializes the Fletcher-32 sums</summary>
public Fletcher32Context()
{
_sum1 = 0xFFFF;
_sum2 = 0xFFFF;
if(!Native.IsSupported)
return;
_nativeContext = fletcher32_init();
_useNative = _nativeContext != IntPtr.Zero;
}
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
/// <param name="data">Data buffer.</param>
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len);
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len, _useNative, _nativeContext);
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
@@ -70,6 +80,12 @@ namespace Aaru.Checksums
{
uint finalSum = (uint)((_sum2 << 16) | _sum1);
if(!_useNative)
return BigEndianBitConverter.GetBytes(finalSum);
fletcher32_final(_nativeContext, ref finalSum);
fletcher32_free(_nativeContext);
return BigEndianBitConverter.GetBytes(finalSum);
}
@@ -77,8 +93,15 @@ namespace Aaru.Checksums
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
uint finalSum = (uint)((_sum2 << 16) | _sum1);
var fletcherOutput = new StringBuilder();
uint finalSum = (uint)((_sum2 << 16) | _sum1);
if(_useNative)
{
fletcher32_final(_nativeContext, ref finalSum);
fletcher32_free(_nativeContext);
}
var fletcherOutput = new StringBuilder();
for(int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++)
fletcherOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2"));
@@ -86,8 +109,28 @@ namespace Aaru.Checksums
return fletcherOutput.ToString();
}
static void Step(ref ushort previousSum1, ref ushort previousSum2, byte[] data, uint len)
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr fletcher32_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int fletcher32_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int fletcher32_final(IntPtr ctx, ref uint crc);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void fletcher32_free(IntPtr ctx);
static void Step(ref ushort previousSum1, ref ushort previousSum2, byte[] data, uint len, bool useNative,
IntPtr nativeContext)
{
if(useNative)
{
fletcher32_update(nativeContext, data, len);
return;
}
uint sum1 = previousSum1;
uint sum2 = previousSum2;
uint n;
@@ -251,6 +294,17 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string File(string filename, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = fletcher32_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
var fileStream = new FileStream(filename, FileMode.Open);
ushort localSum1 = 0xFFFF;
@@ -261,13 +315,19 @@ namespace Aaru.Checksums
while(read > 0)
{
Step(ref localSum1, ref localSum2, buffer, (uint)read);
Step(ref localSum1, ref localSum2, buffer, (uint)read, useNative, nativeContext);
read = fileStream.Read(buffer, 0, 65536);
}
uint finalSum = (uint)((localSum2 << 16) | localSum1);
if(useNative)
{
fletcher32_final(nativeContext, ref finalSum);
fletcher32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var fletcherOutput = new StringBuilder();
@@ -286,13 +346,30 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string Data(byte[] data, uint len, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = fletcher32_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
ushort localSum1 = 0xFFFF;
ushort localSum2 = 0xFFFF;
Step(ref localSum1, ref localSum2, data, len);
Step(ref localSum1, ref localSum2, data, len, useNative, nativeContext);
uint finalSum = (uint)((localSum2 << 16) | localSum1);
if(useNative)
{
fletcher32_final(nativeContext, ref finalSum);
fletcher32_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var adlerOutput = new StringBuilder();
@@ -315,20 +392,29 @@ namespace Aaru.Checksums
{
const byte FLETCHER_MODULE = 0xFF;
const byte NMAX = 22;
byte _sum1, _sum2;
readonly IntPtr _nativeContext;
readonly bool _useNative;
byte _sum1, _sum2;
/// <summary>Initializes the Fletcher-16 sums</summary>
public Fletcher16Context()
{
_sum1 = 0xFF;
_sum2 = 0xFF;
if(!Native.IsSupported)
return;
_nativeContext = fletcher16_init();
_useNative = _nativeContext != IntPtr.Zero;
}
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
/// <param name="data">Data buffer.</param>
/// <param name="len">Length of buffer to hash.</param>
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len);
public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len, _useNative, _nativeContext);
/// <inheritdoc />
/// <summary>Updates the hash with data.</summary>
@@ -341,6 +427,12 @@ namespace Aaru.Checksums
{
ushort finalSum = (ushort)((_sum2 << 8) | _sum1);
if(!_useNative)
return BigEndianBitConverter.GetBytes(finalSum);
fletcher16_final(_nativeContext, ref finalSum);
fletcher16_free(_nativeContext);
return BigEndianBitConverter.GetBytes(finalSum);
}
@@ -348,8 +440,15 @@ namespace Aaru.Checksums
/// <summary>Returns a hexadecimal representation of the hash value.</summary>
public string End()
{
ushort finalSum = (ushort)((_sum2 << 8) | _sum1);
var fletcherOutput = new StringBuilder();
ushort finalSum = (ushort)((_sum2 << 8) | _sum1);
if(_useNative)
{
fletcher16_final(_nativeContext, ref finalSum);
fletcher16_free(_nativeContext);
}
var fletcherOutput = new StringBuilder();
for(int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++)
fletcherOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2"));
@@ -357,8 +456,28 @@ namespace Aaru.Checksums
return fletcherOutput.ToString();
}
static void Step(ref byte previousSum1, ref byte previousSum2, byte[] data, uint len)
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern IntPtr fletcher16_init();
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int fletcher16_update(IntPtr ctx, byte[] data, uint len);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern int fletcher16_final(IntPtr ctx, ref ushort checksum);
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern void fletcher16_free(IntPtr ctx);
static void Step(ref byte previousSum1, ref byte previousSum2, byte[] data, uint len, bool useNative,
IntPtr nativeContext)
{
if(useNative)
{
fletcher16_update(nativeContext, data, len);
return;
}
uint sum1 = previousSum1;
uint sum2 = previousSum2;
uint n;
@@ -502,6 +621,17 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string File(string filename, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = fletcher16_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
var fileStream = new FileStream(filename, FileMode.Open);
byte localSum1 = 0xFF;
@@ -512,13 +642,19 @@ namespace Aaru.Checksums
while(read > 0)
{
Step(ref localSum1, ref localSum2, buffer, (uint)read);
Step(ref localSum1, ref localSum2, buffer, (uint)read, useNative, nativeContext);
read = fileStream.Read(buffer, 0, 65536);
}
ushort finalSum = (ushort)((localSum2 << 8) | localSum1);
if(useNative)
{
fletcher16_final(nativeContext, ref finalSum);
fletcher16_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var fletcherOutput = new StringBuilder();
@@ -537,13 +673,30 @@ namespace Aaru.Checksums
/// <param name="hash">Byte array of the hash value.</param>
public static string Data(byte[] data, uint len, out byte[] hash)
{
bool useNative = Native.IsSupported;
IntPtr nativeContext = IntPtr.Zero;
if(useNative)
{
nativeContext = fletcher16_init();
if(nativeContext == IntPtr.Zero)
useNative = false;
}
byte localSum1 = 0xFF;
byte localSum2 = 0xFF;
Step(ref localSum1, ref localSum2, data, len);
Step(ref localSum1, ref localSum2, data, len, useNative, nativeContext);
ushort finalSum = (ushort)((localSum2 << 8) | localSum1);
if(useNative)
{
fletcher16_final(nativeContext, ref finalSum);
fletcher16_free(nativeContext);
}
hash = BigEndianBitConverter.GetBytes(finalSum);
var adlerOutput = new StringBuilder();

83
Native.cs Normal file
View File

@@ -0,0 +1,83 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Native.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Checksums.
//
// --[ Description ] ----------------------------------------------------------
//
// Checks that Aaru.Checksums.Native library is available and usable.
//
// --[ License ] --------------------------------------------------------------
//
// 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, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2021 Natalia Portillo
// ****************************************************************************/
using System.Runtime.InteropServices;
namespace Aaru.Checksums
{
public static class Native
{
static bool _checked;
static bool _supported;
/// <summary>Set to return native as never supported</summary>
public static bool ForceManaged { get; set; }
/// <summary>
/// If set to <c>true</c> the native library was found and loaded correctly and its reported version is
/// compatible.
/// </summary>
public static bool IsSupported
{
get
{
if(ForceManaged)
return false;
if(_checked)
return _supported;
ulong version;
_checked = true;
try
{
version = get_acn_version();
}
catch
{
_supported = false;
return false;
}
// TODO: Check version compatibility
_supported = version >= 0x06000000;
return _supported;
}
}
[DllImport("libAaru.Checksums.Native", SetLastError = true)]
static extern ulong get_acn_version();
}
}