Sources/OSS/Utils/CRC64.swift (161 lines of code) (raw):

import Foundation public struct CRC64: Sendable { public static let `default` = CRC64() private let poly: UInt64 = 0xC96C_5795_D787_0F42 private let table: [[UInt64]] private let isLittleEndian: Bool init() { let n: Int64 = 1 isLittleEndian = (n == n.littleEndian) var crc: UInt64 var table = [[UInt64]](repeating: [UInt64](repeating: 0, count: 256), count: 8) for n in 0 ..< 256 { crc = UInt64(n) for _ in 0 ..< 8 { crc = ((crc & 1) != 0) ? poly ^ (crc >> 1) : crc >> 1 } table[0][n] = crc } for n in 0 ..< 256 { crc = table[0][n] for k in 1 ..< 8 { crc = table[0][Int(crc & 0xFF)] ^ (crc >> 8) table[k][n] = crc } } self.table = table if !isLittleEndian { for k in 0 ..< 8 { for n in 0 ..< 256 { table[k][n] = rev8(a: table[k][n]) } } } } @inlinable func rev8(a: UInt64) -> UInt64 { var m: UInt64 m = 0xFF_00FF_00FF_00FF var a = ((a >> 8) & m) | (a & m) << 8 m = 0xFFFF_0000_FFFF a = ((a >> 16) & m) | (a & m) << 16 return a >> 32 | a << 32 } public func crc64(crc: UInt64, buf: UnsafeRawPointer, len: Int) -> UInt64 { if isLittleEndian { return crc64Little(crc: crc, buf: buf, len: len) } else { return crc64Big(crc: crc, buf: buf, len: len) } } private func crc64Little(crc: UInt64, buf: UnsafeRawPointer, len: Int) -> UInt64 { var next = UnsafeMutableRawPointer(mutating: buf) var len = len var crc = ~crc while len > 0 && (Int(bitPattern: buf) & 7) != 0 { crc = table[0][Int((crc ^ UInt64(next.load(as: UInt8.self))) & 0xFF)] ^ (crc >> 8) next += 1 len -= 1 } while len >= 8 { crc ^= next.load(as: UInt64.self) crc = table[7][Int(crc & 0xFF)] ^ table[6][Int((crc >> 8) & 0xFF)] ^ table[5][Int((crc >> 16) & 0xFF)] ^ table[4][Int((crc >> 24) & 0xFF)] ^ table[3][Int((crc >> 32) & 0xFF)] ^ table[2][Int((crc >> 40) & 0xFF)] ^ table[1][Int((crc >> 48) & 0xFF)] ^ table[0][Int(crc >> 56)] next += 8 len -= 8 } while len > 0 { crc = table[0][Int((crc ^ UInt64(next.load(as: UInt8.self))) & 0xFF)] ^ (crc >> 8) next += 1 len -= 1 } return ~crc } private func crc64Big(crc: UInt64, buf: UnsafeRawPointer, len: Int) -> UInt64 { var next = UnsafeMutableRawPointer(mutating: buf) var len = len var crc = ~rev8(a: crc) while len > 0 && (Int(bitPattern: buf) & 7) != 0 { crc = table[0][Int((crc >> 56) ^ UInt64(next.load(as: UInt8.self)))] ^ (crc << 8) next += 1 len -= 1 } while len >= 8 { crc ^= next.load(as: UInt64.self) crc = table[0][Int(crc & 0xFF)] ^ table[1][Int((crc >> 8) & 0xFF)] ^ table[2][Int((crc >> 16) & 0xFF)] ^ table[3][Int((crc >> 24) & 0xFF)] ^ table[4][Int((crc >> 32) & 0xFF)] ^ table[5][Int((crc >> 40) & 0xFF)] ^ table[6][Int((crc >> 48) & 0xFF)] ^ table[7][Int(crc >> 56)] next += 8 len -= 8 } while len > 0 { crc = table[0][Int((crc >> 56) ^ UInt64(next.load(as: UInt8.self)))] ^ (crc << 8) next += 1 len -= 1 } return ~rev8(a: crc) } private func gf2MatrixTimes(_ mat: UnsafeMutablePointer<UInt64>, _ vec: UInt64) -> UInt64 { var mat = mat var vec = vec var sum: UInt64 = 0 while vec != 0 { if (vec & 1) != 0 { sum ^= mat.pointee } vec >>= 1 mat += 1 } return sum } private func gf2MatrixSquare(_ square: UnsafeMutablePointer<UInt64>, _ mat: UnsafeMutablePointer<UInt64>) { for n in 0 ..< 64 { square[n] = gf2MatrixTimes(mat, mat[n]) } } public func crc64Combine(crc1: UInt64, crc2: UInt64, len2: uintmax_t) -> UInt64 { var crc1 = crc1 var len2 = len2 var row: UInt64 = 1 var even = [UInt64](repeating: 0, count: 64) /* even-power-of-two zeros operator */ var odd = [UInt64](repeating: 0, count: 64) /* odd-power-of-two zeros operator */ /* degenerate case */ if len2 == 0 { return crc1 } /* put operator for one zero bit in odd */ odd[0] = 0xC96C_5795_D787_0F42 /* CRC-64 polynomial */ for n in 1 ..< 64 { odd[n] = row row <<= 1 } /* put operator for two zero bits in even */ gf2MatrixSquare(&even, &odd) /* put operator for four zero bits in odd */ gf2MatrixSquare(&odd, &even) /* apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) */ repeat { /* apply zeros operator for this bit of len2 */ gf2MatrixSquare(&even, &odd) if (len2 & 1) != 0 { crc1 = gf2MatrixTimes(&even, crc1) } len2 >>= 1 /* if no more bits set, then done */ if len2 == 0 { break } /* another iteration of the loop with odd and even swapped */ gf2MatrixSquare(&odd, &even) if (len2 & 1) != 0 { crc1 = gf2MatrixTimes(&odd, crc1) } len2 >>= 1 /* if no more bits set, then done */ } while len2 != 0 /* return combined crc */ crc1 ^= crc2 return crc1 } }