Created
December 26, 2012 02:01
-
-
Save magcius/4377221 to your computer and use it in GitHub Desktop.
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
var BYTE_LENGTHS = { | |
'y': 1, | |
'n': 2, | |
'q': 2, | |
'i': 4, | |
'u': 4, | |
'h': 4, | |
'x': 8, | |
't': 8, | |
}; | |
var NUMBER_SIGNEDS = { | |
'y': false, | |
'q': false, 'n': true, | |
'u': false, 'i': true, | |
't': false, 'x': true, | |
}; | |
function _readNumber(type, stream, littleEndian) { | |
var byteLength = BYTE_LENGTHS[type]; | |
var bitLength = byteLength * 8; | |
var signed = NUMBER_SIGNEDS[type]; | |
var funcName = 'get' + (signed ? 'Int' : 'Uint') + bitLength; | |
var ret = stream[funcName](stream.pos, littleEndian); | |
stream.pos += byteLength; | |
return ret; | |
} | |
function _readDouble(stream, littleEndian) { | |
var ret = stream.getFloat64(stream.pos, littleEndian); | |
stream.pos += 8; | |
return ret; | |
} | |
function _readBoolean(stream, littleEndian) { | |
var ret = _readNumber('u', stream, number); | |
if (ret === 0) | |
return false; | |
else if (ret === 1) | |
return true; | |
else | |
throw new Error("Invalid DBus boolean value"); | |
} | |
function _collectString(stream, length) { | |
var S = ''; | |
for (var i = 0; i < length; i++) { | |
S += String.fromCharCode(stream.getByte(stream.pos++)); | |
} | |
return S; | |
} | |
function _readString(stream, littleEndian) { | |
// Align | |
stream.pos += 3; | |
stream.pos &= ~3; | |
var length = _readNumber('u', stream, littleEndian); | |
return _collectString(stream, length); | |
} | |
function _validateObjectPath(path) { | |
// The path must begin with an ASCII '/' (integer 47) character... | |
if (path[0] !== '/') | |
return false; | |
// A trailing '/' character is not allowed unless the path is the root path (a single '/' character). | |
if (path.slice(-1) === '/' && path !== '/') | |
return false; | |
// The path must consist of elements separated by slash characters. | |
var elements = path.split('/'); | |
elements.shift(); // First, empty element | |
// Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_". | |
if (!elements.every(function(element) { | |
return /[a-zA-Z0-9_]/.test(element); | |
})) | |
return false; | |
// No element may be the empty string. | |
if (elements.some(function(element) { | |
return element === ''; | |
})) | |
return false; | |
return true; | |
} | |
function _readObjectPath(stream, littleEndian) { | |
var path = _readString(stream, littleEndian); | |
if (!_validateObjectPath(path)) | |
throw new Error("Invalid object path"); | |
return path; | |
} | |
function _readSignature(stream, littleEndian) { | |
var length = stream.getByte(stream.pos++); | |
return _collectString(stream, length); | |
} | |
function _readVariant(stream, littleEndian) { | |
var type = _readSignature('g', stream, littleEndian); | |
return _parseDBusSignature(type)(stream, littleEndian); | |
} | |
var LEAF_DISPATCH = { | |
's': _readString, | |
'o': _readObjectPath, | |
'g': _readSignature, | |
'v': _readVariant, | |
}; | |
['y', 'n', 'q', 'i', 'u', 'b', 'x', 't', 'd'].forEach(function(type) { | |
LEAF_DISPATCH[type] = _readNumber.bind(null, type); | |
}); | |
Object.keys(LEAF_DISPATCH).forEach(function(type) { | |
LEAF_DISPATCH[type].getSignature = function() { | |
return type; | |
}; | |
}); | |
function _makeReadArray(func) { | |
var f = function(stream, littleEndian) { | |
var length = _readNumber('u', stream, littleEndian); | |
var elements = []; | |
for (var i = 0; i < length; i++) | |
elements.push(func(stream, littleEndian)); | |
return elements; | |
}; | |
f.getSignature = function() { | |
return 'a' + func.getSignature(); | |
}; | |
return f; | |
} | |
function _makeReadDictionaryArray(func) { | |
var inner = _makeReadArray(func); | |
var f = function(stream, littleEndian) { | |
var structures = f(stream, littleEndian); | |
var obj = {}; | |
f(stream, littleEndian).forEach(function(element) { | |
var key = element[0], value = element[1]; | |
obj[key] = value; | |
}); | |
return obj; | |
}; | |
f.getSignature = function() { | |
return 'a' + func.getSignature(); | |
}; | |
return f; | |
} | |
function _makeReadStruct(funcs) { | |
var f = function(stream, littleEndian) { | |
// Align | |
stream.pos += 7; | |
stream.pos &= ~7; | |
// Read elements. | |
return funcs.map(function(f) { | |
return f(stream, littleEndian); | |
}); | |
}; | |
f.getSignature = function() { | |
return '(' + funcs.map(function(f) { return f.getSignature(); }).join('') + ')'; | |
}; | |
return f; | |
} | |
function _makeReadDictionaryEntry(keyFunc, valFunc) { | |
var f = _makeReadStruct([keyFunc, valFunc]); | |
f.getSignature = function() { | |
return '{' + keyFunc.getSignature() + valFunc.getSignature() + '}'; | |
}; | |
return f; | |
} | |
function _parseDBusSignatureInternal(signature, idx) { | |
var chr; | |
var func = null; | |
function chewChar() { | |
return (chr = signature[idx++]); | |
} | |
function chew() { | |
var parsed = _parseDBusSignatureInternal(signature, idx); | |
idx = parsed.idx; | |
chr = signature[idx]; | |
return parsed.func; | |
} | |
var beginIdx = idx; | |
var beginChar = chewChar(); | |
if (chr === undefined) { | |
throw new Error("Invalid DBus signature"); | |
} else if (LEAF_DISPATCH[chr] !== undefined) { | |
func = LEAF_DISPATCH[chr]; | |
} else if (chr === 'a') { | |
var eltype = chew(); | |
// Special case: dictionaries | |
if (eltype.getSignature()[0] === '{') | |
func = _makeReadDictionaryArray(eltype); | |
else | |
func = _makeReadArray(eltype); | |
} else if (chr === '(') { | |
var funcs = []; | |
while (chr !== ')') { | |
var f = chew(); | |
funcs.push(f); | |
} | |
chewChar(); // Eat up the ')' | |
func = _makeReadStruct(funcs); | |
} else if (chr === '{') { | |
var keyFunc = chew(); | |
var valueFunc = chew(); | |
if (chewChar() != '}') | |
throw new Error("Invalid DBus signature"); | |
func = _makeReadDictionaryEntry(keyFunc, valueFunc); | |
} | |
return { func: func, idx: idx }; | |
} | |
function _parseDBusSignature(signature) { | |
signature = '(' + signature + ')'; | |
return _parseDBusSignatureInternal(signature, 0).func; | |
} | |
// Does not have the endianness flag. Handled in parseMessage | |
var _headerFormat = _parseDBusSignature('yyyuua(yv)'); | |
function parseHeader(stream) { | |
var endianness = stream.getByte(stream.pos++); | |
var littleEndian; | |
if (endianness === 'l') | |
littleEndian = true; | |
else if (endianness === 'B') | |
littleEndian = false; | |
else | |
throw new Error("Invalid DBus header"); | |
var restOfHeader = _headerFormat(stream, littleEndian); | |
} | |
function newStream(o) { | |
var stream; | |
if (o instanceof DataView) { | |
stream = o; | |
} else if (o instanceof ArrayBuffer) { | |
stream = new DataView(o); | |
} else if (o.buffer !== undefined) { | |
stream = new DataView(o.buffer); | |
} else if (o instanceof String) { | |
} else { | |
throw new Error("Cannot make a stream"); | |
} | |
stream.pos = 0; | |
return stream; | |
} | |
function makeData(string) { | |
var bytes = o.split('').map(function(c) { | |
return c.charCodeAt(0); | |
}); | |
return new Uint8Array(bytes); | |
} | |
function test() { | |
var stream = newStream(new Uint8Array([1, 10, 0, 10])); | |
print(_parseDBusSignature('qq')(stream, false)); | |
} | |
test(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment