From d03d7082ccd8fc3ffab8d887bccb0867a42c0dae Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 23 Sep 2021 00:53:20 +0100 Subject: [PATCH] Use zlib-inspired algorithm for Adler and Fletcher checksums. --- Adler32Context.cs | 176 +++++++++++++++++++++--- FletcherContext.cs | 334 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 462 insertions(+), 48 deletions(-) diff --git a/Adler32Context.cs b/Adler32Context.cs index 3b72244..1ba13a6 100644 --- a/Adler32Context.cs +++ b/Adler32Context.cs @@ -42,6 +42,7 @@ namespace Aaru.Checksums public sealed class Adler32Context : IChecksum { const ushort ADLER_MODULE = 65521; + const uint NMAX = 5552; ushort _sum1, _sum2; /// Initializes the Adler-32 sums @@ -55,14 +56,7 @@ namespace Aaru.Checksums /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - for(int i = 0; i < len; i++) - { - _sum1 = (ushort)((_sum1 + data[i]) % ADLER_MODULE); - _sum2 = (ushort)((_sum2 + _sum1) % ADLER_MODULE); - } - } + public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len); /// /// Updates the hash with data. @@ -91,6 +85,157 @@ namespace Aaru.Checksums return adlerOutput.ToString(); } + static void Step(ref ushort preSum1, ref ushort preSum2, byte[] data, uint len) + { + uint sum1 = preSum1; + uint sum2 = preSum2; + uint n; + int dataOff = 0; + + /* in case user likes doing a byte at a time, keep it fast */ + if(len == 1) + { + sum1 += data[dataOff]; + + if(sum1 >= ADLER_MODULE) + sum1 -= ADLER_MODULE; + + sum2 += sum1; + + if(sum2 >= ADLER_MODULE) + sum2 -= ADLER_MODULE; + + preSum1 = (ushort)(sum1 & 0xFFFF); + preSum2 = (ushort)(sum2 & 0xFFFF); + + return; + } + + /* in case short lengths are provided, keep it somewhat fast */ + if(len < 16) + { + while(len-- > 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + if(sum1 >= ADLER_MODULE) + sum1 -= ADLER_MODULE; + + sum2 %= ADLER_MODULE; /* only added so many ADLER_MODULE's */ + preSum1 = (ushort)(sum1 & 0xFFFF); + preSum2 = (ushort)(sum2 & 0xFFFF); + + return; + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while(len >= NMAX) + { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + + do + { + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2 + 1]; + sum2 += sum1; + + /* 16 sums unrolled */ + dataOff += 16; + } while(--n != 0); + + sum1 %= ADLER_MODULE; + sum2 %= ADLER_MODULE; + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if(len != 0) + { + /* avoid modulos if none remaining */ + while(len >= 16) + { + len -= 16; + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2 + 1]; + sum2 += sum1; + + dataOff += 16; + } + + while(len-- != 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + sum1 %= ADLER_MODULE; + sum2 %= ADLER_MODULE; + } + + preSum1 = (ushort)(sum1 & 0xFFFF); + preSum2 = (ushort)(sum2 & 0xFFFF); + } + /// Gets the hash of a file /// File path. public static byte[] File(string filename) @@ -110,10 +255,13 @@ namespace Aaru.Checksums ushort localSum1 = 1; ushort localSum2 = 0; - for(int i = 0; i < fileStream.Length; i++) + byte[] buffer = new byte[65536]; + int read = fileStream.Read(buffer, 0, 65536); + + while(read > 0) { - localSum1 = (ushort)((localSum1 + fileStream.ReadByte()) % ADLER_MODULE); - localSum2 = (ushort)((localSum2 + localSum1) % ADLER_MODULE); + Step(ref localSum1, ref localSum2, buffer, (uint)read); + read = fileStream.Read(buffer, 0, 65536); } uint finalSum = (uint)((localSum2 << 16) | localSum1); @@ -139,11 +287,7 @@ namespace Aaru.Checksums ushort localSum1 = 1; ushort localSum2 = 0; - for(int i = 0; i < len; i++) - { - localSum1 = (ushort)((localSum1 + data[i]) % ADLER_MODULE); - localSum2 = (ushort)((localSum2 + localSum1) % ADLER_MODULE); - } + Step(ref localSum1, ref localSum2, data, len); uint finalSum = (uint)((localSum2 << 16) | localSum1); diff --git a/FletcherContext.cs b/FletcherContext.cs index 480cca3..c633423 100644 --- a/FletcherContext.cs +++ b/FletcherContext.cs @@ -43,6 +43,7 @@ namespace Aaru.Checksums public sealed class Fletcher32Context : IChecksum { const ushort FLETCHER_MODULE = 0xFFFF; + const uint NMAX = 5552; ushort _sum1, _sum2; /// Initializes the Fletcher-32 sums @@ -56,14 +57,7 @@ namespace Aaru.Checksums /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - for(int i = 0; i < len; i++) - { - _sum1 = (ushort)((_sum1 + data[i]) % FLETCHER_MODULE); - _sum2 = (ushort)((_sum2 + _sum1) % FLETCHER_MODULE); - } - } + public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len); /// /// Updates the hash with data. @@ -92,6 +86,157 @@ namespace Aaru.Checksums return fletcherOutput.ToString(); } + static void Step(ref ushort previousSum1, ref ushort previousSum2, byte[] data, uint len) + { + uint sum1 = previousSum1; + uint sum2 = previousSum2; + uint n; + int dataOff = 0; + + /* in case user likes doing a byte at a time, keep it fast */ + if(len == 1) + { + sum1 += data[dataOff]; + + if(sum1 >= FLETCHER_MODULE) + sum1 -= FLETCHER_MODULE; + + sum2 += sum1; + + if(sum2 >= FLETCHER_MODULE) + sum2 -= FLETCHER_MODULE; + + previousSum1 = (ushort)(sum1 & 0xFFFF); + previousSum2 = (ushort)(sum2 & 0xFFFF); + + return; + } + + /* in case short lengths are provided, keep it somewhat fast */ + if(len < 16) + { + while(len-- > 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + if(sum1 >= FLETCHER_MODULE) + sum1 -= FLETCHER_MODULE; + + sum2 %= FLETCHER_MODULE; /* only added so many FLETCHER_MODULE's */ + previousSum1 = (ushort)(sum1 & 0xFFFF); + previousSum2 = (ushort)(sum2 & 0xFFFF); + + return; + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while(len >= NMAX) + { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + + do + { + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2 + 1]; + sum2 += sum1; + + /* 16 sums unrolled */ + dataOff += 16; + } while(--n != 0); + + sum1 %= FLETCHER_MODULE; + sum2 %= FLETCHER_MODULE; + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if(len != 0) + { + /* avoid modulos if none remaining */ + while(len >= 16) + { + len -= 16; + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 4 + 2 + 1]; + sum2 += sum1; + + dataOff += 16; + } + + while(len-- != 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + sum1 %= FLETCHER_MODULE; + sum2 %= FLETCHER_MODULE; + } + + previousSum1 = (ushort)(sum1 & 0xFFFF); + previousSum2 = (ushort)(sum2 & 0xFFFF); + } + /// Gets the hash of a file /// File path. public static byte[] File(string filename) @@ -111,10 +256,14 @@ namespace Aaru.Checksums ushort localSum1 = 0xFFFF; ushort localSum2 = 0xFFFF; - for(int i = 0; i < fileStream.Length; i++) + byte[] buffer = new byte[65536]; + int read = fileStream.Read(buffer, 0, 65536); + + while(read > 0) { - localSum1 = (ushort)((localSum1 + fileStream.ReadByte()) % FLETCHER_MODULE); - localSum2 = (ushort)((localSum2 + localSum1) % FLETCHER_MODULE); + Step(ref localSum1, ref localSum2, buffer, (uint)read); + + read = fileStream.Read(buffer, 0, 65536); } uint finalSum = (uint)((localSum2 << 16) | localSum1); @@ -140,11 +289,7 @@ namespace Aaru.Checksums ushort localSum1 = 0xFFFF; ushort localSum2 = 0xFFFF; - for(int i = 0; i < len; i++) - { - localSum1 = (ushort)((localSum1 + data[i]) % FLETCHER_MODULE); - localSum2 = (ushort)((localSum2 + localSum1) % FLETCHER_MODULE); - } + Step(ref localSum1, ref localSum2, data, len); uint finalSum = (uint)((localSum2 << 16) | localSum1); @@ -169,6 +314,7 @@ namespace Aaru.Checksums public sealed class Fletcher16Context : IChecksum { const byte FLETCHER_MODULE = 0xFF; + const byte NMAX = 22; byte _sum1, _sum2; /// Initializes the Fletcher-16 sums @@ -182,14 +328,7 @@ namespace Aaru.Checksums /// Updates the hash with data. /// Data buffer. /// Length of buffer to hash. - public void Update(byte[] data, uint len) - { - for(int i = 0; i < len; i++) - { - _sum1 = (byte)((_sum1 + data[i]) % FLETCHER_MODULE); - _sum2 = (byte)((_sum2 + _sum1) % FLETCHER_MODULE); - } - } + public void Update(byte[] data, uint len) => Step(ref _sum1, ref _sum2, data, len); /// /// Updates the hash with data. @@ -218,6 +357,137 @@ namespace Aaru.Checksums return fletcherOutput.ToString(); } + static void Step(ref byte previousSum1, ref byte previousSum2, byte[] data, uint len) + { + uint sum1 = previousSum1; + uint sum2 = previousSum2; + uint n; + int dataOff = 0; + + /* in case user likes doing a byte at a time, keep it fast */ + if(len == 1) + { + sum1 += data[dataOff]; + + if(sum1 >= FLETCHER_MODULE) + sum1 -= FLETCHER_MODULE; + + sum2 += sum1; + + if(sum2 >= FLETCHER_MODULE) + sum2 -= FLETCHER_MODULE; + + previousSum1 = (byte)(sum1 & 0xFF); + previousSum2 = (byte)(sum2 & 0xFF); + + return; + } + + /* in case short lengths are provided, keep it somewhat fast */ + if(len < 11) + { + while(len-- > 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + if(sum1 >= FLETCHER_MODULE) + sum1 -= FLETCHER_MODULE; + + sum2 %= FLETCHER_MODULE; /* only added so many FLETCHER_MODULE's */ + previousSum1 = (byte)(sum1 & 0xFF); + previousSum2 = (byte)(sum2 & 0xFF); + + return; + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while(len >= NMAX) + { + len -= NMAX; + n = NMAX / 11; /* NMAX is divisible by 16 */ + + do + { + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + + /* 11 sums unrolled */ + dataOff += 11; + } while(--n != 0); + + sum1 %= FLETCHER_MODULE; + sum2 %= FLETCHER_MODULE; + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if(len != 0) + { + /* avoid modulos if none remaining */ + while(len >= 11) + { + len -= 11; + sum1 += data[dataOff]; + sum2 += sum1; + sum1 += data[dataOff + 1]; + sum2 += sum1; + sum1 += data[dataOff + 2]; + sum2 += sum1; + sum1 += data[dataOff + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2]; + sum2 += sum1; + sum1 += data[dataOff + 4 + 2 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 1]; + sum2 += sum1; + sum1 += data[dataOff + 8 + 2]; + sum2 += sum1; + + dataOff += 11; + } + + while(len-- != 0) + { + sum1 += data[dataOff++]; + sum2 += sum1; + } + + sum1 %= FLETCHER_MODULE; + sum2 %= FLETCHER_MODULE; + } + + previousSum1 = (byte)(sum1 & 0xFF); + previousSum2 = (byte)(sum2 & 0xFF); + } + /// Gets the hash of a file /// File path. public static byte[] File(string filename) @@ -237,10 +507,14 @@ namespace Aaru.Checksums byte localSum1 = 0xFF; byte localSum2 = 0xFF; - for(int i = 0; i < fileStream.Length; i++) + byte[] buffer = new byte[65536]; + int read = fileStream.Read(buffer, 0, 65536); + + while(read > 0) { - localSum1 = (byte)((localSum1 + fileStream.ReadByte()) % FLETCHER_MODULE); - localSum2 = (byte)((localSum2 + localSum1) % FLETCHER_MODULE); + Step(ref localSum1, ref localSum2, buffer, (uint)read); + + read = fileStream.Read(buffer, 0, 65536); } ushort finalSum = (ushort)((localSum2 << 8) | localSum1); @@ -266,11 +540,7 @@ namespace Aaru.Checksums byte localSum1 = 0xFF; byte localSum2 = 0xFF; - for(int i = 0; i < len; i++) - { - localSum1 = (byte)((localSum1 + data[i]) % FLETCHER_MODULE); - localSum2 = (byte)((localSum2 + localSum1) % FLETCHER_MODULE); - } + Step(ref localSum1, ref localSum2, data, len); ushort finalSum = (ushort)((localSum2 << 8) | localSum1);