From 5c1aa38fc91d3a48b4f085cda6067f375da0272d Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 13 Oct 2021 23:33:36 +0100 Subject: [PATCH] Add native CRC16. --- Aaru6.Checksums/CRC16Context.cs | 345 +++++++++++++++++++++++--- Aaru6.Checksums/CRC16IBMContext.cs | 4 +- AaruBenchmark/Checksums/Aaru6.cs | 14 +- AaruBenchmark/Checksums/AaruNative.cs | 86 ++----- 4 files changed, 343 insertions(+), 106 deletions(-) diff --git a/Aaru6.Checksums/CRC16Context.cs b/Aaru6.Checksums/CRC16Context.cs index 0539b4c..f98f857 100644 --- a/Aaru6.Checksums/CRC16Context.cs +++ b/Aaru6.Checksums/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; @@ -46,6 +47,10 @@ namespace Aaru6.Checksums readonly bool _inverse; readonly ushort[][] _table; ushort _hashInt; + readonly IntPtr _nativeContext; + readonly bool _useCcitt; + readonly bool _useIbm; + readonly bool _useNative; /// Initializes the CRC16 table with a custom polynomial and seed public Crc16Context(ushort polynomial, ushort seed, ushort[][] table, bool inverse) @@ -54,7 +59,29 @@ namespace Aaru6.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 Aaru6.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 Aaru6.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 Aaru6.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 Aaru6.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 Aaru6.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 Aaru6.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 Aaru6.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/Aaru6.Checksums/CRC16IBMContext.cs b/Aaru6.Checksums/CRC16IBMContext.cs index 3399587..75f353e 100644 --- a/Aaru6.Checksums/CRC16IBMContext.cs +++ b/Aaru6.Checksums/CRC16IBMContext.cs @@ -36,8 +36,8 @@ namespace Aaru6.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/AaruBenchmark/Checksums/Aaru6.cs b/AaruBenchmark/Checksums/Aaru6.cs index 6961b30..24a3aaf 100644 --- a/AaruBenchmark/Checksums/Aaru6.cs +++ b/AaruBenchmark/Checksums/Aaru6.cs @@ -139,6 +139,8 @@ namespace AaruBenchmark.Checksums public static void Crc16Ccitt() { + Native.ForceManaged = true; + byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Program.Folder, "random"), FileMode.Open, FileAccess.Read); @@ -149,16 +151,18 @@ namespace AaruBenchmark.Checksums IChecksum ctx = new CRC16CCITTContext(); ctx.Update(data); byte[] result = ctx.Final(); - /* - if(result?.Length != _expectedRandomCrc16Ccitt.Length) - throw new Exception("Invalid hash length"); - if(result.Where((t, i) => t != _expectedRandomCrc16Ccitt[i]).Any()) - throw new Exception("Invalid hash value");*/ + if(result?.Length != _expectedRandomCrc16Ccitt.Length) + throw new Exception("Invalid hash length"); + + if(result.Where((t, i) => t != _expectedRandomCrc16Ccitt[i]).Any()) + throw new Exception("Invalid hash value"); } public static void Crc16() { + Native.ForceManaged = true; + byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Program.Folder, "random"), FileMode.Open, FileAccess.Read); diff --git a/AaruBenchmark/Checksums/AaruNative.cs b/AaruBenchmark/Checksums/AaruNative.cs index 74bf77a..cbcddf2 100644 --- a/AaruBenchmark/Checksums/AaruNative.cs +++ b/AaruBenchmark/Checksums/AaruNative.cs @@ -100,30 +100,6 @@ namespace AaruBenchmark.Checksums [DllImport("libAaru.Checksums.Native", SetLastError = true)] static extern void fletcher32_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); - - [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 crc32_init(); @@ -259,75 +235,45 @@ namespace AaruBenchmark.Checksums public static void Crc16Ccitt() { + Native.ForceManaged = true; + byte[] data = new byte[1048576]; - ushort crc = 0; - byte[] hash; var fs = new FileStream(Path.Combine(Program.Folder, "random"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); + IChecksum ctx = new CRC16CCITTContext(); + ctx.Update(data); + byte[] result = ctx.Final(); - IntPtr ctx = crc16_ccitt_init(); + if(result?.Length != _expectedRandomCrc16Ccitt.Length) + throw new Exception("Invalid hash length"); - if(ctx == IntPtr.Zero) - throw new Exception("Could not initialize digest"); - - int ret = crc16_ccitt_update(ctx, data, (uint)data.Length); - - if(ret != 0) - throw new Exception("Could not digest block"); - - ret = crc16_ccitt_final(ctx, ref crc); - - if(ret != 0) - throw new Exception("Could not finalize hash"); - - crc16_ccitt_free(ctx); - - crc = (ushort)((crc << 8) | (crc >> 8)); - - hash = BitConverter.GetBytes(crc); - - if(hash.Where((t, i) => t != _expectedRandomCrc16Ccitt[i]).Any()) + if(result.Where((t, i) => t != _expectedRandomCrc16Ccitt[i]).Any()) throw new Exception("Invalid hash value"); } public static void Crc16() { + Native.ForceManaged = false; + byte[] data = new byte[1048576]; - ushort crc = 0; - byte[] hash; var fs = new FileStream(Path.Combine(Program.Folder, "random"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); + IChecksum ctx = new CRC16IBMContext(); + ctx.Update(data); + byte[] result = ctx.Final(); - IntPtr ctx = crc16_init(); + if(result?.Length != _expectedRandomCrc16.Length) + throw new Exception("Invalid hash length"); - if(ctx == IntPtr.Zero) - throw new Exception("Could not initialize digest"); - - int ret = crc16_update(ctx, data, (uint)data.Length); - - if(ret != 0) - throw new Exception("Could not digest block"); - - ret = crc16_final(ctx, ref crc); - - if(ret != 0) - throw new Exception("Could not finalize hash"); - - crc16_free(ctx); - - crc = (ushort)((crc << 8) | (crc >> 8)); - - hash = BitConverter.GetBytes(crc); - - if(hash.Where((t, i) => t != _expectedRandomCrc16[i]).Any()) + if(result.Where((t, i) => t != _expectedRandomCrc16[i]).Any()) throw new Exception("Invalid hash value"); }