Skip to content

Commit

Permalink
refactor(vats): Pare down CRC and meet style
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Sep 20, 2021
1 parent fadc726 commit 2d2f6ad
Showing 1 changed file with 146 additions and 215 deletions.
361 changes: 146 additions & 215 deletions packages/vats/src/crc.js
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 };

0 comments on commit 2d2f6ad

Please sign in to comment.