Create a gist now

Instantly share code, notes, and snippets.

anonymous /marshal.js
Created Dec 31, 2012

Embed
NodeJS module to allow unmarshaling of Ruby serialized objects. Works fine for simple objects, but there's no clear way to implement classes, etc.
"use strict";
/**
* @author geoff
*/
var Marshal = (function() {
var debug = false;
var symbols;
var marshal_major = 4, marshal_minor = 8;
var charBuffer = new Buffer(1);
var types = {
TYPE_NIL: '0',
TYPE_TRUE: 'T',
TYPE_FALSE: 'F',
TYPE_FIXNUM: 'i',
TYPE_EXTENDED: 'e',
TYPE_UCLASS: 'C',
TYPE_OBJECT: 'o',
TYPE_DATA: 'd',
TYPE_USERDEF: 'u',
TYPE_USRMARSHAL:'U',
TYPE_FLOAT: 'f',
TYPE_BIGNUM: 'l',
TYPE_STRING: '"',
TYPE_REGEXP: '/',
TYPE_ARRAY: '[',
TYPE_HASH: '{',
TYPE_HASH_DEF: '}',
TYPE_STRUCT: 'S',
TYPE_MODULE_OLD:'M',
TYPE_CLASS: 'c',
TYPE_MODULE: 'm',
TYPE_SYMBOL: ':',
TYPE_SYMLINK: ';',
TYPE_IVAR: 'I',
TYPE_LINK: '@' }
var r_char = function(data, type) {
charBuffer[0] = data.val[data.pos++];
return charBuffer.toString('ascii');
};
var r_byte = function(data, type) {
if (type == 'string') {
//DO SOMETHING
}
else {
return data.val[data.pos++];
}
};
var r_bytes = function(data, type) {
if (type == 'string') {
var len = r_long(data);
return data.val.toString().substring(data.pos, ( data.pos += len ) );
}
};
var r_long = function(data) {
var c = (r_byte(data) ^ 128) - 128; //TODO: Fix
if (c == 0) {
return 0;
};
if (c > 0) {
if (4 < c && c < 128) {
return c - 5;
}
var x = 0;
for (var i = 0; i < c; i++) {
x |= r_byte(data) << (8*i);
}
}
else {
if (-129 < c && c < -4) {
return c + 5;
}
c = -c;
x = -1;
for (var i = 0; i < c; i++) {
x &= ~(0xff << (8*i));
x |= r_byte(data) << (8*i);
}
}
return x;
};
var r_symreal = function(data) {
var str = r_bytes(data, 'string');
symbols.push(str);
return str;
}
var r_ivar = function(data) {
var len = r_long(data);
var ivars = {};
for (var i = 0; i < len; i++) {
var sym = parseToken(data);
var val = parseToken(data);
ivars[sym] = val; //TODO: Should we bother with instance variables?
}
return ivars;
};
var transforms = {
'0': function() { return null },
'T': function() { return true },
'F': function() { return false },
'i': function(data) {
return r_long(data);
},
'"': function(data) {
return r_bytes(data, 'string');
},
'{': function(data) {
var len = r_long(data);
var res = {};
var k, v, i = 0;
//try {
while (i++ < len && data.pos < data.val.length) {
k = parseToken(data);
v = parseToken(data);
res[k] = v;
}
//} catch (e) {
// if (e.message != "hash-end") {
// throw e;
// }
//}
return res;
},
'}': function(data) {
throw "hash-end";
},
':': function(data) {
return r_symreal(data);
},
';': function(data) {
return symbols[r_long(data)];
},
'[': function(data) {
var len = r_long(data);
var res = new Array();
for (var i = 0; i < len; i++) {
res.push(parseToken(data));
}
return res;
},
'I': function(data) {
var v = parseToken(data);
var ivars = r_ivar(data);
for (var k in ivars) {
v[k] = ivars[k];
}
return v;
},
'C': function(data) {
var res = {};
var sym = parseToken(data);
var inst = parseToken(data);
return res[sym] = inst;
},
'o': function(data) {
var res = {};
var sym = parseToken(data);
var ivars = r_ivar(data);
return sym;
}
};
var parseToken = function(data) {
var token = r_char(data);
if (debug) console.log(["Parsing token", data, token, data.pos, data.val[data.pos - 1] ]);
var res = transforms[token](data);
if (debug) console.log(res);
return res;
};
var _unmarshal = function(val) {
var res = [];
var data = { pos: 0, val: val }
var major = r_byte(data);
var minor = r_byte(data);
symbols = [];
if (major != marshal_major || minor > marshal_minor) throw "Invalid version (expected " + marshal_major + "," + marshal_minor + "), (got " + major + "," + minor + ")";
while (data.pos < data.val.length) {
res.push(parseToken(data));
}
return res;
}
return {
load: function(val) {
return _unmarshal(val);
}
}
})();
module.exports = Marshal;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment