Skip to content

Instantly share code, notes, and snippets.

@magcius
Created December 26, 2012 02:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save magcius/4377221 to your computer and use it in GitHub Desktop.
Save magcius/4377221 to your computer and use it in GitHub Desktop.
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