UNPKG

dbus-sdk

Version:

A Node.js SDK for interacting with DBus, enabling seamless service calling and exposure with TypeScript support

651 lines (650 loc) 35.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DBusBufferDecoder = void 0; const DBusMessageEndianness_1 = require("./enums/DBusMessageEndianness"); const DBusSignedValue_1 = require("./DBusSignedValue"); const Signature_1 = require("./Signature"); const Errors_1 = require("./Errors"); const SignedValueToTypeClassInstance_1 = require("./SignedValueToTypeClassInstance"); /** * A decoder for reading DBus data from a binary buffer, following the DBus wire format. * Supports various DBus data types with proper alignment and endianness handling. * This class provides methods to decode basic types (e.g., integers, strings) and * container types (e.g., arrays, structs) as per the DBus specification. */ class DBusBufferDecoder { /** * Creates a new DBusBufferDecoder instance. * Initializes the decoder with the specified endianness and buffer, * optionally setting the initial offset for reading. * * @param endianness - The endianness to use for reading multi-byte values (default: Little Endian). * @param buffer - The binary buffer containing the DBus data to decode. * @param offset - The initial offset to start reading from (default: 0). * @param convertBigIntToNumber - Convert bigint value to number (default: false) */ constructor(endianness = DBusMessageEndianness_1.DBusMessageEndianness.LE, buffer, offset = 0, convertBigIntToNumber = false) { /** * The endianness of the buffer data (Little Endian or Big Endian). * Default is Little Endian (LE) as specified by DBusMessageEndianness.LE, * which determines how multi-byte values are read from the buffer. */ this.endianness = DBusMessageEndianness_1.DBusMessageEndianness.LE; /** * Auto convert BigInt type to Number type * @protected */ this.convertBigIntToNumber = false; /** * The current reading position (offset) in the buffer. * Incremented as data is read from the buffer, tracking the progress of decoding. */ this.offset = 0; this.endianness = endianness; this.convertBigIntToNumber = convertBigIntToNumber; this.buffer = buffer; // Set the initial offset for reading from the buffer this.offset = offset; } /** * Aligns the current offset to the specified byte boundary. * This ensures that data reads start at the correct position as per DBus alignment rules, * adding padding bytes to the offset if necessary. * * @param alignment - The byte boundary to align to (e.g., 1, 2, 4, 8). * @returns The instance itself for method chaining. * @throws {AlignmentError} If the aligned offset would exceed the buffer length. * @protected */ align(alignment) { // Calculate the remainder of the current offset divided by the alignment boundary const remainder = this.offset % alignment; if (remainder === 0) return this; // Offset is already aligned, no action needed // Calculate the number of padding bytes needed to reach the next aligned position const padding = alignment - remainder; // Increment the offset by the padding amount to align it this.offset += padding; // Ensure the new offset does not exceed the buffer length if (this.offset > this.buffer.length) { throw new Errors_1.AlignmentError(`Alignment exceeds buffer length: attempted to align to ${alignment} bytes, resulting offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } return this; } /** * Reads a BYTE type value from the buffer. * BYTE is an 8-bit unsigned integer with no specific alignment requirement (1-byte alignment). * * @returns The byte value (0-255) wrapped in a DBusSignedValue instance with signature 'y'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a BYTE. */ readByte() { this.align(1); // Ensure 1-byte alignment (no practical effect since BYTE has no alignment requirement) // Check if there is enough data in the buffer to read 1 byte if (this.offset + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read BYTE: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read an 8-bit unsigned integer from the current offset const value = this.buffer.readUInt8(this.offset); this.offset += 1; // Increment offset by 1 byte return new DBusSignedValue_1.DBusSignedValue('y', value); } /** * Reads a BOOLEAN type value from the buffer. * BOOLEAN is stored as a 32-bit unsigned integer (0 for false, 1 for true) and requires 4-byte alignment. * * @returns The boolean value (true or false) wrapped in a DBusSignedValue instance with signature 'b'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a BOOLEAN. * @throws {InvalidValueError} If the read value is neither 0 nor 1. */ readBoolean() { this.align(4); // Ensure 4-byte alignment for the 32-bit integer // Check if there is enough data in the buffer to read 4 bytes if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read BOOLEAN: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readUInt32LE(this.offset); // Read as little-endian } else { value = this.buffer.readUInt32BE(this.offset); // Read as big-endian } this.offset += 4; // Increment offset by 4 bytes // Validate that the value is either 0 (false) or 1 (true) if (value !== 0 && value !== 1) { throw new Errors_1.InvalidValueError(`Invalid BOOLEAN value: ${value}. Must be 0 (false) or 1 (true)`); } // Convert numeric value to boolean: 1 is true, 0 is false const booleanValue = value === 1; return new DBusSignedValue_1.DBusSignedValue('b', booleanValue); } /** * Reads an INT16 type value from the buffer. * INT16 is a 16-bit signed integer and requires 2-byte alignment. * * @returns The 16-bit signed integer value wrapped in a DBusSignedValue instance with signature 'n'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read an INT16. */ readInt16() { this.align(2); // Ensure 2-byte alignment for the 16-bit integer // Check if there is enough data in the buffer to read 2 bytes if (this.offset + 2 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read INT16: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readInt16LE(this.offset); // Read as little-endian } else { value = this.buffer.readInt16BE(this.offset); // Read as big-endian } this.offset += 2; // Increment offset by 2 bytes return new DBusSignedValue_1.DBusSignedValue('n', value); } /** * Reads a UINT16 type value from the buffer. * UINT16 is a 16-bit unsigned integer and requires 2-byte alignment. * * @returns The 16-bit unsigned integer value wrapped in a DBusSignedValue instance with signature 'q'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a UINT16. */ readUInt16() { this.align(2); // Ensure 2-byte alignment for the 16-bit integer // Check if there is enough data in the buffer to read 2 bytes if (this.offset + 2 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read UINT16: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readUInt16LE(this.offset); // Read as little-endian } else { value = this.buffer.readUInt16BE(this.offset); // Read as big-endian } this.offset += 2; // Increment offset by 2 bytes return new DBusSignedValue_1.DBusSignedValue('q', value); } /** * Reads an INT32 type value from the buffer. * INT32 is a 32-bit signed integer and requires 4-byte alignment. * * @returns The 32-bit signed integer value wrapped in a DBusSignedValue instance with signature 'i'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read an INT32. */ readInt32() { this.align(4); // Ensure 4-byte alignment for the 32-bit integer // Check if there is enough data in the buffer to read 4 bytes if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read INT32: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readInt32LE(this.offset); // Read as little-endian } else { value = this.buffer.readInt32BE(this.offset); // Read as big-endian } this.offset += 4; // Increment offset by 4 bytes return new DBusSignedValue_1.DBusSignedValue('i', value); } /** * Reads a UINT32 type value from the buffer. * UINT32 is a 32-bit unsigned integer and requires 4-byte alignment. * * @returns The 32-bit unsigned integer value wrapped in a DBusSignedValue instance with signature 'u'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a UINT32. */ readUInt32() { this.align(4); // Ensure 4-byte alignment for the 32-bit integer // Check if there is enough data in the buffer to read 4 bytes if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read UINT32: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readUInt32LE(this.offset); // Read as little-endian } else { value = this.buffer.readUInt32BE(this.offset); // Read as big-endian } this.offset += 4; // Increment offset by 4 bytes return new DBusSignedValue_1.DBusSignedValue('u', value); } /** * Reads an INT64 type value from the buffer. * INT64 is a 64-bit signed integer and requires 8-byte alignment. * * @returns The 64-bit signed integer value as a bigint wrapped in a DBusSignedValue instance with signature 'x'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read an INT64. */ readInt64() { this.align(8); // Ensure 8-byte alignment for the 64-bit integer // Check if there is enough data in the buffer to read 8 bytes if (this.offset + 8 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read INT64: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readBigInt64LE(this.offset); // Read as little-endian } else { value = this.buffer.readBigInt64BE(this.offset); // Read as big-endian } this.offset += 8; // Increment offset by 8 bytes // return new DBusSignedValue('x', value) return new DBusSignedValue_1.DBusSignedValue('x', this.convertBigIntToNumber ? parseInt(value.toString()) : value); } /** * Reads a UINT64 type value from the buffer. * UINT64 is a 64-bit unsigned integer and requires 8-byte alignment. * * @returns The 64-bit unsigned integer value as a bigint wrapped in a DBusSignedValue instance with signature 't'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a UINT64. */ readUInt64() { this.align(8); // Ensure 8-byte alignment for the 64-bit integer // Check if there is enough data in the buffer to read 8 bytes if (this.offset + 8 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read UINT64: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readBigUInt64LE(this.offset); // Read as little-endian } else { value = this.buffer.readBigUInt64BE(this.offset); // Read as big-endian } this.offset += 8; // Increment offset by 8 bytes // return new DBusSignedValue('t', value) return new DBusSignedValue_1.DBusSignedValue('t', this.convertBigIntToNumber ? parseInt(value.toString()) : value); } /** * Reads a DOUBLE type value from the buffer. * DOUBLE is a 64-bit double-precision floating-point number and requires 8-byte alignment. * * @returns The double-precision floating-point value wrapped in a DBusSignedValue instance with signature 'd'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a DOUBLE. */ readDouble() { this.align(8); // Ensure 8-byte alignment for the 64-bit double // Check if there is enough data in the buffer to read 8 bytes if (this.offset + 8 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read DOUBLE: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readDoubleLE(this.offset); // Read as little-endian } else { value = this.buffer.readDoubleBE(this.offset); // Read as big-endian } this.offset += 8; // Increment offset by 8 bytes return new DBusSignedValue_1.DBusSignedValue('d', value); } /** * Reads a UNIX_FD type value from the buffer. * UNIX_FD is a 32-bit unsigned integer representing a file descriptor index and requires 4-byte alignment. * * @returns The file descriptor index wrapped in a DBusSignedValue instance with signature 'h'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read a UNIX_FD. */ readUnixFD() { this.align(4); // Ensure 4-byte alignment for the 32-bit integer // Check if there is enough data in the buffer to read 4 bytes if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read UNIX_FD: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } let value; // Read the value based on the specified endianness if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { value = this.buffer.readUInt32LE(this.offset); // Read as little-endian } else { value = this.buffer.readUInt32BE(this.offset); // Read as big-endian } this.offset += 4; // Increment offset by 4 bytes return new DBusSignedValue_1.DBusSignedValue('h', value); } /** * Reads a STRING type value from the buffer. * STRING consists of a 32-bit length field followed by UTF-8 encoded characters and a null terminator. * The length field requires 4-byte alignment. * * @returns The string value wrapped in a DBusSignedValue instance with signature 's'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read the STRING length or content. * @throws {ReadBufferError} If the null terminator is not found or is invalid. */ readString() { this.align(4); // Ensure 4-byte alignment for the length field // Check if there is enough data in the buffer to read the length field (4 bytes) if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read STRING length: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read the length of the string (32-bit unsigned integer) let length; if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { length = this.buffer.readUInt32LE(this.offset); // Read length as little-endian } else { length = this.buffer.readUInt32BE(this.offset); // Read length as big-endian } this.offset += 4; // Increment offset by 4 bytes for length field // Check if there is enough data to read the string content plus the null terminator if (this.offset + length + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read STRING content: offset ${this.offset} + length ${length} + 1 exceeds buffer length ${this.buffer.length}`); } // Read the string content as UTF-8 encoded text const value = this.buffer.toString('utf8', this.offset, this.offset + length); this.offset += length; // Increment offset by string length // Check and skip the null terminator (must be 0) const nullTerminator = this.buffer.readUInt8(this.offset); if (nullTerminator !== 0) { throw new Errors_1.ReadBufferError(`Invalid STRING: expected null terminator at offset ${this.offset}, but found ${nullTerminator}`); } this.offset += 1; // Increment offset by 1 byte for null terminator return new DBusSignedValue_1.DBusSignedValue('s', value); } /** * Reads an OBJECT_PATH type value from the buffer. * OBJECT_PATH is a string with specific formatting rules, stored like STRING with a 32-bit length field. * The length field requires 4-byte alignment. * * @returns The object path as a string wrapped in a DBusSignedValue instance with signature 'o'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read the OBJECT_PATH length or content. * @throws {ReadBufferError} If the null terminator is not found or is invalid. */ readObjectPath() { this.align(4); // Ensure 4-byte alignment for the length field // Check if there is enough data in the buffer to read the length field (4 bytes) if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read OBJECT_PATH length: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read the length of the object path (32-bit unsigned integer) let length; if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { length = this.buffer.readUInt32LE(this.offset); // Read length as little-endian } else { length = this.buffer.readUInt32BE(this.offset); // Read length as big-endian } this.offset += 4; // Increment offset by 4 bytes for length field // Check if there is enough data to read the object path content plus the null terminator if (this.offset + length + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read OBJECT_PATH content: offset ${this.offset} + length ${length} + 1 exceeds buffer length ${this.buffer.length}`); } // Read the object path content as UTF-8 encoded text const value = this.buffer.toString('utf8', this.offset, this.offset + length); this.offset += length; // Increment offset by path length // Check and skip the null terminator (must be 0) const nullTerminator = this.buffer.readUInt8(this.offset); if (nullTerminator !== 0) { throw new Errors_1.ReadBufferError(`Invalid OBJECT_PATH: expected null terminator at offset ${this.offset}, but found ${nullTerminator}`); } this.offset += 1; // Increment offset by 1 byte for null terminator return new DBusSignedValue_1.DBusSignedValue('o', value); } /** * Reads a SIGNATURE type value from the buffer. * SIGNATURE is a string of type codes with a 1-byte length field and no specific alignment requirement. * * @returns The signature as a string wrapped in a DBusSignedValue instance with signature 'g'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read the SIGNATURE length or content. * @throws {ReadBufferError} If the null terminator is not found or is invalid. */ readSignature() { // No alignment needed for 1-byte length field // Check if there is enough data in the buffer to read the length field (1 byte) if (this.offset + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read SIGNATURE length: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read the length of the signature (8-bit unsigned integer) const length = this.buffer.readUInt8(this.offset); this.offset += 1; // Increment offset by 1 byte for length field // Check if there is enough data to read the signature content plus the null terminator if (this.offset + length + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read SIGNATURE content: offset ${this.offset} + length ${length} + 1 exceeds buffer length ${this.buffer.length}`); } // Read the signature content as ASCII text const value = this.buffer.toString('ascii', this.offset, this.offset + length); this.offset += length; // Increment offset by signature length // Check and skip the null terminator (must be 0) const nullTerminator = this.buffer.readUInt8(this.offset); if (nullTerminator !== 0) { throw new Errors_1.ReadBufferError(`Invalid SIGNATURE: expected null terminator at offset ${this.offset}, but found ${nullTerminator}`); } this.offset += 1; // Increment offset by 1 byte for null terminator return new DBusSignedValue_1.DBusSignedValue('g', value); } /** * Reads an ARRAY type value from the buffer. * ARRAY starts with a 32-bit length field (total byte length of array data) and requires 4-byte alignment. * Elements are read within a sub-buffer to isolate alignment. * * @param elementType - The DataType of the array elements, required for parsing elements. * @returns The array of elements wrapped in a DBusSignedValue instance with signature 'a{elementType}'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read the ARRAY length or content. */ readArray(elementType) { this.align(4); // Ensure 4-byte alignment for the length field // Check if there is enough data in the buffer to read the length field (4 bytes) if (this.offset + 4 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read ARRAY length: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read the byte length of the array data (32-bit unsigned integer) let byteLength; if (this.endianness === DBusMessageEndianness_1.DBusMessageEndianness.LE) { byteLength = this.buffer.readUInt32LE(this.offset); // Read length as little-endian } else { byteLength = this.buffer.readUInt32BE(this.offset); // Read length as big-endian } this.offset += 4; // Increment offset by 4 bytes for length field switch (elementType.type) { case '{': case '(': this.align(8); // Special case: dictionary entries require 8-byte alignment } // Calculate the end offset of the array data const arrayEndOffset = this.offset + byteLength; // Extract a sub-buffer for the array data to isolate reading const arrayBuffer = this.buffer.subarray(this.offset, arrayEndOffset); // Create a new decoder instance for the array sub-buffer to manage local offset const arrayDecoder = new DBusBufferDecoder(this.endianness, arrayBuffer, 0, this.convertBigIntToNumber); const elements = []; // Read elements until the sub-buffer is fully consumed while (arrayDecoder.offset < arrayBuffer.length) { const element = arrayDecoder.readSignedValue(elementType); // Recursively read each element elements.push(element); } // Update the main offset to the end of the array data this.offset = arrayEndOffset; return new DBusSignedValue_1.DBusSignedValue({ type: 'a', child: [elementType] }, elements); } /** * Reads a STRUCT type value from the buffer. * STRUCT is a sequence of fields and requires 8-byte alignment at the start. * * @param fieldTypes - The DataType array representing the types of struct fields. * @returns The struct value as an array of fields wrapped in a DBusSignedValue instance with signature '('. * @throws {SignatureError} If field types are not provided or empty. */ readStruct(fieldTypes) { this.align(8); // Ensure 8-byte alignment for struct start as per DBus specification // Validate that field types are provided and not empty if (!fieldTypes || fieldTypes.length === 0) { throw new Errors_1.SignatureError('Field types for STRUCT are not provided or empty'); } // Read each field of the struct based on its type const fields = []; for (const fieldType of fieldTypes) { // Recursively read each field, alignment is handled by specific read methods const fieldValue = this.readSignedValue(fieldType); fields.push(fieldValue); } // Return a DBusSignedValue with signature '(' for struct, containing field values return new DBusSignedValue_1.DBusSignedValue('(', fields); } /** * Reads a DICT_ENTRY type value from the buffer. * DICT_ENTRY is a key-value pair used in dictionaries and requires 8-byte alignment at the start. * * @param keyType - The DataType representing the key type of the dictionary entry. * @param valueType - The DataType representing the value type of the dictionary entry. * @returns The dictionary entry value as a key-value pair wrapped in a DBusSignedValue instance with signature '{'. * @throws {SignatureError} If key or value types are not provided. */ readDictEntry(keyType, valueType) { this.align(8); // Ensure 8-byte alignment for dict entry start as per DBus specification // Validate that key and value types are provided if (!keyType || !valueType) { throw new Errors_1.SignatureError('Key and value types for DICT_ENTRY are not provided'); } // Read the key and value of the dictionary entry based on their types // Recursively read the key, alignment is handled by the specific read method const keyValue = this.readSignedValue(keyType); // Recursively read the value, alignment is handled by the specific read method const valueValue = this.readSignedValue(valueType); // Return a DBusSignedValue with signature '{' for dict entry, containing key and value return new DBusSignedValue_1.DBusSignedValue('{', [keyValue, valueValue]); } /** * Reads a VARIANT type value from the buffer. * VARIANT is a dynamic type container with a signature field followed by data, no specific alignment required. * * @returns The variant value wrapped in a DBusSignedValue instance with signature 'v'. * @throws {ReadBufferError} If there are not enough bytes left in the buffer to read the VARIANT signature or content. * @throws {SignatureError} If the variant signature does not describe exactly one type. */ readVariant() { // No alignment needed for variant (signature length field is 1 byte) // Check if there is enough data in the buffer to read the signature length field (1 byte) if (this.offset + 1 > this.buffer.length) { throw new Errors_1.ReadBufferError(`Cannot read VARIANT signature length: offset ${this.offset} exceeds buffer length ${this.buffer.length}`); } // Read the signature of the contained type const signatureValue = this.readSignature(); // Extract the signature string from the DBusSignedValue const variantSignature = signatureValue.$value; // Parse the signature string to get the corresponding DataType(s) const dataTypes = Signature_1.Signature.parseSignature(variantSignature); // Validate that the variant signature describes exactly one type if (dataTypes.length !== 1) { throw new Errors_1.SignatureError(`VARIANT signature must describe exactly one type, got: ${variantSignature}`); } const dataType = dataTypes[0]; // Read the actual value based on the parsed signature const variantContent = this.readSignedValue(dataType); // Return a DBusSignedValue with signature 'v' and the contained value return new DBusSignedValue_1.DBusSignedValue('v', variantContent); } /** * Reads a single value from the buffer based on the provided DataType. * Dispatches the reading to the appropriate method based on the type signature. * Handles both basic and container types recursively. * * @param type - The DataType describing the type to read from the buffer. * @returns The value wrapped in a DBusSignedValue instance with the appropriate signature. * @throws {SignatureError} If the type is unsupported or has invalid child types for containers. */ readSignedValue(type) { // Switch based on the type code to call the appropriate read method switch (type.type) { // Basic data types case 'y': return this.readByte(); case 'b': return this.readBoolean(); case 'n': return this.readInt16(); case 'q': return this.readUInt16(); case 'u': return this.readUInt32(); case 'i': return this.readInt32(); case 'g': return this.readSignature(); case 's': return this.readString(); case 'o': return this.readObjectPath(); case 'x': return this.readInt64(); case 't': return this.readUInt64(); case 'd': return this.readDouble(); case 'h': return this.readUnixFD(); // Container data types case 'a': // Validate that array has exactly one child type for elements if (!type.child || type.child.length !== 1) { throw new Errors_1.SignatureError('ARRAY type must have exactly one child type'); } return this.readArray(type.child[0]); // Pass the element type to readArray case '(': // Validate that struct has at least one child type for fields if (!type.child || type.child.length === 0) { throw new Errors_1.SignatureError('STRUCT type must have at least one child type'); } return this.readStruct(type.child); // Pass the field types to readStruct case '{': // Validate that dict entry has exactly two child types (key and value) if (!type.child || type.child.length !== 2) { throw new Errors_1.SignatureError('DICT_ENTRY type must have exactly two child types (key and value)'); } return this.readDictEntry(type.child[0], type.child[1]); // Pass the key and value types to readDictEntry case 'v': return this.readVariant(); // Variant does not require child type at this level default: throw new Errors_1.SignatureError(`Unsupported type: ${type.type}`); } } /** * Reads multiple values from the buffer based on the provided signature string. * The signature is parsed into DataType(s), and values are read accordingly, then converted to DBusTypeClass instances. * * @param signature - The DBus signature string describing the type(s) to read (e.g., 'is' for integer and string). * @returns An array of DBusTypeClass instances representing the read values. * @throws {SignatureError} If the signature is empty or invalid. */ toSignedValues(signature) { // Parse the signature string into an array of DataType objects const dataTypes = Signature_1.Signature.parseSignature(signature); // Validate that the signature is not empty if (dataTypes.length === 0) { throw new Errors_1.SignatureError('Empty signature provided'); } // Read each value based on its corresponding DataType const results = []; for (const dataType of dataTypes) { const value = this.readSignedValue(dataType); results.push((0, SignedValueToTypeClassInstance_1.SignedValueToTypeClassInstance)(value)); } return results; } /** * Decodes values from the buffer based on the provided signature and optionally converts them to plain JavaScript values or typed DBusTypeClass instances. * This method can unwrap DBusSignedValue instances into raw values for easier use or return typed instances for further processing. * * @param signature - The DBus signature string describing the type(s) to read (e.g., 'is' for integer and string). * @param typed - If true, returns an array of DBusTypeClass instances; if false, returns plain JavaScript values (default: false). * @returns An array of either plain JavaScript values or DBusTypeClass instances, based on the typed parameter. * @throws {SignatureError} If the signature is empty or invalid. */ decode(signature, typed = false) { // Read values as DBusSignedValue instances and convert them based on the typed parameter return typed ? this.toSignedValues(signature) : DBusSignedValue_1.DBusSignedValue.toJSON(this.toSignedValues(signature)); } } exports.DBusBufferDecoder = DBusBufferDecoder;