Last active
February 26, 2025 14:45
-
-
Save 101arrowz/e58695f7ccfdf74f60ba22018093edea to your computer and use it in GitHub Desktop.
Fast CRC32 in JavaScript
This file contains hidden or 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
/**! | |
* 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) |
This file contains hidden or 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
/**! | |
* 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})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An example how to use this would be great.