UNPKG

@quadratclown/dbus-next

Version:
308 lines (289 loc) 8.44 kB
const Buffer = require('safe-buffer').Buffer; const align = require('./align').align; const { parseSignature } = require('../lib/signature'); const Long = require('long'); const { getBigIntCompat } = require('./library-options'); const JSBI = require('jsbi'); const { _getJSBIConstants, _getBigIntConstants } = require('./constants'); /* * MakeSimpleMarshaller * @param signature - the signature of the data you want to check * @returns a simple marshaller with the "check" method * * check returns nothing - it only raises errors if the data is * invalid for the signature */ const MakeSimpleMarshaller = function (signature) { const marshaller = {}; function checkValidString (data) { if (typeof data !== 'string') { throw new Error(`Data: ${data} was not of type string`); } else if (data.indexOf('\0') !== -1) { throw new Error('String contains null byte'); } } function checkValidSignature (data) { if (data.length > 0xff) { throw new Error( `Data: ${data} is too long for signature type (${data.length} > 255)` ); } let parenCount = 0; for (let ii = 0; ii < data.length; ++ii) { if (parenCount > 32) { throw new Error( `Maximum container type nesting exceeded in signature type:${data}` ); } switch (data[ii]) { case '(': ++parenCount; break; case ')': --parenCount; break; default: /* no-op */ break; } } parseSignature(data); } switch (signature) { case 'o': // object path // TODO: verify object path here? case 's': // eslint-disable-line no-fallthrough // STRING marshaller.check = function (data) { checkValidString(data); }; marshaller.marshall = function (ps, data) { this.check(data); // utf8 string align(ps, 4); const buff = Buffer.from(data, 'utf8'); ps .word32le(buff.length) .put(buff) .word8(0); ps._offset += 5 + buff.length; }; break; case 'g': // SIGNATURE marshaller.check = function (data) { checkValidString(data); checkValidSignature(data); }; marshaller.marshall = function (ps, data) { this.check(data); // signature const buff = Buffer.from(data, 'ascii'); ps .word8(data.length) .put(buff) .word8(0); ps._offset += 2 + buff.length; }; break; case 'y': // BYTE marshaller.check = function (data) { checkInteger(data); checkRange(0x00, 0xff, data); }; marshaller.marshall = function (ps, data) { this.check(data); ps.word8(data); ps._offset++; }; break; case 'b': // BOOLEAN marshaller.check = function (data) { checkBoolean(data); }; marshaller.marshall = function (ps, data) { this.check(data); // booleans serialised as 0/1 unsigned 32 bit int data = data ? 1 : 0; align(ps, 4); ps.word32le(data); ps._offset += 4; }; break; case 'n': // INT16 marshaller.check = function (data) { checkInteger(data); checkRange(-0x7fff - 1, 0x7fff, data); }; marshaller.marshall = function (ps, data) { this.check(data); align(ps, 2); const buff = Buffer.alloc(2); buff.writeInt16LE(parseInt(data), 0); ps.put(buff); ps._offset += 2; }; break; case 'q': // UINT16 marshaller.check = function (data) { checkInteger(data); checkRange(0, 0xffff, data); }; marshaller.marshall = function (ps, data) { this.check(data); align(ps, 2); ps.word16le(data); ps._offset += 2; }; break; case 'h': case 'i': // INT32 marshaller.check = function (data) { checkInteger(data); checkRange(-0x7fffffff - 1, 0x7fffffff, data); }; marshaller.marshall = function (ps, data) { this.check(data); align(ps, 4); const buff = Buffer.alloc(4); buff.writeInt32LE(parseInt(data), 0); ps.put(buff); ps._offset += 4; }; break; case 'u': // UINT32 marshaller.check = function (data) { checkInteger(data); checkRange(0, 0xffffffff, data); }; marshaller.marshall = function (ps, data) { this.check(data); // 32 t unsigned int align(ps, 4); ps.word32le(data); ps._offset += 4; }; break; case 't': // UINT64 marshaller.check = function (data) { return checkLong(data, false); }; marshaller.marshall = function (ps, data) { data = this.check(data); align(ps, 8); ps.word32le(data.low); ps.word32le(data.high); ps._offset += 8; }; break; case 'x': // INT64 marshaller.check = function (data) { return checkLong(data, true); }; marshaller.marshall = function (ps, data) { data = this.check(data); align(ps, 8); ps.word32le(data.low); ps.word32le(data.high); ps._offset += 8; }; break; case 'd': // DOUBLE marshaller.check = function (data) { if (typeof data !== 'number') { throw new Error(`Data: ${data} was not of type number`); } else if (Number.isNaN(data)) { throw new Error(`Data: ${data} was not a number`); } else if (!Number.isFinite(data)) { throw new Error('Number outside range'); } }; marshaller.marshall = function (ps, data) { this.check(data); align(ps, 8); const buff = Buffer.alloc(8); buff.writeDoubleLE(parseFloat(data), 0); ps.put(buff); ps._offset += 8; }; break; default: throw new Error(`Unknown data type format: ${signature}`); } return marshaller; }; exports.MakeSimpleMarshaller = MakeSimpleMarshaller; const checkRange = function (minValue, maxValue, data) { if (data > maxValue || data < minValue) { throw new Error('Number outside range'); } }; const checkInteger = function (data) { if (typeof data !== 'number') { throw new Error(`Data: ${data} was not of type number`); } if (Math.floor(data) !== data) { throw new Error(`Data: ${data} was not an integer`); } }; const checkBoolean = function (data) { if (!(typeof data === 'boolean' || data === 0 || data === 1)) { throw new Error(`Data: ${data} was not of type boolean`); } }; const checkJSBILong = function (data, signed) { const { MAX_INT64, MIN_INT64, MAX_UINT64, MIN_UINT64 } = _getJSBIConstants(); data = JSBI.BigInt(data.toString()); if (signed) { if (JSBI.greaterThan(data, MAX_INT64)) { throw new Error('data was out of range (greater than max int64)'); } else if (JSBI.lessThan(data, MIN_INT64)) { throw new Error('data was out of range (less than min int64)'); } } else { if (JSBI.greaterThan(data, MAX_UINT64)) { throw new Error('data was out of range (greater than max uint64)'); } else if (JSBI.lessThan(data, MIN_UINT64)) { throw new Error('data was out of range (less than min uint64)'); } } return Long.fromString(data.toString(), true); }; const checkBigIntLong = function (data, signed) { const { MAX_INT64, MIN_INT64, MAX_UINT64, MIN_UINT64 } = _getBigIntConstants(); if (typeof data !== 'bigint') { data = BigInt(data.toString()); } if (signed) { if (data > MAX_INT64) { throw new Error('data was out of range (greater than max int64)'); } else if (data < MIN_INT64) { throw new Error('data was out of range (less than min int64)'); } } else { if (data > MAX_UINT64) { throw new Error('data was out of range (greater than max uint64)'); } else if (data < MIN_UINT64) { throw new Error('data was out of range (less than min uint64)'); } } return Long.fromString(data.toString(), true); }; const checkLong = function (data, signed) { const compat = getBigIntCompat(); if (compat) { return checkJSBILong(data, signed); } else { return checkBigIntLong(data, signed); } };