using System.Runtime.CompilerServices; namespace ICSharpCode.SharpZipLib.Checksum { internal static class CrcUtilities { /// /// The number of slicing lookup tables to generate. /// internal const int SlicingDegree = 16; /// /// Generates multiple CRC lookup tables for a given polynomial, stored /// in a linear array of uints. The first block (i.e. the first 256 /// elements) is the same as the byte-by-byte CRC lookup table. /// /// The generating CRC polynomial /// Whether the polynomial is in reversed bit order /// A linear array of 256 * elements /// /// This table could also be generated as a rectangular array, but the /// JIT compiler generates slower code than if we use a linear array. /// Known issue, see: https://github.com/dotnet/runtime/issues/30275 /// internal static uint[] GenerateSlicingLookupTable(uint polynomial, bool isReversed) { var table = new uint[256 * SlicingDegree]; uint one = isReversed ? 1 : (1U << 31); for (int i = 0; i < 256; i++) { uint res = (uint)(isReversed ? i : i << 24); for (int j = 0; j < SlicingDegree; j++) { for (int k = 0; k < 8; k++) { if (isReversed) { res = (res & one) == 1 ? polynomial ^ (res >> 1) : res >> 1; } else { res = (res & one) != 0 ? polynomial ^ (res << 1) : res << 1; } } table[(256 * j) + i] = res; } } return table; } /// /// Mixes the first four bytes of input with /// using normal ordering before calling . /// /// Array of data to checksum /// Offset to start reading from /// The table to use for slicing-by-16 lookup /// Checksum state before this update call /// A new unfinalized checksum value /// /// /// Assumes input[offset]..input[offset + 15] are valid array indexes. /// For performance reasons, this must be checked by the caller. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint UpdateDataForNormalPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) { byte x1 = (byte)((byte)(checkValue >> 24) ^ input[offset]); byte x2 = (byte)((byte)(checkValue >> 16) ^ input[offset + 1]); byte x3 = (byte)((byte)(checkValue >> 8) ^ input[offset + 2]); byte x4 = (byte)((byte)checkValue ^ input[offset + 3]); return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); } /// /// Mixes the first four bytes of input with /// using reflected ordering before calling . /// /// Array of data to checksum /// Offset to start reading from /// The table to use for slicing-by-16 lookup /// Checksum state before this update call /// A new unfinalized checksum value /// /// /// Assumes input[offset]..input[offset + 15] are valid array indexes. /// For performance reasons, this must be checked by the caller. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint UpdateDataForReversedPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) { byte x1 = (byte)((byte)checkValue ^ input[offset]); byte x2 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 1]); byte x3 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 2]); byte x4 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 3]); return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); } /// /// A shared method for updating an unfinalized CRC checksum using slicing-by-16. /// /// Array of data to checksum /// Offset to start reading from /// The table to use for slicing-by-16 lookup /// First byte of input after mixing with the old CRC /// Second byte of input after mixing with the old CRC /// Third byte of input after mixing with the old CRC /// Fourth byte of input after mixing with the old CRC /// A new unfinalized checksum value /// /// /// Even though the first four bytes of input are fed in as arguments, /// should be the same value passed to this /// function's caller (either or /// ). This method will get inlined /// into both functions, so using the same offset produces faster code. /// /// /// Because most processors running C# have some kind of instruction-level /// parallelism, the order of XOR operations can affect performance. This /// ordering assumes that the assembly code generated by the just-in-time /// compiler will emit a bunch of arithmetic operations for checking array /// bounds. Then it opportunistically XORs a1 and a2 to keep the processor /// busy while those other parts of the pipeline handle the range check /// calculations. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint UpdateDataCommon(byte[] input, int offset, uint[] crcTable, byte x1, byte x2, byte x3, byte x4) { uint result; uint a1 = crcTable[x1 + 3840] ^ crcTable[x2 + 3584]; uint a2 = crcTable[x3 + 3328] ^ crcTable[x4 + 3072]; result = crcTable[input[offset + 4] + 2816]; result ^= crcTable[input[offset + 5] + 2560]; a1 ^= crcTable[input[offset + 9] + 1536]; result ^= crcTable[input[offset + 6] + 2304]; result ^= crcTable[input[offset + 7] + 2048]; result ^= crcTable[input[offset + 8] + 1792]; a2 ^= crcTable[input[offset + 13] + 512]; result ^= crcTable[input[offset + 10] + 1280]; result ^= crcTable[input[offset + 11] + 1024]; result ^= crcTable[input[offset + 12] + 768]; result ^= a1; result ^= crcTable[input[offset + 14] + 256]; result ^= crcTable[input[offset + 15]]; result ^= a2; return result; } } }