-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(vats): Pare down CRC and meet style
- Loading branch information
Showing
1 changed file
with
146 additions
and
215 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,245 +1,176 @@ | ||
/* | ||
https://en.wikipedia.org/wiki/Cyclic_redundancy_check | ||
Copyright (c) 2018 Vladimir Latyshev | ||
License: MIT | ||
Algorithms ported from https://pycrc.org/ | ||
Forked from npm polycrc@1.1.0 for module system compatibility. | ||
*/ | ||
|
||
export class CRC { | ||
constructor (width, poly, xor_in, xor_out, reflect) { | ||
this.converter = makeBufferConverter() | ||
this.width = width | ||
this.poly = poly | ||
this.xor_in = xor_in | ||
this.reflected_xor_in = mirror(xor_in, width) | ||
this.xor_out = xor_out | ||
this.reflect = reflect | ||
this.msb_mask = 1 << (this.width - 1) | ||
this.mask = ((this.msb_mask - 1) << 1) | 1 | ||
this.crc_shift = this.width < 8 ? 8 - this.width : 0 | ||
this.shifted_xor_in = this.xor_in << this.crc_shift | ||
* https://en.wikipedia.org/wiki/Cyclic_redundancy_check | ||
* Copyright (c) 2018 Vladimir Latyshev | ||
* License: MIT | ||
* Algorithms ported from https://pycrc.org/ | ||
* Forked from npm polycrc@1.1.0 for module system compatibility, then trimmed | ||
* down to a version that assumes TypedArrays are universal. | ||
*/ | ||
// @ts-check | ||
/* eslint-disable no-bitwise */ | ||
|
||
/** | ||
* @typedef {string | number | Uint8Array | ArrayBuffer} Data | ||
*/ | ||
|
||
const encoder = new TextEncoder(); | ||
|
||
/** | ||
* @param {Data} data | ||
*/ | ||
function convert(data) { | ||
if (typeof data === 'number') { | ||
const bytes = []; | ||
// For compatibility with polycrc@1.0.0, we make it at least 4 bytes in length. | ||
// If we have a number more than 32 bits, we will have more bytes already. | ||
while (data > 0 || bytes.length < 4) { | ||
bytes.unshift(data % 256); | ||
data = Math.floor(data / 256); | ||
} | ||
return new Uint8Array(bytes); | ||
} else if (typeof data === 'string') { | ||
return encoder.encode(data); | ||
} else if (ArrayBuffer.isView(data)) { | ||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); | ||
} else if (data instanceof ArrayBuffer) { | ||
return new Uint8Array(data); | ||
} | ||
throw new TypeError( | ||
`crc requires 32 bit number, a string or TypedArray for input`, | ||
); | ||
} | ||
|
||
let table = this.gen_table() | ||
this.table = table | ||
/** | ||
* @param {number} data | ||
* @param {number} width | ||
*/ | ||
function mirror(data, width) { | ||
let res = 0; | ||
for (let i = 0; i < width; i += 1) { | ||
res = (res << 1) | (data & 1); | ||
data >>= 1; | ||
} | ||
return res; | ||
} | ||
|
||
if (this.width === 8 && !this.xor_in && !this.xor_out && !this.reflect) { | ||
this.calculate = function (buffer) { | ||
buffer = this.converter.validate_buffer(buffer) | ||
let crc = 0 | ||
for (let i = 0; i < buffer.length; i++) { crc = table[crc ^ buffer[i]] } | ||
return crc | ||
} | ||
export class CRC { | ||
/** | ||
* @param {number} width | ||
* @param {number} poly | ||
* @param {number} xorIn | ||
* @param {number} xorOut | ||
* @param {boolean} reflect | ||
*/ | ||
constructor(width, poly, xorIn, xorOut, reflect) { | ||
this.width = width; | ||
this.poly = poly; | ||
this.xorIn = xorIn; | ||
this.reflectedXorIn = mirror(xorIn, width); | ||
this.xorOut = xorOut; | ||
this.reflect = reflect; | ||
this.msbMask = 1 << (this.width - 1); | ||
this.mask = ((this.msbMask - 1) << 1) | 1; | ||
this.crcShift = this.width < 8 ? 8 - this.width : 0; | ||
this.shiftedXorIn = this.xorIn << this.crcShift; | ||
|
||
const table = this.genTable(); | ||
this.table = table; | ||
|
||
if (this.width === 8 && !this.xorIn && !this.xorOut && !this.reflect) { | ||
/** | ||
* @param {Data} data | ||
*/ | ||
this.calculate = function calculate(data) { | ||
const buffer = convert(data); | ||
let crc = 0; | ||
for (let i = 0; i < buffer.length; i += 1) { | ||
crc = table[crc ^ buffer[i]]; | ||
} | ||
return crc; | ||
}; | ||
} | ||
} | ||
|
||
calculate (buffer) { | ||
buffer = this.converter.validate_buffer(buffer) | ||
let crc | ||
let { table, width, crc_shift, mask } = this | ||
/** | ||
* @param {Data} data | ||
*/ | ||
calculate(data) { | ||
const buffer = convert(data); | ||
let crc; | ||
const { table, width, crcShift, mask } = this; | ||
if (this.reflect) { | ||
crc = this.reflected_xor_in | ||
for (let i = 0; i < buffer.length; i++) { | ||
let byte = buffer[i] | ||
crc = (table[(crc ^ byte) & 0xff] ^ crc >>> 8) & mask | ||
crc = this.reflectedXorIn; | ||
for (let i = 0; i < buffer.length; i += 1) { | ||
const byte = buffer[i]; | ||
crc = (table[(crc ^ byte) & 0xff] ^ (crc >>> 8)) & mask; | ||
} | ||
} else { | ||
crc = this.shifted_xor_in | ||
for (let i = 0; i < buffer.length; i++) { | ||
crc = table[((crc >> (width - 8 + crc_shift)) ^ buffer[i]) & 0xff] << crc_shift ^ | ||
crc << (8 - crc_shift) & | ||
mask << crc_shift | ||
crc = this.shiftedXorIn; | ||
for (let i = 0; i < buffer.length; i += 1) { | ||
crc = | ||
(table[((crc >> (width - 8 + crcShift)) ^ buffer[i]) & 0xff] << | ||
crcShift) ^ | ||
((crc << (8 - crcShift)) & (mask << crcShift)); | ||
} | ||
crc >>= crc_shift | ||
crc >>= crcShift; | ||
} | ||
crc ^= this.xor_out | ||
return crc >>> 0 | ||
crc ^= this.xorOut; | ||
return crc >>> 0; | ||
} | ||
|
||
calculate_no_table (buffer) { | ||
buffer = this.converter.validate_buffer(buffer) | ||
let crc = this.xor_in | ||
for (let i = 0; i < buffer.length; i++) { | ||
let octet = buffer[i] | ||
if (this.reflect) octet = mirror(octet, 8) | ||
for (let i = 0; i < 8; i++) { | ||
let topbit = crc & this.msb_mask | ||
if (octet & (0x80 >> i)) topbit ^= this.msb_mask | ||
crc <<= 1 | ||
if (topbit) crc ^= this.poly | ||
/** | ||
* @param {Data} data | ||
*/ | ||
calculateNoTable(data) { | ||
const buffer = convert(data); | ||
let crc = this.xorIn; | ||
for (let i = 0; i < buffer.length; i += 1) { | ||
let octet = buffer[i]; | ||
if (this.reflect) octet = mirror(octet, 8); | ||
for (let j = 0; j < 8; j += 1) { | ||
let topbit = crc & this.msbMask; | ||
if (octet & (0x80 >> j)) topbit ^= this.msbMask; | ||
crc <<= 1; | ||
if (topbit) crc ^= this.poly; | ||
} | ||
crc &= this.mask | ||
crc &= this.mask; | ||
} | ||
if (this.reflect) crc = mirror(crc, this.width) | ||
crc ^= this.xor_out | ||
return crc >>> 0 | ||
if (this.reflect) crc = mirror(crc, this.width); | ||
crc ^= this.xorOut; | ||
return crc >>> 0; | ||
} | ||
|
||
gen_table () { | ||
let table_length = 256 | ||
let table = [] | ||
for (let i = 0; i < table_length; i++) { | ||
let reg = i | ||
if (this.reflect) reg = mirror(reg, 8) | ||
reg = reg << (this.width - 8 + this.crc_shift) | ||
for (let j = 0; j < 8; j++) { | ||
if ((reg & (this.msb_mask << this.crc_shift)) !== 0) { | ||
reg <<= 1 | ||
reg ^= this.poly << this.crc_shift | ||
genTable() { | ||
const tableLength = 256; | ||
const table = []; | ||
for (let i = 0; i < tableLength; i += 1) { | ||
let reg = i; | ||
if (this.reflect) reg = mirror(reg, 8); | ||
reg <<= this.width - 8 + this.crcShift; | ||
for (let j = 0; j < 8; j += 1) { | ||
if ((reg & (this.msbMask << this.crcShift)) !== 0) { | ||
reg <<= 1; | ||
reg ^= this.poly << this.crcShift; | ||
} else { | ||
reg <<= 1 | ||
reg <<= 1; | ||
} | ||
} | ||
if (this.reflect) reg = mirror(reg >> this.crc_shift, this.width) << this.crc_shift | ||
reg = (reg >> this.crc_shift) & this.mask | ||
table[i] = reg >>> 0 | ||
} | ||
return new Int32Array(table) | ||
} | ||
|
||
print_table () { | ||
let table = this.table | ||
let digits = Math.ceil(this.width / 4) | ||
let shift = (digits < 4) ? 4 : 3 | ||
let rows = table.length >> shift | ||
let columns = 1 << shift | ||
let result = '' | ||
for (let r = 0; r < rows; r++) { | ||
let row = '' | ||
for (let c = 0; c < columns; c++) { | ||
let val = table[r << shift | c] | ||
val = '000000000' + val.toString(16) | ||
val = val.substr(val.length - digits) | ||
row += '0x' + val + ', ' | ||
} | ||
result += ' ' + row + '\n' | ||
if (this.reflect) | ||
reg = mirror(reg >> this.crcShift, this.width) << this.crcShift; | ||
reg = (reg >> this.crcShift) & this.mask; | ||
table[i] = reg >>> 0; | ||
} | ||
result = '[\n' + result.slice(0, -3) + '\n]' | ||
return result | ||
return new Int32Array(table); | ||
} | ||
} | ||
|
||
const hasBuffer = typeof Buffer !== 'undefined' | ||
const hasTypedArrays = typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined' | ||
|
||
const b256 = typeof BigInt !== undefined && BigInt(256) | ||
|
||
class BaseConverter { | ||
validate_buffer (data) { | ||
switch (typeof data) { | ||
case 'number': { | ||
if (!Number.isSafeInteger(data) || data < 0) { | ||
throw Error(`number data must be a nonnegative safe integer, not ${data}`) | ||
} | ||
|
||
if (this.fromUInt32 && data <= 0xffffffff) { | ||
return this.fromUInt32(data) | ||
} | ||
|
||
// Unpack the number into a big-endian array of 8-bit values. | ||
const bytes = [] | ||
|
||
// For compatibility with polycrc@1.0.0, we make it at least 4 bytes in length. | ||
// If we have a number more than 32 bits, we will have more bytes already. | ||
while (data > 0 || bytes.length < 4) { | ||
bytes.unshift(data % 256) | ||
data = Math.floor(data / 256) | ||
} | ||
|
||
// Just create a buffer from that array. | ||
return this.fromByteArray(bytes) | ||
} | ||
case 'string': { | ||
return this.fromString(data) | ||
} | ||
case 'bigint': { | ||
if (data < 0) { | ||
throw Error(`bigint data must be nonnegative, not ${data}`) | ||
} | ||
|
||
// Unpack the bigint into a big-endian array of 8-bit values. | ||
const bytes = [] | ||
|
||
// For compatibility with polycrc@1.0.0, we make it at least 4 bytes in length. | ||
// If we have a number more than 32 bits, we will have more bytes already. | ||
while (data > 0 || bytes.length < 4) { | ||
bytes.unshift(Number(data % b256)) | ||
data /= b256 | ||
} | ||
return this.fromByteArray(bytes) | ||
} | ||
default: { | ||
// These conversions are not object methods because we want both | ||
// BufferConverter and TypedArrayConverter to support whatever the | ||
// platform is capable of. | ||
if (hasBuffer && Buffer.isBuffer(data)) { | ||
return data | ||
} | ||
if (hasTypedArrays) { | ||
if (ArrayBuffer.isView(data)) { | ||
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength) | ||
} | ||
if (data instanceof ArrayBuffer) { | ||
return new Uint8Array(data) | ||
} | ||
} | ||
throw new Error(`Unrecognized data type ${typeof data}: ${data}`) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Convert most things to Buffers. | ||
class BufferConverter extends BaseConverter { | ||
fromUInt32 (data) { | ||
const buffer = Buffer.alloc(4) | ||
buffer.writeUInt32BE(data) | ||
return buffer | ||
} | ||
|
||
fromByteArray = Buffer.from.bind(Buffer) | ||
fromString = Buffer.from.bind(Buffer) | ||
} | ||
|
||
// Convert most things to TypedArrays. | ||
class TypedArrayConverter extends BaseConverter { | ||
fromByteArray = Uint8Array.from.bind(Uint8Array) | ||
fromString (data) { | ||
return new TextEncoder('utf-8').encode(data) | ||
} | ||
} | ||
|
||
export function makeBufferConverter (preferTypedArrays = false) { | ||
if (hasTypedArrays && (preferTypedArrays || !hasBuffer)) { | ||
return new TypedArrayConverter() | ||
} | ||
if (hasBuffer) { | ||
return new BufferConverter() | ||
} | ||
throw Error('either need TypedArrays or Buffer') | ||
} | ||
|
||
function mirror (data, width) { | ||
let res = 0 | ||
for (let i = 0; i < width; i++) { | ||
res = res << 1 | data & 1 | ||
data >>= 1 | ||
} | ||
return res | ||
} | ||
|
||
export function crc (width, poly, xor_in, xor_out, reflect) { | ||
let model = new CRC(width, poly, xor_in, xor_out, reflect) | ||
return model.calculate.bind(model) | ||
} | ||
|
||
export const crc1 = new CRC(1, 0x01, 0x00, 0x00, false); | ||
export const crc6 = new CRC(6, 0x2F, 0x00, 0x00, false); | ||
export const crc6 = new CRC(6, 0x2f, 0x00, 0x00, false); | ||
export const crc8 = new CRC(8, 0x07, 0x00, 0x00, false); | ||
export const crc10 = new CRC(10, 0x233, 0x0000, 0x0000, false); | ||
export const crc16 = new CRC(16, 0x8005, 0x0000, 0x0000, true); | ||
export const crc24 = new CRC(24, 0x864CFB, 0xB704CE, 0x000000, false); | ||
export const crc32 = new CRC(32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true); | ||
export const crc32c = new CRC(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true); | ||
export const crc24 = new CRC(24, 0x864cfb, 0xb704ce, 0x000000, false); | ||
export const crc32 = new CRC(32, 0x04c11db7, 0xffffffff, 0xffffffff, true); | ||
export const crc32c = new CRC(32, 0x1edc6f41, 0xffffffff, 0xffffffff, true); | ||
|
||
export const models = { crc1, crc6, crc8, crc10, crc16, crc24, crc32, crc32c }; |