diff --git a/Aaru.Checksums.csproj b/Aaru.Checksums.csproj index 62391a3..9a18a37 100644 --- a/Aaru.Checksums.csproj +++ b/Aaru.Checksums.csproj @@ -65,6 +65,7 @@ + @@ -100,6 +101,7 @@ + diff --git a/Adler32Context.cs b/Adler32Context.cs index c92bfb2..974e080 100644 --- a/Adler32Context.cs +++ b/Adler32Context.cs @@ -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; /// Initializes the Adler-32 sums @@ -59,13 +63,19 @@ namespace Aaru.Checksums { _sum1 = 1; _sum2 = 0; + + if(!Native.IsSupported) + return; + + _nativeContext = adler32_init(); + _useNative = _nativeContext != IntPtr.Zero; } /// /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - 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); /// /// Updates the hash with data. @@ -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 /// Returns a hexadecimal representation of the hash value. 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 /// Byte array of the hash value. 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 /// Byte array of the hash value. 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(); diff --git a/CRC16Context.cs b/CRC16Context.cs index 688543a..971ea87 100644 --- a/CRC16Context.cs +++ b/CRC16Context.cs @@ -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; /// Initializes the CRC16 table with a custom polynomial and seed @@ -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); } /// @@ -63,10 +90,26 @@ namespace Aaru.Checksums /// Length of buffer to hash. 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; + } + } } /// @@ -76,19 +119,65 @@ namespace Aaru.Checksums /// /// Returns a byte array of the hash value. - 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); + } /// /// Returns a hexadecimal representation of the hash value. 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 /// CRC16 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; } diff --git a/CRC16IBMContext.cs b/CRC16IBMContext.cs index f5e539f..259fd60 100644 --- a/CRC16IBMContext.cs +++ b/CRC16IBMContext.cs @@ -36,8 +36,8 @@ namespace Aaru.Checksums /// Implements the CRC16 algorithm with IBM polynomial and seed 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 = { diff --git a/CRC32Context.cs b/CRC32Context.cs index 132869b..0995ed2 100644 --- a/CRC32Context.cs +++ b/CRC32Context.cs @@ -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; /// Initializes the CRC32 table and seed as CRC32-ISO @@ -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; } /// Initializes the CRC32 table with a custom polynomial and seed @@ -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); } /// /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - 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); /// /// Updates the hash with data. @@ -369,20 +385,52 @@ namespace Aaru.Checksums /// /// Returns a byte array of the hash value. - 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); + } /// /// Returns a hexadecimal representation of the hash value. 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 /// CRC seed 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 /// CRC seed 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(); diff --git a/CRC64Context.cs b/CRC64Context.cs index f9c0855..834e99a 100644 --- a/CRC64Context.cs +++ b/CRC64Context.cs @@ -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; /// Initializes the CRC64 table and seed as CRC64-ECMA @@ -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; } /// Initializes the CRC16 table with a custom polynomial and seed 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); } /// /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - 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); /// /// Updates the hash with data. @@ -309,20 +325,52 @@ namespace Aaru.Checksums /// /// Returns a byte array of the hash value. - 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); + } /// /// Returns a hexadecimal representation of the hash value. 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 /// CRC seed 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 /// CRC seed 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(); diff --git a/FletcherContext.cs b/FletcherContext.cs index c633423..bdabdca 100644 --- a/FletcherContext.cs +++ b/FletcherContext.cs @@ -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 /// Implements the Fletcher-32 algorithm 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; /// Initializes the Fletcher-32 sums public Fletcher32Context() { _sum1 = 0xFFFF; _sum2 = 0xFFFF; + + if(!Native.IsSupported) + return; + + _nativeContext = fletcher32_init(); + _useNative = _nativeContext != IntPtr.Zero; } /// /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - 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); /// /// Updates the hash with data. @@ -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 /// Returns a hexadecimal representation of the hash value. 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 /// Byte array of the hash value. 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 /// Byte array of the hash value. 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; /// Initializes the Fletcher-16 sums public Fletcher16Context() { _sum1 = 0xFF; _sum2 = 0xFF; + + if(!Native.IsSupported) + return; + + _nativeContext = fletcher16_init(); + _useNative = _nativeContext != IntPtr.Zero; } /// /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - 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); /// /// Updates the hash with data. @@ -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 /// Returns a hexadecimal representation of the hash value. 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 /// Byte array of the hash value. 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 /// Byte array of the hash value. 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(); diff --git a/Native.cs b/Native.cs new file mode 100644 index 0000000..8a39e0f --- /dev/null +++ b/Native.cs @@ -0,0 +1,83 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : Native.cs +// Author(s) : Natalia Portillo +// +// 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2021 Natalia Portillo +// ****************************************************************************/ + +using System.Runtime.InteropServices; + +namespace Aaru.Checksums +{ + public static class Native + { + static bool _checked; + static bool _supported; + + /// Set to return native as never supported + public static bool ForceManaged { get; set; } + + /// + /// If set to true the native library was found and loaded correctly and its reported version is + /// compatible. + /// + 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(); + } +} \ No newline at end of file