diff --git a/.flowconfig b/.flowconfig index fe6a070..91b9157 100644 --- a/.flowconfig +++ b/.flowconfig @@ -10,13 +10,18 @@ .*/perf/.* .*/demo/browser.js +.*/shim.js [include] adler32.flow.js +.*/bin/.*.njs .*/demo/browser.flow.js [libs] bits/10_types.js misc/flow.js +misc/flowdeps.js [options] +module.file_ext=.js +module.file_ext=.njs diff --git a/.gitignore b/.gitignore index 1d607da..0365d5f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ test_files/*.py test_files/*.js test_files/baseline* misc/coverage.html +ctest/sauce* diff --git a/Makefile b/Makefile index 727c4a1..d76764d 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ LIB=adler32 REQS= ADDONS= AUXTARGETS=demo/browser.js +CMDS=bin/adler32.njs HTMLLINT=index.html ULIB=$(shell echo $(LIB) | tr a-z A-Z) @@ -31,12 +32,13 @@ clean: clean-baseline ## Remove targets and build artifacts .PHONY: test mocha test mocha: test.js $(TARGET) baseline ## Run test suite - mocha -R spec -t 20000 + mocha -R spec -t 30000 .PHONY: ctest ctest: ## Build browser test (into ctest/ subdirectory) cat misc/*.js > ctest/fixtures.js cp -f test.js ctest/test.js + cp -f shim.js ctest/shim.js cp -f $(TARGET) ctest/ .PHONY: ctestserv @@ -56,6 +58,7 @@ clean-baseline: ## Remove test baselines .PHONY: lint lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks @jshint --show-non-errors $(TARGET) $(AUXTARGETS) + @jshint --show-non-errors $(CMDS) @jshint --show-non-errors package.json @jshint --show-non-errors --extract=always $(HTMLLINT) @jscs $(TARGET) $(AUXTARGETS) @@ -68,7 +71,7 @@ flow: lint ## Run flow checker cov: misc/coverage.html ## Run coverage test misc/coverage.html: $(TARGET) test.js - mocha --require blanket -R html-cov -t 20000 > $@ + mocha --require blanket -R html-cov -t 30000 > $@ .PHONY: coveralls coveralls: ## Coverage Test + Send to coveralls.io diff --git a/README.md b/README.md index ce6af31..2504b08 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,63 @@ # adler32 Signed ADLER-32 algorithm implementation in JS (for the browser and nodejs). -Emphasis on correctness and performance. +Emphasis on correctness, performance, and IE6+ support. ## Installation With [npm](https://www.npmjs.org/package/adler-32): - npm install adler-32 +```bash +$ npm install adler-32 +``` In the browser: - +```html + +``` -The browser exposes a variable ADLER32 +The browser exposes a variable `ADLER32`. When installed globally, npm installs a script `adler32` that computes the checksum for a specified file or standard input. +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_ADLER`. + ## Usage -- `ADLER32.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 an argument representing data and an +optional second argument representing the starting "seed" (for running hash). -- `ADLER32.bstr(binary string)` interprets the argument as a binary string where - the `i`-th byte is `str.charCodeAt(i)` +The return value is a signed 32-bit integer. -- `ADLER32.str(string)` interprets the argument as a standard JS string +- `ADLER32.buf(byte array or buffer[, seed])` assumes the argument is a sequence + of 8-bit unsigned integers (e.g. nodejs `Buffer` or simple array of ints). + +- `ADLER32.bstr(binary string[, seed])` assumes the argument as a binary string + where byte `i` is the low byte of the UCS-2 char: `str.charCodeAt(i) & 0xFF` + +- `ADLER32.str(string)` assumes the argument as a standard JS string and + calculates the hash of the UTF-8 encoding. + +For example: + +```js +// var ADLER32 = require('adler-32'); // uncomment if in node +ADLER32.str("SheetJS") // 176947863 +ADLER32.bstr("SheetJS") // 176947863 +ADLER32.buf([ 83, 104, 101, 101, 116, 74, 83 ]) // 176947863 + +adler32 = ADLER32.buf([83, 104]) // 17825980 "Sh" +adler32 = ADLER32.str("eet", adler32) // 95486458 "Sheet" +ADLER32.bstr("JS", adler32) // 176947863 "SheetJS" + +[ADLER32.str("\u2603"), ADLER32.str("\u0003")] // [ 73138686, 262148 ] +[ADLER32.bstr("\u2603"), ADLER32.bstr("\u0003")] // [ 262148, 262148 ] +[ADLER32.buf([0x2603]), ADLER32.buf([0x0003])] // [ 262148, 262148 ] +``` ## Testing @@ -39,7 +70,7 @@ To update the browser artifacts, run `make ctest`. To generate the bits file, use the `adler32` function from python zlib: -``` +```python >>> from zlib import adler32 >>> x="foo bar baz٪☃🍣" >>> adler32(x) @@ -52,7 +83,7 @@ To generate the bits file, use the `adler32` function from python zlib: The included `adler32.njs` script can process files or stdin: -``` +```bash $ echo "this is a test" > t.txt $ bin/adler32.njs t.txt 726861088 @@ -60,7 +91,7 @@ $ bin/adler32.njs t.txt For comparison, the included `adler32.py` script uses python zlib: -``` +```bash $ bin/adler32.py t.txt 726861088 ``` @@ -70,8 +101,6 @@ $ bin/adler32.py t.txt `make perf` will run algorithmic performance tests (which should justify certain decisions in the code). -[js-crc](http://git.io/crc32) has more performance notes - Bit twiddling is much faster than taking the mod on Safari and older Firefoxes. Instead of taking the literal mod 65521, it is faster to keep it in the integers by bit-shifting: `65536 ~ 15 mod 65521` so for nonnegative integer `a`: @@ -87,7 +116,7 @@ The mod is taken at the very end, since the intermediate result may exceed 65521 The magic numbers were chosen so as to not overflow a 31-bit integer: -``` +```mathematica F[n_] := Reduce[x*(x + 1)*n/2 + (x + 1)*(65521) < (2^31 - 1) && x > 0, x, Integers] F[255] (* bstr: x \[Element] Integers && 1 <= x <= 3854 *) F[127] (* ascii: x \[Element] Integers && 1 <= x <= 5321 *) @@ -102,9 +131,10 @@ granted by the Apache 2.0 license are reserved by the Original Author. ## Badges +[![Sauce Test Status](https://saucelabs.com/browser-matrix/adler32.svg)](https://saucelabs.com/u/adler32) + [![Build Status](https://travis-ci.org/SheetJS/js-adler32.svg?branch=master)](https://travis-ci.org/SheetJS/js-adler32) -[![Coverage Status](https://coveralls.io/repos/SheetJS/js-adler32/badge.png?branch=master)](https://coveralls.io/r/SheetJS/js-adler32?branch=master) +[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-adler32/master.svg)](https://coveralls.io/r/SheetJS/js-adler32?branch=master) [![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-adler32?pixel)](https://github.com/SheetJS/js-adler32) - diff --git a/adler32.flow.js b/adler32.flow.js index 9d3435e..0aff418 100644 --- a/adler32.flow.js +++ b/adler32.flow.js @@ -23,22 +23,20 @@ var ADLER32; } /*jshint ignore:end */ }(function(ADLER32) { -ADLER32.version = '0.4.0'; +ADLER32.version = '1.0.0'; /*:: type ADLER32Type = number; type ABuf = Array | Buffer; */ /*# consult README.md for the magic number */ /*# charCodeAt is the best approach for binary strings */ -/*global Buffer */ -var use_buffer = typeof Buffer !== 'undefined'; -function adler32_bstr(bstr/*:string*/)/*:ADLER32Type*/ { - if(bstr.length > 32768) if(use_buffer) return adler32_buf(new Buffer(bstr)); +function adler32_bstr(bstr/*:string*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = bstr.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -47,12 +45,13 @@ function adler32_bstr(bstr/*:string*/)/*:ADLER32Type*/ { return ((b%65521) << 16) | (a%65521); } -function adler32_buf(buf/*:ABuf*/)/*:ADLER32Type*/ { +function adler32_buf(buf/*:ABuf*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = buf.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = (seed >>> 16) & 0xFFFF; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -62,8 +61,9 @@ function adler32_buf(buf/*:ABuf*/)/*:ADLER32Type*/ { } /*# much much faster to intertwine utf8 and adler */ -function adler32_str(str/*:string*/)/*:ADLER32Type*/ { +function adler32_str(str/*:string*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = str.length, M = 0, c = 0, d = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850); while(M>0) { @@ -88,7 +88,7 @@ function adler32_str(str/*:string*/)/*:ADLER32Type*/ { a = (15*(a>>>16)+(a&65535)); b = (15*(b>>>16)+(b&65535)); } - return (b << 16) | a; + return ((b%65521) << 16) | (a%65521); } ADLER32.bstr = adler32_bstr; ADLER32.buf = adler32_buf; diff --git a/adler32.js b/adler32.js index 681f1a6..895d37f 100644 --- a/adler32.js +++ b/adler32.js @@ -21,16 +21,14 @@ var ADLER32; } /*jshint ignore:end */ }(function(ADLER32) { -ADLER32.version = '0.4.0'; -/*global Buffer */ -var use_buffer = typeof Buffer !== 'undefined'; -function adler32_bstr(bstr) { - if(bstr.length > 32768) if(use_buffer) return adler32_buf(new Buffer(bstr)); +ADLER32.version = '1.0.0'; +function adler32_bstr(bstr, seed) { var a = 1, b = 0, L = bstr.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -39,12 +37,13 @@ function adler32_bstr(bstr) { return ((b%65521) << 16) | (a%65521); } -function adler32_buf(buf) { +function adler32_buf(buf, seed) { var a = 1, b = 0, L = buf.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = (seed >>> 16) & 0xFFFF; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -53,8 +52,9 @@ function adler32_buf(buf) { return ((b%65521) << 16) | (a%65521); } -function adler32_str(str) { +function adler32_str(str, seed) { var a = 1, b = 0, L = str.length, M = 0, c = 0, d = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850); while(M>0) { @@ -79,7 +79,7 @@ function adler32_str(str) { a = (15*(a>>>16)+(a&65535)); b = (15*(b>>>16)+(b&65535)); } - return (b << 16) | a; + return ((b%65521) << 16) | (a%65521); } ADLER32.bstr = adler32_bstr; ADLER32.buf = adler32_buf; diff --git a/bin/adler32.njs b/bin/adler32.njs index 168260c..534e09e 100755 --- a/bin/adler32.njs +++ b/bin/adler32.njs @@ -2,36 +2,77 @@ /* adler32.js (C) 2014-present SheetJS -- http://sheetjs.com */ /* vim: set ts=2 ft=javascript: */ -var X; +var X/*:ADLER32Module*/; try { X = require('../'); } catch(e) { X = require('adler-32'); } + +function help()/*:number*/ { +[ +"usage: adler32 [options] [filename]", +"", +"Options:", +" -h, --help output usage information", +" -V, --version output the version number", +" -S, --seed= use integer seed as starting value (default 1)", +" -H, --hex-seed= use hex seed as starting value (default 1)", +" -d, --signed print result with format `%d` (default)", +" -u, --unsigned print result with format `%u`", +" -x, --hex print result with format `%0.8x`", +" -X, --HEX print result with format `%0.8X`", +" -F, --format= use specified printf format", +"", +"Set filename = '-' or pipe data into adler32 to read from stdin", +"Default output mode is signed (-d)", +"" +].forEach(function(l) { console.log(l); }); + return 0; +} + +function version()/*:number*/ { console.log(X.version); return 0; } + var fs = require('fs'); require('exit-on-epipe'); -var args = process.argv.slice(2); +function die(msg/*:string*/, ec/*:?number*/)/*:void*/ { console.error(msg); process.exit(ec || 0); } -var filename; -if(args[0]) filename = args[0]; +var args/*:Array*/ = process.argv.slice(2); +var filename/*:string*/ = ""; +var fmt/*:string*/ = ""; +var seed = 1, r = 10; + +for(var i = 0; i < args.length; ++i) { + var arg = args[i]; + if(arg.charCodeAt(0) != 45) { if(filename === "") filename = arg; continue; } + var m = arg.indexOf("=") == -1 ? arg : arg.substr(0, arg.indexOf("=")); + switch(m) { + case "-": filename = "-"; break; + + case "--help": case "-h": process.exit(help()); break; + case "--version": case "-V": process.exit(version()); break; + + case "--signed": case "-d": fmt = "%d"; break; + case "--unsigned": case "-u": fmt = "%u"; break; + case "--hex": case "-x": fmt = "%0.8x"; break; + case "--HEX": case "-X": fmt = "%0.8X"; break; + case "--format": case "-F": + fmt = ((m!=arg) ? arg.substr(m.length+1) : args[++i])||""; break; + + case "--hex-seed": case "-H": r = 16; + /* falls through */ + case "--seed": case "-S": + seed=parseInt((m!=arg) ? arg.substr(m.length+1) : args[++i], r)||1; break; + + default: die("adler32: unrecognized option `" + arg + "'", 22); + } +} if(!process.stdin.isTTY) filename = filename || "-"; +if(filename.length===0) die("adler32: must specify a filename ('-' for stdin)",1); -if(!filename) { - console.error("adler32: must specify a filename ('-' for stdin)"); - process.exit(1); -} - -if(filename === "-h" || filename === "--help") { - console.log("usage: " + process.argv[0] + " [filename]"); - process.exit(0); -} - -if(filename !== "-" && !fs.existsSync(filename)) { - console.error("adler32: " + filename + ": No such file or directory"); - process.exit(2); +function process_data(data/*:Buffer*/) { + var out/*:ADLER32Type*/ = X.buf(data, seed); + return console.log(fmt === "" ? out : require("printj").sprintf(fmt, out)); } if(filename === "-") process.stdin.pipe(require('concat-stream')(process_data)); -else process_data(fs.readFileSync(filename)); - -function process_data(data) { - console.log(X.buf(data)); -} +else if(fs.existsSync(filename)) process_data(fs.readFileSync(filename)); +else die("adler32: " + filename + ": No such file or directory", 2); diff --git a/bits/01_version.js b/bits/01_version.js index 7d1df1b..b5778c1 100644 --- a/bits/01_version.js +++ b/bits/01_version.js @@ -1 +1 @@ -ADLER32.version = '0.4.0'; +ADLER32.version = '1.0.0'; diff --git a/bits/40_adler.js b/bits/40_adler.js index 33e29ed..6b101e1 100644 --- a/bits/40_adler.js +++ b/bits/40_adler.js @@ -1,14 +1,12 @@ /*# consult README.md for the magic number */ /*# charCodeAt is the best approach for binary strings */ -/*global Buffer */ -var use_buffer = typeof Buffer !== 'undefined'; -function adler32_bstr(bstr/*:string*/)/*:ADLER32Type*/ { - if(bstr.length > 32768) if(use_buffer) return adler32_buf(new Buffer(bstr)); +function adler32_bstr(bstr/*:string*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = bstr.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -17,12 +15,13 @@ function adler32_bstr(bstr/*:string*/)/*:ADLER32Type*/ { return ((b%65521) << 16) | (a%65521); } -function adler32_buf(buf/*:ABuf*/)/*:ADLER32Type*/ { +function adler32_buf(buf/*:ABuf*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = buf.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = (seed >>> 16) & 0xFFFF; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -32,8 +31,9 @@ function adler32_buf(buf/*:ABuf*/)/*:ADLER32Type*/ { } /*# much much faster to intertwine utf8 and adler */ -function adler32_str(str/*:string*/)/*:ADLER32Type*/ { +function adler32_str(str/*:string*/, seed/*:?ADLER32Type*/)/*:ADLER32Type*/ { var a = 1, b = 0, L = str.length, M = 0, c = 0, d = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850); while(M>0) { @@ -58,5 +58,5 @@ function adler32_str(str/*:string*/)/*:ADLER32Type*/ { a = (15*(a>>>16)+(a&65535)); b = (15*(b>>>16)+(b&65535)); } - return (b << 16) | a; + return ((b%65521) << 16) | (a%65521); } diff --git a/ctest/adler32.js b/ctest/adler32.js index 681f1a6..895d37f 100644 --- a/ctest/adler32.js +++ b/ctest/adler32.js @@ -21,16 +21,14 @@ var ADLER32; } /*jshint ignore:end */ }(function(ADLER32) { -ADLER32.version = '0.4.0'; -/*global Buffer */ -var use_buffer = typeof Buffer !== 'undefined'; -function adler32_bstr(bstr) { - if(bstr.length > 32768) if(use_buffer) return adler32_buf(new Buffer(bstr)); +ADLER32.version = '1.0.0'; +function adler32_bstr(bstr, seed) { var a = 1, b = 0, L = bstr.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -39,12 +37,13 @@ function adler32_bstr(bstr) { return ((b%65521) << 16) | (a%65521); } -function adler32_buf(buf) { +function adler32_buf(buf, seed) { var a = 1, b = 0, L = buf.length, M = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = (seed >>> 16) & 0xFFFF; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850)+i; for(;i>>16)+(a&65535)); @@ -53,8 +52,9 @@ function adler32_buf(buf) { return ((b%65521) << 16) | (a%65521); } -function adler32_str(str) { +function adler32_str(str, seed) { var a = 1, b = 0, L = str.length, M = 0, c = 0, d = 0; + if(typeof seed === 'number') { a = seed & 0xFFFF; b = seed >>> 16; } for(var i = 0; i < L;) { M = Math.min(L-i, 3850); while(M>0) { @@ -79,7 +79,7 @@ function adler32_str(str) { a = (15*(a>>>16)+(a&65535)); b = (15*(b>>>16)+(b&65535)); } - return (b << 16) | a; + return ((b%65521) << 16) | (a%65521); } ADLER32.bstr = adler32_bstr; ADLER32.buf = adler32_buf; diff --git a/ctest/fakeassert.js b/ctest/fakeassert.js index dd20b48..16eb822 100644 --- a/ctest/fakeassert.js +++ b/ctest/fakeassert.js @@ -1,2 +1,2 @@ var assert = {}; -assert.equal = function(x,y) { if(x !== y) throw x + " !== " + y; }; +assert.equal = function(x,y) { if(x !== y) throw new Error(x + " !== " + y); }; diff --git a/ctest/fixtures.js b/ctest/fixtures.js index 864cb19..7f11a14 100644 --- a/ctest/fixtures.js +++ b/ctest/fixtures.js @@ -1,9 +1,21 @@ var o = "foo bar baz٪☃🍣"; +var m = "foobar"; for(var i = 0; i != 11; ++i) m+=m; +var m1 = m + m, m2 = m1 + m1, m3 = m2 + m2, m4 = m3 + m3; +var M1 = m + "𝑹" + m, M2 = M1 + "𝐀" + M1, M3 = M2 + "𝓜" + M2, M4 = M3 + "𝙖" + M3; var bits = [ [ "Wikipedia", 300286872, 1 ], [ "foo bar baz", 398066679, 1 ], [ "foo bar baz٪", 570688890 ], [ "foo bar baz٪☃", 919275383 ], + [ m, -747910882, 1 ], + [ m1, 1286443594, 1], + [ m2, 812328098, 1 ], + [ m3, -1124316861, 1 ], + [ m4, -357657979, 1 ], + [ M1, -792947423 ], + [ M2, -1841877779 ], + [ M3, 869751957 ], + [ M4, -1344947227 ], [ o, 1543572022 ], [ o+o, -2076896149 ], [ o+o+o, 2023497376 ] @@ -14,8 +26,21 @@ type ArrayLike = any; type Stringifier = {(d:ArrayLike):string}; declare class ADLER32Module { - bstr(s:string):ADLER32Type; - buf(b:ABuf):ADLER32Type; - str(s:string):ADLER32Type; + bstr(s:string, seed:?ADLER32Type):ADLER32Type; + buf(b:ABuf, seed:?ADLER32Type):ADLER32Type; + str(s:string, seed:?ADLER32Type):ADLER32Type; + version:string; +}; +*/ +/*:: +type _CB = {(data:Buffer):void;}; +declare module 'concat-stream' {declare function exports(f:_CB):stream$Duplex;}; +declare module 'exit-on-epipe' {}; + +declare module 'adler-32' { declare var exports:ADLER32Module; }; +declare module '../' { declare var exports:ADLER32Module; }; + +declare module 'printj' { + declare function sprintf(fmt:string, ...args:any):string; }; */ diff --git a/ctest/index.html b/ctest/index.html index a7f8223..8b6cd18 100644 --- a/ctest/index.html +++ b/ctest/index.html @@ -8,6 +8,7 @@
+ diff --git a/ctest/shim.js b/ctest/shim.js new file mode 100644 index 0000000..e3d7840 --- /dev/null +++ b/ctest/shim.js @@ -0,0 +1,237 @@ +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys +if (!Object.keys) { + Object.keys = (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function (obj) { + if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object'); + + var result = []; + + for (var prop in obj) { + if (hasOwnProperty.call(obj, prop)) result.push(prop); + } + + if (hasDontEnumBug) { + for (var i=0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); + } + } + return result; + }; + })(); +} + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter +if (!Array.prototype.filter) +{ + Array.prototype.filter = function(fun /*, thisp */) + { + "use strict"; + + if (this == null) + throw new TypeError(); + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun != "function") + throw new TypeError(); + + var res = []; + var thisp = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in t) + { + var val = t[i]; // in case fun mutates this + if (fun.call(thisp, val, i, t)) + res.push(val); + } + } + + return res; + }; +} + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim +if (!String.prototype.trim) { + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g, ''); + }; +} + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fun /*, thisArg */) + { + "use strict"; + + if (this === void 0 || this === null) + throw new TypeError(); + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") + throw new TypeError(); + + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + for (var i = 0; i < len; i++) + { + if (i in t) + fun.call(thisArg, t[i], i, t); + } + }; +} + +// Production steps of ECMA-262, Edition 5, 15.4.4.19 +// Reference: http://es5.github.com/#x15.4.4.19 +if (!Array.prototype.map) { + Array.prototype.map = function(callback, thisArg) { + + var T, A, k; + + if (this == null) { + throw new TypeError(" this is null or not defined"); + } + + // 1. Let O be the result of calling ToObject passing the |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If IsCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + " is not a function"); + } + + // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (thisArg) { + T = thisArg; + } + + // 6. Let A be a new array created as if by the expression new Array(len) where Array is + // the standard built-in constructor with that name and len is the value of len. + A = new Array(len); + + // 7. Let k be 0 + k = 0; + + // 8. Repeat, while k < len + while(k < len) { + + var kValue, mappedValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + + // i. Let kValue be the result of calling the Get internal method of O with argument Pk. + kValue = O[ k ]; + + // ii. Let mappedValue be the result of calling the Call internal method of callback + // with T as the this value and argument list containing kValue, k, and O. + mappedValue = callback.call(T, kValue, k, O); + + // iii. Call the DefineOwnProperty internal method of A with arguments + // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true}, + // and false. + + // In browsers that support Object.defineProperty, use the following: + // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); + + // For best browser support, use the following: + A[ k ] = mappedValue; + } + // d. Increase k by 1. + k++; + } + + // 9. return A + return A; + }; +} + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement, fromIndex) { + if ( this === undefined || this === null ) { + throw new TypeError( '"this" is null or not defined' ); + } + + var length = this.length >>> 0; // Hack to convert object.length to a UInt32 + + fromIndex = +fromIndex || 0; + + if (Math.abs(fromIndex) === Infinity) { + fromIndex = 0; + } + + if (fromIndex < 0) { + fromIndex += length; + if (fromIndex < 0) { + fromIndex = 0; + } + } + + for (;fromIndex < length; fromIndex++) { + if (this[fromIndex] === searchElement) { + return fromIndex; + } + } + + return -1; + }; +} +// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + +if (! Array.isArray) { + Array.isArray = function(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; +} + +// https://github.com/ttaubert/node-arraybuffer-slice +// (c) 2013 Tim Taubert +// arraybuffer-slice may be freely distributed under the MIT license. + +"use strict"; + +if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) { + ArrayBuffer.prototype.slice = function (begin, end) { + begin = (begin|0) || 0; + var num = this.byteLength; + end = end === (void 0) ? num : (end|0); + + // Handle negative values. + if (begin < 0) begin += num; + if (end < 0) end += num; + + if (num === 0 || begin >= num || begin >= end) { + return new ArrayBuffer(0); + } + + var length = Math.min(num - begin, end - begin); + var target = new ArrayBuffer(length); + var targetArray = new Uint8Array(target); + targetArray.set(new Uint8Array(this, begin, length)); + return target; + }; +} diff --git a/ctest/test.js b/ctest/test.js index 08a48b0..ab9c178 100644 --- a/ctest/test.js +++ b/ctest/test.js @@ -9,14 +9,41 @@ if(typeof require !== 'undefined') { function readlines(f) { return fs.readFileSync(f, "ascii").split("\n"); } +function msieversion() +{ + if(typeof window == 'undefined') return Infinity; + if(typeof window.navigator == 'undefined') return Infinity; + var ua = window.navigator.userAgent + var msie = ua.indexOf ( "MSIE " ) + if(msie < 0) return Infinity; + return parseInt (ua.substring (msie+5, ua.indexOf (".", msie ))); +} + describe('adler32 bits', function() { bits.forEach(function(i) { var msg = i[0], l = i[0].length, L = i[1]|0; if(l > 20) msg = i[0].substr(0,5) + "...(" + l + ")..." + i[0].substr(-5); + if(l > 100 && msieversion() < 9) return; + if(l > 20000 && typeof Buffer === 'undefined') return; it(msg, function() { if(i[2] === 1) assert.equal(X.bstr(i[0]), L); assert.equal(X.str(i[0]), i[1]|0); if(typeof Buffer !== 'undefined') assert.equal(X.buf(new Buffer(i[0])), L); + var len = i[0].length, step = len < 20000 ? 1 : len < 50000 ? Math.ceil(len / 20000) : Math.ceil(len / 2000); + for(var x = 0; x < len; x += step) { + if(i[0].charCodeAt(x) >= 0xD800 && i[0].charCodeAt(x) < 0xE000) continue; + if(i[2] === 1) { + var bstradl = X.bstr(i[0].substr(x), X.bstr(i[0].substr(0, x))); + assert.equal(bstradl, L); + } + var stradl = X.str(i[0].substr(x), X.str(i[0].substr(0, x))); + assert.equal(stradl, i[1]|0); + if(typeof Buffer !== 'undefined') { + var buf = new Buffer(i[0]); + var bufadl = X.buf(buf.slice(x), X.buf(buf.slice(0, x))); + assert.equal(bufadl, L); + } + } }); }); }); @@ -34,9 +61,9 @@ if(typeof require !== 'undefined') describe("unicode", function() { if(c.charCodeAt(0) >= 0xD800 && c.charCodeAt(0) < 0xE000) continue; var cc = corpus[ucidx], dd = X.str(c); assert.equal(dd, cc, ":" + ucidx + ":" + c + ":" + cc + ":" + dd); - var ee = X.buf(new Buffer(c, "utf8")); - assert.equal(ee, cc, ":" + ucidx + ":" + c + ":" + cc + ":" + ee); if(typeof Buffer !== 'undefined') { + var ee = X.buf(new Buffer(c, "utf8")); + assert.equal(ee, cc, ":" + ucidx + ":" + c + ":" + cc + ":" + ee); var ff = X.bstr(String.fromCharCode.apply(null, new Buffer(c, "utf8"))); assert.equal(ff, cc, ":" + ucidx + ":" + c + ":" + cc + ":" + ff); } diff --git a/demo/browser.flow.js b/demo/browser.flow.js index d265791..5dd168c 100644 --- a/demo/browser.flow.js +++ b/demo/browser.flow.js @@ -26,10 +26,10 @@ function make_chunk_buf_to_str(BType/*:function*/)/*:Stringifier*/ { }; } /*# buffer to binary string */ -var bstrify/*:Stringifier*/ = make_chunk_buf_to_str(Uint8Array); +var bstrify/*:Stringifier*/ = make_chunk_buf_to_str(typeof Uint8Array !== 'undefined' ? Uint8Array : Array); /*# readAsBinaryString support */ -var rABS/*:boolean*/ = is_defined(FileReader, ['prototype', 'readAsBinaryString']); +var rABS/*:boolean*/ = typeof FileReader !== 'undefined' && is_defined(FileReader, ['prototype', 'readAsBinaryString']); var userABS/*:HTMLInputElement*/ = (document.getElementsByName("userabs")[0]/*:any*/); if(!rABS) { userABS.disabled = true; @@ -37,7 +37,7 @@ if(!rABS) { } /*## Process Result */ -/*:: declare class HTMLPreElement extends HTMLElement { innerText:string; } */ +/*:: declare class HTMLPreElement extends HTMLElement { innerText?:string; } */ function process_value(val/*:ADLER32Type*/) { var output = []; output[0] = "Signed : " + val; diff --git a/demo/browser.js b/demo/browser.js index ecabf67..f7dcf00 100644 --- a/demo/browser.js +++ b/demo/browser.js @@ -15,7 +15,6 @@ function is_defined(val, keys) { return keys.length === 0 || is_defined(val[keys[0]], keys.slice(1)); } - function make_chunk_buf_to_str(BType) { return function(data) { var o = "", l = 0, w = 10240, L = data.byteLength/w; @@ -24,18 +23,15 @@ function make_chunk_buf_to_str(BType) { return o; }; } +var bstrify = make_chunk_buf_to_str(typeof Uint8Array !== 'undefined' ? Uint8Array : Array); -var bstrify = make_chunk_buf_to_str(Uint8Array); - - -var rABS = is_defined(FileReader, ['prototype', 'readAsBinaryString']); +var rABS = typeof FileReader !== 'undefined' && is_defined(FileReader, ['prototype', 'readAsBinaryString']); var userABS = (document.getElementsByName("userabs")[0]); if(!rABS) { userABS.disabled = true; userABS.checked = false; } - function process_value(val) { var output = []; output[0] = "Signed : " + val; @@ -49,7 +45,6 @@ function process_value(val) { console_log("output", new Date()); } - var dotxt = (document.getElementById('dotext')); dotxt.onclick = function() { var txt=(document.getElementById('rawdata')); @@ -58,8 +53,6 @@ dotxt.onclick = function() { process_value(wb); }; - - var readcb = function(e) { console_log("onload", new Date(), rABS, false); var target = (e.target); @@ -68,7 +61,6 @@ var readcb = function(e) { process_value(val); }; - var handle_file = function(e) { rABS = userABS.checked; var otarget = (e.target); @@ -85,7 +77,6 @@ var handle_file = function(e) { var xlf = (document.getElementById('xlf')); if(xlf.addEventListener) xlf.addEventListener('change', handle_file, false); - var handle_drop = (function(e) { e.stopPropagation(); e.preventDefault(); diff --git a/index.html b/index.html index 33026a9..a719405 100644 --- a/index.html +++ b/index.html @@ -34,6 +34,7 @@ Use readAsBinaryString: (when available) .
+