-
Notifications
You must be signed in to change notification settings - Fork 212
/
crc.js
165 lines (153 loc) · 4.9 KB
/
crc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
* https://en.wikipedia.org/wiki/Cyclic_redundancy_check
* Copyright (c) 2018 Vladimir Latyshev
* License: MIT
* Algorithms ported from https://pycrc.org/
* Forked from npm [email protected] for module system compatibility, then trimmed
* down to a version that assumes TypedArrays are universal.
*/
/* 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 [email protected], 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 TypeError(
`crc requires 32 bit number, a string or TypedArray for input`,
);
}
/**
* @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;
}
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;
};
}
}
/** @param {Data} data */
calculate(data) {
const buffer = convert(data);
let crc;
const { table, width, crcShift, mask } = this;
if (this.reflect) {
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.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 >>= crcShift;
}
crc ^= this.xorOut;
return crc >>> 0;
}
/** @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;
}
if (this.reflect) crc = mirror(crc, this.width);
crc ^= this.xorOut;
return crc >>> 0;
}
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;
}
}
if (this.reflect)
reg = mirror(reg >> this.crcShift, this.width) << this.crcShift;
reg = (reg >> this.crcShift) & this.mask;
table[i] = reg >>> 0;
}
return new Int32Array(table);
}
}
export const crc1 = new CRC(1, 0x01, 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 models = { crc1, crc6, crc8, crc10, crc16, crc24, crc32, crc32c };