blob: 1dbd1fa6480773883da788cae0f0d91f62a0e190 [file] [log] [blame]
Matthias Andreas Benkardb5d657a2022-02-03 21:14:30 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01005package eu.mulk.jgvariant.core;
6
7import static java.nio.ByteOrder.LITTLE_ENDIAN;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +01008import static java.util.stream.Collectors.toMap;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01009
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010010import java.lang.reflect.InvocationTargetException;
11import java.lang.reflect.RecordComponent;
12import java.nio.ByteBuffer;
13import java.nio.ByteOrder;
14import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010015import java.text.ParseException;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010016import java.util.AbstractMap.SimpleImmutableEntry;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010017import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.List;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010020import java.util.Map;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010021import java.util.Map.Entry;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010022import java.util.Objects;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010023import java.util.Optional;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010024import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010025import java.util.function.Predicate;
26import java.util.function.UnaryOperator;
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010027import org.apiguardian.api.API;
28import org.apiguardian.api.API.Status;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010029import org.jetbrains.annotations.Nullable;
30
31/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010032 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010033 *
34 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
35 * the type you wish to decode.
36 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010037 * <p><strong>Example</strong>
38 *
39 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
40 * {@code int}, you can use the following code:
41 *
42 * <pre>{@code
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010043 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010044 *
45 * var decoder =
46 * Decoder.ofArray(
47 * Decoder.ofStructure(
48 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010049 * Decoder.ofString(UTF_8),
50 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010051 *
52 * byte[] bytes = ...;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010053 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010054 * }</pre>
55 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010056 * @param <T> the type that the {@link Decoder} can decode.
57 */
58@SuppressWarnings("java:S1610")
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010059@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010060public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010061
62 private Decoder() {}
63
64 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010065 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
66 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010067 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
68 * important that the start and end boundaries of the passed byte slice correspond to the actual
69 * start and end of the serialized value. The format does generally not allow for the dynamic
70 * discovery of the end of the data structure.
71 *
72 * @param byteSlice a byte slice holding a serialized GVariant.
73 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010074 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
75 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010076 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010077 */
78 public abstract T decode(ByteBuffer byteSlice);
79
80 abstract byte alignment();
81
82 @Nullable
83 abstract Integer fixedSize();
84
85 final boolean hasFixedSize() {
86 return fixedSize() != null;
87 }
88
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010089 /**
90 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
91 *
92 * @param byteOrder the byte order to use.
93 * @return a new, decorated {@link Decoder}.
94 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010095 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +010096 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010097 }
98
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010099 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100100 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
101 *
102 * @param function the function to apply.
103 * @return a new, decorated {@link Decoder}.
104 * @see java.util.stream.Stream#map
105 */
106 public final <U> Decoder<U> map(Function<T, U> function) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100107 return new MappingDecoder<>(function);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100108 }
109
110 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100111 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
112 *
113 * @param function the function to apply.
114 * @return a new, decorated {@link Decoder}.
115 * @see java.util.stream.Stream#map
116 */
117 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> function) {
118 return new ContramappingDecoder(function);
119 }
120
121 /**
122 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
123 * condition on the input {@link ByteBuffer}.
124 *
125 * @param selector the predicate to use to determine the decoder to use.
126 * @return a new {@link Decoder}.
127 */
128 public static <U> Decoder<U> ofPredicate(
129 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
130 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
131 }
132
133 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100134 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100135 *
136 * @param elementDecoder a {@link Decoder} for the elements of the array.
137 * @param <U> the element type.
138 * @return a new {@link Decoder}.
139 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100140 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100141 return new ArrayDecoder<>(elementDecoder);
142 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100143
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100144 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100145 * Creates a {@link Decoder} for a {@code Dictionary} type.
146 *
147 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
148 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
149 * @return a new {@link Decoder}.
150 */
151 public static <K, V> Decoder<Map<K, V>> ofDictionary(
152 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
153 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
154 }
155
156 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100157 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
158 * primitive {@code byte[]} array.
159 *
160 * @return a new {@link Decoder}.
161 */
162 public static Decoder<byte[]> ofByteArray() {
163 return new ByteArrayDecoder();
164 }
165
166 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100167 * Creates a {@link Decoder} for a {@code Maybe} type.
168 *
169 * @param elementDecoder a {@link Decoder} for the contained element.
170 * @param <U> the element type.
171 * @return a new {@link Decoder}.
172 */
173 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
174 return new MaybeDecoder<>(elementDecoder);
175 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100176
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100177 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100178 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100179 *
180 * @param recordType the {@link Record} type that represents the components of the structure.
181 * @param componentDecoders a {@link Decoder} for each component of the structure.
182 * @param <U> the {@link Record} type that represents the components of the structure.
183 * @return a new {@link Decoder}.
184 */
185 public static <U extends Record> Decoder<U> ofStructure(
186 Class<U> recordType, Decoder<?>... componentDecoders) {
187 return new StructureDecoder<>(recordType, componentDecoders);
188 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100189
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100190 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100191 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
192 *
193 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
194 * more convenient.
195 *
196 * @param componentDecoders a {@link Decoder} for each component of the structure.
197 * @return a new {@link Decoder}.
198 */
199 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
200 return new TupleDecoder(componentDecoders);
201 }
202
203 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100204 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
205 * Map.Entry}.
206 *
207 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
208 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
209 * @return a new {@link Decoder}.
210 */
211 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
212 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
213 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
214 }
215
216 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100217 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100218 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100219 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100220 *
221 * <ul>
222 * <li>{@link Boolean}
223 * <li>{@link Byte}
224 * <li>{@link Short}
225 * <li>{@link Integer}
226 * <li>{@link Long}
227 * <li>{@link String}
228 * <li>{@link Optional} (a GVariant {@code Maybe} type)
229 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100230 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100231 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100232 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100233 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100234 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100235 *
236 * @return a new {@link Decoder}.
237 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100238 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100239 return new VariantDecoder();
240 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100241
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100242 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100243 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100244 *
245 * @return a new {@link Decoder}.
246 */
247 public static Decoder<Boolean> ofBoolean() {
248 return new BooleanDecoder();
249 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100250
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100251 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100252 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100253 *
254 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
255 * result of this method.
256 *
257 * @return a new {@link Decoder}.
258 */
259 public static Decoder<Byte> ofByte() {
260 return new ByteDecoder();
261 }
262
263 /**
264 * Creates a {@link Decoder} for the 16-bit {@code short} type.
265 *
266 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
267 * result of this method.
268 *
269 * @return a new {@link Decoder}.
270 */
271 public static Decoder<Short> ofShort() {
272 return new ShortDecoder();
273 }
274
275 /**
276 * Creates a {@link Decoder} for the 32-bit {@code int} type.
277 *
278 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
279 * result of this method.
280 *
281 * @return a new {@link Decoder}.
282 */
283 public static Decoder<Integer> ofInt() {
284 return new IntegerDecoder();
285 }
286
287 /**
288 * Creates a {@link Decoder} for the 64-bit {@code long} type.
289 *
290 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
291 * result of this method.
292 *
293 * @return a new {@link Decoder}.
294 */
295 public static Decoder<Long> ofLong() {
296 return new LongDecoder();
297 }
298
299 /**
300 * Creates a {@link Decoder} for the {@code double} type.
301 *
302 * @return a new {@link Decoder}.
303 */
304 public static Decoder<Double> ofDouble() {
305 return new DoubleDecoder();
306 }
307
308 /**
309 * Creates a {@link Decoder} for the {@link String} type.
310 *
311 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
312 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
313 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100314 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100315 * @return a new {@link Decoder}.
316 */
317 public static Decoder<String> ofString(Charset charset) {
318 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100319 }
320
321 private static int align(int offset, byte alignment) {
322 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
323 }
324
325 private static int getIntN(ByteBuffer byteSlice) {
326 var intBytes = new byte[4];
327 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
328 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
329 }
330
331 @SuppressWarnings("java:S3358")
332 private static int byteCount(int n) {
333 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
334 }
335
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100336 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100337
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100338 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100339
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100340 ArrayDecoder(Decoder<U> elementDecoder) {
341 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100342 }
343
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100344 @Override
345 public byte alignment() {
346 return elementDecoder.alignment();
347 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100348
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100349 @Override
350 @Nullable
351 Integer fixedSize() {
352 return null;
353 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100354
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100355 @Override
356 public List<U> decode(ByteBuffer byteSlice) {
357 List<U> elements;
358
359 var elementSize = elementDecoder.fixedSize();
360 if (elementSize != null) {
361 // A simple C-style array.
362 elements = new ArrayList<>(byteSlice.limit() / elementSize);
363 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100364 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100365 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100366 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100367 } else if (byteSlice.limit() == 0) {
368 // A degenerate zero-length array of variable width.
369 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100370 } else {
371 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100372 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100373 int lastFramingOffset =
374 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
375 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100376
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100377 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100378 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100379 for (int i = 0; i < elementCount; i++) {
380 int framingOffset =
381 getIntN(
382 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100383 elements.add(
384 elementDecoder.decode(
385 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100386 position = align(framingOffset, alignment());
387 }
388 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100389
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100390 return elements;
391 }
392 }
393
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100394 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
395
396 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
397
398 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
399 this.entryArrayDecoder =
400 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
401 }
402
403 @Override
404 public byte alignment() {
405 return entryArrayDecoder.alignment();
406 }
407
408 @Override
409 public Integer fixedSize() {
410 return entryArrayDecoder.fixedSize();
411 }
412
413 @Override
414 public Map<K, V> decode(ByteBuffer byteSlice) {
415 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
416 return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
417 }
418 }
419
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100420 private static class ByteArrayDecoder extends Decoder<byte[]> {
421
422 private static final int ELEMENT_SIZE = 1;
423
424 @Override
425 public byte alignment() {
426 return ELEMENT_SIZE;
427 }
428
429 @Override
430 @Nullable
431 Integer fixedSize() {
432 return null;
433 }
434
435 @Override
436 public byte[] decode(ByteBuffer byteSlice) {
437 // A simple C-style array.
438 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
439 byteSlice.get(elements);
440 return elements;
441 }
442 }
443
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100444 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
445
446 private final Decoder<U> elementDecoder;
447
448 MaybeDecoder(Decoder<U> elementDecoder) {
449 this.elementDecoder = elementDecoder;
450 }
451
452 @Override
453 public byte alignment() {
454 return elementDecoder.alignment();
455 }
456
457 @Override
458 @Nullable
459 Integer fixedSize() {
460 return null;
461 }
462
463 @Override
464 public Optional<U> decode(ByteBuffer byteSlice) {
465 if (!byteSlice.hasRemaining()) {
466 return Optional.empty();
467 } else {
468 if (!elementDecoder.hasFixedSize()) {
469 // Remove trailing zero byte.
470 byteSlice.limit(byteSlice.limit() - 1);
471 }
472
473 return Optional.of(elementDecoder.decode(byteSlice));
474 }
475 }
476 }
477
478 private static class StructureDecoder<U extends Record> extends Decoder<U> {
479
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100480 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100481 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100482
483 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
484 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100485 if (componentDecoders.length != recordComponents.length) {
486 throw new IllegalArgumentException(
487 "number of decoders (%d) does not match number of structure components (%d)"
488 .formatted(componentDecoders.length, recordComponents.length));
489 }
490
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100491 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100492 this.tupleDecoder = new TupleDecoder(componentDecoders);
493 }
494
495 @Override
496 public byte alignment() {
497 return tupleDecoder.alignment();
498 }
499
500 @Override
501 public Integer fixedSize() {
502 return tupleDecoder.fixedSize();
503 }
504
505 @Override
506 public U decode(ByteBuffer byteSlice) {
507 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
508
509 try {
510 var recordComponentTypes =
511 Arrays.stream(recordType.getRecordComponents())
512 .map(RecordComponent::getType)
513 .toArray(Class<?>[]::new);
514 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
515 return recordConstructor.newInstance(recordConstructorArguments);
516 } catch (NoSuchMethodException
517 | InstantiationException
518 | IllegalAccessException
519 | InvocationTargetException e) {
520 throw new IllegalStateException(e);
521 }
522 }
523 }
524
525 private static class TupleDecoder extends Decoder<Object[]> {
526
527 private final Decoder<?>[] componentDecoders;
528
529 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100530 this.componentDecoders = componentDecoders;
531 }
532
533 @Override
534 public byte alignment() {
535 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
536 }
537
538 @Override
539 public Integer fixedSize() {
540 int position = 0;
541 for (var componentDecoder : componentDecoders) {
542 var fixedComponentSize = componentDecoder.fixedSize();
543 if (fixedComponentSize == null) {
544 return null;
545 }
546
547 position = align(position, componentDecoder.alignment());
548 position += fixedComponentSize;
549 }
550
551 if (position == 0) {
552 return 1;
553 }
554
555 return align(position, alignment());
556 }
557
558 @Override
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100559 public Object[] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100560 int framingOffsetSize = byteCount(byteSlice.limit());
561
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100562 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100563
564 int position = 0;
565 int framingOffsetIndex = 0;
566 int componentIndex = 0;
567 for (var componentDecoder : componentDecoders) {
568 position = align(position, componentDecoder.alignment());
569
570 var fixedComponentSize = componentDecoder.fixedSize();
571 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100572 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100573 componentDecoder.decode(
574 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100575 position += fixedComponentSize;
576 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100577 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100578 // The last component never has a framing offset.
579 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100580 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100581 componentDecoder.decode(
582 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100583 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100584 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100585 int framingOffset =
586 getIntN(
587 byteSlice.slice(
588 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
589 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100590 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100591 componentDecoder.decode(
592 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100593 position = framingOffset;
594 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100595 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100596 }
597
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100598 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100599 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100600
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100601 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100602 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100603 }
604
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100605 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
606
607 private final TupleDecoder tupleDecoder;
608
609 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
610 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
611 }
612
613 @Override
614 public byte alignment() {
615 return tupleDecoder.alignment();
616 }
617
618 @Override
619 public Integer fixedSize() {
620 return tupleDecoder.fixedSize();
621 }
622
623 @Override
624 @SuppressWarnings("unchecked")
625 public Map.Entry<K, V> decode(ByteBuffer byteSlice) {
626 Object[] components = tupleDecoder.decode(byteSlice);
627 return new SimpleImmutableEntry<>((K) components[0], (V) components[1]);
628 }
629 }
630
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100631 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100632
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100633 @Override
634 public byte alignment() {
635 return 8;
636 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100637
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100638 @Override
639 @Nullable
640 Integer fixedSize() {
641 return null;
642 }
643
644 @Override
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100645 public Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100646 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
647 if (byteSlice.get(i) != 0) {
648 continue;
649 }
650
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100651 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100652 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100653
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100654 Signature signature;
655 try {
656 signature = Signature.parse(signatureBytes);
657 } catch (ParseException e) {
658 throw new IllegalArgumentException(e);
659 }
660
661 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100662 }
663
664 throw new IllegalArgumentException("variant signature not found");
665 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100666 }
667
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100668 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100669
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100670 @Override
671 public byte alignment() {
672 return 1;
673 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100674
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100675 @Override
676 public Integer fixedSize() {
677 return 1;
678 }
679
680 @Override
681 public Boolean decode(ByteBuffer byteSlice) {
682 return byteSlice.get() != 0;
683 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100684 }
685
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100686 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100687
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100688 @Override
689 public byte alignment() {
690 return 1;
691 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100692
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100693 @Override
694 public Integer fixedSize() {
695 return 1;
696 }
697
698 @Override
699 public Byte decode(ByteBuffer byteSlice) {
700 return byteSlice.get();
701 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100702 }
703
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100704 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100705
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100706 @Override
707 public byte alignment() {
708 return 2;
709 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100710
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100711 @Override
712 public Integer fixedSize() {
713 return 2;
714 }
715
716 @Override
717 public Short decode(ByteBuffer byteSlice) {
718 return byteSlice.getShort();
719 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100720 }
721
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100722 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100723
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100724 @Override
725 public byte alignment() {
726 return 4;
727 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100728
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100729 @Override
730 public Integer fixedSize() {
731 return 4;
732 }
733
734 @Override
735 public Integer decode(ByteBuffer byteSlice) {
736 return byteSlice.getInt();
737 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100738 }
739
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100740 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100741
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100742 @Override
743 public byte alignment() {
744 return 8;
745 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100746
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100747 @Override
748 public Integer fixedSize() {
749 return 8;
750 }
751
752 @Override
753 public Long decode(ByteBuffer byteSlice) {
754 return byteSlice.getLong();
755 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100756 }
757
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100758 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100759
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100760 @Override
761 public byte alignment() {
762 return 8;
763 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100764
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100765 @Override
766 public Integer fixedSize() {
767 return 8;
768 }
769
770 @Override
771 public Double decode(ByteBuffer byteSlice) {
772 return byteSlice.getDouble();
773 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100774 }
775
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100776 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100777
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100778 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100779
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100780 public StringDecoder(Charset charset) {
781 this.charset = charset;
782 }
783
784 @Override
785 public byte alignment() {
786 return 1;
787 }
788
789 @Override
790 @Nullable
791 Integer fixedSize() {
792 return null;
793 }
794
795 @Override
796 public String decode(ByteBuffer byteSlice) {
797 byteSlice.limit(byteSlice.limit() - 1);
798 return charset.decode(byteSlice).toString();
799 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100800 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100801
802 private class MappingDecoder<U> extends Decoder<U> {
803
804 private final Function<T, U> function;
805
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100806 MappingDecoder(Function<T, U> function) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100807 this.function = function;
808 }
809
810 @Override
811 public byte alignment() {
812 return Decoder.this.alignment();
813 }
814
815 @Override
816 public @Nullable Integer fixedSize() {
817 return Decoder.this.fixedSize();
818 }
819
820 @Override
821 public U decode(ByteBuffer byteSlice) {
822 return function.apply(Decoder.this.decode(byteSlice));
823 }
824 }
825
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100826 private class ContramappingDecoder extends Decoder<T> {
827
828 private final UnaryOperator<ByteBuffer> function;
829
830 ContramappingDecoder(UnaryOperator<ByteBuffer> function) {
831 this.function = function;
832 }
833
834 @Override
835 public byte alignment() {
836 return Decoder.this.alignment();
837 }
838
839 @Override
840 public @Nullable Integer fixedSize() {
841 return Decoder.this.fixedSize();
842 }
843
844 @Override
845 public T decode(ByteBuffer byteSlice) {
846 var transformedBuffer = function.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
847 return Decoder.this.decode(transformedBuffer);
848 }
849 }
850
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100851 private class ByteOrderFixingDecoder extends Decoder<T> {
852
853 private final ByteOrder byteOrder;
854
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100855 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100856 this.byteOrder = byteOrder;
857 }
858
859 @Override
860 public byte alignment() {
861 return Decoder.this.alignment();
862 }
863
864 @Override
865 public @Nullable Integer fixedSize() {
866 return Decoder.this.fixedSize();
867 }
868
869 @Override
870 public T decode(ByteBuffer byteSlice) {
871 var newByteSlice = byteSlice.duplicate();
872 newByteSlice.order(byteOrder);
873 return Decoder.this.decode(newByteSlice);
874 }
875 }
876
877 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
878 return byteSlice.slice(index, length).order(byteSlice.order());
879 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100880
881 private static class PredicateDecoder<U> extends Decoder<U> {
882
883 private final Predicate<ByteBuffer> selector;
884 private final Decoder<U> thenDecoder;
885 private final Decoder<U> elseDecoder;
886
887 PredicateDecoder(
888 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
889 this.selector = selector;
890 this.thenDecoder = thenDecoder;
891 this.elseDecoder = elseDecoder;
892 if (thenDecoder.alignment() != elseDecoder.alignment()) {
893 throw new IllegalArgumentException(
894 "incompatible alignments in predicate branches: then=%d, else=%d"
895 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
896 }
897
898 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
899 throw new IllegalArgumentException(
900 "incompatible sizes in predicate branches: then=%s, else=%s"
901 .formatted(thenDecoder.fixedSize(), elseDecoder.fixedSize()));
902 }
903 }
904
905 @Override
906 public byte alignment() {
907 return thenDecoder.alignment();
908 }
909
910 @Override
911 public @Nullable Integer fixedSize() {
912 return thenDecoder.fixedSize();
913 }
914
915 @Override
916 public U decode(ByteBuffer byteSlice) {
917 var b = selector.test(byteSlice);
918 byteSlice.rewind();
919 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
920 }
921 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100922}