Last active
December 17, 2015 15:19
-
-
Save siddMahen/5631260 to your computer and use it in GitHub Desktop.
msgpack.js rewrite, copyright Siddharth Mahendraker, MIT License
This file contains 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
// make everything legible | |
var MSG_PACK_POS_FIXNUM = 0x00, | |
MSG_PACK_MAP_FIX = 0x80, | |
MSG_PACK_ARRAY_FIX = 0x90, | |
MSG_PACK_RAW_FIX = 0xa0, | |
MSG_PACK_NIL = 0xc0, | |
MSG_PACK_FALSE = 0xc2, | |
MSG_PACK_TRUE = 0xc3, | |
MSG_PACK_FLOAT = 0xca, /* Not supported */ | |
MSG_PACK_DOUBLE = 0xcb, | |
MSG_PACK_UINT_8 = 0xcc, | |
MSG_PACK_UINT_16 = 0xcd, | |
MSG_PACK_UINT_32 = 0xce, | |
MSG_PACK_UINT_64 = 0xcf, /* Not supported */ | |
MSG_PACK_INT_8 = 0xd0, | |
MSG_PACK_INT_16 = 0xd1, | |
MSG_PACK_INT_32 = 0xd2, | |
MSG_PACK_INT_64 = 0xd3, /* Not supported */ | |
MSG_PACK_RAW_16 = 0xda, | |
MSG_PACK_RAW_32 = 0xdb, | |
MSG_PACK_ARRAY_16 = 0xdc, | |
MSG_PACK_ARRAY_32 = 0xdd, | |
MSG_PACK_MAP_16 = 0xde, | |
MSG_PACK_MAP_32 = 0xdf, | |
MSG_PACK_NEG_FIXNUM = 0xe0; | |
// these size lookup tables give the sizes of the headers of each msgpack type | |
// in bytes | |
function sizeof_logic(obj){ | |
switch(obj){ | |
case MSG_PACK_NIL: | |
case MSG_PACK_FALSE: | |
case MSG_PACK_TRUE: | |
return 1; | |
break; | |
} | |
return 0; | |
} | |
function sizeof_number(obj){ | |
switch(obj){ | |
case MSG_PACK_POS_FIXNUM: | |
case MSG_PACK_NEG_FIXNUM: | |
return 1; | |
break; | |
case MSG_PACK_UINT_8: | |
case MSG_PACK_INT_8: | |
return 2; | |
break; | |
case MSG_PACK_UINT_16: | |
case MSG_PACK_INT_16: | |
return 3; | |
break; | |
case MSG_PACK_UINT_32: | |
case MSG_PACK_INT_32: | |
return 5; | |
break; | |
case MSG_PACK_DOUBLE: | |
return 9; | |
break; | |
} | |
return 0; | |
} | |
function sizeof_raw(obj){ | |
switch(obj){ | |
case MSG_PACK_RAW_FIX: return 1; | |
case MSG_PACK_RAW_16: return 3; | |
case MSG_PACK_RAW_32: return 5; | |
} | |
return 0; | |
} | |
function sizeof_array(obj){ | |
switch(obj){ | |
case MSG_PACK_ARRAY_FIX: return 1; | |
case MSG_PACK_ARRAY_16: return 3; | |
case MSG_PACK_ARRAY_32: return 5; | |
} | |
return 0; | |
} | |
function sizeof_map(obj){ | |
switch(obj){ | |
case MSG_PACK_MAP_FIX: return 1; | |
case MSG_PACK_MAP_16: return 3; | |
case MSG_PACK_MAP_32: return 5; | |
} | |
return 0; | |
} | |
// given a javascript object, determines its msgpack type | |
function type(obj){ | |
switch(typeof obj){ | |
case "string": | |
var size = obj.length; | |
if(size < 32){ | |
return MSG_PACK_RAW_FIX; | |
}else if(size < 0x10000){ | |
return MSG_PACK_RAW_16; | |
}else if(size < 0x100000000){ | |
return MSG_PACK_RAW_32; | |
} | |
case "boolean": | |
if(obj === true) | |
return MSG_PACK_TRUE; | |
else | |
return MSG_PACK_FALSE; | |
case "number": | |
// TODO: Take a reserved value for NaN and Inf | |
if(obj !== obj){ | |
return "nan"; | |
}else if(obj === Infinity){ | |
return "infinity"; | |
}else if(Math.floor(obj) === obj){ | |
if(obj < 0){ | |
if(obj >= -32){ | |
return 0xe0; | |
}else if(obj > -0x80){ | |
return MSG_PACK_INT_8; | |
}else if(obj > -0x8000){ | |
return MSG_PACK_INT_16; | |
}else if(obj > -0x80000000){ | |
return MSG_PACK_INT_32; | |
}else{ | |
return MSG_PACK_INT_64; | |
} | |
}else{ | |
if(obj < MSG_PACK_MAP_FIX){ | |
return MSG_PACK_POS_FIXNUM; | |
}else if(obj < 0x100){ | |
return MSG_PACK_UINT_8; | |
}else if(obj < 0x10000){ | |
return MSG_PACK_UINT_16; | |
}else if(obj < 0x100000000){ | |
return MSG_PACK_UINT_32; | |
}else{ | |
return MSG_PACK_UINT_64; | |
} | |
} | |
}else{ | |
return MSG_PACK_DOUBLE; | |
} | |
case "object": | |
if(obj == null){ | |
return MSG_PACK_NIL; | |
}else if(Array.isArray(obj)){ | |
var size = obj.length; | |
if(size < 16){ | |
return MSG_PACK_ARRAY_FIX; | |
}else if(size < 0x10000){ | |
return MSG_PACK_ARRAY_16; | |
}else if(size < 0x100000000){ | |
return MSG_PACK_ARRAY_32; | |
} | |
}else{ | |
var size = Object.keys(obj).length; | |
if(size < 16){ | |
return MSG_PACK_MAP_FIX; | |
}else if(size < 0x10000){ | |
return MSG_PACK_MAP_16; | |
}else if(size < 0x100000000){ | |
return MSG_PACK_MAP_32; | |
} | |
} | |
case "undefined": | |
return MSG_PACK_NIL; | |
default: | |
throw new Error("unknow type " + obj); | |
} | |
} | |
// given a javascript object, determines the number of bytes it will | |
// take to store it in msgpack form | |
function sizeof_object(obj){ | |
var desc = type(obj), | |
size = sizeof_number(desc) || sizeof_logic(desc) || 0; | |
if(size === 0){ | |
var hdrsize = 0; | |
if((hdrsize = sizeof_raw(desc))){ | |
size += obj.length + hdrsize; | |
}else if((hdrsize = sizeof_array(desc))){ | |
for(var i = 0; i < obj.length; i++) | |
size += sizeof_object(obj[i]); | |
size += hdrsize; | |
}else if((hdrsize = sizeof_map(desc))){ | |
var keys = Object.keys(obj); | |
for(var i = 0; i < keys.length; i++){ | |
size += sizeof_object(keys[i]); | |
size += sizeof_object(obj[keys[i]]); | |
} | |
size += hdrsize; | |
} | |
} | |
return size; | |
} | |
// pack an object, given a dataview and an offset | |
function pack(obj, dv, offset){ | |
var desc = type(obj), | |
ssize; | |
if((ssize = sizeof_map(desc))){ | |
var keys = Object.keys(obj), | |
size = keys.length; | |
switch(desc){ | |
case MSG_PACK_MAP_FIX: | |
dv.setUint8(offset, MSG_PACK_MAP_FIX | size); | |
break; | |
case MSG_PACK_MAP_16: | |
dv.setUint8(offset, MSG_PACK_MAP_16); | |
dv.setUint16(offset + 1, size, false); | |
break; | |
case MSG_PACK_MAP_32: | |
dv.setUint8(offset, MSG_PACK_MAP_32); | |
dv.setUint32(offset + 1, size, false); | |
break; | |
default: | |
throw new Error("WTF"); | |
break; | |
} | |
offset += ssize; | |
for(var i = 0; i < size; i++){ | |
offset = pack(keys[i], dv, offset); | |
offset = pack(obj[keys[i]], dv, offset); | |
} | |
}else if((ssize = sizeof_array(desc))){ | |
var size = obj.length; | |
switch(desc){ | |
case MSG_PACK_ARRAY_FIX: | |
dv.setUint8(offset, MSG_PACK_ARRAY_FIX | size); | |
break; | |
case MSG_PACK_ARRAY_16: | |
dv.setUint8(offset, MSG_PACK_ARRAY_16); | |
dv.setUint16(offset + 1, size, false); | |
break; | |
case MSG_PACK_ARRAY_32: | |
dv.setUint8(offset, MSG_PACK_ARRAY_32); | |
dv.setUint32(offset + 1, size, false); | |
break; | |
default: | |
throw new Error("WTF"); | |
break; | |
} | |
offset += ssize; | |
for(var i = 0; i < size; i++){ | |
offset = pack(obj[i], dv, offset); | |
} | |
}else if((ssize = sizeof_raw(desc))){ | |
var size = obj.length; | |
switch(desc){ | |
case MSG_PACK_RAW_FIX: | |
dv.setUint8(offset, MSG_PACK_RAW_FIX | size); | |
break; | |
case MSG_PACK_RAW_16: | |
dv.setUint8(offset, MSG_PACK_RAW_16); | |
dv.setUint16(offset + 1, size, false); | |
break; | |
case MSG_PACK_RAW_32: | |
dv.setUint8(offset, MSG_PACK_RAW_32); | |
dv.setUint32(offset + 1, size, false); | |
break; | |
default: | |
throw new Error("WTF"); | |
break; | |
} | |
offset += ssize; | |
for(var i = 0; i < size; i++){ | |
dv.setUint8(offset + i, obj.charCodeAt(i)); | |
} | |
offset += size; | |
}else if((ssize = sizeof_number(desc))){ | |
switch(desc){ | |
case MSG_PACK_POS_FIXNUM: | |
dv.setUint8(offset, obj); | |
break; | |
case MSG_PACK_NEG_FIXNUM: | |
dv.setInt8(offset, obj); | |
break; | |
case MSG_PACK_UINT_8: | |
dv.setUint8(offset, MSG_PACK_UINT_8); | |
dv.setUint8(offset + 1, obj); | |
break; | |
case MSG_PACK_INT_8: | |
dv.setUint8(offset, MSG_PACK_INT_8); | |
dv.setInt8(offset + 1, obj); | |
break; | |
case MSG_PACK_UINT_16: | |
dv.setUint8(offset, MSG_PACK_UINT_16); | |
dv.setUint16(offset + 1, obj, false); | |
break; | |
case MSG_PACK_INT_16: | |
dv.setUint8(offset, MSG_PACK_INT_16); | |
dv.setInt16(offset + 1, obj, false); | |
break; | |
case MSG_PACK_UINT_32: | |
dv.setUint8(offset, MSG_PACK_UINT_32); | |
dv.setUint32(offset + 1, obj, false); | |
break; | |
case MSG_PACK_INT_32: | |
dv.setUint8(offset, MSG_PACK_INT_32); | |
dv.setInt32(offset + 1, obj, false); | |
break; | |
case MSG_PACK_DOUBLE: | |
dv.setUint8(offset, MSG_PACK_DOUBLE); | |
dv.setFloat64(offset + 1, obj, false); | |
break; | |
default: | |
throw new Error("WTF"); | |
break; | |
} | |
offset += ssize; | |
}else if((ssize = sizeof_logic(desc))){ | |
dv.setUint8(offset, desc); | |
offset += ssize; | |
} | |
return offset; | |
} | |
exports.pack = function(obj){ | |
var dv = exports.pack.pure(obj), | |
buf = new Buffer(dv.byteLength); | |
for(var i = 0; i < buf.length; i++){ | |
buf[i] = dv[i]; | |
} | |
return buf; | |
} | |
exports.pack.pure = function(obj){ | |
var size = sizeof_object(obj), | |
buf = new ArrayBuffer(size), | |
dv = new DataView(buf); | |
pack(obj, dv, 0); | |
return dv; | |
} | |
// given a msgpack header, determine which class it falls into | |
function reverseType(desc){ | |
if(desc <= 0x7f){ | |
return MSG_PACK_POS_FIXNUM; | |
}else if(desc <= 0x8f){ | |
return MSG_PACK_MAP_FIX; | |
}else if(desc <= 0x9f){ | |
return MSG_PACK_ARRAY_FIX; | |
}else if(desc <= 0xbf){ | |
return MSG_PACK_RAW_FIX; | |
}else if(desc >= 0xe0){ | |
return MSG_PACK_NEG_FIXNUM; | |
} | |
return desc; | |
} | |
// given a dataview and an offset function, return a | |
// javascript object | |
function unpack(dv, offset){ | |
var desc = reverseType(dv[offset()]), | |
ssize; | |
if((ssize = sizeof_map(desc))){ | |
var len; | |
switch(desc){ | |
case MSG_PACK_MAP_FIX: | |
len = dv[offset()] ^ MSG_PACK_MAP_FIX; | |
break; | |
case MSG_PACK_MAP_16: | |
len = dv.getUint16(offset() + 1, false); | |
break; | |
case MSG_PACK_MAP_32: | |
len = dv.getUint32(offset() + 1, false); | |
break; | |
} | |
offset(ssize); | |
var obj = {}; | |
for(var i = 0; i < 2*len; i+=2){ | |
key = unpack(dv, offset); | |
val = unpack(dv, offset); | |
obj[key] = val; | |
} | |
return obj; | |
}else if((ssize = sizeof_array(desc))){ | |
var len; | |
switch(desc){ | |
case MSG_PACK_ARRAY_FIX: | |
len = dv[offset()] ^ MSG_PACK_ARRAY_FIX; | |
break; | |
case MSG_PACK_ARRAY_16: | |
len = dv.getUint16(offset() + 1, false); | |
break; | |
case MSG_PACK_ARRAY_32: | |
len = dv.getUint32(offset() + 1, false); | |
break; | |
} | |
offset(ssize); | |
obj = []; | |
for(i = 0; i < len; i++){ | |
obj[i] = unpack(dv, offset); | |
} | |
return obj; | |
}else if((ssize = sizeof_raw(desc))){ | |
var len; | |
switch(desc){ | |
case MSG_PACK_RAW_FIX: | |
len = dv[offset()] ^ MSG_PACK_RAW_FIX; | |
break; | |
case MSG_PACK_RAW_16: | |
len = dv.getUint16(offset() + 1, false); | |
break; | |
case MSG_PACK_RAW_32: | |
len = dv.getUint32(offset() + 1, false); | |
break; | |
} | |
offset(ssize); | |
// TODO: change this, too slow | |
var obj = ""; | |
for(var i = 0; i < len; i++){ | |
obj += String.fromCharCode(dv[offset() + i]); | |
} | |
offset(len); | |
return obj; | |
}else if((ssize = sizeof_number(desc))){ | |
var obj = 0; | |
switch(desc){ | |
case MSG_PACK_POS_FIXNUM: | |
obj = dv[offset()]; | |
break; | |
case MSG_PACK_NEG_FIXNUM: | |
obj = dv.getInt8(offset()); | |
break; | |
case MSG_PACK_UINT_8: | |
obj = dv.getUint8(offset() + 1); | |
break; | |
case MSG_PACK_INT_8: | |
obj = dv.getInt8(offset() + 1); | |
break; | |
case MSG_PACK_UINT_16: | |
obj = dv.getUint16(offset() + 1, false); | |
break; | |
case MSG_PACK_INT_16: | |
obj = dv.getInt16(offset() + 1, false); | |
break; | |
case MSG_PACK_UINT_32: | |
obj = dv.getUint32(offset() + 1, false); | |
break; | |
case MSG_PACK_INT_32: | |
obj = dv.getInt32(offset() + 1, false); | |
break; | |
case MSG_PACK_DOUBLE: | |
obj = dv.getFloat64(offset() + 1, false); | |
break; | |
} | |
offset(ssize); | |
return obj; | |
}else if((ssize = sizeof_logic(desc))){ | |
offset(ssize); | |
if(desc === MSG_PACK_NIL) return null; | |
else return (desc === MSG_PACK_TRUE) || false; | |
} | |
} | |
exports.unpack = function(buf){ | |
var nbuf = new ArrayBuffer(buf.length), | |
dv = new DataView(nbuf); | |
for(var i = 0; i < buf.length; i++){ | |
dv[i] = buf[i]; | |
} | |
return exports.unpack.pure(dv); | |
} | |
exports.unpack.pure = function(dv){ | |
// act as a pass by val counter | |
var offset = (function(){ | |
ofst = 0; | |
return function(k){ | |
if(k) return ofst += k; | |
else return ofst; | |
} | |
})(); | |
return unpack(dv, offset); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment