diff --git a/Makefile b/Makefile index 47df70a..f3d8918 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,13 @@ FLOWTARGET=$(LIB).flow.js FLOWTGTS=$(TARGET) $(AUXTARGETS) CLOSURE=/usr/local/lib/node_modules/google-closure-compiler/compiler.jar +ESMJSTGT=crc32.mjs +ESMJSDEPS=$(shell cat misc/mjs.lst) + ## Main Targets .PHONY: all -all: $(TARGET) $(AUXTARGETS) ## Build library and auxiliary scripts +all: $(TARGET) $(AUXTARGETS) $(ESMJSTGT) crc32c.mjs ## Build library and auxiliary scripts $(FLOWTGTS): %.js : %.flow.js node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^[ \t]*\/\*[:#][^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*[:#][^*]*\*\//gm,""))' > $@ @@ -23,6 +26,9 @@ $(FLOWTGTS): %.js : %.flow.js $(FLOWTARGET): $(DEPS) cat $^ | tr -d '\15\32' > $@ +$(ESMJSTGT): $(ESMJSDEPS) + cat $^ | tr -d '\15\32' > $@ + bits/01_version.js: package.json echo "$(ULIB).version = '"`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`"';" > $@ @@ -33,6 +39,9 @@ clean: clean-baseline ## Remove targets and build artifacts crc32c.flow.js: crc32.flow.js cat $^ | sed 's/-306674912/-2097792136/g; s/CRC32\([ \/\.]\)/CRC32C\1/g' > $@ +crc32c.mjs: crc32.mjs + cat $^ | sed 's/-306674912/-2097792136/g; s/CRC32\([ \/\.]\)/CRC32C\1/g' > $@ + ## Testing .PHONY: test mocha diff --git a/README.md b/README.md index 43ecf2f..bbff3d9 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,18 @@ checksum for a specified file or standard input. ## Integration -Using NodeJS or a bundler: +Using NodeJS or a bundler with `require`: ```js var CRC32 = require("crc-32"); ``` +Using NodeJS or a bundler with `import`: + +```js +import { bstr, buf, str } from "crc-32"; +``` + In the browser, the `crc32.js` script can be loaded directly: ```html @@ -55,6 +61,12 @@ Using NodeJS or a bundler: var CRC32C = require("crc-32/crc32c"); ``` +Using NodeJS or a bundler with `import`: + +```js +import { bstr, buf, str } from "crc-32/crc32c"; +``` + In the browser, the `crc32c.js` script can be loaded directly: ```html @@ -71,7 +83,7 @@ desirable. To prevent the behavior, define `DO_NOT_EXPORT_CRC`. In all cases, the relevant function takes an argument representing data and an optional second argument representing the starting "seed" (for rolling CRC). -The return value is a signed 32-bit integer. +**The return value is a signed 32-bit integer!** - `CRC32.buf(byte array or buffer[, seed])` assumes the argument is a sequence of 8-bit unsigned integers (nodejs `Buffer`, `Uint8Array` or array of bytes). @@ -130,6 +142,25 @@ crc32 = CRC32.buf(Buffer.from(bstr, "binary"), 0); This does not apply to browser `Buffer` shims, and thus is not implemented in the library directly. +### Signed Integers + +Unconventional for a CRC32 checksum, this library uses signed 32-bit integers. +This is for performance reasons. Standard JS operators can convert between +signed and unsigned 32-bit integers: + +```js +CRC32.str("SheetJS") // -1647298270 (signed) +CRC32.str("SheetJS") >>> 0 // 2647669026 (unsigned) +(CRC32.str("SheetJS")>>>0).toString(16) // "9dd03922" (hex) + +(2647669026 | 0) // -1647298270 +``` + +- `x >>> 0` converts a number value to unsigned 32-bit integer. + +- `x | 0` converts a number value to signed 32-bit integer. + + ## Testing `make test` will run the nodejs-based test. diff --git a/crc32.mjs b/crc32.mjs new file mode 100644 index 0000000..a10f230 --- /dev/null +++ b/crc32.mjs @@ -0,0 +1,95 @@ +/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +var CRC32 = {}; +CRC32.version = '1.2.2'; +/*:: +type CRC32Type = number; +type ABuf = Array | Buffer | Uint8Array; +type CRC32TableType = Array | Int32Array; +*/ +/*global Int32Array */ +function signed_crc_table()/*:CRC32TableType*/ { + var c = 0, table/*:Array*/ = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T0 = signed_crc_table(); +function slice_by_16_tables(T) { + var c = 0, v = 0, n = 0, table/*:Array*/ = typeof Int32Array !== 'undefined' ? new Int32Array(4096) : new Array(4096) ; + + for(n = 0; n != 256; ++n) table[n] = T[n]; + for(n = 0; n != 256; ++n) { + v = T[n]; + for(c = 256 + n; c < 4096; c += 256) v = table[c] = (v >>> 8) ^ T[v & 0xFF]; + } + var out = []; + for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256); + return out; +} +var TT = slice_by_16_tables(T0); +var T1 = TT[0], T2 = TT[1], T3 = TT[2], T4 = TT[3], T5 = TT[4]; +var T6 = TT[5], T7 = TT[6], T8 = TT[7], T9 = TT[8], Ta = TT[9]; +var Tb = TT[10], Tc = TT[11], Td = TT[12], Te = TT[13], Tf = TT[14]; +function crc32_bstr(bstr/*:string*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1; + for(var i = 0, L = bstr.length; i < L;) C = (C>>>8) ^ T0[(C^bstr.charCodeAt(i++))&0xFF]; + return ~C; +} + +function crc32_buf(B/*:ABuf*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1, L = B.length - 15, i = 0; + for(; i < L;) C = + Tf[B[i++] ^ (C & 255)] ^ + Te[B[i++] ^ ((C >> 8) & 255)] ^ + Td[B[i++] ^ ((C >> 16) & 255)] ^ + Tc[B[i++] ^ (C >>> 24)] ^ + Tb[B[i++]] ^ Ta[B[i++]] ^ T9[B[i++]] ^ T8[B[i++]] ^ + T7[B[i++]] ^ T6[B[i++]] ^ T5[B[i++]] ^ T4[B[i++]] ^ + T3[B[i++]] ^ T2[B[i++]] ^ T1[B[i++]] ^ T0[B[i++]]; + L += 15; + while(i < L) C = (C>>>8) ^ T0[(C^B[i++])&0xFF]; + return ~C; +} + +function crc32_str(str/*:string*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1; + for(var i = 0, L = str.length, c = 0, d = 0; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T0[(C^c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T0[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T0[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T0[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } + } + return ~C; +} +export const version = CRC32.version; +export const table = T0; +export const bstr = crc32_bstr; +export const buf = crc32_buf; +export const str = crc32_str; diff --git a/crc32c.mjs b/crc32c.mjs new file mode 100644 index 0000000..8316f62 --- /dev/null +++ b/crc32c.mjs @@ -0,0 +1,95 @@ +/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +var CRC32C = {}; +CRC32C.version = '1.2.2'; +/*:: +type CRC32Type = number; +type ABuf = Array | Buffer | Uint8Array; +type CRC32TableType = Array | Int32Array; +*/ +/*global Int32Array */ +function signed_crc_table()/*:CRC32TableType*/ { + var c = 0, table/*:Array*/ = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-2097792136 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T0 = signed_crc_table(); +function slice_by_16_tables(T) { + var c = 0, v = 0, n = 0, table/*:Array*/ = typeof Int32Array !== 'undefined' ? new Int32Array(4096) : new Array(4096) ; + + for(n = 0; n != 256; ++n) table[n] = T[n]; + for(n = 0; n != 256; ++n) { + v = T[n]; + for(c = 256 + n; c < 4096; c += 256) v = table[c] = (v >>> 8) ^ T[v & 0xFF]; + } + var out = []; + for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256); + return out; +} +var TT = slice_by_16_tables(T0); +var T1 = TT[0], T2 = TT[1], T3 = TT[2], T4 = TT[3], T5 = TT[4]; +var T6 = TT[5], T7 = TT[6], T8 = TT[7], T9 = TT[8], Ta = TT[9]; +var Tb = TT[10], Tc = TT[11], Td = TT[12], Te = TT[13], Tf = TT[14]; +function crc32_bstr(bstr/*:string*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1; + for(var i = 0, L = bstr.length; i < L;) C = (C>>>8) ^ T0[(C^bstr.charCodeAt(i++))&0xFF]; + return ~C; +} + +function crc32_buf(B/*:ABuf*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1, L = B.length - 15, i = 0; + for(; i < L;) C = + Tf[B[i++] ^ (C & 255)] ^ + Te[B[i++] ^ ((C >> 8) & 255)] ^ + Td[B[i++] ^ ((C >> 16) & 255)] ^ + Tc[B[i++] ^ (C >>> 24)] ^ + Tb[B[i++]] ^ Ta[B[i++]] ^ T9[B[i++]] ^ T8[B[i++]] ^ + T7[B[i++]] ^ T6[B[i++]] ^ T5[B[i++]] ^ T4[B[i++]] ^ + T3[B[i++]] ^ T2[B[i++]] ^ T1[B[i++]] ^ T0[B[i++]]; + L += 15; + while(i < L) C = (C>>>8) ^ T0[(C^B[i++])&0xFF]; + return ~C; +} + +function crc32_str(str/*:string*/, seed/*:?CRC32Type*/)/*:CRC32Type*/ { + var C = seed/*:: ? 0 : 0 */ ^ -1; + for(var i = 0, L = str.length, c = 0, d = 0; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T0[(C^c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T0[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T0[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T0[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF]; + } + } + return ~C; +} +export const version = CRC32C.version; +export const table = T0; +export const bstr = crc32_bstr; +export const buf = crc32_buf; +export const str = crc32_str; diff --git a/misc/00_header.js b/misc/00_header.js new file mode 100644 index 0000000..a172cbd --- /dev/null +++ b/misc/00_header.js @@ -0,0 +1,3 @@ +/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +var CRC32 = {}; diff --git a/misc/99_footer.js b/misc/99_footer.js new file mode 100644 index 0000000..0f3318d --- /dev/null +++ b/misc/99_footer.js @@ -0,0 +1,5 @@ +export const version = CRC32.version; +export const table = T0; +export const bstr = crc32_bstr; +export const buf = crc32_buf; +export const str = crc32_str; diff --git a/misc/mjs.lst b/misc/mjs.lst new file mode 100644 index 0000000..697323e --- /dev/null +++ b/misc/mjs.lst @@ -0,0 +1,6 @@ +misc/00_header.js +bits/01_version.js +bits/10_types.js +bits/20_crctable.js +bits/40_crc.js +misc/99_footer.js diff --git a/package.json b/package.json index a523291..14bdd82 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,25 @@ "crc32": "bin/crc32.njs" }, "main": "crc32.js", + "module": "crc32.mjs", "types": "types/index.d.ts", "typesVersions": { "*": { "*": ["types/index.d.ts" ] } }, + "exports": { + ".": { + "import": "./crc32.mjs", + "require": "./crc32.js" + }, + "./crc32c": { + "import": "./crc32c.mjs", + "require": "./crc32c.js" + }, + "./crc32.mjs": { + "import": "./crc32.mjs" + }, + "./crc32c.mjs": { + "import": "./crc32c.mjs" + } + }, "dependencies": { }, "devDependencies": { @@ -36,7 +53,7 @@ } }, "homepage": "https://sheetjs.com/", - "files": ["crc32.js", "crc32c.js", "bin/crc32.njs", "LICENSE", "README.md", "types/index.d.ts", "types/*.json"], + "files": ["crc32.js", "crc32c.js", "crc32.mjs", "crc32c.mjs", "bin/crc32.njs", "LICENSE", "README.md", "types/index.d.ts", "types/*.json"], "bugs": { "url": "https://github.com/SheetJS/js-crc32/issues" }, "license": "Apache-2.0", "engines": { "node": ">=0.8" }