diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..44cd99d9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,45 @@ +module.exports = { + extends: [ + // https://eslint.org/docs/rules/ + "eslint:recommended", + // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json + "plugin:@typescript-eslint/recommended", + // https://prettier.io/docs/en/eslint.html + "plugin:prettier/recommended", + ], + // https://reactjs.org/docs/hooks-rules.html + plugins: [], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "./tsconfig.json", + }, + settings: {}, + rules: { + "no-undef": "off", // useless in TypeScript + "no-constant-condition": ["warn", { checkLoops: false }], + "no-useless-escape": "warn", + "no-console": "warn", + + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_|^intl$" }], + "@typescript-eslint/array-type": ["error", "generic"], + "@typescript-eslint/camelcase": "warn", + "@typescript-eslint/class-name-casing": "warn", // to allow the initial underscore + "@typescript-eslint/no-non-null-assertion": "warn", // NOTE: pay attention to it because it may cause unexpected behavior + "@typescript-eslint/prefer-for-of": "warn", + "@typescript-eslint/prefer-includes": "warn", + "@typescript-eslint/prefer-string-starts-ends-with": "warn", + "@typescript-eslint/no-use-before-define": "warn", + + "@typescript-eslint/indent": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/no-object-literal-type-assertion": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-parameter-properties": "off", + "@typescript-eslint/no-var-requires": "off", // not a part of ECMA-262 + "@typescript-eslint/prefer-interface": "off", + + "prettier/prettier": "warn", + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a97a0d2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +node_modules/ +dist/ +build/ +package-lock.json +.nyc_output/ +coverage/ + +# v8 profiler logs +isolate-*.log + +# flamebearer +flamegraph.html diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 00000000..a3f0859d --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,7 @@ +{ + "include": ["src/**/*.ts"], + "extension": [".ts"], + "reporter": ["text-summary", "html"], + "sourceMap": true, + "instrument": true +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ae804356 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "10" + - "12" +cache: npm diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3662b370 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f0e7f3e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright 2019 The MessagePack Community. + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README b/README deleted file mode 100644 index e69de29b..00000000 diff --git a/README.md b/README.md new file mode 100644 index 00000000..06c606c7 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# MessagePack for JavaScript [](https://travis-ci.org/msgpack/msgpack-javascript) + +This is the pure-JavaScript implementation of MessagePack: + +https://msgpack.org/ + +## Usage + +TBD + +## Install + +```shell +npm install @msgpack/msgpack +``` + +## License + +Copyright 2019 The MessagePack Community. + +This software is licensed under the ISC license: + +https://opensource.org/licenses/ISC + +See [LICENSE](./LICENSE) for details. diff --git a/benchmark/benchmark-from-msgpack-lite-data.json b/benchmark/benchmark-from-msgpack-lite-data.json new file mode 100644 index 00000000..874f3b8b --- /dev/null +++ b/benchmark/benchmark-from-msgpack-lite-data.json @@ -0,0 +1,52 @@ +{ + "int0": 0, + "int1": 1, + "int1-": -1, + "int8": 255, + "int8-": -255, + "int16": 256, + "int16-": -256, + "int32": 65536, + "int32-": -65536, + "nil": null, + "true": true, + "false": false, + "float": 0.5, + "float-": -0.5, + "string0": "", + "string1": "A", + "string4": "foobarbaz", + "string8": "Omnes viae Romam ducunt.", + "string16": "L’homme n’est qu’un roseau, le plus faible de la nature ; mais c’est un roseau pensant. Il ne faut pas que l’univers entier s’arme pour l’écraser : une vapeur, une goutte d’eau, suffit pour le tuer. Mais, quand l’univers l’écraserait, l’homme serait encore plus noble que ce qui le tue, puisqu’il sait qu’il meurt, et l’avantage que l’univers a sur lui, l’univers n’en sait rien. Toute notre dignité consiste donc en la pensée. C’est de là qu’il faut nous relever et non de l’espace et de la durée, que nous ne saurions remplir. Travaillons donc à bien penser : voilà le principe de la morale.", + "array0": [], + "array1": [ + "foo" + ], + "array8": [ + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2048, + 4096, + 8192, + 16384, + 32768, + 65536, + 131072, + 262144, + 524288, + 1048576 + ], + "map0": {}, + "map1": { + "foo": "bar" + } +} \ No newline at end of file diff --git a/benchmark/benchmark-from-msgpack-lite.ts b/benchmark/benchmark-from-msgpack-lite.ts new file mode 100644 index 00000000..61fb3a80 --- /dev/null +++ b/benchmark/benchmark-from-msgpack-lite.ts @@ -0,0 +1,156 @@ +// original: https://raw.githubusercontent.com/kawanet/msgpack-lite/master/lib/benchmark.js + +// automatically use the dist version if available +var msgpack_msgpack = try_require("..") || require("../src"); + +var msgpack_node = try_require("msgpack"); +var msgpack_lite = try_require("msgpack-lite"); +var msgpack_js = try_require("msgpack-js"); +var msgpack_js_v5 = try_require("msgpack-js-v5"); +var msgpack5 = try_require("msgpack5"); +var msgpack_unpack = try_require("msgpack-unpack"); +var notepack = try_require("notepack"); + +msgpack5 = msgpack5 && msgpack5(); + +var pkg = require("../package.json"); +var data = require("./benchmark-from-msgpack-lite-data.json"); +var packed = msgpack_lite.encode(data); +var expected = JSON.stringify(data); + +var argv = Array.prototype.slice.call(process.argv, 2); + +if (argv[0] === "-v") { + console.warn(pkg.name + " " + pkg.version); + process.exit(0); +} + +var limit = 5; +if (argv[0] - 0) limit = argv.shift() - 0; +limit *= 1000; + +var COL1 = 65; +var COL2 = 7; +var COL3 = 5; +var COL4 = 7; + +console.log(`Benchmark on NodeJS/${process.version}\n`) +console.log(rpad("operation", COL1), "|", " op ", "|", " ms ", "|", " op/s "); +console.log(rpad("", COL1, "-"), "|", lpad(":", COL2, "-"), "|", lpad(":", COL3, "-"), "|", lpad(":", COL4, "-")); + +var buf, obj; + +if (JSON) { + buf = bench('buf = Buffer.from(JSON.stringify(obj));', JSON_stringify, data); + buf = bench('buf = JSON.stringify(obj);', JSON.stringify, data); + obj = bench('obj = JSON.parse(buf);', JSON.parse, buf); + runTest(obj); +} + +if (msgpack_lite) { + buf = bench('buf = require("msgpack-lite").encode(obj);', msgpack_lite.encode, data); + obj = bench('obj = require("msgpack-lite").decode(buf);', msgpack_lite.decode, packed); + runTest(obj); +} + +if (msgpack_node) { + buf = bench('buf = require("msgpack").pack(obj);', msgpack_node.pack, data); + obj = bench('obj = require("msgpack").unpack(buf);', msgpack_node.unpack, buf); + runTest(obj); +} + +if (msgpack_msgpack) { + buf = bench('buf = require("@msgpack/msgpack").encode(obj);', msgpack_msgpack.encode, data); + buf = bench('buf = Buffer.from(require("@msgpack/msgpack").encode(obj));', msgpack_msgpack_encode, data); + obj = bench('obj = require("@msgpack/msgpack").decode(buf);', msgpack_msgpack.decode, buf); + runTest(obj); +} + +if (msgpack_js_v5) { + buf = bench('buf = require("msgpack-js-v5").encode(obj);', msgpack_js_v5.encode, data); + obj = bench('obj = require("msgpack-js-v5").decode(buf);', msgpack_js_v5.decode, buf); + runTest(obj); +} + +if (msgpack_js) { + buf = bench('buf = require("msgpack-js").encode(obj);', msgpack_js.encode, data); + obj = bench('obj = require("msgpack-js").decode(buf);', msgpack_js.decode, buf); + runTest(obj); +} + +if (msgpack5) { + buf = bench('buf = require("msgpack5")().encode(obj);', msgpack5.encode, data); + obj = bench('obj = require("msgpack5")().decode(buf);', msgpack5.decode, buf); + runTest(obj); +} + +if (notepack) { + buf = bench('buf = require("notepack").encode(obj);', notepack.encode, data); + obj = bench('obj = require("notepack").decode(buf);', notepack.decode, buf); + runTest(obj); +} + +if (msgpack_unpack) { + obj = bench('obj = require("msgpack-unpack").decode(buf);', msgpack_unpack, packed); + runTest(obj); +} + +function JSON_stringify(src: any) { + return Buffer.from(JSON.stringify(src)); +} + +function msgpack_msgpack_encode(data: any) { + return Buffer.from(msgpack_msgpack.encode(data)); +} + +function bench(name: string, func: (...args: any[]) => any, src: any) { + if (argv.length) { + var match = argv.filter(function(grep) { + return (name.indexOf(grep) > -1); + }); + if (!match.length) return SKIP; + } + // warm up + func(src); + + var ret, duration: number; + var start = Date.now(); + var count = 0; + while (1) { + var end = Date.now(); + duration = end - start; + if (duration >= limit) break; + while ((++count) % 100) ret = func(src); + } + name = rpad(name, COL1); + var score = Math.floor(count / duration! * 1000); + console.log(name, "|", lpad(`${count}`, COL2), "|", lpad(`${duration}`, COL3), "|", lpad(`${score}`, COL4)); + return ret; +} + +function rpad(str: string, len: number, chr = " ") { + return str.padEnd(len, chr); +} + +function lpad(str: string, len: number, chr = " ") { + return str.padStart(len, chr); +} + +function runTest(actual: any) { + if (actual === SKIP) return; + actual = JSON.stringify(actual); + if (actual === expected) return; + console.warn("expected: " + expected); + console.warn("actual: " + actual); +} + +function SKIP() { +} + +function try_require(name: string) { + try { + return require(name); + } catch (e) { + // ignore + } +} diff --git a/benchmark/profile-decode.ts b/benchmark/profile-decode.ts new file mode 100644 index 00000000..58e38e6a --- /dev/null +++ b/benchmark/profile-decode.ts @@ -0,0 +1,16 @@ +import { encode, decode } from "../src"; + +const data = require("./benchmark-from-msgpack-lite-data.json"); +const encoded = encode(data); + +console.time("decode #1"); +for (let i = 0; i < 10000; i++) { + decode(encoded); +} +console.timeEnd("decode #1"); + +console.time("decode #2"); +for (let i = 0; i < 10000; i++) { + decode(encoded); +} +console.timeEnd("decode #2"); diff --git a/benchmark/profile-encode.ts b/benchmark/profile-encode.ts new file mode 100644 index 00000000..53e5716d --- /dev/null +++ b/benchmark/profile-encode.ts @@ -0,0 +1,15 @@ +import { encode } from "../src"; + +const data = require("./benchmark-from-msgpack-lite-data.json"); + +console.time("encode #1"); +for (let i = 0; i < 10000; i++) { + encode(data); +} +console.timeEnd("encode #1"); + +console.time("encode #2"); +for (let i = 0; i < 10000; i++) { + encode(data); +} +console.timeEnd("encode #2"); diff --git a/msgpack.base.js b/msgpack.base.js deleted file mode 100644 index d8f027b5..00000000 --- a/msgpack.base.js +++ /dev/null @@ -1,597 +0,0 @@ - -// === msgpack === -// MessagePack -> http://msgpack.sourceforge.net/ - -this.msgpack || (function(globalScope) { - -globalScope.msgpack = { - pack: msgpackpack, // msgpack.pack(data:Mix):ByteArray - unpack: msgpackunpack, // msgpack.unpack(data:BinaryString/ByteArray):Mix - worker: "msgpack.js", // msgpack.worker - WebWorkers script filename - upload: msgpackupload, // msgpack.upload(url:String, option:Hash, callback:Function) - download: msgpackdownload // msgpack.download(url:String, option:Hash, callback:Function) -}; - -var _ie = /MSIE/.test(navigator.userAgent), - _bit2num = {}, // BitStringToNumber { "00000000": 0, ... "11111111": 255 } - _bin2num = {}, // BinaryStringToNumber { "\00": 0, ... "\ff": 255 } - _num2bin = {}, // NumberToBinaryString { 0: "\00", ... 255: "\ff" } - _num2b64 = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz0123456789+/").split(""), - _sign = { 8: 0x80, 16: 0x8000, 32: 0x80000000 }, - _split8char = /.{8}/g; - -// for WebWorkers Code Block -self.importScripts && (onmessage = function(event) { - if (event.data.method === "pack") { - postMessage(base64encode(msgpackpack(event.data.data))); - } else { - postMessage(msgpackunpack(event.data.data)); - } -}); - -// msgpack.pack -function msgpackpack(data) { // @param Mix: - // @return ByteArray: - return encode([], data); -} - -// msgpack.unpack -function msgpackunpack(data) { // @param BinaryString/ByteArray: - // @return Mix: - return { data: typeof data === "string" ? toByteArray(data) - : data, - index: -1, decode: decode }.decode(); -} - -// inner - encoder -function encode(rv, // @param ByteArray: result - mix) { // @param Mix: source data - var size = 0, i = 0, iz, c, ary, hash, - high, low, i64 = 0, sign, exp, frac; - - if (mix == null) { // null or undefined - rv.push(0xc0); - } else { - switch (typeof mix) { - case "boolean": - rv.push(mix ? 0xc3 : 0xc2); - break; - case "number": - if (mix !== mix) { // isNaN - rv.push(0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff); // quiet NaN - } else if (mix === Infinity) { - rv.push(0xcb, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // positive infinity - } else if (Math.floor(mix) === mix) { - if (mix < 0) { // int - if (mix >= -32) { // negative fixnum - rv.push(0xe0 + mix + 32); - } else if (mix > -0x80) { - rv.push(0xd0, mix + 0x100); - } else if (mix > -0x8000) { - mix += 0x10000; - rv.push(0xd1, mix >> 8, mix & 0xff); - } else if (mix > -0x80000000) { - mix += 0x100000000; - rv.push(0xd2, mix >>> 24, (mix >> 16) & 0xff, - (mix >> 8) & 0xff, mix & 0xff); - } else { - ++i64; - } - } else { // uint - if (mix < 0x80) { - rv.push(mix); // positive fixnum - } else if (mix < 0x100) { // uint 8 - rv.push(0xcc, mix); - } else if (mix < 0x10000) { // uint 16 - rv.push(0xcd, mix >> 8, mix & 0xff); - } else if (mix < 0x100000000) { // uint 32 - rv.push(0xce, mix >>> 24, (mix >> 16) & 0xff, - (mix >> 8) & 0xff, mix & 0xff); - } else { - ++i64; - } - } - if (i64) { - high = Math.floor(mix / 0x100000000); - low = mix & (0x100000000 - 1); - rv.push(mix < 0 ? 0xd3 : 0xcf, - (high >> 24) & 0xff, (high >> 16) & 0xff, - (high >> 8) & 0xff, high & 0xff, - (low >> 24) & 0xff, (low >> 16) & 0xff, - (low >> 8) & 0xff, low & 0xff); - } - } else { // double - // THX! edvakf - // http://javascript.g.hatena.ne.jp/edvakf/20100614/1276503044 - hash = _bit2num; - sign = mix < 0; - sign && (mix *= -1); - - // add offset 1023 to ensure positive - exp = Math.log(mix) / Math.LN2 + 1023 | 0; - - // shift 52 - (exp - 1023) bits to make integer part exactly 53 bits, - // then throw away trash less than decimal point - frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))). - toString(2).slice(1); - - // exp is between 1 and 2047. make it 11 bits - exp = ("000000000" + exp.toString(2)).slice(-11); - - ary = (+sign + exp + frac).match(_split8char); - rv.push(0xcb, hash[ary[0]], hash[ary[1]], - hash[ary[2]], hash[ary[3]], - hash[ary[4]], hash[ary[5]], - hash[ary[6]], hash[ary[7]]); - } - break; - case "string": - // utf8.encode - for (ary = [], iz = mix.length, i = 0; i < iz; ++i) { - c = mix.charCodeAt(i); - if (c < 0x80) { // ASCII(0x00 ~ 0x7f) - ary.push(c & 0x7f); - } else if (c < 0x0800) { - ary.push(((c >>> 6) & 0x1f) | 0xc0, (c & 0x3f) | 0x80); - } else if (c < 0x10000) { - ary.push(((c >>> 12) & 0x0f) | 0xe0, - ((c >>> 6) & 0x3f) | 0x80, (c & 0x3f) | 0x80); - } - } - setType(rv, 32, ary.length, [0xa0, 0xda, 0xdb]); - Array.prototype.push.apply(rv, ary); - break; - default: // array or hash - if (Object.prototype.toString.call(mix) === "[object Array]") { // array - size = mix.length; - setType(rv, 16, size, [0x90, 0xdc, 0xdd]); - for (; i < size; ++i) { - encode(rv, mix[i]); - } - } else { // hash - if (Object.keys) { - size = Object.keys(mix).length; - } else { - for (i in mix) { - mix.hasOwnProperty(i) && ++size; - } - } - setType(rv, 16, size, [0x80, 0xde, 0xdf]); - for (i in mix) { - encode(rv, i); - encode(rv, mix[i]); - } - } - } - } - return rv; -} - -// inner - decoder -function decode() { // @return Mix: - var rv, undef, size, i = 0, iz, msb = 0, c, sign, exp, frac, key, - that = this, - data = that.data, - type = data[++that.index]; - - if (type >= 0xe0) { // Negative FixNum (111x xxxx) (-32 ~ -1) - return type - 0x100; - } - if (type < 0x80) { // Positive FixNum (0xxx xxxx) (0 ~ 127) - return type; - } - if (type < 0x90) { // FixMap (1000 xxxx) - size = type - 0x80; - type = 0x80; - } else if (type < 0xa0) { // FixArray (1001 xxxx) - size = type - 0x90; - type = 0x90; - } else if (type < 0xc0) { // FixRaw (101x xxxx) - size = type - 0xa0; - type = 0xa0; - } - switch (type) { - case 0xc0: return null; - case 0xc2: return false; - case 0xc3: return true; - case 0xca: rv = readByte(that, 4); // float - sign = rv & _sign[32]; // 1bit - exp = (rv >> 23) & 0xff; // 8bits - frac = rv & 0x7fffff; // 23bits - if (!rv || rv === 0x80000000) { // 0.0 or -0.0 - return 0; - } - if (exp === 0xff) { // NaN or Infinity - return frac ? NaN : Infinity; - } - return (sign ? -1 : 1) * - (frac | 0x800000) * Math.pow(2, exp - 127 - 23); // 127: bias - case 0xcb: rv = readByte(that, 4); // double - sign = rv & _sign[32]; // 1bit - exp = (rv >> 20) & 0x7ff; // 11bits - frac = rv & 0xfffff; // 52bits - 32bits (high word) - if (!rv || rv === 0x80000000) { // 0.0 or -0.0 - return 0; - } - if (exp === 0x7ff) { // NaN or Infinity - readByte(that, 4); // seek index - return frac ? NaN : Infinity; - } - return (sign ? -1 : 1) * - ((frac | 0x100000) * Math.pow(2, exp - 1023 - 20) // 1023: bias - + readByte(that, 4) * Math.pow(2, exp - 1023 - 52)); - case 0xcf: return readByte(that, 4) * Math.pow(2, 32) + - readByte(that, 4); // uint 64 - case 0xce: return readByte(that, 4); // uint 32 - case 0xcd: return readByte(that, 2); // uint 16 - case 0xcc: return readByte(that, 1); // uint 8 - case 0xd3: return decodeInt64(that); // int 64 - case 0xd2: rv = readByte(that, 4); // int 32 - case 0xd1: rv === undef && (rv = readByte(that, 2)); // int 16 - case 0xd0: rv === undef && (rv = readByte(that, 1)); // int 8 - msb = 4 << ((type & 0x3) + 1); // 8, 16, 32 - return rv < _sign[msb] ? rv : rv - _sign[msb] * 2; - case 0xdb: size = readByte(that, 4); // raw 32 - case 0xda: size === undef && (size = readByte(that, 2)); // raw 16 - case 0xa0: i = that.index + 1; // raw - that.index += size; - // utf8.decode - for (rv = [], ri = -1, iz = i + size; i < iz; ++i) { - c = data[i]; // first byte - if (c < 0x80) { // ASCII(0x00 ~ 0x7f) - rv[++ri] = c; - } else if (c < 0xe0) { - rv[++ri] = (c & 0x1f) << 6 | (data[++i] & 0x3f); - } else if (c < 0xf0) { - rv[++ri] = (c & 0x0f) << 12 | (data[++i] & 0x3f) << 6 - | (data[++i] & 0x3f); - } - } - return String.fromCharCode.apply(null, rv); - case 0xdf: size = readByte(that, 4); // map 32 - case 0xde: size === undef && (size = readByte(that, 2)); // map 16 - case 0x80: for (rv = {}; i < size; ++i) { // map - key = that.decode(); - rv[key] = that.decode(); // key/value pair - } - return rv; - case 0xdd: size = readByte(that, 4); // array 32 - case 0xdc: size === undef && (size = readByte(that, 2)); // array 16 - case 0x90: for (rv = []; i < size; ++i) { // array - rv.push(that.decode()); - } - } - return rv; -} - -// inner - read byte -function readByte(that, // @param Object: - size) { // @param Number: - // @return Number: - var rv = 0, data = that.data, i = that.index; - - switch (size) { - case 4: rv += data[++i] * 0x1000000 + (data[++i] << 16); - case 2: rv += data[++i] << 8; - case 1: rv += data[++i]; - } - that.index = i; - return rv; -} - -// inner - decode int64 -function decodeInt64(that) { // @param Object: - // @return Number: - var rv, overflow = 0, - bytes = that.data.slice(that.index + 1, that.index + 9); - - that.index += 8; - - // avoid overflow - if (bytes[0] & 0x80) { - - ++overflow; - bytes[0] ^= 0xff; - bytes[1] ^= 0xff; - bytes[2] ^= 0xff; - bytes[3] ^= 0xff; - bytes[4] ^= 0xff; - bytes[5] ^= 0xff; - bytes[6] ^= 0xff; - bytes[7] ^= 0xff; - } - rv = bytes[0] * 0x100000000000000 - + bytes[1] * 0x1000000000000 - + bytes[2] * 0x10000000000 - + bytes[3] * 0x100000000 - + bytes[4] * 0x1000000 - + bytes[5] * 0x10000 - + bytes[6] * 0x100 - + bytes[7]; - return overflow ? (rv + 1) * -1 : rv; -} - -// inner - set type and fixed size -function setType(rv, // @param ByteArray: result - fixSize, // @param Number: fix size. 16 or 32 - size, // @param Number: size - types) { // @param ByteArray: type formats. eg: [0x90, 0xdc, 0xdd] - if (size < fixSize) { - rv.push(types[0] + size); - } else if (size < 0x10000) { // 16 - rv.push(types[1], size >> 8, size & 0xff); - } else if (size < 0x100000000) { // 32 - rv.push(types[2], size >>> 24, (size >> 16) & 0xff, - (size >> 8) & 0xff, size & 0xff); - } -} - -// msgpack.download - load from server -function msgpackdownload(url, // @param String: - option, // @param Hash: { worker, timeout, before, after } - // option.worker - Boolean(= false): true is use WebWorkers - // option.timeout - Number(= 10): timeout sec - // option.before - Function: before(xhr, option) - // option.after - Function: after(xhr, option, { status, ok }) - callback) { // @param Function: callback(data, option, { status, ok }) - // data - Mix/null: - // option - Hash: - // status - Number: HTTP status code - // ok - Boolean: - option.method = "GET"; - option.binary = true; - ajax(url, option, callback); -} - -// msgpack.upload - save to server -function msgpackupload(url, // @param String: - option, // @param Hash: { data, worker, timeout, before, after } - // option.data - Mix: - // option.worker - Boolean(= false): true is use WebWorkers - // option.timeout - Number(= 10): timeout sec - // option.before - Function: before(xhr, option) - // option.after - Function: after(xhr, option, { status, ok }) - callback) { // @param Function: callback(data, option, { status, ok }) - // data - String: responseText - // option - Hash: - // status - Number: HTTP status code - // ok - Boolean: - option.method = "PUT"; - option.binary = true; - - if (option.worker && globalScope.Worker) { - var worker = new Worker(msgpack.worker); - - worker.onmessage = function(event) { - option.data = event.data; - ajax(url, option, callback); - }; - worker.postMessage({ method: "pack", data: option.data }); - } else { - // pack and base64 encode - option.data = base64encode(msgpackpack(option.data)); - ajax(url, option, callback); - } -} - -// inner - -function ajax(url, // @param String: - option, // @param Hash: { data, ifmod, method, timeout, - // header, binary, before, after, worker } - // option.data - Mix: upload data - // option.ifmod - Boolean: true is "If-Modified-Since" header - // option.method - String: "GET", "POST", "PUT" - // option.timeout - Number(= 10): timeout sec - // option.header - Hash(= {}): { key: "value", ... } - // option.binary - Boolean(= false): true is binary data - // option.before - Function: before(xhr, option) - // option.after - Function: after(xhr, option, { status, ok }) - // option.worker - Boolean(= false): true is use WebWorkers - callback) { // @param Function: callback(data, option, { status, ok }) - // data - String/Mix/null: - // option - Hash: - // status - Number: HTTP status code - // ok - Boolean: - function readyStateChange() { - if (xhr.readyState === 4) { - var data, status = xhr.status, worker, byteArray, - rv = { status: status, ok: status >= 200 && status < 300 }; - - if (!run++) { - if (method === "PUT") { - data = rv.ok ? xhr.responseText : ""; - } else { - if (rv.ok) { - if (option.worker && globalScope.Worker) { - worker = new Worker(msgpack.worker); - worker.onmessage = function(event) { - callback(event.data, option, rv); - }; - worker.postMessage({ method: "unpack", - data: xhr.responseText }); - gc(); - return; - } else { - byteArray = _ie ? toByteArrayIE(xhr) - : toByteArray(xhr.responseText); - data = msgpackunpack(byteArray); - } - } - } - after && after(xhr, option, rv); - callback(data, option, rv); - gc(); - } - } - } - - function ng(abort, status) { - if (!run++) { - var rv = { status: status || 400, ok: false }; - - after && after(xhr, option, rv); - callback(null, option, rv); - gc(abort); - } - } - - function gc(abort) { - abort && xhr && xhr.abort && xhr.abort(); - watchdog && (clearTimeout(watchdog), watchdog = 0); - xhr = null; - globalScope.addEventListener && - globalScope.removeEventListener("beforeunload", ng, false); - } - - var watchdog = 0, - method = option.method || "GET", - header = option.header || {}, - before = option.before, - after = option.after, - data = option.data || null, - xhr = globalScope.XMLHttpRequest ? new XMLHttpRequest() : - globalScope.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : - null, - run = 0, i, - overrideMimeType = "overrideMimeType", - setRequestHeader = "setRequestHeader", - getbinary = method === "GET" && option.binary; - - try { - xhr.onreadystatechange = readyStateChange; - xhr.open(method, url, true); // ASync - - before && before(xhr, option); - - getbinary && xhr[overrideMimeType] && - xhr[overrideMimeType]("text/plain; charset=x-user-defined"); - data && - xhr[setRequestHeader]("Content-Type", - "application/x-www-form-urlencoded"); - - for (i in header) { - xhr[setRequestHeader](i, header[i]); - } - - globalScope.addEventListener && - globalScope.addEventListener("beforeunload", ng, false); // 400: Bad Request - - xhr.send(data); - watchdog = setTimeout(function() { - ng(1, 408); // 408: Request Time-out - }, (option.timeout || 10) * 1000); - } catch (err) { - ng(0, 400); // 400: Bad Request - } -} - -// inner - BinaryString To ByteArray -function toByteArray(data) { // @param BinaryString: "\00\01" - // @return ByteArray: [0x00, 0x01] - var rv = [], bin2num = _bin2num, remain, - ary = data.split(""), - i = -1, iz; - - iz = ary.length; - remain = iz % 8; - - while (remain--) { - ++i; - rv[i] = bin2num[ary[i]]; - } - remain = iz >> 3; - while (remain--) { - rv.push(bin2num[ary[++i]], bin2num[ary[++i]], - bin2num[ary[++i]], bin2num[ary[++i]], - bin2num[ary[++i]], bin2num[ary[++i]], - bin2num[ary[++i]], bin2num[ary[++i]]); - } - return rv; -} - -// inner - BinaryString to ByteArray -function toByteArrayIE(xhr) { - var rv = [], data, remain, - charCodeAt = "charCodeAt", - loop, v0, v1, v2, v3, v4, v5, v6, v7, - i = -1, iz; - - iz = vblen(xhr); - data = vbstr(xhr); - loop = Math.ceil(iz / 2); - remain = loop % 8; - - while (remain--) { - v0 = data[charCodeAt](++i); // 0x00,0x01 -> 0x0100 - rv.push(v0 & 0xff, v0 >> 8); - } - remain = loop >> 3; - while (remain--) { - v0 = data[charCodeAt](++i); - v1 = data[charCodeAt](++i); - v2 = data[charCodeAt](++i); - v3 = data[charCodeAt](++i); - v4 = data[charCodeAt](++i); - v5 = data[charCodeAt](++i); - v6 = data[charCodeAt](++i); - v7 = data[charCodeAt](++i); - rv.push(v0 & 0xff, v0 >> 8, v1 & 0xff, v1 >> 8, - v2 & 0xff, v2 >> 8, v3 & 0xff, v3 >> 8, - v4 & 0xff, v4 >> 8, v5 & 0xff, v5 >> 8, - v6 & 0xff, v6 >> 8, v7 & 0xff, v7 >> 8); - } - iz % 2 && rv.pop(); - - return rv; -} - -// inner - base64.encode -function base64encode(data) { // @param ByteArray: - // @return Base64String: - var rv = [], - c = 0, i = -1, iz = data.length, - pad = [0, 2, 1][data.length % 3], - num2bin = _num2bin, - num2b64 = _num2b64; - - if (globalScope.btoa) { - while (i < iz) { - rv.push(num2bin[data[++i]]); - } - return btoa(rv.join("")); - } - --iz; - while (i < iz) { - c = (data[++i] << 16) | (data[++i] << 8) | (data[++i]); // 24bit - rv.push(num2b64[(c >> 18) & 0x3f], - num2b64[(c >> 12) & 0x3f], - num2b64[(c >> 6) & 0x3f], - num2b64[ c & 0x3f]); - } - pad > 1 && (rv[rv.length - 2] = "="); - pad > 0 && (rv[rv.length - 1] = "="); - return rv.join(""); -} - -// --- init --- -(function() { - var i = 0, v; - - for (; i < 0x100; ++i) { - v = String.fromCharCode(i); - _bit2num[("0000000" + i.toString(2)).slice(-8)] = i; - _bin2num[v] = i; // "\00" -> 0x00 - _num2bin[i] = v; // 0 -> "\00" - } - // http://twitter.com/edvakf/statuses/15576483807 - for (i = 0x80; i < 0x100; ++i) { // [Webkit][Gecko] - _bin2num[String.fromCharCode(0xf700 + i)] = i; // "\f780" -> 0x80 - } -})(); - -_ie && document.write(' diff --git a/test/ajax.htm b/test/ajax.htm deleted file mode 100644 index c5d5bda9..00000000 --- a/test/ajax.htm +++ /dev/null @@ -1,179 +0,0 @@ - -
- -Choose Download data | -- | Upload Last data | -- | Download Last data | -
- - - - - | --> | -- - | --> | -- - | -
Choose Download data | -- | Upload Last data | -- | Download Last data | -
- - - - - | --> | -- - | --> | -- - | -
Prepare Data | -- | Bench ! | -
-
-
-
- - - - - |
- - -> - | -
-
- - - |
-
Prepare Data | -- | Bench ! | -
-
-
-
- - - - - |
- - -> - | -
-
- - - |
-