From e1c36afdeca5b46b504d8b124f9925a33b129090 Mon Sep 17 00:00:00 2001 From: Evgeny Morozov Date: Wed, 30 Mar 2022 12:17:14 +0200 Subject: [PATCH] Update Adler32 from ImageSharp v2.1.0 Adler32.cs is taken from https://github.com/SixLabors/ImageSharp/commit/09b2cdb83aab0a889dbde8ec2453d605a1be1725 with minimal change to make it build as part of SharpCompress. Fixes https://github.com/adamhathcock/sharpcompress/issues/650 and https://github.com/adamhathcock/sharpcompress/issues/645. --- src/SharpCompress/Algorithms/Adler32.cs | 420 ++++++++++++++++++++++++ src/SharpCompress/Algorithms/Alder32.cs | 280 ---------------- 2 files changed, 420 insertions(+), 280 deletions(-) create mode 100644 src/SharpCompress/Algorithms/Adler32.cs delete mode 100644 src/SharpCompress/Algorithms/Alder32.cs diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs new file mode 100644 index 00000000..e98c864e --- /dev/null +++ b/src/SharpCompress/Algorithms/Adler32.cs @@ -0,0 +1,420 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK +#define SUPPORTS_RUNTIME_INTRINSICS +#define SUPPORTS_HOTPATH +#endif + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if SUPPORTS_RUNTIME_INTRINSICS +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +#endif + +#pragma warning disable IDE0007 // Use implicit type +namespace SharpCompress.Algorithms +{ + /// + /// Calculates the 32 bit Adler checksum of a given buffer according to + /// RFC 1950. ZLIB Compressed Data Format Specification version 3.3) + /// + internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs + { + /// + /// Global inlining options. Helps temporarily disable inlining for better profiler output. + /// + private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs + { + /// + /// regardless of the build conditions. + /// + public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining; +#if PROFILING + public const MethodImplOptions HotPath = MethodImplOptions.NoInlining; + public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; +#else +#if SUPPORTS_HOTPATH + public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization; +#else + public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining; +#endif + public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; +#endif + public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining; + } + +#if SUPPORTS_RUNTIME_INTRINSICS + /// + /// Provides optimized static methods for trigonometric, logarithmic, + /// and other common mathematical functions. + /// + private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs + { + /// + /// Reduces elements of the vector into one sum. + /// + /// The accumulator to reduce. + /// The sum of all elements. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReduceSum(Vector256 accumulator) + { + // Add upper lane to lower lane. + Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); + + // Add odd to even. + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01)); + + // Add high to low. + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); + + return Sse2.ConvertToInt32(vsum); + } + + /// + /// Reduces even elements of the vector into one sum. + /// + /// The accumulator to reduce. + /// The sum of even elements. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int EvenReduceSum(Vector256 accumulator) + { + Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low + + // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882 + return Sse2.ConvertToInt32(vsum); + } + } +#endif + + /// + /// The default initial seed value of a Adler32 checksum calculation. + /// + public const uint SeedValue = 1U; + + // Largest prime smaller than 65536 + private const uint BASE = 65521; + + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const uint NMAX = 5552; + +#if SUPPORTS_RUNTIME_INTRINSICS + private const int MinBufferSize = 64; + + private const int BlockSize = 1 << 5; + + // The C# compiler emits this as a compile-time constant embedded in the PE file. + private static ReadOnlySpan Tap1Tap2 => new byte[] + { + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, // tap1 + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 // tap2 + }; +#endif + + /// + /// Calculates the Adler32 checksum with the bytes taken from the span. + /// + /// The readonly span of bytes. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static uint Calculate(ReadOnlySpan buffer) + => Calculate(SeedValue, buffer); + + /// + /// Calculates the Adler32 checksum with the bytes taken from the span and seed. + /// + /// The input Adler32 value. + /// The readonly span of bytes. + /// The . + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + public static uint Calculate(uint adler, ReadOnlySpan buffer) + { + if (buffer.IsEmpty) + { + return adler; + } + +#if SUPPORTS_RUNTIME_INTRINSICS + if (Avx2.IsSupported && buffer.Length >= MinBufferSize) + { + return CalculateAvx2(adler, buffer); + } + + if (Ssse3.IsSupported && buffer.Length >= MinBufferSize) + { + return CalculateSse(adler, buffer); + } + + return CalculateScalar(adler, buffer); +#else + return CalculateScalar(adler, buffer); +#endif + } + + // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c +#if SUPPORTS_RUNTIME_INTRINSICS + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + + // Process the data in blocks. + uint length = (uint)buffer.Length; + uint blocks = length / BlockSize; + length -= blocks * BlockSize; + + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2)) + { + byte* localBufferPtr = bufferPtr; + + // _mm_setr_epi8 on x86 + Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr); + Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); + Vector128 zero = Vector128.Zero; + var ones = Vector128.Create((short)1); + + while (blocks > 0) + { + uint n = NMAX / BlockSize; /* The NMAX constraint. */ + if (n > blocks) + { + n = blocks; + } + + blocks -= n; + + // Process n blocks of data. At most NMAX data bytes can be + // processed before s2 must be reduced modulo BASE. + Vector128 v_ps = Vector128.CreateScalar(s1 * n); + Vector128 v_s2 = Vector128.CreateScalar(s2); + Vector128 v_s1 = Vector128.Zero; + + do + { + // Load 32 input bytes. + Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr); + Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10); + + // Add previous block byte sum to v_ps. + v_ps = Sse2.Add(v_ps, v_s1); + + // Horizontally add the bytes for s1, multiply-adds the + // bytes by [ 32, 31, 30, ... ] for s2. + v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32()); + Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); + v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32()); + + v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32()); + Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); + v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32()); + + localBufferPtr += BlockSize; + } + while (--n > 0); + + v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5)); + + // Sum epi32 ints v_s1(s2) and accumulate in s1(s2). + const byte S2301 = 0b1011_0001; // A B C D -> B A D C + const byte S1032 = 0b0100_1110; // A B C D -> C D A B + + v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032)); + + s1 += v_s1.ToScalar(); + + v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301)); + v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032)); + + s2 = v_s2.ToScalar(); + + // Reduce. + s1 %= BASE; + s2 %= BASE; + } + + if (length > 0) + { + HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + } + + return s1 | (s2 << 16); + } + } + } + + // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + uint length = (uint)buffer.Length; + + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + byte* localBufferPtr = bufferPtr; + + Vector256 zero = Vector256.Zero; + var dot3v = Vector256.Create((short)1); + var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + + // Process n blocks of data. At most NMAX data bytes can be + // processed before s2 must be reduced modulo BASE. + var vs1 = Vector256.CreateScalar(s1); + var vs2 = Vector256.CreateScalar(s2); + + while (length >= 32) + { + int k = length < NMAX ? (int)length : (int)NMAX; + k -= k % 32; + length -= (uint)k; + + Vector256 vs10 = vs1; + Vector256 vs3 = Vector256.Zero; + + while (k >= 32) + { + // Load 32 input bytes. + Vector256 block = Avx.LoadVector256(localBufferPtr); + + // Sum of abs diff, resulting in 2 x int32's + Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero); + + vs1 = Avx2.Add(vs1, vs1sad.AsUInt32()); + vs3 = Avx2.Add(vs3, vs10); + + // sum 32 uint8s to 16 shorts. + Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v); + + // sum 16 shorts to 8 uint32s. + Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v); + + vs2 = Avx2.Add(vsum2.AsUInt32(), vs2); + vs10 = vs1; + + localBufferPtr += BlockSize; + k -= 32; + } + + // Defer the multiplication with 32 to outside of the loop. + vs3 = Avx2.ShiftLeftLogical(vs3, 5); + vs2 = Avx2.Add(vs2, vs3); + + s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32()); + s2 = (uint)Numerics.ReduceSum(vs2.AsInt32()); + + s1 %= BASE; + s2 %= BASE; + + vs1 = Vector256.CreateScalar(s1); + vs2 = Vector256.CreateScalar(s2); + } + + if (length > 0) + { + HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + } + + return s1 | (s2 << 16); + } + } + + private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2) + { + if (length >= 16) + { + s2 += s1 += localBufferPtr[0]; + s2 += s1 += localBufferPtr[1]; + s2 += s1 += localBufferPtr[2]; + s2 += s1 += localBufferPtr[3]; + s2 += s1 += localBufferPtr[4]; + s2 += s1 += localBufferPtr[5]; + s2 += s1 += localBufferPtr[6]; + s2 += s1 += localBufferPtr[7]; + s2 += s1 += localBufferPtr[8]; + s2 += s1 += localBufferPtr[9]; + s2 += s1 += localBufferPtr[10]; + s2 += s1 += localBufferPtr[11]; + s2 += s1 += localBufferPtr[12]; + s2 += s1 += localBufferPtr[13]; + s2 += s1 += localBufferPtr[14]; + s2 += s1 += localBufferPtr[15]; + + localBufferPtr += 16; + length -= 16; + } + + while (length-- > 0) + { + s2 += s1 += *localBufferPtr++; + } + + if (s1 >= BASE) + { + s1 -= BASE; + } + + s2 %= BASE; + } +#endif + + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + uint k; + + fixed (byte* bufferPtr = buffer) + { + var localBufferPtr = bufferPtr; + uint length = (uint)buffer.Length; + + while (length > 0) + { + k = length < NMAX ? length : NMAX; + length -= k; + + while (k >= 16) + { + s2 += s1 += localBufferPtr[0]; + s2 += s1 += localBufferPtr[1]; + s2 += s1 += localBufferPtr[2]; + s2 += s1 += localBufferPtr[3]; + s2 += s1 += localBufferPtr[4]; + s2 += s1 += localBufferPtr[5]; + s2 += s1 += localBufferPtr[6]; + s2 += s1 += localBufferPtr[7]; + s2 += s1 += localBufferPtr[8]; + s2 += s1 += localBufferPtr[9]; + s2 += s1 += localBufferPtr[10]; + s2 += s1 += localBufferPtr[11]; + s2 += s1 += localBufferPtr[12]; + s2 += s1 += localBufferPtr[13]; + s2 += s1 += localBufferPtr[14]; + s2 += s1 += localBufferPtr[15]; + + localBufferPtr += 16; + k -= 16; + } + + while (k-- > 0) + { + s2 += s1 += *localBufferPtr++; + } + + s1 %= BASE; + s2 %= BASE; + } + + return (s2 << 16) | s1; + } + } + } +} diff --git a/src/SharpCompress/Algorithms/Alder32.cs b/src/SharpCompress/Algorithms/Alder32.cs deleted file mode 100644 index 9a353993..00000000 --- a/src/SharpCompress/Algorithms/Alder32.cs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the GNU Affero General Public License, Version 3. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -#endif - -namespace SharpCompress.Algorithms -{ - /// - /// Calculates the 32 bit Adler checksum of a given buffer according to - /// RFC 1950. ZLIB Compressed Data Format Specification version 3.3) - /// - internal static class Adler32 - { - /// - /// The default initial seed value of a Adler32 checksum calculation. - /// - public const uint SeedValue = 1U; - -#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK - private const int MinBufferSize = 64; -#endif - - // Largest prime smaller than 65536 - private const uint BASE = 65521; - - // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - private const uint NMAX = 5552; - - /// - /// Calculates the Adler32 checksum with the bytes taken from the span. - /// - /// The readonly span of bytes. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Calculate(ReadOnlySpan buffer) - { - return Calculate(SeedValue, buffer); - } - - /// - /// Calculates the Adler32 checksum with the bytes taken from the span and seed. - /// - /// The input Adler32 value. - /// The readonly span of bytes. - /// The . - public static uint Calculate(uint adler, ReadOnlySpan buffer) - { -#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK - if (Sse3.IsSupported && buffer.Length >= MinBufferSize) - { - return CalculateSse(adler, buffer); - } - - return CalculateScalar(adler, buffer); -#else - return CalculateScalar(adler, buffer); -#endif - } - - // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c -#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK - private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer) - { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; - - // Process the data in blocks. - const int BLOCK_SIZE = 1 << 5; - - uint length = (uint)buffer.Length; - uint blocks = length / BLOCK_SIZE; - length -= blocks * BLOCK_SIZE; - - int index = 0; - fixed (byte* bufferPtr = &buffer[0]) - { - index += (int)blocks * BLOCK_SIZE; - var localBufferPtr = bufferPtr; - - // _mm_setr_epi8 on x86 - var tap1 = Vector128.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17); - var tap2 = Vector128.Create(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); - Vector128 zero = Vector128.Zero; - var ones = Vector128.Create((short)1); - - while (blocks > 0) - { - uint n = NMAX / BLOCK_SIZE; /* The NMAX constraint. */ - if (n > blocks) - { - n = blocks; - } - - blocks -= n; - - // Process n blocks of data. At most NMAX data bytes can be - // processed before s2 must be reduced modulo BASE. - Vector128 v_ps = Vector128.CreateScalar(s1 * n).AsInt32(); - Vector128 v_s2 = Vector128.CreateScalar(s2).AsInt32(); - Vector128 v_s1 = Vector128.Zero; - - do - { - // Load 32 input bytes. - Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr); - Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 16); - - // Add previous block byte sum to v_ps. - v_ps = Sse2.Add(v_ps, v_s1); - - // Horizontally add the bytes for s1, multiply-adds the - // bytes by [ 32, 31, 30, ... ] for s2. - v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsInt32()); - Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); - v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones)); - - v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsInt32()); - Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); - v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones)); - - localBufferPtr += BLOCK_SIZE; - } - while (--n > 0); - - v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5)); - - // Sum epi32 ints v_s1(s2) and accumulate in s1(s2). - const byte S2301 = 0b1011_0001; // A B C D -> B A D C - const byte S1032 = 0b0100_1110; // A B C D -> C D A B - - v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S2301)); - v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032)); - - s1 += (uint)v_s1.ToScalar(); - - v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301)); - v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032)); - - s2 = (uint)v_s2.ToScalar(); - - // Reduce. - s1 %= BASE; - s2 %= BASE; - } - } - - ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); - - if (length > 0) - { - if (length >= 16) - { - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - length -= 16; - } - - while (length-- > 0) - { - s2 += s1 += Unsafe.Add(ref bufferRef, index++); - } - - if (s1 >= BASE) - { - s1 -= BASE; - } - - s2 %= BASE; - } - - return s1 | (s2 << 16); - } -#endif - - private static uint CalculateScalar(uint adler, ReadOnlySpan buffer) - { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; - uint k; - - ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); - uint length = (uint)buffer.Length; - int index = 0; - - while (length > 0) - { - k = length < NMAX ? length : NMAX; - length -= k; - - while (k >= 16) - { - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - k -= 16; - } - - if (k != 0) - { - do - { - s1 += Unsafe.Add(ref bufferRef, index++); - s2 += s1; - } - while (--k != 0); - } - - s1 %= BASE; - s2 %= BASE; - } - - return (s2 << 16) | s1; - } - } -}