Skip to content

Instantly share code, notes, and snippets.

@siddMahen
Last active December 17, 2015 15:19
Show Gist options
  • Save siddMahen/5631260 to your computer and use it in GitHub Desktop.
Save siddMahen/5631260 to your computer and use it in GitHub Desktop.
msgpack.js rewrite, copyright Siddharth Mahendraker, MIT License
// 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