blob: d2f2403f051fbec786d16200b6d39d46a6197cb5 [file] [log] [blame]
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001package eu.mulk.jgvariant.core;
2
3import static java.nio.ByteOrder.LITTLE_ENDIAN;
4
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01005import java.lang.reflect.InvocationTargetException;
6import java.lang.reflect.RecordComponent;
7import java.nio.ByteBuffer;
8import java.nio.ByteOrder;
9import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010010import java.text.ParseException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010011import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.List;
14import java.util.Optional;
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010015import org.apiguardian.api.API;
16import org.apiguardian.api.API.Status;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010017import org.jetbrains.annotations.Nullable;
18
19/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010020 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010021 *
22 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
23 * the type you wish to decode.
24 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010025 * <p><strong>Example</strong>
26 *
27 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
28 * {@code int}, you can use the following code:
29 *
30 * <pre>{@code
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010031 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010032 *
33 * var decoder =
34 * Decoder.ofArray(
35 * Decoder.ofStructure(
36 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010037 * Decoder.ofString(UTF_8),
38 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010039 *
40 * byte[] bytes = ...;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010041 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010042 * }</pre>
43 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010044 * @param <T> the type that the {@link Decoder} can decode.
45 */
46@SuppressWarnings("java:S1610")
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010047@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010048public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010049
50 private Decoder() {}
51
52 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010053 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
54 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010055 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
56 * important that the start and end boundaries of the passed byte slice correspond to the actual
57 * start and end of the serialized value. The format does generally not allow for the dynamic
58 * discovery of the end of the data structure.
59 *
60 * @param byteSlice a byte slice holding a serialized GVariant.
61 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010062 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
63 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010064 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010065 */
66 public abstract T decode(ByteBuffer byteSlice);
67
68 abstract byte alignment();
69
70 @Nullable
71 abstract Integer fixedSize();
72
73 final boolean hasFixedSize() {
74 return fixedSize() != null;
75 }
76
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010077 /**
78 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
79 *
80 * @param byteOrder the byte order to use.
81 * @return a new, decorated {@link Decoder}.
82 */
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010083 public Decoder<T> withByteOrder(ByteOrder byteOrder) {
84 var delegate = this;
85
86 return new Decoder<>() {
87 @Override
88 public byte alignment() {
89 return delegate.alignment();
90 }
91
92 @Override
93 public @Nullable Integer fixedSize() {
94 return delegate.fixedSize();
95 }
96
97 @Override
98 public T decode(ByteBuffer byteSlice) {
99 byteSlice.order(byteOrder);
100 return delegate.decode(byteSlice);
101 }
102 };
103 }
104
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100105 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100106 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100107 *
108 * @param elementDecoder a {@link Decoder} for the elements of the array.
109 * @param <U> the element type.
110 * @return a new {@link Decoder}.
111 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100112 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100113 return new ArrayDecoder<>(elementDecoder);
114 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100115
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100116 /**
117 * Creates a {@link Decoder} for a {@code Maybe} type.
118 *
119 * @param elementDecoder a {@link Decoder} for the contained element.
120 * @param <U> the element type.
121 * @return a new {@link Decoder}.
122 */
123 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
124 return new MaybeDecoder<>(elementDecoder);
125 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100126
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100127 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100128 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100129 *
130 * @param recordType the {@link Record} type that represents the components of the structure.
131 * @param componentDecoders a {@link Decoder} for each component of the structure.
132 * @param <U> the {@link Record} type that represents the components of the structure.
133 * @return a new {@link Decoder}.
134 */
135 public static <U extends Record> Decoder<U> ofStructure(
136 Class<U> recordType, Decoder<?>... componentDecoders) {
137 return new StructureDecoder<>(recordType, componentDecoders);
138 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100139
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100140 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100141 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
142 *
143 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
144 * more convenient.
145 *
146 * @param componentDecoders a {@link Decoder} for each component of the structure.
147 * @return a new {@link Decoder}.
148 */
149 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
150 return new TupleDecoder(componentDecoders);
151 }
152
153 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100154 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100155 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100156 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100157 *
158 * <ul>
159 * <li>{@link Boolean}
160 * <li>{@link Byte}
161 * <li>{@link Short}
162 * <li>{@link Integer}
163 * <li>{@link Long}
164 * <li>{@link String}
165 * <li>{@link Optional} (a GVariant {@code Maybe} type)
166 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100167 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100168 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100169 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100170 *
171 * @return a new {@link Decoder}.
172 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100173 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100174 return new VariantDecoder();
175 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100176
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100177 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100178 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100179 *
180 * @return a new {@link Decoder}.
181 */
182 public static Decoder<Boolean> ofBoolean() {
183 return new BooleanDecoder();
184 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100185
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100186 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100187 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100188 *
189 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
190 * result of this method.
191 *
192 * @return a new {@link Decoder}.
193 */
194 public static Decoder<Byte> ofByte() {
195 return new ByteDecoder();
196 }
197
198 /**
199 * Creates a {@link Decoder} for the 16-bit {@code short} type.
200 *
201 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
202 * result of this method.
203 *
204 * @return a new {@link Decoder}.
205 */
206 public static Decoder<Short> ofShort() {
207 return new ShortDecoder();
208 }
209
210 /**
211 * Creates a {@link Decoder} for the 32-bit {@code int} type.
212 *
213 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
214 * result of this method.
215 *
216 * @return a new {@link Decoder}.
217 */
218 public static Decoder<Integer> ofInt() {
219 return new IntegerDecoder();
220 }
221
222 /**
223 * Creates a {@link Decoder} for the 64-bit {@code long} type.
224 *
225 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
226 * result of this method.
227 *
228 * @return a new {@link Decoder}.
229 */
230 public static Decoder<Long> ofLong() {
231 return new LongDecoder();
232 }
233
234 /**
235 * Creates a {@link Decoder} for the {@code double} type.
236 *
237 * @return a new {@link Decoder}.
238 */
239 public static Decoder<Double> ofDouble() {
240 return new DoubleDecoder();
241 }
242
243 /**
244 * Creates a {@link Decoder} for the {@link String} type.
245 *
246 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
247 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
248 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100249 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100250 * @return a new {@link Decoder}.
251 */
252 public static Decoder<String> ofString(Charset charset) {
253 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100254 }
255
256 private static int align(int offset, byte alignment) {
257 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
258 }
259
260 private static int getIntN(ByteBuffer byteSlice) {
261 var intBytes = new byte[4];
262 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
263 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
264 }
265
266 @SuppressWarnings("java:S3358")
267 private static int byteCount(int n) {
268 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
269 }
270
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100271 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100272
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100273 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100274
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100275 ArrayDecoder(Decoder<U> elementDecoder) {
276 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100277 }
278
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100279 @Override
280 public byte alignment() {
281 return elementDecoder.alignment();
282 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100283
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100284 @Override
285 @Nullable
286 Integer fixedSize() {
287 return null;
288 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100289
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100290 @Override
291 public List<U> decode(ByteBuffer byteSlice) {
292 List<U> elements;
293
294 var elementSize = elementDecoder.fixedSize();
295 if (elementSize != null) {
296 // A simple C-style array.
297 elements = new ArrayList<>(byteSlice.limit() / elementSize);
298 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
299 var element = elementDecoder.decode(byteSlice.slice(i, elementSize));
300 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100301 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100302 } else {
303 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100304 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100305 int lastFramingOffset =
306 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
307 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100308
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100309 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100310 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100311 for (int i = 0; i < elementCount; i++) {
312 int framingOffset =
313 getIntN(
314 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
315 elements.add(elementDecoder.decode(byteSlice.slice(position, framingOffset - position)));
316 position = align(framingOffset, alignment());
317 }
318 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100319
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100320 return elements;
321 }
322 }
323
324 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
325
326 private final Decoder<U> elementDecoder;
327
328 MaybeDecoder(Decoder<U> elementDecoder) {
329 this.elementDecoder = elementDecoder;
330 }
331
332 @Override
333 public byte alignment() {
334 return elementDecoder.alignment();
335 }
336
337 @Override
338 @Nullable
339 Integer fixedSize() {
340 return null;
341 }
342
343 @Override
344 public Optional<U> decode(ByteBuffer byteSlice) {
345 if (!byteSlice.hasRemaining()) {
346 return Optional.empty();
347 } else {
348 if (!elementDecoder.hasFixedSize()) {
349 // Remove trailing zero byte.
350 byteSlice.limit(byteSlice.limit() - 1);
351 }
352
353 return Optional.of(elementDecoder.decode(byteSlice));
354 }
355 }
356 }
357
358 private static class StructureDecoder<U extends Record> extends Decoder<U> {
359
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100360 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100361 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100362
363 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
364 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100365 if (componentDecoders.length != recordComponents.length) {
366 throw new IllegalArgumentException(
367 "number of decoders (%d) does not match number of structure components (%d)"
368 .formatted(componentDecoders.length, recordComponents.length));
369 }
370
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100371 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100372 this.tupleDecoder = new TupleDecoder(componentDecoders);
373 }
374
375 @Override
376 public byte alignment() {
377 return tupleDecoder.alignment();
378 }
379
380 @Override
381 public Integer fixedSize() {
382 return tupleDecoder.fixedSize();
383 }
384
385 @Override
386 public U decode(ByteBuffer byteSlice) {
387 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
388
389 try {
390 var recordComponentTypes =
391 Arrays.stream(recordType.getRecordComponents())
392 .map(RecordComponent::getType)
393 .toArray(Class<?>[]::new);
394 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
395 return recordConstructor.newInstance(recordConstructorArguments);
396 } catch (NoSuchMethodException
397 | InstantiationException
398 | IllegalAccessException
399 | InvocationTargetException e) {
400 throw new IllegalStateException(e);
401 }
402 }
403 }
404
405 private static class TupleDecoder extends Decoder<Object[]> {
406
407 private final Decoder<?>[] componentDecoders;
408
409 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100410 this.componentDecoders = componentDecoders;
411 }
412
413 @Override
414 public byte alignment() {
415 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
416 }
417
418 @Override
419 public Integer fixedSize() {
420 int position = 0;
421 for (var componentDecoder : componentDecoders) {
422 var fixedComponentSize = componentDecoder.fixedSize();
423 if (fixedComponentSize == null) {
424 return null;
425 }
426
427 position = align(position, componentDecoder.alignment());
428 position += fixedComponentSize;
429 }
430
431 if (position == 0) {
432 return 1;
433 }
434
435 return align(position, alignment());
436 }
437
438 @Override
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100439 public Object[] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100440 int framingOffsetSize = byteCount(byteSlice.limit());
441
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100442 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100443
444 int position = 0;
445 int framingOffsetIndex = 0;
446 int componentIndex = 0;
447 for (var componentDecoder : componentDecoders) {
448 position = align(position, componentDecoder.alignment());
449
450 var fixedComponentSize = componentDecoder.fixedSize();
451 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100452 objects[componentIndex] =
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100453 componentDecoder.decode(byteSlice.slice(position, fixedComponentSize));
454 position += fixedComponentSize;
455 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100456 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100457 // The last component never has a framing offset.
458 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100459 objects[componentIndex] =
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100460 componentDecoder.decode(byteSlice.slice(position, endPosition - position));
461 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100462 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100463 int framingOffset =
464 getIntN(
465 byteSlice.slice(
466 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
467 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100468 objects[componentIndex] =
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100469 componentDecoder.decode(byteSlice.slice(position, framingOffset - position));
470 position = framingOffset;
471 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100472 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100473 }
474
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100475 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100476 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100477
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100478 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100479 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100480 }
481
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100482 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100483
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100484 @Override
485 public byte alignment() {
486 return 8;
487 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100488
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100489 @Override
490 @Nullable
491 Integer fixedSize() {
492 return null;
493 }
494
495 @Override
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100496 public Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100497 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
498 if (byteSlice.get(i) != 0) {
499 continue;
500 }
501
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100502 var dataBytes = byteSlice.slice(0, i);
503 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100504
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100505 Signature signature;
506 try {
507 signature = Signature.parse(signatureBytes);
508 } catch (ParseException e) {
509 throw new IllegalArgumentException(e);
510 }
511
512 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100513 }
514
515 throw new IllegalArgumentException("variant signature not found");
516 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100517 }
518
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100519 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100520
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100521 @Override
522 public byte alignment() {
523 return 1;
524 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100525
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100526 @Override
527 public Integer fixedSize() {
528 return 1;
529 }
530
531 @Override
532 public Boolean decode(ByteBuffer byteSlice) {
533 return byteSlice.get() != 0;
534 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100535 }
536
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100537 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100538
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100539 @Override
540 public byte alignment() {
541 return 1;
542 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100543
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100544 @Override
545 public Integer fixedSize() {
546 return 1;
547 }
548
549 @Override
550 public Byte decode(ByteBuffer byteSlice) {
551 return byteSlice.get();
552 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100553 }
554
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100555 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100556
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100557 @Override
558 public byte alignment() {
559 return 2;
560 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100561
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100562 @Override
563 public Integer fixedSize() {
564 return 2;
565 }
566
567 @Override
568 public Short decode(ByteBuffer byteSlice) {
569 return byteSlice.getShort();
570 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100571 }
572
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100573 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100574
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100575 @Override
576 public byte alignment() {
577 return 4;
578 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100579
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100580 @Override
581 public Integer fixedSize() {
582 return 4;
583 }
584
585 @Override
586 public Integer decode(ByteBuffer byteSlice) {
587 return byteSlice.getInt();
588 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100589 }
590
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100591 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100592
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100593 @Override
594 public byte alignment() {
595 return 8;
596 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100597
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100598 @Override
599 public Integer fixedSize() {
600 return 8;
601 }
602
603 @Override
604 public Long decode(ByteBuffer byteSlice) {
605 return byteSlice.getLong();
606 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100607 }
608
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100609 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100610
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100611 @Override
612 public byte alignment() {
613 return 8;
614 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100615
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100616 @Override
617 public Integer fixedSize() {
618 return 8;
619 }
620
621 @Override
622 public Double decode(ByteBuffer byteSlice) {
623 return byteSlice.getDouble();
624 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100625 }
626
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100627 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100628
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100629 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100630
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100631 public StringDecoder(Charset charset) {
632 this.charset = charset;
633 }
634
635 @Override
636 public byte alignment() {
637 return 1;
638 }
639
640 @Override
641 @Nullable
642 Integer fixedSize() {
643 return null;
644 }
645
646 @Override
647 public String decode(ByteBuffer byteSlice) {
648 byteSlice.limit(byteSlice.limit() - 1);
649 return charset.decode(byteSlice).toString();
650 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100651 }
652}