From 6cdecfeb22f1ab8bf6f0fac32bf738184cb0e0b6 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Wed, 6 May 2015 14:47:18 -0700 Subject: [PATCH] version bump 0.3.0: cleanup and flow --- .flowconfig | 17 ++++++++ .jshintrc | 4 ++ Makefile | 33 +++++++++++---- README.md | 64 ++++++++++++++-------------- bits/00_header.js | 1 + bits/01_version.js | 2 +- bits/10_types.js | 5 +++ bits/20_crctable.js | 4 +- bits/40_crc.js | 10 ++--- crc32.flow.js | 100 ++++++++++++++++++++++++++++++++++++++++++++ crc32.js | 6 +-- ctest/crc32.js | 6 +-- package.json | 6 +-- 13 files changed, 200 insertions(+), 58 deletions(-) create mode 100644 .flowconfig create mode 100644 .jshintrc create mode 100644 bits/10_types.js create mode 100644 crc32.flow.js diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..9338415 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,17 @@ +[ignore] +.*/node_modules/.* +.*/dist/.* +.*/test.js +.*/crc32.js + +.*/bits/.* +.*/ctest/.* +.*/misc/.* +.*/perf/.* + +[include] +crc32.flow.js + +[libs] + +[options] diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..5cd205b --- /dev/null +++ b/.jshintrc @@ -0,0 +1,4 @@ +{ + "bitwise": false, + "curly": false +} diff --git a/Makefile b/Makefile index 59428c2..b87f11e 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,20 @@ LIB=crc32 +REQS= +ADDONS= +AUXTARGETS= + +ULIB=$(shell echo $(LIB) | tr a-z A-Z) DEPS=$(sort $(wildcard bits/*.js)) TARGET=$(LIB).js -$(TARGET): $(DEPS) +.PHONY: all +all: $(TARGET) $(AUXTARGETS) + +$(TARGET) $(AUXTARGETS): %.js : %.flow.js + node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^\s*\/\*:[^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*:[^*]*\*\//gm,""))' > $@ + +$(LIB).flow.js: $(DEPS) cat $^ | tr -d '\15\32' > $@ - cp -f $@ ctest/ bits/01_version.js: package.json echo "CRC32.version = '"`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`"';" > $@ @@ -15,18 +25,23 @@ clean: .PHONY: test mocha test mocha: test.js - mocha -R spec + mocha -R spec -t 20000 .PHONY: ctest ctest: cat misc/*.js > ctest/fixtures.js cp -f test.js ctest/test.js - cp -f $(TARGET) ctest/ + cp -f $(TARGET) ctest/ .PHONY: lint -lint: $(TARGET) - jshint --show-non-errors $(TARGET) - jscs $(TARGET) +lint: $(TARGET) $(AUXTARGETS) + jshint --show-non-errors $(TARGET) $(AUXTARGETS) + jshint --show-non-errors package.json + jscs $(TARGET) $(AUXTARGETS) + +.PHONY: flow +flow: lint + flow check --all --show-all-errors .PHONY: cov cov-spin cov: misc/coverage.html @@ -39,11 +54,11 @@ $(COVFMT): cov_%: FMTS=$* make cov misc/coverage.html: $(TARGET) test.js - mocha --require blanket -R html-cov > $@ + mocha --require blanket -R html-cov -t 20000 > $@ .PHONY: coveralls coveralls-spin coveralls: - mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js + mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | ./node_modules/coveralls/bin/coveralls.js coveralls-spin: make coveralls & bash misc/spin.sh $$! diff --git a/README.md b/README.md index 830cb6f..beb5476 100644 --- a/README.md +++ b/README.md @@ -5,59 +5,61 @@ Emphasis on correctness and performance. ## Installation -In [nodejs](https://www.npmjs.org/package/crc-32): +With [npm](https://www.npmjs.org/package/crc-32): - npm install crc-32 + $ npm install crc-32 In the browser: - + -The browser exposes a variable CRC32 +The script will manipulate `module.exports` if available (e.g. in a CommonJS +`require` context). This is not always desirable. To prevent the behavior, +define `DO_NOT_EXPORT_CRC` ## Usage -- `CRC32.buf(byte array or buffer)` assumes the argument is a set of 8 bit - unsigned integers (e.g. nodejs `Buffer` or simple array of ints) +In all cases, the relevant function takes a single argument representing data. + +The return value is a signed 32-bit integer. + +- `CRC32.buf(byte array or buffer)` assumes the argument is a set of 8-bit + unsigned integers (e.g. nodejs `Buffer` or simple array of ints). - `CRC32.bstr(binary string)` interprets the argument as a binary string where - the `i`-th byte is `str.charCodeAt(i)` + the `i`-th byte is the low byte of the UCS-2 char: `str.charCodeAt(i) & 0xFF` - `CRC32.str(string)` interprets the argument as a standard JS string +For example: + +```js +> // var CRC32 = require('crc-32'); // uncomment this line if in node +> CRC32.str("SheetJS") // -1647298270 +> CRC32.bstr("SheetJS") // -1647298270 +> CRC32.buf([ 83, 104, 101, 101, 116, 74, 83 ]) // -1647298270 + +> [CRC32.str("\u2603"), CRC32.str("\u0003")] // [ -1743909036, 1259060791 ] +> [CRC32.bstr("\u2603"), CRC32.bstr("\u0003")] // [ 1259060791, 1259060791 ] +> [CRC32.buf([0x2603]), CRC32.buf([0x0003])] // [ 1259060791, 1259060791 ] +``` + ## Testing -`make test` will run the nodejs-based test. To run the in-browser tests, run a -local server and go to the `ctest` directory. To update the browser artifacts, -run `make ctest`. +`make test` will run the node-based tests. -## Performance - -`make perf` will run algorithmic performance tests (which should justify certain -decisions in the code). - -`make perf-all` compares the performance of various crc-32 algorithms that -implement the correct form (note that the SSE intrinsic is designed for the -CRC32C checksum and uses a different polynomial). - -Unexpected code patterns were based on performance testing in node and browser: - -- [Loop unrolling helps!](http://jsperf.com/crc32-table/2) - -## In the future ... - -- Specifying an arbitrary initial CRC value - -- Supporting different polynomials (e.g. CRC32C) +To run the in-browser tests, run a local server and go to the `ctest` directory. +To update the browser artifacts, run `make ctest`. ## License Please consult the attached LICENSE file for details. All rights not explicitly granted by the Apache 2.0 license are reserved by the Original Author. +## Badges + [![Build Status](https://travis-ci.org/SheetJS/js-crc32.svg?branch=master)](https://travis-ci.org/SheetJS/js-crc32) -[![Coverage Status](https://img.shields.io/coveralls/SheetJS/js-crc32/master.svg)](https://coveralls.io/r/SheetJS/js-crc32?branch=master) - -[![githalytics.com alpha](https://cruel-carlota.pagodabox.com/ee0e89f8b1d5b861ffbf264b8ce329a6 "githalytics.com")](http://githalytics.com/SheetJS/js-crc32) +[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-crc32/master.svg)](https://coveralls.io/r/SheetJS/js-crc32?branch=master) +[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-crc32?pixel)](https://github.com/SheetJS/js-crc32) diff --git a/bits/00_header.js b/bits/00_header.js index b204b48..8d5d5da 100644 --- a/bits/00_header.js +++ b/bits/00_header.js @@ -1,4 +1,5 @@ /* crc32.js (C) 2014 SheetJS -- http://sheetjs.com */ /* vim: set ts=2: */ var CRC32 = {}; +/*:: declare var DO_NOT_EXPORT_CRC: any; */ (function(CRC32) { diff --git a/bits/01_version.js b/bits/01_version.js index 73f41d5..c88838f 100644 --- a/bits/01_version.js +++ b/bits/01_version.js @@ -1 +1 @@ -CRC32.version = '0.2.2'; +CRC32.version = '0.3.0'; diff --git a/bits/10_types.js b/bits/10_types.js new file mode 100644 index 0000000..2d11289 --- /dev/null +++ b/bits/10_types.js @@ -0,0 +1,5 @@ +/*:: +type CRC32Type = number; +type ABuf = Array | Buffer; +type CRC32TableType = Array | Int32Array; +*/ diff --git a/bits/20_crctable.js b/bits/20_crctable.js index 57cdb60..cd7d108 100644 --- a/bits/20_crctable.js +++ b/bits/20_crctable.js @@ -1,6 +1,6 @@ /* see perf/crc32table.js */ -function signed_crc_table() { - var c, table = new Array(256); +function signed_crc_table()/*:CRC32TableType*/ { + var c = 0, table/*:Array*/ = new Array(256); for(var n =0; n != 256; ++n){ c = n; diff --git a/bits/40_crc.js b/bits/40_crc.js index d378abc..cf8a719 100644 --- a/bits/40_crc.js +++ b/bits/40_crc.js @@ -1,7 +1,7 @@ /* charCodeAt is the best approach for binary strings */ var use_buffer = typeof Buffer !== 'undefined'; -function crc32_bstr(bstr) { - if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(Buffer(bstr)); +function crc32_bstr(bstr/*:string*/)/*:CRC32Type*/ { + if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(new Buffer(bstr)); var crc = -1, L = bstr.length - 1; for(var i = 0; i < L;) { crc = table[(crc ^ bstr.charCodeAt(i++)) & 0xFF] ^ (crc >>> 8); @@ -11,7 +11,7 @@ function crc32_bstr(bstr) { return crc ^ -1; } -function crc32_buf(buf) { +function crc32_buf(buf/*:ABuf*/)/*:CRC32Type*/ { if(buf.length > 10000) return crc32_buf_8(buf); for(var crc = -1, i = 0, L=buf.length-3; i < L;) { crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; @@ -23,7 +23,7 @@ function crc32_buf(buf) { return crc ^ -1; } -function crc32_buf_8(buf) { +function crc32_buf_8(buf/*:ABuf*/)/*:CRC32Type*/ { for(var crc = -1, i = 0, L=buf.length-7; i < L;) { crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; @@ -39,7 +39,7 @@ function crc32_buf_8(buf) { } /* much much faster to intertwine utf8 and crc */ -function crc32_str(str) { +function crc32_str(str/*:string*/)/*:CRC32Type*/ { for(var crc = -1, i = 0, L=str.length, c, d; i < L;) { c = str.charCodeAt(i++); if(c < 0x80) { diff --git a/crc32.flow.js b/crc32.flow.js new file mode 100644 index 0000000..8f62636 --- /dev/null +++ b/crc32.flow.js @@ -0,0 +1,100 @@ +/* crc32.js (C) 2014 SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +var CRC32 = {}; +/*:: declare var DO_NOT_EXPORT_CRC: any; */ +(function(CRC32) { +CRC32.version = '0.3.0'; +/*:: +type CRC32Type = number; +type ABuf = Array | Buffer; +type CRC32TableType = Array | Int32Array; +*/ +/* see perf/crc32table.js */ +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 table = signed_crc_table(); +/* charCodeAt is the best approach for binary strings */ +var use_buffer = typeof Buffer !== 'undefined'; +function crc32_bstr(bstr/*:string*/)/*:CRC32Type*/ { + if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(new Buffer(bstr)); + var crc = -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + crc = table[(crc ^ bstr.charCodeAt(i++)) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ bstr.charCodeAt(i++)) & 0xFF] ^ (crc >>> 8); + } + if(i === L) crc = (crc >>> 8) ^ table[(crc ^ bstr.charCodeAt(i)) & 0xFF]; + return crc ^ -1; +} + +function crc32_buf(buf/*:ABuf*/)/*:CRC32Type*/ { + if(buf.length > 10000) return crc32_buf_8(buf); + for(var crc = -1, i = 0, L=buf.length-3; i < L;) { + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + } + while(i < L+3) crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + return crc ^ -1; +} + +function crc32_buf_8(buf/*:ABuf*/)/*:CRC32Type*/ { + for(var crc = -1, i = 0, L=buf.length-7; i < L;) { + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + } + while(i < L+7) crc = (crc >>> 8) ^ table[(crc^buf[i++])&0xFF]; + return crc ^ -1; +} + +/* much much faster to intertwine utf8 and crc */ +function crc32_str(str/*:string*/)/*:CRC32Type*/ { + for(var crc = -1, i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + crc = (crc >>> 8) ^ table[(crc ^ c) & 0xFF]; + } else if(c < 0x800) { + crc = (crc >>> 8) ^ table[(crc ^ (192|((c>>6)&31))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|(c&63))) & 0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++) & 1023; + crc = (crc >>> 8) ^ table[(crc ^ (240|((c>>8)&7))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|((c>>2)&63))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|((d>>6)&15)|(c&3))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|(d&63))) & 0xFF]; + } else { + crc = (crc >>> 8) ^ table[(crc ^ (224|((c>>12)&15))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|((c>>6)&63))) & 0xFF]; + crc = (crc >>> 8) ^ table[(crc ^ (128|(c&63))) & 0xFF]; + } + } + return crc ^ -1; +} +CRC32.table = table; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})(typeof exports !== "undefined" && typeof DO_NOT_EXPORT_CRC === 'undefined' ? exports : CRC32); diff --git a/crc32.js b/crc32.js index ea4527b..3f00ceb 100644 --- a/crc32.js +++ b/crc32.js @@ -2,10 +2,10 @@ /* vim: set ts=2: */ var CRC32 = {}; (function(CRC32) { -CRC32.version = '0.2.2'; +CRC32.version = '0.3.0'; /* see perf/crc32table.js */ function signed_crc_table() { - var c, table = new Array(256); + var c = 0, table = new Array(256); for(var n =0; n != 256; ++n){ c = n; @@ -27,7 +27,7 @@ var table = signed_crc_table(); /* charCodeAt is the best approach for binary strings */ var use_buffer = typeof Buffer !== 'undefined'; function crc32_bstr(bstr) { - if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(Buffer(bstr)); + if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(new Buffer(bstr)); var crc = -1, L = bstr.length - 1; for(var i = 0; i < L;) { crc = table[(crc ^ bstr.charCodeAt(i++)) & 0xFF] ^ (crc >>> 8); diff --git a/ctest/crc32.js b/ctest/crc32.js index ea4527b..3f00ceb 100644 --- a/ctest/crc32.js +++ b/ctest/crc32.js @@ -2,10 +2,10 @@ /* vim: set ts=2: */ var CRC32 = {}; (function(CRC32) { -CRC32.version = '0.2.2'; +CRC32.version = '0.3.0'; /* see perf/crc32table.js */ function signed_crc_table() { - var c, table = new Array(256); + var c = 0, table = new Array(256); for(var n =0; n != 256; ++n){ c = n; @@ -27,7 +27,7 @@ var table = signed_crc_table(); /* charCodeAt is the best approach for binary strings */ var use_buffer = typeof Buffer !== 'undefined'; function crc32_bstr(bstr) { - if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(Buffer(bstr)); + if(bstr.length > 32768) if(use_buffer) return crc32_buf_8(new Buffer(bstr)); var crc = -1, L = bstr.length - 1; for(var i = 0; i < L;) { crc = table[(crc ^ bstr.charCodeAt(i++)) & 0xFF] ^ (crc >>> 8); diff --git a/package.json b/package.json index c0879b5..384b031 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,13 @@ { "name": "crc-32", - "version": "0.2.2", + "version": "0.3.0", "author": "sheetjs", "description": "Pure-JS CRC-32", "keywords": [ "crc32", "checksum", "crc" ], "main": "./crc32", "devDependencies": { "mocha":"", - "xlsjs":"", - "uglify-js":"", - "codepage":"" + "uglify-js":"" }, "repository": { "type":"git", "url":"git://github.com/SheetJS/js-crc32.git" }, "scripts": {