Skip to content

Instantly share code, notes, and snippets.

@101arrowz
Last active February 26, 2025 14:45
Show Gist options
  • Save 101arrowz/e58695f7ccfdf74f60ba22018093edea to your computer and use it in GitHub Desktop.
Save 101arrowz/e58695f7ccfdf74f60ba22018093edea to your computer and use it in GitHub Desktop.
Fast CRC32 in JavaScript
/**!
* Fast CRC32 in JavaScript
* 101arrowz (https://github.com/101arrowz)
* License: MIT
*/
// If you use this code, please link this gist or attribute it somehow.
// This code uses the Slice-by-16 algorithm to achieve performance
// roughly 2x greater than all other JS CRC32 implementations (e.g.
// crc32-js).
// Per local testing, Slice-by-16 outperforms Slice-by-4 by around 50%
// and Slice-by-8/Slice-by-32/Slice-by-64 by 10-30%
// This CRC implementation can compete with WASM CRC implementations
// as well, and it tends to perform between 30% faster and 10% slower
// than WASM CRC32 (>1MB input chunks is faster on WASM).
// The minified bundle size is under 1kB, about 700B gzipped.
// CRC32 table
// perf: signed integers are 2x more likely to be Smi
// Smi is a V8 datatype in (-2**30, 2**30-1)
// Smi operations are much faster
const crct = new Int32Array(4096);
for (let i = 0; i < 256; ++i) {
let c = i, k = 9;
while (--k) c = ((c & 1) && -306674912) ^ (c >>> 1);
crct[i] = c;
}
for (let i = 0; i < 256; ++i) {
let lv = crct[i];
for (let j = 256; j < 4096; j += 256) lv = crct[i | j] = (lv >>> 8) ^ crct[lv & 255];
}
const crcts = [];
for (let i = 0; i < 16;) crcts[i] = crct.subarray(i << 8, ++i << 8);
const [
t1, t2, t3, t4, t5, t6, t7, t8,
t9, t10, t11, t12, t13, t14, t15, t16
] = crcts;
// raw CRC function
// stream by passing in previous CRC output as second parameter
const rawCRC = (d, c) => {
// when second param not specified, defaults to ~0 = -1
c = ~c;
let i = 0;
const max = d.length - 16;
for (; i < max;) {
c =
t16[d[i++] ^ (c & 255)] ^
t15[d[i++] ^ ((c >> 8) & 255)] ^
t14[d[i++] ^ ((c >> 16) & 255)] ^
t13[d[i++] ^ (c >>> 24)] ^
t12[d[i++]] ^
t11[d[i++]] ^
t10[d[i++]] ^
t9[d[i++]] ^
t8[d[i++]] ^
t7[d[i++]] ^
t6[d[i++]] ^
t5[d[i++]] ^
t4[d[i++]] ^
t3[d[i++]] ^
t2[d[i++]] ^
t1[d[i++]];
}
for (; i < d.length; ++i) c = t1[(c & 255) ^ d[i]] ^ (c >>> 8);
return ~c;
}
// Nicer API here
// Remove this for smaller bundle
class CRC {
/**
* Updates the CRC value by processing the buffer provided
* @param {Uint8Array} buf The buffer of data to process
* @returns {CRC} The current CRC
*/
update(buf) {
if (!(buf instanceof Uint8Array)) {
throw new TypeError(
'need a Uint8Array, not' +
Object.prototype.toString.call(buf).slice(8, -1)
);
}
this.value = rawCRC(buf, this.value);
return this;
}
/**
* Returns the current value of the CRC
* @param {('hex'|'raw')} type The method with which to digest, default hex
* @returns The CRC value as hex or binary
*/
digest(type) {
const value = this.value < 0
? 0x1_0000_0000 + this.value
: this.value;
if (type == 'raw') {
return value;
} else if (type == 'hex' || type == undefined) {
return value.toString(16);
} else {
throw new TypeError(`cannot digest in form ${type}`)
}
}
}
// License: MIT (use freely, but please attribute/link this gist)
/**!
* Fast CRC32 in JavaScript
* 101arrowz (https://github.com/101arrowz)
* License: MIT
*/
!function(e){"undefined"!=typeof module&&"object"==typeof exports?module.exports=e():"undefined"!=typeof define&&define.amd?define(["CRC",e]):("undefined"!=typeof self?self:this).CRC=e()}((function(){for(var e=new Int32Array(4096),t=0;t<256;++t){for(var r=t,n=9;--n;)r=(1&r&&-306674912)^r>>>1;e[t]=r}for(t=0;t<256;++t)for(var o=e[t],i=256;i<4096;i+=256)o=e[t|i]=o>>>8^e[255&o];var f=[];for(t=0;t<16;)f[t]=e.subarray(t<<8,++t<<8);var a=f[0],u=f[1],d=f[2],l=f[3],s=f[4],p=f[5],c=f[6],y=f[7],h=f[8],v=f[9],g=f[10],w=f[11],m=f[12],C=f[13],b=f[14],x=f[15],A=function(){};return A.prototype.update=function(e){if(!(e instanceof Uint8Array))throw new TypeError("need a Uint8Array, not"+Object.prototype.toString.call(e).slice(8,-1));return this.value=function(e,t){t=~t;for(var r=0,n=e.length-16;r<n;)t=x[e[r++]^255&t]^b[e[r++]^t>>8&255]^C[e[r++]^t>>16&255]^m[e[r++]^t>>>24]^w[e[r++]]^g[e[r++]]^v[e[r++]]^h[e[r++]]^y[e[r++]]^c[e[r++]]^p[e[r++]]^s[e[r++]]^l[e[r++]]^d[e[r++]]^u[e[r++]]^a[e[r++]];for(;r<e.length;++r)t=a[255&t^e[r]]^t>>>8;return~t}(e,this.value),this},A.prototype.digest=function(e){var t=this.value<0?4294967296+this.value:this.value;if("raw"==e)return t;if("hex"==e||null==e)return t.toString(16);throw new TypeError("cannot digest in form "+e)},A}))
@vanowm
Copy link

vanowm commented Oct 30, 2024

An example how to use this would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment