Created
November 18, 2012 17:34
-
-
Save uupaa/4106426 to your computer and use it in GitHub Desktop.
msgpack.2.0alpha.js
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
/*!{id:"msgpack.js",ver:"2.0alpha",license:"MIT",author:"[email protected]"}*/ | |
(function(global) { // @arg Global: window or global | |
// --- header ---------------------------------------------- | |
global.msgpack = { | |
pack: msgpack_pack, // msgpack.pack(mix:Mix, | |
// size:Integer = 16Kb):Uint8Array | |
unpack: msgpack_unpack // msgpack.unpack(view:Uint8Array):Mix | |
}; | |
// --- library scope vars ---------------------------------- | |
var _idx = 0, // decoder: offset | |
_MAX_DEPTH = 512; | |
// --- implement ------------------------------------------- | |
function msgpack_pack(mix, // @arg Mix: | |
size) { // @arg Integer(= 16Kb): buffer size | |
// @ret Uint8Array: | |
// @desc: msgpack.pack | |
size = size || 1024 * 16; | |
var view, offset; | |
while (1) { | |
view = new Uint8Array(size); | |
try { | |
offset = _recursiveMessagePackEncoder(view, 0, size, mix, 0); | |
if (offset > size) { | |
throw new RangeError("INDEX_IS_OUT_OF_RANGE"); | |
} | |
break; | |
} catch (err) { | |
if (err instanceof RangeError) { | |
size *= 2; // buffer size auto expansion -> retry | |
} else { | |
throw err; | |
} | |
} | |
} | |
return view.subarray(0, offset); | |
} | |
function _recursiveMessagePackEncoder(view, offset, size, mix, depth) { | |
if (++depth >= _MAX_DEPTH) { | |
throw new TypeError("CYCLIC_REFERENCE_ERROR"); | |
} | |
if (offset > size) { | |
throw new RangeError("INDEX_IS_OUT_OF_RANGE"); | |
} | |
if (mix == null) { // null or undefined | |
view[offset++] = 0xc0; | |
return offset; | |
} | |
if (Array.isArray(mix)) { | |
return _Array_msgpack(view, offset, size, mix, depth); | |
} | |
switch (typeof mix) { | |
case "boolean": view[offset++] = mix ? 0xc2 : 0xc3; return offset; | |
case "number": return _Number_msgpack(view, offset, size, mix); | |
case "string": return _String_msgpack(view, offset, size, mix); | |
default: return _Object_msgpack(view, offset, size, mix, depth); | |
} | |
return offset; | |
} | |
function _Object_msgpack(view, offset, size, mix, depth) { | |
var keys = Object.keys(mix), key, i = 0, iz = keys.length; | |
if (iz < 16) { // fix map | |
view[offset++] = 0x80 + iz; | |
} else if (iz < 0x10000) { // map 16 | |
view.set([0xde, iz >> 8, iz], offset); | |
offset += 3; | |
} else if (iz < 0x100000000) { // map 32 | |
view.set([0xdf, iz >> 24, iz >> 16, iz >> 8, iz], offset); | |
offset += 5; | |
} | |
for (; i < iz; ++i) { // uupaa-looper | |
key = keys[i]; | |
offset = _String_msgpack(view, offset, size, key); | |
offset = _recursiveMessagePackEncoder(view, offset, size, | |
mix[key], depth); | |
} | |
return offset; | |
} | |
function _Array_msgpack(view, offset, size, mix, depth) { | |
var i = 0, iz = mix.length; | |
if (iz < 16) { // fix array | |
view[offset++] = 0x90 + iz; | |
} else if (iz < 0x10000) { // array 16 | |
view.set([0xdc, iz >> 8, iz], offset); | |
offset += 3; | |
} else if (iz < 0x100000000) { // array 32 | |
view.set([0xdd, iz >> 24, iz >> 16, iz >> 8, iz], offset); | |
offset += 5; | |
} | |
for (; i < iz; ++i) { | |
offset = _recursiveMessagePackEncoder(view, offset, size, mix[i], depth); | |
} | |
return offset; | |
} | |
function _Number_msgpack(view, offset, size, mix) { | |
var high, low, | |
sign, exp, frac; | |
if (mix !== mix) { // quiet NaN | |
view.set([0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], offset); | |
offset += 9; | |
} else if (mix === Infinity) { // positive infinity | |
view.set([0xcb, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], offset); | |
offset += 9; | |
} else if (Math.floor(mix) === mix) { // int or uint | |
if (mix < 0) { // --- negative --- | |
if (mix >= -32) { // fixnum -> [111xxxxx] | |
view[offset++] = 0xe0 + mix + 32; | |
} else if (mix > -0x80) { // int 8 -> [0xd0, value] | |
view[offset++] = 0xd0; | |
view[offset++] = mix + 0x100; | |
} else if (mix > -0x8000) { // int 16 -> [0xd1, value x 2] | |
mix += 0x10000; | |
view.set([0xd1, mix >> 8, mix], offset); | |
offset += 3; | |
} else if (mix > -0x80000000) { // int 32 -> [0xd2, value x 4] | |
mix += 0x100000000; | |
view.set([0xd2, mix >> 24, mix >> 16, mix >> 8, mix], offset); | |
offset += 5; | |
} else { // int 64 -> [0xd3, value x 8] | |
high = Math.floor(mix / 0x100000000); | |
low = mix & 0xffffffff; | |
view.set([0xd3, high >> 24, high >> 16, high >> 8, high, | |
low >> 24, low >> 16, low >> 8, low], offset); | |
offset += 9; | |
} | |
} else { // --- positive --- | |
if (mix < 0x80) { // fixnum -> [value] | |
view[offset++] = mix; | |
} else if (mix < 0x100) { // uint 8 -> [0xcc, value] | |
view[offset++] = 0xcc; | |
view[offset++] = mix; | |
} else if (mix < 0x10000) { // uint 16 -> [0xcd, value x 2] | |
view.set([0xcd, mix >> 8, mix], offset); | |
offset += 3; | |
} else if (mix < 0x100000000) { // uint 32 -> [0xce, value x 4] | |
view.set([0xce, mix >> 24, mix >> 16, mix >> 8, mix], offset); | |
offset += 5; | |
} else { // uint 64 -> [0xcf, value x 8] | |
high = Math.floor(mix / 0x100000000); | |
low = mix & 0xffffffff; | |
view.set([0xcf, high >> 24, high >> 16, high >> 8, high, | |
low >> 24, low >> 16, low >> 8, low], offset); | |
offset += 9; | |
} | |
} | |
} else { // --- double --- | |
// THX!! @edvakf | |
// http://javascript.g.hatena.ne.jp/edvakf/20101128/1291000731 | |
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 = mix * Math.pow(2, 52 + 1023 - exp); | |
// S+-Exp(11)--++-----------------Fraction(52bits)-----------------------+ | |
// || || | | |
// v+----------++--------------------------------------------------------+ | |
// 00000000|00000000|00000000|00000000|00000000|00000000|00000000|00000000 | |
// 6 5 55 4 4 3 2 1 8 0 | |
// 3 6 21 8 0 2 4 6 | |
// | |
// +----------high(32bits)-----------+ +----------low(32bits)------------+ | |
// | | | | | |
// +---------------------------------+ +---------------------------------+ | |
// 3 2 21 1 8 0 | |
// 1 4 09 6 | |
low = frac & 0xffffffff; | |
sign && (exp |= 0x800); | |
high = ((frac / 0x100000000) & 0xfffff) | (exp << 20); | |
view.set([0xcb, high >> 24, high >> 16, high >> 8, high, | |
low >> 24, low >> 16, low >> 8, low], offset); | |
offset += 9; | |
} | |
return offset; | |
} | |
function _String_msgpack(view, offset, size, mix) { | |
var ary = _utf8_encode(mix), size = ary.length; | |
if (size < 32) { // fix raw | |
view[offset++] = 0xa0 + size; | |
} else if (size < 0x10000) { // raw 16 | |
view.set([0xda, size >> 8, size], offset); | |
offset += 3; | |
} else if (size < 0x100000000) { // raw 32 | |
view.set([0xdb, size >> 24, size >> 16, size >> 8, size], offset); | |
offset += 5; | |
} | |
view.set(ary, offset); | |
offset += ary.length; | |
return offset; | |
} | |
// --------------------------------------------------------- | |
function msgpack_unpack(view) { // @arg Uint8Array: | |
// @ret Mix: | |
// @desc: msgpack.unpack | |
_idx = -1; | |
return _recursiveMessagePackDecoder(view); | |
} | |
function _recursiveMessagePackDecoder(view) { | |
var size, num = 0, | |
sign, exp, frac, | |
obj, key, ary, | |
type = view[++_idx]; | |
if (type >= 0xe0) { // Negative FixNum (111x xxxx) (-32 ~ -1) | |
return type - 0x100; | |
} | |
if (type < 0xc0) { | |
if (type < 0x80) { // Positive FixNum (0xxx xxxx) (0 ~ 127) | |
return type; | |
} | |
if (type < 0x90) { // FixMap (1000 xxxx) | |
num = type - 0x80; | |
type = 0x80; | |
} else if (type < 0xa0) { // FixArray (1001 xxxx) | |
num = type - 0x90; | |
type = 0x90; | |
} else if (type < 0xc0) { // FixRaw (101x xxxx) | |
num = type - 0xa0; | |
type = 0xa0; | |
} | |
} | |
switch (type) { | |
case 0xc0: return null; | |
case 0xc2: return false; | |
case 0xc3: return true; | |
case 0xca: // float | |
num = view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
sign = num > 0x7fffffff; // 1bit | |
exp = (num >> 23) & 0xff; // 8bits | |
frac = num & 0x7fffff; // 23bits | |
if (!num || num === 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: // double | |
num = view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
sign = num > 0x7fffffff; // 1bit | |
exp = (num >> 20) & 0x7ff; // 11bits | |
frac = num & 0xfffff; // 52bits - 32bits (high word) | |
if (!num || num === 0x80000000) { // 0.0 or -0.0 | |
_idx += 4; | |
return 0; | |
} | |
if (exp === 0x7ff) { // NaN or Infinity | |
_idx += 4; | |
return frac ? NaN : Infinity; | |
} | |
num = view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
return (sign ? -1 : 1) * | |
((frac | 0x100000) * Math.pow(2, exp - 1023 - 20) // 1023: bias | |
+ num * Math.pow(2, exp - 1023 - 52)); | |
// 0xcf: uint64, 0xce: uint32, 0xcd: uint16, 0xcc: uint8 | |
case 0xcf: num = view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
return num * 0x100000000 + | |
view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
case 0xce: num += view[++_idx] * 0x1000000 + (view[++_idx] << 16); | |
case 0xcd: num += view[++_idx] << 8; | |
case 0xcc: return num + view[++_idx]; | |
// 0xd3: int64, 0xd2: int32, 0xd1: int16, 0xd0: int8 | |
case 0xd3: num = view[++_idx]; | |
if (num & 0x80) { // sign -> avoid overflow | |
return ((num ^ 0xff) * 0x100000000000000 + | |
(view[++_idx] ^ 0xff) * 0x1000000000000 + | |
(view[++_idx] ^ 0xff) * 0x10000000000 + | |
(view[++_idx] ^ 0xff) * 0x100000000 + | |
(view[++_idx] ^ 0xff) * 0x1000000 + | |
(view[++_idx] ^ 0xff) * 0x10000 + | |
(view[++_idx] ^ 0xff) * 0x100 + | |
(view[++_idx] ^ 0xff) + 1) * -1; | |
} | |
return num * 0x100000000000000 + | |
view[++_idx] * 0x1000000000000 + | |
view[++_idx] * 0x10000000000 + | |
view[++_idx] * 0x100000000 + | |
view[++_idx] * 0x1000000 + | |
view[++_idx] * 0x10000 + | |
view[++_idx] * 0x100 + | |
view[++_idx]; | |
case 0xd2: num = view[++_idx] * 0x1000000 + (view[++_idx] << 16) + | |
(view[++_idx] << 8) + view[++_idx]; | |
return num < 0x80000000 ? num : num - 0x100000000; // 0x80000000 * 2 | |
case 0xd1: num = (view[++_idx] << 8) + view[++_idx]; | |
return num < 0x8000 ? num : num - 0x10000; // 0x8000 * 2 | |
case 0xd0: num = view[++_idx]; | |
return num < 0x80 ? num : num - 0x100; // 0x80 * 2 | |
// 0xdb: raw32, 0xda: raw16, 0xa0: raw ( string ) | |
case 0xdb: num += view[++_idx] * 0x1000000 + (view[++_idx] << 16); | |
case 0xda: num += (view[++_idx] << 8) + view[++_idx]; | |
case 0xa0: _idx += num; | |
return _toString( _utf8_decode(view, _idx - num + 1, num) ); | |
// 0xdf: map32, 0xde: map16, 0x80: map | |
case 0xdf: num += view[++_idx] * 0x1000000 + (view[++_idx] << 16); | |
case 0xde: num += (view[++_idx] << 8) + view[++_idx]; | |
case 0x80: obj = {}; | |
while (num--) { | |
// make key/value pair | |
size = view[++_idx] - 0xa0; | |
key = _toString( _utf8_decode(view, _idx + 1, size) ); | |
_idx += size; | |
obj[key] = _recursiveMessagePackDecoder(view); | |
} | |
return obj; | |
// 0xdd: array32, 0xdc: array16, 0x90: array | |
case 0xdd: num += view[++_idx] * 0x1000000 + (view[++_idx] << 16); | |
case 0xdc: num += (view[++_idx] << 8) + view[++_idx]; | |
case 0x90: ary = []; | |
while (num--) { | |
ary.push( _recursiveMessagePackDecoder(view) ); | |
} | |
return ary; | |
} | |
throw new TypeError("UNKNOWN_TYPE"); | |
return; | |
} | |
function _toString(data) { | |
var rv = [], i = 0, iz = data.length, bulkSize = 10240; | |
// avoid String.fromCharCode.apply(null, BigArray) exception | |
for (; i < iz; i += bulkSize) { | |
rv.push( String.fromCharCode.apply(null, data.slice(i, i + bulkSize)) ); | |
} | |
return rv.join(""); | |
} | |
function _utf8_encode(ary) { | |
var rv = [], c, i = 0, iz = ary.length; | |
for (; i < iz; ++i) { | |
c = ary.charCodeAt(i); | |
if (c < 0x80) { // ASCII(0x00 ~ 0x7f) | |
rv.push(c & 0x7f); | |
} else if (c < 0x0800) { | |
rv.push(((c >> 6) & 0x1f) | 0xc0, (c & 0x3f) | 0x80); | |
} else if (c < 0x10000) { | |
rv.push(((c >> 12) & 0x0f) | 0xe0, | |
((c >> 6) & 0x3f) | 0x80, (c & 0x3f) | 0x80); | |
} | |
} | |
return rv; | |
} | |
function _utf8_decode(view, offset, size) { | |
var rv = [], c, i = offset, iz = offset + size; | |
for (; i < iz; ) { | |
c = view[i++]; // lead byte | |
rv.push(c < 0x80 ? c : // ASCII(0x00 ~ 0x7f) | |
c < 0xe0 ? ((c & 0x1f) << 6 | (ary[i++] & 0x3f)) : | |
((c & 0x0f) << 12 | (ary[i++] & 0x3f) << 6 | |
| (ary[i++] & 0x3f))); | |
} | |
return rv; | |
} | |
})(this); |
function _utf8_decode()内のary[i++] ですが、これはview[i++]の誤りですか..?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
a = msgpack.pack({ a: [1,2,3, { b: 4, c: "hoge" }, "abc"] }); // ->
[129, 161, 97, 149, 1, 2, 3, 130, 161, 98, 4, 161, 99, 164, 104, 111, 103, 101, 163, 97, 98, 99]
msgpack.unpack(a); // -> { a: [1,2,3, { b: 4, c: "hoge" }, "abc"] }