Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.freedesktop.dbus.messages.Message Maven / Gradle / Ivy
Go to download
Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/).
This is the OSGi compliant bundle of all required libraries in one bundle.
package org.freedesktop.dbus.messages;
import static org.freedesktop.dbus.messages.constants.ArgumentType.*;
import org.freedesktop.dbus.*;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MarshallingException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import org.freedesktop.dbus.exceptions.UnknownTypeCodeException;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.messages.constants.ArgumentType;
import org.freedesktop.dbus.messages.constants.Endian;
import org.freedesktop.dbus.messages.constants.HeaderField;
import org.freedesktop.dbus.types.*;
import org.freedesktop.dbus.utils.Hexdump;
import org.freedesktop.dbus.utils.LoggingHelper;
import org.freedesktop.dbus.utils.PrimitiveUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
/**
* Superclass of all messages which are sent over the Bus.
* This class deals with all the marshalling to/from the wire format.
*/
public class Message {
public static final int MAXIMUM_ARRAY_LENGTH = 67108864;
public static final int MAXIMUM_MESSAGE_LENGTH = MAXIMUM_ARRAY_LENGTH * 2;
public static final int MAXIMUM_NUM_UNIX_FDS = MAXIMUM_MESSAGE_LENGTH / 4;
/** The current protocol major version. */
public static final byte PROTOCOL = 1;
/** Default extraction options. */
private static final ExtractOptions DEFAULT_OPTIONS = new ExtractOptions(false, List.of());
/** Position of data offset in int array. */
private static final int OFFSET_DATA = 1;
/** Position of signature offset in int array. */
private static final int OFFSET_SIG = 0;
/** Keep a static reference to each size of padding array to prevent allocation. */
private static byte[][] padding;
static {
padding = new byte[][] {
null, new byte[1], new byte[2], new byte[3], new byte[4], new byte[5], new byte[6], new byte[7]
};
}
/** Steps to increment the buffer array. */
private static final int BUFFERINCREMENT = 20;
private static final AtomicLong GLOBAL_SERIAL = new AtomicLong(0);
//CHECKSTYLE:OFF
protected final Logger logger = LoggerFactory.getLogger(getClass());
//CHECKSTYLE:ON
private final List filedescriptors = new ArrayList<>();
private final Object[] headers = new Object[HeaderField.MAX_FIELDS];
private byte[][] wiredata = new byte[BUFFERINCREMENT][];
private long bytecounter = 0;
private long serial;
private byte type;
private byte flags;
private byte protover;
private boolean big;
private Object[] args;
private byte[] body;
private long bodylen = 0;
private int preallocated = 0;
private int paofs = 0;
private byte[] pabuf;
private int bufferuse = 0;
private boolean endianWasSet;
/**
* Create a message; only to be called by sub-classes.
*
* @param _endian The endianness to create the message.
* @param _type The message type.
* @param _flags Any message flags.
* @throws DBusException on error
*/
protected Message(byte _endian, byte _type, byte _flags) throws DBusException {
this();
big = Endian.BIG == _endian;
setSerial(GLOBAL_SERIAL.incrementAndGet());
logger.debug("Creating message with serial {}", getSerial());
type = _type;
flags = _flags;
preallocate(4);
endianWasSet = _endian != (byte) 0;
append("yyyy", _endian, _type, _flags, PROTOCOL);
}
/**
* Create a blank message. Only to be used when calling populate.
*/
protected Message() {
}
public void updateEndianess(byte _endianess) {
if (endianWasSet) {
return;
}
if (wiredata[0] != null) {
wiredata[0][0] = _endianess;
} else {
wiredata[0] = new byte[] {_endianess, 0, 0, 0};
}
endianWasSet = true;
}
/**
* Create a message from wire-format data.
*
* @param _msg D-Bus serialized data of type yyyuu
* @param _headers D-Bus serialized data of type a(yv)
* @param _body D-Bus serialized data of the signature defined in headers.
*/
@SuppressWarnings("unchecked")
void populate(byte[] _msg, byte[] _headers, byte[] _body, List _descriptors) throws DBusException {
// create a copy of the given arrays to be sure that the content is not changed outside
byte[] msgBuf = new byte[_msg.length];
System.arraycopy(_msg, 0, msgBuf, 0, _msg.length);
byte[] headerBuf = new byte[_headers.length];
System.arraycopy(_headers, 0, headerBuf, 0, _headers.length);
byte[] bodyBuf = new byte[_body.length];
System.arraycopy(_body, 0, bodyBuf, 0, _body.length);
endianWasSet = true;
big = msgBuf[0] == Endian.BIG;
type = msgBuf[1];
flags = msgBuf[2];
protover = msgBuf[3];
wiredata[0] = msgBuf;
wiredata[1] = headerBuf;
wiredata[2] = bodyBuf;
body = bodyBuf;
bufferuse = 3;
bodylen = ((Number) extract(UINT32_STRING, msgBuf, 4, DEFAULT_OPTIONS)[0]).longValue();
long extractedSerial = ((Number) extract(UINT32_STRING, msgBuf, 8, DEFAULT_OPTIONS)[0]).longValue();
logger.debug("Received message of type {} with serial {}", type, extractedSerial);
setSerial(extractedSerial);
// cast to ensure everything is upgraded to long
bytecounter = (long) msgBuf.length + headerBuf.length + bodyBuf.length;
filedescriptors.clear();
if (_descriptors != null) {
filedescriptors.addAll(_descriptors);
}
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Message header: {}", Hexdump.toAscii(headerBuf)));
Object[] hs = extractHeader(headerBuf);
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("Extracted objects: {}", LoggingHelper.arraysVeryDeepString(hs)));
List list = (List) hs[0];
for (Object o : list) {
Object[] objArr = (Object[]) o;
byte idx = (byte) objArr[0];
this.headers[idx] = objArr[1];
}
}
protected Object[] getHeader() {
return headers;
}
/**
* Set header content.
* null
value is ignored.
*
* @param _header header to set
*/
protected void setHeader(Object[] _header) {
if (_header == null) {
return;
}
if (_header.length > headers.length) {
throw new IllegalArgumentException("Given header is larger (" + _header.length + ") than allowed header size: " + headers.length);
}
System.arraycopy(_header, 0, headers, 0, _header.length);
}
protected long getByteCounter() {
return bytecounter;
}
protected void setByteCounter(long _bytecounter) {
bytecounter = _bytecounter;
}
protected synchronized void setSerial(long _serial) {
serial = _serial;
}
protected void setWireData(byte[][] _wiredata) {
wiredata = _wiredata;
}
byte getProtover() {
return protover;
}
long getBodylen() {
return bodylen;
}
/**
* Create a buffer of num bytes. Data is copied to this rather than added to the buffer list.
*/
private void preallocate(int _num) {
preallocated = 0;
pabuf = new byte[_num];
appendBytes(pabuf);
preallocated = _num;
paofs = 0;
}
/**
* Ensures there are enough free buffers.
*
* @param _num number of free buffers to create.
*/
private void ensureBuffers(int _num) {
int increase = _num - wiredata.length + bufferuse;
if (increase > 0) {
if (increase < BUFFERINCREMENT) {
increase = BUFFERINCREMENT;
}
logger.trace("Resizing {}", bufferuse);
byte[][] temp = new byte[wiredata.length + increase][];
System.arraycopy(wiredata, 0, temp, 0, wiredata.length);
wiredata = temp;
}
}
/**
* Appends a buffer to the buffer list.
*
* @param _buf buffer byte array
*/
protected void appendBytes(byte[] _buf) {
if (null == _buf) {
return;
}
if (preallocated > 0) {
if (paofs + _buf.length > pabuf.length) {
throw new ArrayIndexOutOfBoundsException(
MessageFormat.format("Array index out of bounds, paofs={0}, pabuf.length={1}, buf.length={2}.",
paofs, pabuf.length, _buf.length));
}
System.arraycopy(_buf, 0, pabuf, paofs, _buf.length);
paofs += _buf.length;
preallocated -= _buf.length;
} else {
if (bufferuse == wiredata.length) {
logger.trace("Resizing {}", bufferuse);
byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][];
System.arraycopy(wiredata, 0, temp, 0, wiredata.length);
wiredata = temp;
}
wiredata[bufferuse++] = _buf;
bytecounter += _buf.length;
}
}
/**
* Appends a byte to the buffer list.
*
* @param _b byte
*/
protected void appendByte(byte _b) {
if (preallocated > 0) {
pabuf[paofs++] = _b;
preallocated--;
} else {
if (bufferuse == wiredata.length) {
logger.trace("Resizing {}", bufferuse);
byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][];
System.arraycopy(wiredata, 0, temp, 0, wiredata.length);
wiredata = temp;
}
wiredata[bufferuse++] = new byte[] {
_b
};
bytecounter++;
}
}
/**
* Demarshalls an integer of a given width from a buffer. Endianness is determined from the format of the message.
*
* @param _buf The buffer to demarshall from.
* @param _ofs The offset to demarshall from.
* @param _width The byte-width of the int.
*
* @return long
*/
protected long demarshallint(byte[] _buf, int _ofs, int _width) {
return big ? demarshallintBig(_buf, _ofs, _width) : demarshallintLittle(_buf, _ofs, _width);
}
/**
* Marshalls an integer of a given width and appends it to the message. Endianness is determined from the message.
*
* @param _l The integer to marshall.
* @param _width The byte-width of the int.
*/
protected void appendint(long _l, int _width) {
byte[] buf = new byte[_width];
marshallint(_l, buf, 0, _width);
appendBytes(buf);
}
/**
* Marshalls an integer of a given width into a buffer. Endianness is determined from the message.
*
* @param _l The integer to marshall.
* @param _buf The buffer to marshall to.
* @param _ofs The offset to marshall to.
* @param _width The byte-width of the int.
*/
protected void marshallint(long _l, byte[] _buf, int _ofs, int _width) {
if (big) {
marshallintBig(_l, _buf, _ofs, _width);
} else {
marshallintLittle(_l, _buf, _ofs, _width);
}
LoggingHelper.logIf(logger.isTraceEnabled(),
() -> logger.trace("Marshalled int {} to {}", _l, Hexdump.toHex(_buf, _ofs, _width, true)));
}
public byte[][] getWireData() {
return wiredata;
}
public List getFiledescriptors() {
return filedescriptors;
}
/**
* Formats the message in a human-readable format.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append('(');
sb.append(flags);
sb.append(',');
sb.append(getSerial());
sb.append(')');
sb.append(' ');
sb.append('{');
sb.append(' ');
if (headers.length == 0) {
sb.append('}');
} else {
for (int i = 0; i < headers.length; i++) {
sb.append(getHeaderFieldName((byte) i));
sb.append('=');
sb.append('>');
sb.append(headers[i]);
sb.append(',');
sb.append(' ');
}
sb.setCharAt(sb.length() - 2, ' ');
sb.setCharAt(sb.length() - 1, '}');
}
sb.append(' ');
sb.append('{');
sb.append(' ');
Object[] largs = null;
try {
largs = extractArgs(null);
} catch (DBusException _ex) {
logger.debug("", _ex);
}
if (null == largs || 0 == largs.length) {
sb.append('}');
} else {
for (Object o : largs) {
if (o == null) {
sb.append("null");
} else if (o instanceof Object[] oa) {
sb.append(Arrays.deepToString(oa));
} else if (o instanceof byte[] ba) {
sb.append(Arrays.toString(ba));
} else if (o instanceof int[] ia) {
sb.append(Arrays.toString(ia));
} else if (o instanceof short[] sa) {
sb.append(Arrays.toString(sa));
} else if (o instanceof long[] la) {
sb.append(Arrays.toString(la));
} else if (o instanceof boolean[] ba) {
sb.append(Arrays.toString(ba));
} else if (o instanceof double[] da) {
sb.append(Arrays.toString(da));
} else if (o instanceof float[] fa) {
sb.append(Arrays.toString(fa));
} else {
sb.append(o);
}
sb.append(',');
sb.append(' ');
}
sb.setCharAt(sb.length() - 2, ' ');
sb.setCharAt(sb.length() - 1, '}');
}
return sb.toString();
}
/**
* Returns the value of the header field of a given field.
*
* @param _type The field to return.
* @return The value of the field or null if unset.
*/
protected Object getHeader(byte _type) {
return headers.length == 0 || headers.length < _type ? null : headers[_type];
}
/**
* Pad the message to the proper alignment for the given type.
*
* @param _type type
*/
protected void pad(byte _type) {
logger.trace("padding for {}", (char) _type);
int a = getAlignment(_type);
logger.trace("{} {} {} {}", preallocated, paofs, bytecounter, a);
int b = (int) ((bytecounter - preallocated) % a);
if (0 == b) {
return;
}
a = a - b;
if (preallocated > 0) {
paofs += a;
preallocated -= a;
} else {
appendBytes(padding[a]);
}
logger.trace("{} {} {} {}", preallocated, paofs, bytecounter, a);
}
/**
* Append a series of values to the message.
*
* @param _sig The signature(s) of the value(s).
* @param _data The value(s).
*
* @throws DBusException on error
*/
protected void append(String _sig, Object... _data) throws DBusException {
LoggingHelper.logIf(logger.isDebugEnabled(), () -> logger.debug("Appending sig: {} data: {}", _sig, LoggingHelper.arraysVeryDeepString(_data)));
byte[] sigb = _sig.getBytes();
int j = 0;
for (int i = 0; i < sigb.length; i++) {
logger.trace("Appending item: {} {} {}", i, (char) sigb[i], j);
i = appendOne(sigb, i, _data[j++]);
}
}
/**
* Appends a value to the message. The type of the value is read from a D-Bus signature and used to marshall the
* value.
*
* @param _sigb A buffer of the D-Bus signature.
* @param _sigofs The offset into the signature corresponding to this value.
* @param _data The value to marshall.
* @return The offset into the signature of the end of this value's type.
*/
private int appendOne(byte[] _sigb, int _sigofs, Object _data) throws DBusException {
try {
int i = _sigofs;
logger.trace("{}", bytecounter);
logger.trace("Appending type: {} value: {}", (char) _sigb[i], _data);
// pad to the alignment of this type.
pad(_sigb[i]);
switch (_sigb[i]) {
case BYTE:
appendByte(((Number) _data).byteValue());
break;
case BOOLEAN:
appendint((Boolean) _data ? 1 : 0, 4);
break;
case DOUBLE:
long l = Double.doubleToLongBits(((Number) _data).doubleValue());
appendint(l, 8);
break;
case FLOAT:
int rf = Float.floatToIntBits(((Number) _data).floatValue());
appendint(rf, 4);
break;
case UINT32:
appendint(((Number) _data).longValue(), 4);
break;
case INT64:
appendint(((Number) _data).longValue(), 8);
break;
case UINT64:
if (big) {
appendint(((UInt64) _data).top(), 4);
appendint(((UInt64) _data).bottom(), 4);
} else {
appendint(((UInt64) _data).bottom(), 4);
appendint(((UInt64) _data).top(), 4);
}
break;
case INT32:
appendint(((Number) _data).intValue(), 4);
break;
case UINT16:
appendint(((Number) _data).intValue(), 2);
break;
case INT16:
appendint(((Number) _data).shortValue(), 2);
break;
case FILEDESCRIPTOR:
filedescriptors.add((FileDescriptor) _data);
appendint(filedescriptors.size() - 1L, 4);
logger.debug("Just inserted {} as filedescriptor", filedescriptors.size() - 1);
break;
case STRING, OBJECT_PATH:
String payload;
// if the given data is an object, not a ObjectPath itself
if (_data instanceof DBusInterface di) {
payload = di.getObjectPath();
} else {
// Strings are marshalled as a UInt32 with the length,
// followed by the String, followed by a null byte.
payload = _data.toString();
}
byte[] payloadbytes = payload.getBytes(StandardCharsets.UTF_8);
logger.trace("Appending String of length {}", payloadbytes.length);
appendint(payloadbytes.length, 4);
appendBytes(payloadbytes);
appendBytes(padding[1]);
// pad(ArgumentType.STRING);? do we need this?
break;
case SIGNATURE:
// Signatures are marshalled as a byte with the length,
// followed by the String, followed by a null byte.
// Signatures are generally short, so preallocate the array
// for the string, length and null byte.
if (_data instanceof Type[] ta) {
payload = Marshalling.getDBusType(ta);
} else {
payload = (String) _data;
}
byte[] pbytes = payload.getBytes();
preallocate(2 + pbytes.length);
appendByte((byte) pbytes.length);
appendBytes(pbytes);
appendByte((byte) 0);
break;
case ARRAY:
// Arrays are given as a UInt32 for the length in bytes,
// padding to the element alignment, then elements in
// order. The length is the length from the end of the
// initial padding to the end of the last element.
if (logger.isTraceEnabled() && _data instanceof Object[] oa) {
logger.trace("Appending array: {}", Arrays.deepToString(oa));
}
byte[] alen = new byte[4];
appendBytes(alen);
pad(_sigb[++i]);
long c = bytecounter;
// optimise primitives
if (_data.getClass().isArray() && _data.getClass().getComponentType().isPrimitive()) {
byte[] primbuf;
int algn = getAlignment(_sigb[i]);
int len = Array.getLength(_data);
switch (_sigb[i]) {
case BYTE -> {
primbuf = (byte[]) _data;
}
case INT16, INT32, INT64 -> {
primbuf = new byte[len * algn];
for (int j = 0, k = 0; j < len; j++, k += algn) {
marshallint(Array.getLong(_data, j), primbuf, k, algn);
}
}
case BOOLEAN -> {
primbuf = new byte[len * algn];
for (int j = 0, k = 0; j < len; j++, k += algn) {
marshallint(Array.getBoolean(_data, j) ? 1 : 0, primbuf, k, algn);
}
}
case DOUBLE -> {
primbuf = new byte[len * algn];
if (_data instanceof float[] fa) {
for (int j = 0, k = 0; j < len; j++, k += algn) {
marshallint(Double.doubleToRawLongBits(fa[j]), primbuf, k, algn);
}
} else {
for (int j = 0, k = 0; j < len; j++, k += algn) {
marshallint(Double.doubleToRawLongBits(((double[]) _data)[j]), primbuf, k, algn);
}
}
}
case FLOAT -> {
primbuf = new byte[len * algn];
for (int j = 0, k = 0; j < len; j++, k += algn) {
marshallint(Float.floatToRawIntBits(((float[]) _data)[j]), primbuf, k, algn);
}
}
default -> throw new MarshallingException("Primitive array being sent as non-primitive array.");
}
appendBytes(primbuf);
} else if (_data instanceof Collection> coll) {
Object[] contents = coll.toArray();
int diff = i;
ensureBuffers(contents.length * 4);
for (Object o : contents) {
diff = appendOne(_sigb, i, o);
}
if (contents.length == 0) {
diff = EmptyCollectionHelper.determineSignatureOffsetArray(_sigb, diff);
}
i = diff;
} else if (_data instanceof Map, ?> map) {
int diff = i;
ensureBuffers(map.size() * 6);
for (Map.Entry, ?> o : map.entrySet()) {
diff = appendOne(_sigb, i, o);
}
if (map.isEmpty()) {
diff = EmptyCollectionHelper.determineSignatureOffsetDict(_sigb, diff);
}
i = diff;
} else {
Object[] contents = (Object[]) _data;
ensureBuffers(contents.length * 4);
int diff = i;
for (Object o : contents) {
diff = appendOne(_sigb, i, o);
}
if (contents.length == 0) {
diff = EmptyCollectionHelper.determineSignatureOffsetArray(_sigb, diff);
}
i = diff;
}
logger.trace("start: {} end: {} length: {}", c, bytecounter, bytecounter - c);
marshallint(bytecounter - c, alen, 0, 4);
break;
case STRUCT1:
// Structs are aligned to 8 bytes
// and simply contain each element marshalled in order
Object[] contents;
if (_data instanceof Container cont) {
contents = cont.getParameters();
} else {
contents = (Object[]) _data;
}
ensureBuffers(contents.length * 4);
int j = 0;
for (i++; _sigb[i] != STRUCT2; i++) {
i = appendOne(_sigb, i, contents[j++]);
}
break;
case DICT_ENTRY1:
// Dict entries are the same as structs.
if (_data instanceof Map.Entry, ?> entry) {
i++;
i = appendOne(_sigb, i, entry.getKey());
i++;
i = appendOne(_sigb, i, entry.getValue());
i++;
} else {
contents = (Object[]) _data;
j = 0;
for (i++; _sigb[i] != DICT_ENTRY2; i++) {
i = appendOne(_sigb, i, contents[j++]);
}
}
break;
case VARIANT:
// Variants are marshalled as a signature
// followed by the value.
if (_data instanceof Variant> variant) {
appendOne(new byte[] {
SIGNATURE
}, 0, variant.getSig());
appendOne(variant.getSig().getBytes(), 0, variant.getValue());
} else if (_data instanceof Object[] oa) {
appendOne(new byte[] {
SIGNATURE
}, 0, oa[0]);
appendOne(((String) oa[0]).getBytes(), 0, oa[1]);
} else {
String sig = Marshalling.getDBusType(_data.getClass())[0];
appendOne(new byte[] {
SIGNATURE
}, 0, sig);
appendOne(sig.getBytes(), 0, _data);
}
break;
default:
}
return i;
} catch (ClassCastException _ex) {
logger.debug("Trying to marshall to unconvertible type.", _ex);
throw new MarshallingException(
MessageFormat.format("Trying to marshall to unconvertible type (from {0} to {1}).",
_data.getClass().getName(), (char) _sigb[_sigofs]));
}
}
/**
* Align a counter to the given type.
*
* @param _current The current counter.
* @param _type The type to align to.
* @return The new, aligned, counter.
*/
protected int align(int _current, byte _type) {
logger.trace("aligning to {}", (char) _type);
int a = getAlignment(_type);
if (0 == _current % a) {
return _current;
}
return _current + a - _current % a;
}
/**
* Extracts the header information from the given byte array.
*
* @param _headers D-Bus serialized data of type a(yv)
*
* @return Object array containing header data
*
* @throws DBusException when parsing fails
*/
Object[] extractHeader(byte[] _headers) throws DBusException {
int[] offsets = new int[] {
0, 0
};
return extract("a(yv)", _headers, offsets, DEFAULT_OPTIONS, this::readHeaderVariants);
}
/**
* Special lightweight version to read the variant objects in DBus message header.
* This method will not create {@link Variant} objects it directly extracts the Variant data content.
*
* @param _signatureBuf DBus signature string as byte array
* @param _dataBuf buffer with header data
* @param _offsets current offsets
* @param _options additional options
*
* @return Object
*
* @throws DBusException when parsing fails
*/
private Object readHeaderVariants(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractOptions _options) throws DBusException {
// correct the offsets before extracting values
_offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]);
Object result = null;
if (_signatureBuf[_offsets[OFFSET_SIG]] == ARRAY) {
result = extractArray(_signatureBuf, _dataBuf, _offsets, _options, this::readHeaderVariants);
} else if (_signatureBuf[_offsets[OFFSET_SIG]] == BYTE) {
result = extractByte(_dataBuf, _offsets);
} else if (_signatureBuf[_offsets[OFFSET_SIG]] == VARIANT) {
result = extractVariant(_dataBuf, _offsets, DEFAULT_OPTIONS, (sig, obj) -> obj);
} else if (_signatureBuf[_offsets[OFFSET_SIG]] == STRUCT1) {
result = extractStruct(_signatureBuf, _dataBuf, _offsets, DEFAULT_OPTIONS, this::readHeaderVariants);
} else {
throw new MessageFormatException("Unsupported data type in header: " + _signatureBuf[_offsets[OFFSET_SIG]]);
}
logger.trace("Extracted header signature type '{}' to: '{}'", (char) _signatureBuf[_offsets[OFFSET_SIG]], result);
return result;
}
/**
* Demarshall one value from a buffer.
*
* @param _signatureBuf A buffer of the D-Bus signature.
* @param _dataBuf The buffer to demarshall from.
* @param _offsets An array of two ints, which holds the position of the current signature offset and the current
* offset of the data buffer.
* @param _options extract options
* @return The demarshalled value.
*/
private Object extractOne(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractOptions _options)
throws DBusException {
logger.trace("Extracting type: {} from offset {}", (char) _signatureBuf[_offsets[OFFSET_SIG]],
_offsets[OFFSET_DATA]);
Object rv = null;
_offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]);
switch (_signatureBuf[_offsets[OFFSET_SIG]]) {
case BYTE:
rv = extractByte(_dataBuf, _offsets);
break;
case UINT32:
rv = new UInt32(demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4));
_offsets[OFFSET_DATA] += 4;
break;
case INT32:
rv = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
break;
case INT16:
rv = (short) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 2);
_offsets[OFFSET_DATA] += 2;
break;
case UINT16:
rv = new UInt16((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 2));
_offsets[OFFSET_DATA] += 2;
break;
case INT64:
rv = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 8);
_offsets[OFFSET_DATA] += 8;
break;
case UINT64:
long top;
long bottom;
if (big) {
top = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
bottom = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
} else {
bottom = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
top = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
}
rv = new UInt64(top, bottom);
_offsets[OFFSET_DATA] += 4;
break;
case DOUBLE:
long l = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 8);
_offsets[OFFSET_DATA] += 8;
rv = Double.longBitsToDouble(l);
break;
case FLOAT:
int rf = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
rv = Float.intBitsToFloat(rf);
break;
case BOOLEAN:
rf = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
rv = (1 == rf) ? Boolean.TRUE : Boolean.FALSE;
break;
case ARRAY:
rv = extractArray(_signatureBuf, _dataBuf, _offsets, _options, this::extractOne);
break;
case STRUCT1:
rv = extractStruct(_signatureBuf, _dataBuf, _offsets, _options, this::extractOne);
break;
case DICT_ENTRY1:
Object[] decontents = new Object[2];
LoggingHelper.logIf(logger.isTraceEnabled(), () ->
logger.trace("Extracting Dict Entry ({}) from: {}",
Hexdump.toAscii(_signatureBuf, _offsets[OFFSET_SIG], _signatureBuf.length - _offsets[OFFSET_SIG]),
Hexdump.toHex(_dataBuf, _offsets[OFFSET_DATA], _dataBuf.length - _offsets[OFFSET_DATA], true))
);
_offsets[OFFSET_SIG]++;
decontents[0] = extractOne(_signatureBuf, _dataBuf, _offsets, ExtractOptions.copyWithContainedFlag(_options, true));
_offsets[OFFSET_SIG]++;
decontents[1] = extractOne(_signatureBuf, _dataBuf, _offsets, ExtractOptions.copyWithContainedFlag(_options, true));
_offsets[OFFSET_SIG]++;
rv = decontents;
break;
case VARIANT:
rv = extractVariant(_dataBuf, _offsets, _options, (sig, obj) -> {
logger.trace("Creating Variant with SIG: {} - Value: {}", sig, obj);
return new Variant<>(obj, sig);
});
break;
case FILEDESCRIPTOR:
rv = filedescriptors.get((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4));
_offsets[OFFSET_DATA] += 4;
break;
case STRING:
int length = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
rv = new String(_dataBuf, _offsets[OFFSET_DATA], length, StandardCharsets.UTF_8);
_offsets[OFFSET_DATA] += length + 1;
break;
case OBJECT_PATH:
length = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
_offsets[OFFSET_DATA] += 4;
rv = new DBusPath(getSource(), new String(_dataBuf, _offsets[OFFSET_DATA], length));
_offsets[OFFSET_DATA] += length + 1;
break;
case SIGNATURE:
length = _dataBuf[_offsets[OFFSET_DATA]++] & 0xFF;
rv = new String(_dataBuf, _offsets[OFFSET_DATA], length);
_offsets[OFFSET_DATA] += length + 1;
break;
default:
throw new UnknownTypeCodeException(_signatureBuf[_offsets[OFFSET_SIG]]);
}
if (logger.isTraceEnabled()) {
if (rv instanceof Object[] oa) {
logger.trace("Extracted: {} (now at {})", Arrays.deepToString(oa), _offsets[OFFSET_DATA]);
} else {
logger.trace("Extracted: {} (now at {})", rv, _offsets[OFFSET_DATA]);
}
}
return rv;
}
/**
* Extracts a byte from the data received on bus.
*
* @param _dataBuf buffer holding the byte
* @param _offsets offset position in buffer (will be updated)
*
* @return Object
*/
private Object extractByte(byte[] _dataBuf, int[] _offsets) {
Object rv;
rv = _dataBuf[_offsets[OFFSET_DATA]++];
return rv;
}
/**
* Extracts a struct from the data received on bus.
*
* @param _signatureBuf signature (as byte array) defining the struct content
* @param _dataBuf buffer containing the struct
* @param _offsets offset position in buffer (will be updated)
* @param _options extract options
* @param _extractMethod method to be called for every entry contained of the struct
*
* @return Object
*
* @throws DBusException when parsing fails
*/
private Object extractStruct(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractOptions _options, ExtractMethod _extractMethod) throws DBusException {
Object rv;
List contents = new ArrayList<>();
while (_signatureBuf[++_offsets[OFFSET_SIG]] != STRUCT2) {
contents.add(_extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, ExtractOptions.copyWithContainedFlag(_options, true)));
}
rv = contents.toArray();
return rv;
}
/**
* Extracts an array from the data received on bus.
*
* @param _signatureBuf signature string (as byte array) of the content of the array
* @param _dataBuf buffer containing the array to read
* @param _offsets current offsets in the buffer (will be updated)
* @param _options additional options
* @param _extractMethod method to be called for every entry contained in the array
*
* @return Object
*
* @throws MarshallingException when Array is too large
* @throws DBusException when parsing fails
*/
private Object extractArray(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractOptions _options, ExtractMethod _extractMethod)
throws MarshallingException, DBusException {
Object rv;
long size = demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4);
logger.trace("Reading array of size: {}", size);
_offsets[OFFSET_DATA] += 4;
byte algn = (byte) getAlignment(_signatureBuf[++_offsets[OFFSET_SIG]]);
_offsets[OFFSET_DATA] = align(_offsets[OFFSET_DATA], _signatureBuf[_offsets[OFFSET_SIG]]);
int length = (int) (size / algn);
if (length > AbstractConnection.MAX_ARRAY_LENGTH) {
throw new MarshallingException("Arrays must not exceed " + AbstractConnection.MAX_ARRAY_LENGTH);
}
rv = optimizePrimitives(_signatureBuf, _dataBuf, _offsets, size, algn, length, _options, _extractMethod);
if (_options.contained() && !(rv instanceof List) && !(rv instanceof Map)) {
rv = ArrayFrob.listify(rv);
}
return rv;
}
/**
* Extracts a {@link Variant} from the data received on bus.
*
* @param _dataBuf buffer containing the variant
* @param _offsets current offsets in the buffer (will be updated)
* @param _options extract options
* @param _variantFactory method to create new {@link Variant} objects (or other object types)
*
* @return Object / Variant
*
* @throws DBusException when parsing fails
*/
private Object extractVariant(byte[] _dataBuf, int[] _offsets, ExtractOptions _options, BiFunction _variantFactory) throws DBusException {
Object rv;
int[] newofs = new int[] {
0, _offsets[OFFSET_DATA]
};
String sig = (String) extract(SIGNATURE_STRING, _dataBuf, newofs, _options)[0];
newofs[OFFSET_SIG] = 0;
rv = _variantFactory.apply(sig, extract(sig, _dataBuf, newofs, _options)[0]);
_offsets[OFFSET_DATA] = newofs[OFFSET_DATA];
return rv;
}
/**
* Will create primitive arrays when an array is read.
*
* In case the array is not compatible with primitives (e.g. object types are used or array contains Struct/Maps etc)
* an array of the appropriate type will be created.
*
* @param _signatureBuf signature string (as byte array) containing the type of array
* @param _dataBuf buffer containing the array
* @param _offsets current offset in buffer (will be updated)
* @param _size size of a byte
* @param _algn data offset padding width when reading primitives (except byte)
* @param _length length of the array
* @param _options extract options
* @param _extractMethod method to be called for every entry contained in the array if not primitive array
*
* @return Object array
*
* @throws DBusException when parsing fails
*/
private Object optimizePrimitives(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, long _size, byte _algn,
int _length, ExtractOptions _options, ExtractMethod _extractMethod)
throws DBusException {
Object rv = null;
int offsetPos = _offsets[OFFSET_SIG] - 1; // need to extract one because extractArray will already update offset position
boolean optimize = _options.arrayConvert() != null
&& _options.arrayConvert().size() > offsetPos
&& _options.arrayConvert().get(offsetPos) == ConstructorArgType.PRIMITIVE_ARRAY;
if (optimize) {
switch (_signatureBuf[_offsets[OFFSET_SIG]]) {
case BYTE:
rv = new byte[_length];
System.arraycopy(_dataBuf, _offsets[OFFSET_DATA], rv, 0, _length);
_offsets[OFFSET_DATA] += _size;
break;
case INT16:
rv = new short[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((short[]) rv)[j] = (short) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn);
}
break;
case INT32:
rv = new int[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((int[]) rv)[j] = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn);
}
break;
case INT64:
rv = new long[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((long[]) rv)[j] = demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn);
}
break;
case BOOLEAN:
rv = new boolean[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((boolean[]) rv)[j] = 1 == demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn);
}
break;
case FLOAT:
rv = new float[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((float[]) rv)[j] = Float.intBitsToFloat((int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn));
}
break;
case DOUBLE:
rv = new double[_length];
for (int j = 0; j < _length; j++, _offsets[OFFSET_DATA] += _algn) {
((double[]) rv)[j] = Double.longBitsToDouble(demarshallint(_dataBuf, _offsets[OFFSET_DATA], _algn));
}
break;
default:
break;
}
}
if (_signatureBuf[_offsets[OFFSET_SIG]] == DICT_ENTRY1) {
int ofssave = prepareCollection(_signatureBuf, _offsets, _size);
long end = _offsets[OFFSET_DATA] + _size;
Map map = new LinkedHashMap<>();
while (_offsets[OFFSET_DATA] < end) {
_offsets[OFFSET_SIG] = ofssave;
Object[] data = (Object[]) _extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, ExtractOptions.copyWithContainedFlag(_options, true));
map.put(data[0], data[1]);
}
rv = map;
}
if (rv == null) {
int ofssave = prepareCollection(_signatureBuf, _offsets, _size);
long end = _offsets[OFFSET_DATA] + _size;
List contents = new ArrayList<>();
while (_offsets[OFFSET_DATA] < end) {
_offsets[OFFSET_SIG] = ofssave;
contents.add(_extractMethod.extractOne(_signatureBuf, _dataBuf, _offsets, ExtractOptions.copyWithContainedFlag(_options, true)));
}
rv = contents;
}
return rv;
}
private int prepareCollection(byte[] _signatureBuf, int[] _offsets, long _size) throws DBusException {
if (0 == _size) {
// advance the type parser even on 0-size arrays.
List temp = new ArrayList<>();
byte[] temp2 = new byte[_signatureBuf.length - _offsets[OFFSET_SIG]];
System.arraycopy(_signatureBuf, _offsets[OFFSET_SIG], temp2, 0, temp2.length);
String temp3 = new String(temp2);
// ofs[OFFSET_SIG] gets incremented anyway. Leave one character on the stack
int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1;
_offsets[OFFSET_SIG] += temp4;
logger.trace("Aligned type: {} {} {}", temp3, temp4, _offsets[OFFSET_SIG]);
}
return _offsets[OFFSET_SIG];
}
/**
* Demarshall values from a buffer.
*
* @param _signature The D-Bus signature(s) of the value(s).
* @param _dataBuf The buffer to demarshall from.
* @param _offsets The offset into the data buffer to start.
* @param _options additional options
* @return The demarshalled value(s).
*
* @throws DBusException on error
*/
protected Object[] extract(String _signature, byte[] _dataBuf, int _offsets, ExtractOptions _options) throws DBusException {
return extract(_signature, _dataBuf, new int[] {
0, _offsets
}, _options);
}
/**
* Demarshall values from a buffer.
*
* @param _signature The D-Bus signature(s) of the value(s).
* @param _dataBuf The buffer to demarshall from.
* @param _offsets An array of two ints, which holds the position of the current signature offset and the current
* offset of the data buffer. These values will be updated to the start of the next value after
* demarshalling.
* @param _options additional options
*
* @return The demarshalled value(s).
*
* @throws DBusException on error
*/
protected Object[] extract(String _signature, byte[] _dataBuf, int[] _offsets, ExtractOptions _options) throws DBusException {
return extract(_signature, _dataBuf, _offsets, _options, this::extractOne);
}
Object[] extract(String _signature, byte[] _dataBuf, int[] _offsets, ExtractOptions _options, ExtractMethod _method) throws DBusException {
logger.trace("extract({},#{}, {{},{}}", _signature, _dataBuf.length, _offsets[OFFSET_SIG],
_offsets[OFFSET_DATA]);
List rv = new ArrayList<>();
byte[] sigb = _signature.getBytes();
for (int[] i = _offsets; i[OFFSET_SIG] < sigb.length; i[OFFSET_SIG]++) {
rv.add(_method.extractOne(sigb, _dataBuf, i, ExtractOptions.copyWithContainedFlag(_options, false)));
}
return rv.toArray();
}
/**
* Returns the Bus ID that sent the message.
*
* @return string
*/
public String getSource() {
return (String) getHeader(HeaderField.SENDER);
}
/**
* Returns the destination of the message.
*
* @return string
*/
public String getDestination() {
return (String) getHeader(HeaderField.DESTINATION);
}
/**
* Returns the interface of the message.
*
* @return string
*/
public String getInterface() {
return (String) getHeader(HeaderField.INTERFACE);
}
/**
* Returns the object path of the message.
*
* @return string
*/
public String getPath() {
Object o = getHeader(HeaderField.PATH);
if (null == o) {
return null;
}
return o.toString();
}
/**
* Returns the member name or error name this message represents.
*
* @return string
*/
public String getName() {
if (this instanceof Error) {
return (String) getHeader(HeaderField.ERROR_NAME);
} else {
return (String) getHeader(HeaderField.MEMBER);
}
}
/**
* Returns the dbus signature of the parameters.
*
* @return string
*/
public String getSig() {
return (String) getHeader(HeaderField.SIGNATURE);
}
/**
* Returns the message flags.
*
* @return int
*/
public int getFlags() {
return flags;
}
/**
* Returns the message serial ID (unique for this connection)
*
* @return the message serial.
*/
public synchronized long getSerial() {
return serial;
}
/**
* If this is a reply to a message, this returns its serial.
*
* @return The reply serial, or 0 if it is not a reply.
*/
public long getReplySerial() {
Number l = (Number) getHeader(HeaderField.REPLY_SERIAL);
if (null == l) {
return 0;
}
return l.longValue();
}
/**
* Parses and returns the parameters to this message as an Object array.
*
* @return object array
* @throws DBusException on failure
*/
public Object[] getParameters() throws DBusException {
return getParameters(null);
}
/**
* Parses and returns the parameters to this message as an Object array.
*
* This method takes a list of Type[] where each entry of the list represents a constructor call.
* The constructor arguments are used to determine if a collection of array of
* primitive type should be used when calling the constructor.
*
* @param _constructorArgs list of desired constructor arguments
* @return object array
* @throws DBusException on failure
*/
public Object[] getParameters(List _constructorArgs) throws DBusException {
if (null == args && body != null) {
args = extractArgs(_constructorArgs);
}
return args;
}
/**
* Creates a object array containing all objects which should be used to call a constructor.
*
* @param _constructorArgs list of desired constructor arguments
* @return object array
* @throws DBusException on failure
*/
private Object[] extractArgs(List _constructorArgs) throws DBusException {
String sig = getSig();
ExtractOptions options = DEFAULT_OPTIONS;
if (_constructorArgs != null && !_constructorArgs.isEmpty()) {
List dataType = new ArrayList<>();
Marshalling.getJavaType(getSig(), dataType, -1);
options = new ExtractOptions(DEFAULT_OPTIONS.contained(), usesPrimitives(_constructorArgs, dataType));
}
if (sig != null && body != null && body.length != 0) {
return extract(sig, body, 0, options);
}
return new Object[0];
}
/**
* Compares a list of Type[] with a list of desired types.
* This is used to decide if an array of primitive types,
* an array of object types or a Collection/List of a object type is used in the constructor.
*
* @param _constructorArgs list of constructor types to check
* @param _dataType list of desired types
*
* @return List of ConstructorArgType
*/
static List usesPrimitives(List _constructorArgs, List _dataType) {
Logger logger = LoggerFactory.getLogger(Message.class);
OUTER: for (Type[] ptype : _constructorArgs) {
if (ptype.length == _dataType.size()) {
List argTypes = new ArrayList<>();
for (int i = 0; i < ptype.length; i++) {
logger.trace(">>>>>> Comparing {} with {}", ptype[i], _dataType.get(i));
// this is a list type and an array should be used
if (ptype[i] instanceof Class> constructorClz && constructorClz.isArray()
&& _dataType.get(i) instanceof ParameterizedType pt
&& pt.getRawType() == List.class
&& pt.getActualTypeArguments().length > 0
&& pt.getActualTypeArguments()[0] instanceof Class> sigExpectedClz) {
logger.trace("Found List type when array was required, trying to find proper array type");
if (PrimitiveUtils.isCompatiblePrimitiveOrWrapper(constructorClz.getComponentType(), sigExpectedClz)) {
ConstructorArgType type = constructorClz.getComponentType().isPrimitive() ? ConstructorArgType.PRIMITIVE_ARRAY : ConstructorArgType.ARRAY;
logger.trace("Selecting {} for parameter {} <=> {}", type, constructorClz.getComponentType(), sigExpectedClz);
argTypes.add(type);
} else {
logger.trace("List uses a different type than required. Found {}, required {}", constructorClz.getComponentType(), sigExpectedClz);
continue OUTER;
}
} else if (ptype[i] instanceof Class> clz
&& _dataType.get(i) instanceof ParameterizedType pt
&& clz.isAssignableFrom((Class>) pt.getRawType())
&& Collection.class.isAssignableFrom(clz)) {
logger.trace("Found compatible collection type: {} <=> {}", clz, pt.getRawType());
// the constructor wants some sort of collection
argTypes.add(ConstructorArgType.COLLECTION);
} else if (ptype[i] instanceof Class> constructorClz
&& _dataType.get(i) instanceof Class> sigExpectedClz
&& !sigExpectedClz.isAssignableFrom(constructorClz)
&& !PrimitiveUtils.isCompatiblePrimitiveOrWrapper(constructorClz, sigExpectedClz)
) {
logger.trace("Constructor data type mismatch, must be wrong constructor ({} != {})", constructorClz, sigExpectedClz);
// constructor class type does not match, must be wrong constructor, try next
continue OUTER;
} else {
// not a list/array and type matches, no conversion needed
logger.trace("Type {} is not an array type, skipping", ptype[i]);
argTypes.add(ConstructorArgType.NOT_ARRAY_TYPE);
}
}
return argTypes;
}
}
logger.trace("No matching constructor arguments found");
return List.of();
}
public void setArgs(Object[] _args) {
this.args = _args;
}
/**
* Warning, do not use this method unless you really know what you are doing.
*
* @param _source string
* @throws DBusException on error
*/
public void setSource(String _source) throws DBusException {
if (null != body) {
logger.trace("Setting source");
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("WireData before: {}", dumpWireData()));
wiredata = new byte[BUFFERINCREMENT][];
bufferuse = 0;
bytecounter = 0;
preallocate(12);
append("yyyyuu", big ? Endian.BIG : Endian.LITTLE, type, flags, protover, bodylen, getSerial());
headers[HeaderField.SENDER] = _source;
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("WireData first append: {}", dumpWireData()));
List newHeader = new ArrayList<>(headers.length);
for (int hIdx = 0; hIdx < headers.length; hIdx++) {
Object object = headers[hIdx];
if (object == null) {
continue;
}
if (hIdx == HeaderField.SIGNATURE) {
newHeader.add(createHeaderArgs(HeaderField.SIGNATURE, SIGNATURE_STRING, object));
} else {
newHeader.add(new Object[] {hIdx, object});
}
}
append("a(yv)", newHeader);
LoggingHelper.logIf(logger.isTraceEnabled(), () -> {
logger.trace("New header: {}", LoggingHelper.arraysVeryDeepString(newHeader.toArray()));
logger.trace("WireData after: {}", dumpWireData());
});
pad((byte) 8);
appendBytes(body);
}
}
/**
* Dumps the current content of {@link #wiredata} to String.
*
* @return String, maybe empty
* @since v4.2.2 - 2023-01-20
*/
String dumpWireData() {
StringBuilder sb = new StringBuilder(System.lineSeparator());
for (int i = 0; i < wiredata.length; i++) {
byte[] arr = wiredata[i];
if (arr != null) {
String prefix = "Wiredata[" + i + "]";
String format = Hexdump.format(arr, 80);
String[] split = format.split("\n");
sb.append(prefix).append(": ").append(split[0]).append(System.lineSeparator());
if (split.length > 1) {
sb.append(Arrays.stream(split)
.skip(1)
.map(s -> String.format("%s: %80s", prefix, s))
.collect(Collectors.joining(System.lineSeparator())));
sb.append(System.lineSeparator());
}
}
}
return sb.toString();
}
/**
* Type of this message.
* @return byte
*/
public byte getType() {
return type;
}
public byte getEndianess() {
if (endianWasSet) {
return big ? Endian.BIG : Endian.LITTLE;
}
return 0;
}
/**
* Creates a message header.
* Will automatically add the values to the current instances header map.
*
* @param _header header type (one of {@link HeaderField})
* @param _argType argument type (one of {@link ArgumentType})
* @param _value value
*
* @return Object array
*/
protected Object[] createHeaderArgs(byte _header, String _argType, Object _value) {
getHeader()[_header] = _value;
return new Object[] {
_header, new Object[] {
_argType, _value
}
};
}
/**
* Adds message padding and marshalling.
*
* @param _hargs
* @param _serial
* @param _sig
* @param _args
* @throws DBusException
*/
protected void padAndMarshall(List _hargs, long _serial, String _sig, Object... _args) throws DBusException {
byte[] blen = new byte[4];
appendBytes(blen);
append("ua(yv)", _serial, _hargs.toArray());
pad((byte) 8);
long c = getByteCounter();
if (null != _sig) {
append(_sig, _args);
}
logger.trace("Appended body, type: {} start: {} end: {} size: {}", _sig, c, getByteCounter(), getByteCounter() - c);
marshallint(getByteCounter() - c, blen, 0, 4);
LoggingHelper.logIf(logger.isTraceEnabled(), () -> logger.trace("marshalled size ({}): {}", blen, Hexdump.format(blen)));
}
/**
* Demarshalls an integer of a given width from a buffer.
*
* @param _buf The buffer to demarshall from.
* @param _ofs The offset to demarshall from.
* @param _endian The endianness to use in demarshalling.
* @param _width The byte-width of the int.
*
* @return long
*/
public static long demarshallint(byte[] _buf, int _ofs, byte _endian, int _width) {
return _endian == Endian.BIG ? demarshallintBig(_buf, _ofs, _width) : demarshallintLittle(_buf, _ofs, _width);
}
/**
* Demarshalls an integer of a given width from a buffer using big-endian format.
*
* @param _buf The buffer to demarshall from.
* @param _ofs The offset to demarshall from.
* @param _width The byte-width of the int.
* @return long
*/
public static long demarshallintBig(byte[] _buf, int _ofs, int _width) {
long l = 0;
for (int i = 0; i < _width; i++) {
l <<= 8;
l |= _buf[_ofs + i] & 0xFF;
}
return l;
}
/**
* Demarshalls an integer of a given width from a buffer using little-endian format.
*
* @param _buf The buffer to demarshall from.
* @param _ofs The offset to demarshall from.
* @param _width The byte-width of the int.
*
* @return long
*/
public static long demarshallintLittle(byte[] _buf, int _ofs, int _width) {
long l = 0;
for (int i = _width - 1; i >= 0; i--) {
l <<= 8;
l |= _buf[_ofs + i] & 0xFF;
}
return l;
}
/**
* Marshalls an integer of a given width into a buffer using big-endian format.
*
* @param _l The integer to marshall.
* @param _buf The buffer to marshall to.
* @param _ofs The offset to marshall to.
* @param _width The byte-width of the int.
*/
public static void marshallintBig(long _l, byte[] _buf, int _ofs, int _width) {
long l = _l;
for (int i = _width - 1; i >= 0; i--) {
_buf[i + _ofs] = (byte) (l & 0xFF);
l >>= 8;
}
}
/**
* Marshalls an integer of a given width into a buffer using little-endian format.
*
* @param _l The integer to marshall.
* @param _buf The buffer to demarshall to.
* @param _ofs The offset to demarshall to.
* @param _width The byte-width of the int.
*/
public static void marshallintLittle(long _l, byte[] _buf, int _ofs, int _width) {
long l = _l;
for (int i = 0; i < _width; i++) {
_buf[i + _ofs] = (byte) (l & 0xFF);
l >>= 8;
}
}
/**
* Return the alignment for a given type.
*
* @param _type type
* @return int
*/
public static int getAlignment(byte _type) {
return switch (_type) {
case 2, INT16, UINT16 -> 2;
case 4, BOOLEAN, FLOAT, INT32, UINT32, FILEDESCRIPTOR, STRING, OBJECT_PATH, ARRAY -> 4;
case 8, INT64, UINT64, DOUBLE, STRUCT, DICT_ENTRY, STRUCT1, DICT_ENTRY1, STRUCT2, DICT_ENTRY2 -> 8;
case 1, BYTE, SIGNATURE, VARIANT -> 1;
default -> 1;
};
}
/**
* Returns the name of the given header field.
*
* @param _field field
* @return string
*/
public static String getHeaderFieldName(byte _field) {
return switch (_field) {
case HeaderField.PATH -> "Path";
case HeaderField.INTERFACE -> "Interface";
case HeaderField.MEMBER -> "Member";
case HeaderField.ERROR_NAME -> "Error Name";
case HeaderField.REPLY_SERIAL -> "Reply Serial";
case HeaderField.DESTINATION -> "Destination";
case HeaderField.SENDER -> "Sender";
case HeaderField.SIGNATURE -> "Signature";
case HeaderField.UNIX_FDS -> "Unix FD";
default -> "Invalid";
};
}
/**
* Interface defining a method to extract a specific data type.
* For internal usage only.
*
* @since 4.2.0 - 2022-08-19
*/
@FunctionalInterface
interface ExtractMethod {
Object extractOne(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, ExtractOptions _options)
throws DBusException;
}
/**
* Additional options to optimize value extraction.
* @param contained boolean to indicate if nested lists should be resolved (false usually)
* @param arrayConvert use Collection, array or array of primitives
* @since 5.1.0 - 2024-05-18
*/
record ExtractOptions(
boolean contained,
List arrayConvert
) {
static ExtractOptions copyWithContainedFlag(ExtractOptions _toCopy, boolean _containedFlag) {
return new ExtractOptions(_containedFlag, _toCopy.arrayConvert());
}
}
enum ConstructorArgType {
PRIMITIVE_ARRAY, ARRAY, COLLECTION, NOT_ARRAY_TYPE;
}
}