Initial release of Web Tools 0.0.5
This commit is contained in:
+152
@@ -0,0 +1,152 @@
|
||||
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<Uint8Array>, 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<Uint8Array>, 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<Uint8Array>, 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')
|
||||
}
|
||||
Reference in New Issue
Block a user