export interface CrcParameters { width: number polynomial: bigint initialValue: bigint inputReflected: boolean outputReflected: boolean xorOut: bigint } export interface CrcPreset extends CrcParameters { id: string name: string check: bigint } export const CRC_PRESETS: CrcPreset[] = [ { id: 'crc-32-iso-hdlc', name: 'CRC-32 / ISO-HDLC', width: 32, polynomial: 0x04c11db7n, initialValue: 0xffffffffn, inputReflected: true, outputReflected: true, xorOut: 0xffffffffn, check: 0xcbf43926n }, { id: 'crc-16-ccitt-false', name: 'CRC-16 / CCITT-FALSE', width: 16, polynomial: 0x1021n, initialValue: 0xffffn, inputReflected: false, outputReflected: false, xorOut: 0n, check: 0x29b1n }, { id: 'crc-16-modbus', name: 'CRC-16 / MODBUS', width: 16, polynomial: 0x8005n, initialValue: 0xffffn, inputReflected: true, outputReflected: true, xorOut: 0n, check: 0x4b37n }, { id: 'crc-16-arc', name: 'CRC-16 / ARC', width: 16, polynomial: 0x8005n, initialValue: 0n, inputReflected: true, outputReflected: true, xorOut: 0n, check: 0xbb3dn }, { id: 'crc-16-xmodem', name: 'CRC-16 / XMODEM', width: 16, polynomial: 0x1021n, initialValue: 0n, inputReflected: false, outputReflected: false, xorOut: 0n, check: 0x31c3n }, { id: 'crc-8', name: 'CRC-8', width: 8, polynomial: 0x07n, initialValue: 0n, inputReflected: false, outputReflected: false, xorOut: 0n, check: 0xf4n }, ] function reflectBigInt(value: bigint, width: number): bigint { let reflected = 0n for (let bit = 0; bit < width; bit += 1) { if (value & (1n << BigInt(bit))) reflected |= 1n << BigInt(width - 1 - bit) } return reflected } function validateParameters(parameters: CrcParameters) { if (!Number.isInteger(parameters.width) || parameters.width < 8 || parameters.width > 64) { throw new Error('CRC 位宽必须是 8 至 64 的整数') } const mask = (1n << BigInt(parameters.width)) - 1n if (parameters.polynomial <= 0n || parameters.polynomial > mask) throw new Error('CRC 多项式超出当前位宽') if (parameters.initialValue < 0n || parameters.initialValue > mask) throw new Error('CRC 初始值超出当前位宽') if (parameters.xorOut < 0n || parameters.xorOut > mask) throw new Error('CRC 异或输出值超出当前位宽') } function reflectNumber(value: number, width: number): number { let current = value >>> 0 let reflected = 0 for (let bit = 0; bit < width; bit += 1) { reflected = ((reflected * 2) + (current & 1)) >>> 0 current >>>= 1 } return reflected } function calculateNumberCrc(chunks: Iterable, parameters: CrcParameters): bigint { const width = parameters.width const mask = width === 32 ? 0xffffffff : (2 ** width) - 1 const trim = (value: number) => width === 32 ? value >>> 0 : (value & mask) >>> 0 const polynomial = Number(parameters.polynomial) >>> 0 const table = new Uint32Array(256) let remainder = trim(Number(parameters.initialValue)) if (parameters.inputReflected) { const reflectedPolynomial = reflectNumber(polynomial, width) for (let index = 0; index < 256; index += 1) { let entry = index for (let bit = 0; bit < 8; bit += 1) { entry = entry & 1 ? (entry >>> 1) ^ reflectedPolynomial : entry >>> 1 } table[index] = trim(entry) } for (const data of chunks) { for (const byte of data) { remainder = trim((remainder >>> 8) ^ table[(remainder ^ byte) & 0xff]) } } } else { const topBit = 2 ** (width - 1) for (let index = 0; index < 256; index += 1) { let entry = trim(index << (width - 8)) for (let bit = 0; bit < 8; bit += 1) { entry = trim(entry & topBit ? (entry << 1) ^ polynomial : entry << 1) } table[index] = entry } for (const data of chunks) { for (const byte of data) { const tableIndex = ((remainder >>> (width - 8)) ^ byte) & 0xff remainder = trim((remainder << 8) ^ table[tableIndex]) } } } if (parameters.inputReflected !== parameters.outputReflected) remainder = reflectNumber(remainder, width) return BigInt(trim(remainder ^ Number(parameters.xorOut))) } function calculateBigIntCrc(chunks: Iterable, parameters: CrcParameters): bigint { const width = parameters.width const shift = BigInt(width - 8) const mask = (1n << BigInt(width)) - 1n const table: bigint[] = [] let remainder = parameters.initialValue & mask if (parameters.inputReflected) { const reflectedPolynomial = reflectBigInt(parameters.polynomial, width) for (let index = 0; index < 256; index += 1) { let entry = BigInt(index) for (let bit = 0; bit < 8; bit += 1) { entry = entry & 1n ? (entry >> 1n) ^ reflectedPolynomial : entry >> 1n } table[index] = entry & mask } for (const data of chunks) { for (const byte of data) { remainder = ((remainder >> 8n) ^ table[Number((remainder ^ BigInt(byte)) & 0xffn)]) & mask } } } else { const topBit = 1n << BigInt(width - 1) for (let index = 0; index < 256; index += 1) { let entry = BigInt(index) << shift for (let bit = 0; bit < 8; bit += 1) { entry = entry & topBit ? (entry << 1n) ^ parameters.polynomial : entry << 1n entry &= mask } table[index] = entry } for (const data of chunks) { for (const byte of data) { const tableIndex = Number(((remainder >> shift) ^ BigInt(byte)) & 0xffn) remainder = ((remainder << 8n) ^ table[tableIndex]) & mask } } } if (parameters.inputReflected !== parameters.outputReflected) remainder = reflectBigInt(remainder, width) return (remainder ^ parameters.xorOut) & mask } export function calculateCrcChunks(chunks: Iterable, parameters: CrcParameters): bigint { validateParameters(parameters) return parameters.width <= 32 ? calculateNumberCrc(chunks, parameters) : calculateBigIntCrc(chunks, parameters) } export function calculateCrc(data: Uint8Array, parameters: CrcParameters): bigint { return calculateCrcChunks([data], parameters) } export function formatCrc(value: bigint, width: number): string { return value.toString(16).toUpperCase().padStart(Math.ceil(width / 4), '0') }