Skip to content

Instantly share code, notes, and snippets.

@fabiomcosta
Last active September 1, 2015 17:50
Show Gist options
  • Save fabiomcosta/1fc1304707f8b1d6323c to your computer and use it in GitHub Desktop.
Save fabiomcosta/1fc1304707f8b1d6323c to your computer and use it in GitHub Desktop.
/*!{id:msgpack.js,ver:1.05,license:"MIT",author:"uupaa.js@gmail.com"}*/
// === msgpack ===
// MessagePack -> http://msgpack.sourceforge.net/
this.msgpack || (function(globalScope) {
globalScope.msgpack = {
pack: msgpackpack, // msgpack.pack(data:Mix,
// toString:Boolean = false):ByteArray/ByteString/false
// [1][mix to String] msgpack.pack({}, true) -> "..."
// [2][mix to ByteArray] msgpack.pack({}) -> [...]};
};
var _num2bin = {}, // NumberToBinaryString { 0: "\00", ... 255: "\ff" }
_error = 0, // msgpack.pack() error code. 1 = CYCLIC_REFERENCE_ERROR
_isArray = Array.isArray || (function(mix) {
return Object.prototype.toString.call(mix) === "[object Array]";
}),
_isUint8Array = function(mix) {
return Object.prototype.toString.call(mix) === "[object Uint8Array]";
},
_toString = String.fromCharCode, // CharCode/ByteArray to String
_MAX_DEPTH = 512;
// msgpack.pack
function msgpackpack(data, // @param Mix:
toString) { // @param Boolean(= false):
// @return ByteArray/BinaryString/false:
// false is error return
// [1][mix to String] msgpack.pack({}, true) -> "..."
// [2][mix to ByteArray] msgpack.pack({}) -> [...]
_error = 0;
var byteArray = encode([], data, 0);
return _error ? false
: toString ? byteArrayToByteString(byteArray)
: byteArray;
}
// inner - encoder
function encode(rv, // @param ByteArray: result
mix, // @param Mix: source data
depth) { // @param Number: depth
var size, i, iz, c, pos, // for UTF8.encode, Array.encode, Hash.encode
high, low, sign, exp, frac; // for IEEE754
if (mix == null) { // null or undefined -> 0xc0 ( null )
rv.push(0xc0);
} else if (mix === false) { // false -> 0xc2 ( false )
rv.push(0xc2);
} else if (mix === true) { // true -> 0xc3 ( true )
rv.push(0xc3);
} else {
switch (typeof mix) {
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) { // int or uint
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 {
high = Math.floor(mix / 0x100000000);
low = mix & 0xffffffff;
rv.push(0xd3, (high >> 24) & 0xff, (high >> 16) & 0xff,
(high >> 8) & 0xff, high & 0xff,
(low >> 24) & 0xff, (low >> 16) & 0xff,
(low >> 8) & 0xff, low & 0xff);
}
} 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 {
high = Math.floor(mix / 0x100000000);
low = mix & 0xffffffff;
rv.push(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/20101128/1291000731
sign = mix < 0;
sign && (mix *= -1);
// add offset 1023 to ensure positive
// 0.6931471805599453 = Math.LN2;
exp = ((Math.log(mix) / 0.6931471805599453) + 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);
rv.push(0xcb, (high >> 24) & 0xff, (high >> 16) & 0xff,
(high >> 8) & 0xff, high & 0xff,
(low >> 24) & 0xff, (low >> 16) & 0xff,
(low >> 8) & 0xff, low & 0xff);
}
break;
case "string":
// http://d.hatena.ne.jp/uupaa/20101128
iz = mix.length;
pos = rv.length; // keep rewrite position
rv.push(0); // placeholder
// utf8.encode
for (i = 0; i < iz; ++i) {
c = mix.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);
}
}
size = rv.length - pos - 1;
if (size < 32) {
rv[pos] = 0xa0 + size; // rewrite
} else if (size < 0x100) { // 8
rv.splice(pos, 1, 0xd9, size);
} else if (size < 0x10000) { // 16
rv.splice(pos, 1, 0xda, size >> 8, size & 0xff);
} else if (size < 0x100000000) { // 32
rv.splice(pos, 1, 0xdb,
size >>> 24, (size >> 16) & 0xff,
(size >> 8) & 0xff, size & 0xff);
}
break;
default: // array, hash, or Uint8Array
if (_isUint8Array(mix)) {
size = mix.length;
if (size < 0x100) { // 8
rv.push(0xc4, size);
} else if (size < 0x10000) { // 16
rv.push(0xc5, size >> 8, size & 0xff);
} else if (size < 0x100000000) { // 32
rv.push(0xc6, size >>> 24, (size >> 16) & 0xff,
(size >> 8) & 0xff, size & 0xff);
}
Array.prototype.push.apply(rv, mix);
break;
}
if (++depth >= _MAX_DEPTH) {
_error = 1; // CYCLIC_REFERENCE_ERROR
return rv = []; // clear
}
if (_isArray(mix)) {
size = mix.length;
if (size < 16) {
rv.push(0x90 + size);
} else if (size < 0x10000) { // 16
rv.push(0xdc, size >> 8, size & 0xff);
} else if (size < 0x100000000) { // 32
rv.push(0xdd, size >>> 24, (size >> 16) & 0xff,
(size >> 8) & 0xff, size & 0xff);
}
for (i = 0; i < size; ++i) {
encode(rv, mix[i], depth);
}
} else { // hash
// http://d.hatena.ne.jp/uupaa/20101129
pos = rv.length; // keep rewrite position
rv.push(0); // placeholder
size = 0;
for (i in mix) {
++size;
encode(rv, i, depth);
encode(rv, mix[i], depth);
}
if (size < 16) {
rv[pos] = 0x80 + size; // rewrite
} else if (size < 0x10000) { // 16
rv.splice(pos, 1, 0xde, size >> 8, size & 0xff);
} else if (size < 0x100000000) { // 32
rv.splice(pos, 1, 0xdf,
size >>> 24, (size >> 16) & 0xff,
(size >> 8) & 0xff, size & 0xff);
}
}
}
}
return rv;
}
// inner - byteArray To ByteString
function byteArrayToByteString(byteArray) { // @param ByteArray
// @return String
// http://d.hatena.ne.jp/uupaa/20101128
try {
return _toString.apply(this, byteArray); // toString
} catch(err) {
; // avoid "Maximum call stack size exceeded"
}
var rv = [], i = 0, iz = byteArray.length, num2bin = _num2bin;
for (; i < iz; ++i) {
rv[i] = num2bin[byteArray[i]];
}
return rv.join("");
}
// --- init ---
(function() {
for (var i = 0; i < 0x100; ++i) {
_num2bin[i] = _toString(i); // 0 -> "\00"
}
})();
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment