Initial release of Web Tools 0.0.5

This commit is contained in:
mid
2026-08-07 21:51:15 +08:00
commit fa14e9e5b9
71 changed files with 6365 additions and 0 deletions
+152
View File
@@ -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')
}