UNPKG

@homebridge/dbus-native

Version:

D-bus protocol implementation in native javascript

183 lines (164 loc) 5.15 kB
const Long = require('long'); const parseSignature = require('./signature'); // Buffer + position + global start position ( used in alignment ) function DBusBuffer(buffer, startPos, options) { if (typeof options !== 'object') { options = { ayBuffer: true, ReturnLongjs: false }; } else if (options.ayBuffer === undefined) { // default settings object options.ayBuffer = true; // enforce truthy default props } this.options = options; this.buffer = buffer; (this.startPos = startPos ? startPos : 0), (this.pos = 0); } DBusBuffer.prototype.align = function (power) { var allbits = (1 << power) - 1; var paddedOffset = ((this.pos + this.startPos + allbits) >> power) << power; this.pos = paddedOffset - this.startPos; }; DBusBuffer.prototype.readInt8 = function () { this.pos++; return this.buffer[this.pos - 1]; }; DBusBuffer.prototype.readSInt16 = function () { this.align(1); var res = this.buffer.readInt16LE(this.pos); this.pos += 2; return res; }; DBusBuffer.prototype.readInt16 = function () { this.align(1); var res = this.buffer.readUInt16LE(this.pos); this.pos += 2; return res; }; DBusBuffer.prototype.readSInt32 = function () { this.align(2); var res = this.buffer.readInt32LE(this.pos); this.pos += 4; return res; }; DBusBuffer.prototype.readInt32 = function () { this.align(2); var res = this.buffer.readUInt32LE(this.pos); this.pos += 4; return res; }; DBusBuffer.prototype.readDouble = function () { this.align(3); var res = this.buffer.readDoubleLE(this.pos); this.pos += 8; return res; }; DBusBuffer.prototype.readString = function (len) { if (len === 0) { this.pos++; return ''; } var res = this.buffer.toString('utf8', this.pos, this.pos + len); this.pos += len + 1; // dbus strings are always zero-terminated ('s' and 'g' types) return res; }; DBusBuffer.prototype.readTree = function readTree(tree) { switch (tree.type) { case '(': case '{': case 'r': this.align(3); return this.readStruct(tree.child); case 'a': if (!tree.child || tree.child.length !== 1) throw new Error('Incorrect array element signature'); var arrayBlobLength = this.readInt32(); return this.readArray(tree.child[0], arrayBlobLength); case 'v': return this.readVariant(); default: return this.readSimpleType(tree.type); } }; DBusBuffer.prototype.read = function read(signature) { var tree = parseSignature(signature); return this.readStruct(tree); }; DBusBuffer.prototype.readVariant = function readVariant() { var signature = this.readSimpleType('g'); var tree = parseSignature(signature); return [tree, this.readStruct(tree)]; }; DBusBuffer.prototype.readStruct = function readStruct(struct) { var result = []; for (var i = 0; i < struct.length; ++i) { result.push(this.readTree(struct[i])); } return result; }; DBusBuffer.prototype.readArray = function readArray(eleType, arrayBlobSize) { var result; var start = this.pos; // special case: treat ay as Buffer if (eleType.type === 'y' && this.options.ayBuffer) { this.pos += arrayBlobSize; return this.buffer.slice(start, this.pos); } // end of array is start of first element + array size // we need to add 4 bytes if not on 8-byte boundary // and array element needs 8 byte alignment if (['x', 't', 'd', '{', '(', 'r'].indexOf(eleType.type) !== -1) this.align(3); var end = this.pos + arrayBlobSize; result = []; while (this.pos < end) result.push(this.readTree(eleType)); return result; }; DBusBuffer.prototype.readSimpleType = function readSimpleType(t) { var data, len, word0, word1; switch (t) { case 'y': return this.readInt8(); case 'b': // TODO: spec says that true is strictly 1 and false is strictly 0 // shold we error (or warn?) when non 01 values? return this.readInt32() ? true : false; case 'n': return this.readSInt16(); case 'q': return this.readInt16(); case 'u': return this.readInt32(); case 'i': return this.readSInt32(); case 'g': len = this.readInt8(); return this.readString(len); case 's': case 'o': len = this.readInt32(); return this.readString(len); // TODO: validate object path here //if (t === 'o' && !isValidObjectPath(str)) // throw new Error('string is not a valid object path')); case 'x': //signed this.align(3); word0 = this.readInt32(); word1 = this.readInt32(); data = Long.fromBits(word0, word1, false); if (this.options.ReturnLongjs) return data; return data.toNumber(); // convert to number (good up to 53 bits) case 't': //unsigned this.align(3); word0 = this.readInt32(); word1 = this.readInt32(); data = Long.fromBits(word0, word1, true); if (this.options.ReturnLongjs) return data; return data.toNumber(); // convert to number (good up to 53 bits) case 'd': return this.readDouble(); default: throw new Error(`Unsupported type: ${t}`); } }; module.exports = DBusBuffer;