Skip to content

Instantly share code, notes, and snippets.

@bryc
Last active May 8, 2023 03:15
Show Gist options
  • Save bryc/79d1a62304773285317191f1ae5aa5b8 to your computer and use it in GitHub Desktop.
Save bryc/79d1a62304773285317191f1ae5aa5b8 to your computer and use it in GitHub Desktop.
Optimized CRC implementations in JavaScript
/*
Optimized CRC-16 for 0x1021 (unreflected)
----
With this code, (5) CRC-16 variants can be modelled:
crc=0x0000, xorout=0x0000 = CRC-16/XMODEM (default)
crc=0xFFFF, xorout=0x0000 = CRC-16/IBM-3740
crc=0xFFFF, xorout=0xFFFF = CRC-16/GENIBUS
crc=0x0000, xorout=0xFFFF = CRC-16/GSM
crc=0x1D0F, xorout=0x0000 = CRC-16/SPI-FUJITSU
Variants XMODEM and IBM-3740 have sometimes been misidentified as CRC-CCITT.
The real CRC-CCITT is the reflected version, known as CRC-16/KERMIT.
Note: I've established that the `crc =` line can be altered to construct 512 different polynomimal permutations.
The (crc << 8) and (t << 12) term cannot be changed, but any combination of (t), or (t << 1 to 8) can be made,
though no duplicates can exist (they cancel each other out). This represents polynomials 4096 through 4607.
In these cases, I've confirmed that output matches the more traditional implementation.
But this only applies to this particular pattern (0x1021, unreflected). Others work differently.
*/
function crc16_1021(data, crc = 0, xorout = 0) {
for(let i = 0, t; i < data.length; i++, crc &= 0xFFFF) {
t = (crc >> 8) ^ data[i];
t = (t ^ t >> 4);
crc = (crc << 8) ^ (t << 12) ^ (t << 5) ^ (t);
}
return crc ^ xorout;
}
/*
Don't know who made this one, but it dates to around 2004. It's slightly slower than crc16_pic.
Maybe it is Ashley Roll's original before additional optimizations.
I included some further (seemingly obvious) optimizations in comments.
----------------------------------------------------------------------------------------------------------------------------
Just moving old CRC-16 code here for now.
----------------------------------------------------------------------------------------------------------------------------
uint16_t crc16_compute(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc) {
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
for (uint32_t i = 0; i < size; i++) {
crc = (uint8_t)(crc >> 8) | (crc << 8);
crc ^= p_data[i];
crc ^= (uint8_t)(crc & 0xFF) >> 4;
crc ^= (crc << 8) << 4;
crc ^= ((crc & 0xFF) << 4) << 1;
}
return crc;
}
*/
function crc16_pic(data, crc = 0xFFFF) {
for(let i = 0; i < data.length; i++, crc &= 0xFFFF) {
crc = (crc >>> 8) | (crc << 8);
crc ^= data[i];
crc ^= (crc & 255) >>> 4;
crc ^= (crc << 8) << 4; // crc ^= crc << 12;
crc ^= ((crc & 255) << 4) << 1; // crc ^= (crc & 255) << 5;
}
return crc;
}
/*
Optimized CRC-16 for 0x8005 (unreflected)
----
With this code, (3) CRC-16 variants can be modelled:
crc=0x0000, xorout=0x0000 = CRC-16/UMTS
crc=0xFFFF, xorout=0x0000 = CRC-16/CMS
crc=0x800D, xorout=0x0000 = CRC-16/DDS-110
Very different set of operations compared to the others.
*/
function crc16_8005(data, crc = 0, xorout = 0) {
for(let i = 0, t, z; i < data.length; i++, crc &= 0xFFFF) {
t = (crc >> 8) ^ data[i];
z = t;
t ^= t >> 1;
t ^= t >> 2;
t ^= t >> 4;
t &= 1;
t |= z << 1;
crc = (crc << 8) ^ (t << 15) ^ (t << 1) ^ (t);
}
return crc ^ xorout;
}
/*
Optimized CRC-16 for 0x1021 (reflected)
----
Note: The actual polynomial being used is 0x8408, as it has been reflected.
With this code, (6) CRC-16 variants can be modelled:
crc=0x0000, xorout=0x0000 = CRC-16/KERMIT, aka CRC-16/CCITT (default)
crc=0xFFFF, xorout=0x0000 = CRC-16/MCRF4XX
crc=0xFFFF, xorout=0xFFFF = CRC-16/IBM-SDLC
crc=0x554d, xorout=0x0000 = CRC-16/RIELLO
crc=0x3791, xorout=0x0000 = CRC-16/TMS37157
crc=0x6363, xorout=0x0000 = CRC-16/ISO-IEC-14443-3-A
Note: The bits of the initial crc value must be in reverse order.
The values supplied have been reversed above.
*/
function crc16_1021r(data, crc = 0, xorout = 0) {
for(let i = 0, t; i < data.length; i++, crc &= 0xFFFF) {
t = (crc) ^ data[i];
t = (t ^ t << 4) & 0xFF;
crc = (crc >> 8) ^ (t << 8) ^ (t >> 4) ^ (t << 3);
}
return crc ^ xorout;
}
/*
Optimized CRC-32 for 0x04C11DB7 (reflected)
----
Note: Technically we are using the reflected version of 0xEDB88320.
With this code, two CRC-32 variants can be modelled:
crc=0xFFFFFFFF, xorout=0xFFFFFFFF = CRC-32/ISO-HDLC (default)
crc=0xFFFFFFFF, xorout=0x00000000 = CRC-32/JAMCRC
Credit: Hacker's Delight by Henry S. Warren & Hagai Gold.
https://create.stephan-brumme.com/crc32/#tableless
*/
function crc32_pic(data, crc = 0xFFFFFFFF, xorout = 0xFFFFFFFF) {
for(let i = 0, s, a, b; i < data.length; i++) {
s = crc ^ data[i];
b = (s ^ s << 6) & 255;
a = Math.imul(b, 8404996);
crc = (crc >>> 8) ^
Math.imul(b, 16843008) ^ Math.imul(b, 1052672) ^
(b<<19) ^ (b<<17) ^ (b>>>2) ^ a ^ (a>>>1);
}
return (crc ^ xorout) >>> 0;
}
// Alternative version, slightly slower? Hard to notice much difference in JS.
function crc32_pic(data, crc = 0xFFFFFFFF, xorout = 0xFFFFFFFF) {
for(let i = 0, s, t, x, y, z; i < data.length; i++) {
s = crc ^ data[i],
t = (s ^ s << 6) << 24;
x = (t>>>1) ^ (t>>>2), y = x ^ (x>>>3), z = (t>>>12) ^ (t>>>16);
crc = (crc>>>8) ^ t ^ (t>>>23) ^ y ^ (y>>>6) ^ z ^ (z>>>10);
}
return (crc ^ xorout) >>> 0;
}
// "Full" version of the above.
// Was hoping to see this could be modified for different polynomials, but can't seem to crack it.
function crc32_pic(data, crc = 0xFFFFFFFF, xorout = 0xFFFFFFFF) {
for(let i = 0, s, t, x, y, z; i < data.length; i++) {
s = crc ^ data[i],
t = (s ^ s << 6) << 24;
crc = (crc >>> 8) ^
(t) ^
(t >>> 1) ^ (t >>> 2) ^ (t >>> 4) ^ (t >>> 5) ^
(t >>> 7) ^ (t >>> 8) ^ (t >>> 10) ^ (t >>> 11) ^
(t >>> 12) ^ (t >>> 16) ^
(t >>> 22) ^ (t >>> 26) ^
(t >>> 23);
}
return (crc ^ xorout) >>> 0;
}
// A pretty useless expanded version. From Hacker's Delight.
// I thought it would allow for different polynomials, but doesn't work. Only 0xEDB88320.
function crc32_pic(data, crc = 0xFFFFFFFF, xorout = 0xFFFFFFFF) {
let g0 = 0xEDB88320,
g1 = g0>>>1, g2 = g0>>>2, g3 = g0>>>3, g4 = g0>>>4,
g5 = g0>>>5, g6 = g0 ^ g0>>>6, g7 = (g0 ^ g0>>>6) >>> 1;
for(let i = 0, c; i < data.length; i++) {
crc = crc ^ data[i];
c = (crc<<31>>31) & g7 ^ (crc<<30>>31 & g6) ^
(crc<<29>>31) & g5 ^ (crc<<28>>31 & g4) ^
(crc<<27>>31) & g3 ^ (crc<<26>>31 & g2) ^
(crc<<25>>31) & g1 ^ (crc<<24>>31 & g0);
crc = c ^ (crc >>> 8);
}
return (crc ^ xorout) >>> 0;
}
// Unrolled generic CRC. Should theoretially be easily modifiable to any other CRC variant, and the speed is still comparable.
function crc32(data, crc = -1, xorout = -1) {
for(let i = 0, p = 0xEDB88320; i < data.length; i++) {
crc ^= data[i];
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
crc = crc&1 ? crc>>>1 ^ p : crc >>> 1;
}
return (crc ^ xorout) >>> 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment