ArrayDecoder.java
/*
* SPDX-FileCopyrightText: 2023-2025 Lucimber UG
* SPDX-License-Identifier: Apache-2.0
*/
package com.lucimber.dbus.codec.decoder;
import com.lucimber.dbus.type.DBusArray;
import com.lucimber.dbus.type.DBusSignature;
import com.lucimber.dbus.type.DBusType;
import com.lucimber.dbus.type.DBusUInt32;
import com.lucimber.dbus.type.Type;
import com.lucimber.dbus.type.TypeUtils;
import com.lucimber.dbus.util.LoggerUtils;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A decoder which unmarshals an array from the byte stream format used by D-Bus.
*
* @param <ValueT> The data type of the array.
* @see Decoder
* @see DBusArray
*/
public final class ArrayDecoder<ValueT extends DBusType>
implements Decoder<ByteBuffer, DBusArray<ValueT>> {
private static final Logger LOGGER = LoggerFactory.getLogger(ArrayDecoder.class);
private final DBusSignature signature;
/**
* Creates a new instance with mandatory parameters.
*
* @param signature the signature describing the array
*/
public ArrayDecoder(DBusSignature signature) {
this.signature = Objects.requireNonNull(signature, "signature must not be null");
if (!signature.isArray()) {
throw new IllegalArgumentException("signature must describe an array");
}
}
@Override
public DecoderResult<DBusArray<ValueT>> decode(ByteBuffer buffer, int offset)
throws DecoderException {
Objects.requireNonNull(buffer, "buffer must not be null");
try {
int consumedBytes = 0;
int arrayPadding = DecoderUtils.skipPadding(buffer, offset, Type.ARRAY);
consumedBytes += arrayPadding;
int lengthOffset = offset + consumedBytes;
DecoderResult<DBusUInt32> lengthResult =
new UInt32Decoder().decode(buffer, lengthOffset);
DBusUInt32 length = lengthResult.getValue();
consumedBytes += lengthResult.getConsumedBytes();
DecoderUtils.verifyArrayLength(length);
int arrayLength = length.getDelegate();
DBusSignature elementSig = signature.subContainer();
DBusArray<ValueT> array = new DBusArray<>(signature);
int elementBytes = 0;
while (Integer.compareUnsigned(elementBytes, arrayLength) < 0) {
int elementOffset = offset + consumedBytes + elementBytes;
DecoderResult<ValueT> elementResult =
DecoderUtils.decode(elementSig, buffer, elementOffset);
elementBytes += elementResult.getConsumedBytes();
array.add(elementResult.getValue());
}
consumedBytes += elementBytes;
int typePadding = 0;
if (array.isEmpty()) {
char c = elementSig.toString().charAt(0);
Type type =
TypeUtils.getTypeFromChar(c)
.orElseThrow(
() ->
new DecoderException(
"Cannot map char to alignment: " + c));
int typeOffset = offset + consumedBytes;
typePadding = DecoderUtils.skipPadding(buffer, typeOffset, type);
consumedBytes += typePadding;
}
DecoderResult<DBusArray<ValueT>> result = new DecoderResultImpl<>(consumedBytes, array);
LOGGER.debug(
LoggerUtils.MARSHALLING,
"ARRAY: {}; Offset: {}; Padding: {}; Consumed bytes: {};",
signature,
offset,
arrayPadding + typePadding,
consumedBytes);
return result;
} catch (Exception ex) {
throw new DecoderException("Could not decode ARRAY.", ex);
}
}
}