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