blob: 33b048057f34a897024761a63acf86864955fe10 [file] [log] [blame]
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001package eu.mulk.jgvariant.core;
2
3import static java.nio.ByteOrder.LITTLE_ENDIAN;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +01004import static java.util.stream.Collectors.toMap;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01005
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01006import java.lang.reflect.InvocationTargetException;
7import java.lang.reflect.RecordComponent;
8import java.nio.ByteBuffer;
9import java.nio.ByteOrder;
10import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010011import java.text.ParseException;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010012import java.util.AbstractMap.SimpleImmutableEntry;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010013import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.List;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010016import java.util.Map;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010017import java.util.Map.Entry;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010018import java.util.Optional;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010019import java.util.function.Function;
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010020import org.apiguardian.api.API;
21import org.apiguardian.api.API.Status;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010022import org.jetbrains.annotations.Nullable;
23
24/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010025 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010026 *
27 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
28 * the type you wish to decode.
29 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010030 * <p><strong>Example</strong>
31 *
32 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
33 * {@code int}, you can use the following code:
34 *
35 * <pre>{@code
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010036 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010037 *
38 * var decoder =
39 * Decoder.ofArray(
40 * Decoder.ofStructure(
41 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010042 * Decoder.ofString(UTF_8),
43 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010044 *
45 * byte[] bytes = ...;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010046 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010047 * }</pre>
48 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010049 * @param <T> the type that the {@link Decoder} can decode.
50 */
51@SuppressWarnings("java:S1610")
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010052@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010053public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010054
55 private Decoder() {}
56
57 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010058 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
59 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010060 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
61 * important that the start and end boundaries of the passed byte slice correspond to the actual
62 * start and end of the serialized value. The format does generally not allow for the dynamic
63 * discovery of the end of the data structure.
64 *
65 * @param byteSlice a byte slice holding a serialized GVariant.
66 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010067 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
68 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010069 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010070 */
71 public abstract T decode(ByteBuffer byteSlice);
72
73 abstract byte alignment();
74
75 @Nullable
76 abstract Integer fixedSize();
77
78 final boolean hasFixedSize() {
79 return fixedSize() != null;
80 }
81
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010082 /**
83 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
84 *
85 * @param byteOrder the byte order to use.
86 * @return a new, decorated {@link Decoder}.
87 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010088 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +010089 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010090 }
91
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010092 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010093 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
94 *
95 * @param function the function to apply.
96 * @return a new, decorated {@link Decoder}.
97 * @see java.util.stream.Stream#map
98 */
99 public final <U> Decoder<U> map(Function<T, U> function) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100100 return new MappingDecoder<>(function);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100101 }
102
103 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100104 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100105 *
106 * @param elementDecoder a {@link Decoder} for the elements of the array.
107 * @param <U> the element type.
108 * @return a new {@link Decoder}.
109 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100110 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100111 return new ArrayDecoder<>(elementDecoder);
112 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100113
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100114 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100115 * Creates a {@link Decoder} for a {@code Dictionary} type.
116 *
117 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
118 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
119 * @return a new {@link Decoder}.
120 */
121 public static <K, V> Decoder<Map<K, V>> ofDictionary(
122 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
123 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
124 }
125
126 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100127 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
128 * primitive {@code byte[]} array.
129 *
130 * @return a new {@link Decoder}.
131 */
132 public static Decoder<byte[]> ofByteArray() {
133 return new ByteArrayDecoder();
134 }
135
136 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100137 * Creates a {@link Decoder} for a {@code Maybe} type.
138 *
139 * @param elementDecoder a {@link Decoder} for the contained element.
140 * @param <U> the element type.
141 * @return a new {@link Decoder}.
142 */
143 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
144 return new MaybeDecoder<>(elementDecoder);
145 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100146
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100147 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100148 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100149 *
150 * @param recordType the {@link Record} type that represents the components of the structure.
151 * @param componentDecoders a {@link Decoder} for each component of the structure.
152 * @param <U> the {@link Record} type that represents the components of the structure.
153 * @return a new {@link Decoder}.
154 */
155 public static <U extends Record> Decoder<U> ofStructure(
156 Class<U> recordType, Decoder<?>... componentDecoders) {
157 return new StructureDecoder<>(recordType, componentDecoders);
158 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100159
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100160 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100161 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
162 *
163 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
164 * more convenient.
165 *
166 * @param componentDecoders a {@link Decoder} for each component of the structure.
167 * @return a new {@link Decoder}.
168 */
169 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
170 return new TupleDecoder(componentDecoders);
171 }
172
173 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100174 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
175 * Map.Entry}.
176 *
177 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
178 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
179 * @return a new {@link Decoder}.
180 */
181 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
182 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
183 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
184 }
185
186 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100187 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100188 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100189 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100190 *
191 * <ul>
192 * <li>{@link Boolean}
193 * <li>{@link Byte}
194 * <li>{@link Short}
195 * <li>{@link Integer}
196 * <li>{@link Long}
197 * <li>{@link String}
198 * <li>{@link Optional} (a GVariant {@code Maybe} type)
199 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100200 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100201 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100202 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100203 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100204 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100205 *
206 * @return a new {@link Decoder}.
207 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100208 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100209 return new VariantDecoder();
210 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100211
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100212 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100213 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100214 *
215 * @return a new {@link Decoder}.
216 */
217 public static Decoder<Boolean> ofBoolean() {
218 return new BooleanDecoder();
219 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100220
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100221 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100222 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100223 *
224 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
225 * result of this method.
226 *
227 * @return a new {@link Decoder}.
228 */
229 public static Decoder<Byte> ofByte() {
230 return new ByteDecoder();
231 }
232
233 /**
234 * Creates a {@link Decoder} for the 16-bit {@code short} type.
235 *
236 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
237 * result of this method.
238 *
239 * @return a new {@link Decoder}.
240 */
241 public static Decoder<Short> ofShort() {
242 return new ShortDecoder();
243 }
244
245 /**
246 * Creates a {@link Decoder} for the 32-bit {@code int} type.
247 *
248 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
249 * result of this method.
250 *
251 * @return a new {@link Decoder}.
252 */
253 public static Decoder<Integer> ofInt() {
254 return new IntegerDecoder();
255 }
256
257 /**
258 * Creates a {@link Decoder} for the 64-bit {@code long} type.
259 *
260 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
261 * result of this method.
262 *
263 * @return a new {@link Decoder}.
264 */
265 public static Decoder<Long> ofLong() {
266 return new LongDecoder();
267 }
268
269 /**
270 * Creates a {@link Decoder} for the {@code double} type.
271 *
272 * @return a new {@link Decoder}.
273 */
274 public static Decoder<Double> ofDouble() {
275 return new DoubleDecoder();
276 }
277
278 /**
279 * Creates a {@link Decoder} for the {@link String} type.
280 *
281 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
282 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
283 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100284 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100285 * @return a new {@link Decoder}.
286 */
287 public static Decoder<String> ofString(Charset charset) {
288 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100289 }
290
291 private static int align(int offset, byte alignment) {
292 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
293 }
294
295 private static int getIntN(ByteBuffer byteSlice) {
296 var intBytes = new byte[4];
297 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
298 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
299 }
300
301 @SuppressWarnings("java:S3358")
302 private static int byteCount(int n) {
303 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
304 }
305
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100306 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100307
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100308 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100309
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100310 ArrayDecoder(Decoder<U> elementDecoder) {
311 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100312 }
313
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100314 @Override
315 public byte alignment() {
316 return elementDecoder.alignment();
317 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100318
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100319 @Override
320 @Nullable
321 Integer fixedSize() {
322 return null;
323 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100324
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100325 @Override
326 public List<U> decode(ByteBuffer byteSlice) {
327 List<U> elements;
328
329 var elementSize = elementDecoder.fixedSize();
330 if (elementSize != null) {
331 // A simple C-style array.
332 elements = new ArrayList<>(byteSlice.limit() / elementSize);
333 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100334 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100335 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100336 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100337 } else if (byteSlice.limit() == 0) {
338 // A degenerate zero-length array of variable width.
339 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100340 } else {
341 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100342 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100343 int lastFramingOffset =
344 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
345 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100346
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100347 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100348 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100349 for (int i = 0; i < elementCount; i++) {
350 int framingOffset =
351 getIntN(
352 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100353 elements.add(
354 elementDecoder.decode(
355 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100356 position = align(framingOffset, alignment());
357 }
358 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100359
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100360 return elements;
361 }
362 }
363
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100364 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
365
366 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
367
368 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
369 this.entryArrayDecoder =
370 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
371 }
372
373 @Override
374 public byte alignment() {
375 return entryArrayDecoder.alignment();
376 }
377
378 @Override
379 public Integer fixedSize() {
380 return entryArrayDecoder.fixedSize();
381 }
382
383 @Override
384 public Map<K, V> decode(ByteBuffer byteSlice) {
385 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
386 return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
387 }
388 }
389
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100390 private static class ByteArrayDecoder extends Decoder<byte[]> {
391
392 private static final int ELEMENT_SIZE = 1;
393
394 @Override
395 public byte alignment() {
396 return ELEMENT_SIZE;
397 }
398
399 @Override
400 @Nullable
401 Integer fixedSize() {
402 return null;
403 }
404
405 @Override
406 public byte[] decode(ByteBuffer byteSlice) {
407 // A simple C-style array.
408 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
409 byteSlice.get(elements);
410 return elements;
411 }
412 }
413
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100414 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
415
416 private final Decoder<U> elementDecoder;
417
418 MaybeDecoder(Decoder<U> elementDecoder) {
419 this.elementDecoder = elementDecoder;
420 }
421
422 @Override
423 public byte alignment() {
424 return elementDecoder.alignment();
425 }
426
427 @Override
428 @Nullable
429 Integer fixedSize() {
430 return null;
431 }
432
433 @Override
434 public Optional<U> decode(ByteBuffer byteSlice) {
435 if (!byteSlice.hasRemaining()) {
436 return Optional.empty();
437 } else {
438 if (!elementDecoder.hasFixedSize()) {
439 // Remove trailing zero byte.
440 byteSlice.limit(byteSlice.limit() - 1);
441 }
442
443 return Optional.of(elementDecoder.decode(byteSlice));
444 }
445 }
446 }
447
448 private static class StructureDecoder<U extends Record> extends Decoder<U> {
449
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100450 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100451 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100452
453 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
454 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100455 if (componentDecoders.length != recordComponents.length) {
456 throw new IllegalArgumentException(
457 "number of decoders (%d) does not match number of structure components (%d)"
458 .formatted(componentDecoders.length, recordComponents.length));
459 }
460
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100461 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100462 this.tupleDecoder = new TupleDecoder(componentDecoders);
463 }
464
465 @Override
466 public byte alignment() {
467 return tupleDecoder.alignment();
468 }
469
470 @Override
471 public Integer fixedSize() {
472 return tupleDecoder.fixedSize();
473 }
474
475 @Override
476 public U decode(ByteBuffer byteSlice) {
477 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
478
479 try {
480 var recordComponentTypes =
481 Arrays.stream(recordType.getRecordComponents())
482 .map(RecordComponent::getType)
483 .toArray(Class<?>[]::new);
484 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
485 return recordConstructor.newInstance(recordConstructorArguments);
486 } catch (NoSuchMethodException
487 | InstantiationException
488 | IllegalAccessException
489 | InvocationTargetException e) {
490 throw new IllegalStateException(e);
491 }
492 }
493 }
494
495 private static class TupleDecoder extends Decoder<Object[]> {
496
497 private final Decoder<?>[] componentDecoders;
498
499 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100500 this.componentDecoders = componentDecoders;
501 }
502
503 @Override
504 public byte alignment() {
505 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
506 }
507
508 @Override
509 public Integer fixedSize() {
510 int position = 0;
511 for (var componentDecoder : componentDecoders) {
512 var fixedComponentSize = componentDecoder.fixedSize();
513 if (fixedComponentSize == null) {
514 return null;
515 }
516
517 position = align(position, componentDecoder.alignment());
518 position += fixedComponentSize;
519 }
520
521 if (position == 0) {
522 return 1;
523 }
524
525 return align(position, alignment());
526 }
527
528 @Override
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100529 public Object[] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100530 int framingOffsetSize = byteCount(byteSlice.limit());
531
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100532 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100533
534 int position = 0;
535 int framingOffsetIndex = 0;
536 int componentIndex = 0;
537 for (var componentDecoder : componentDecoders) {
538 position = align(position, componentDecoder.alignment());
539
540 var fixedComponentSize = componentDecoder.fixedSize();
541 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100542 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100543 componentDecoder.decode(
544 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100545 position += fixedComponentSize;
546 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100547 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100548 // The last component never has a framing offset.
549 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100550 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100551 componentDecoder.decode(
552 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100553 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100554 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100555 int framingOffset =
556 getIntN(
557 byteSlice.slice(
558 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
559 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100560 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100561 componentDecoder.decode(
562 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100563 position = framingOffset;
564 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100565 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100566 }
567
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100568 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100569 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100570
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100571 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100572 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100573 }
574
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100575 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
576
577 private final TupleDecoder tupleDecoder;
578
579 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
580 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
581 }
582
583 @Override
584 public byte alignment() {
585 return tupleDecoder.alignment();
586 }
587
588 @Override
589 public Integer fixedSize() {
590 return tupleDecoder.fixedSize();
591 }
592
593 @Override
594 @SuppressWarnings("unchecked")
595 public Map.Entry<K, V> decode(ByteBuffer byteSlice) {
596 Object[] components = tupleDecoder.decode(byteSlice);
597 return new SimpleImmutableEntry<>((K) components[0], (V) components[1]);
598 }
599 }
600
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100601 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100602
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100603 @Override
604 public byte alignment() {
605 return 8;
606 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100607
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100608 @Override
609 @Nullable
610 Integer fixedSize() {
611 return null;
612 }
613
614 @Override
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100615 public Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100616 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
617 if (byteSlice.get(i) != 0) {
618 continue;
619 }
620
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100621 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100622 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100623
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100624 Signature signature;
625 try {
626 signature = Signature.parse(signatureBytes);
627 } catch (ParseException e) {
628 throw new IllegalArgumentException(e);
629 }
630
631 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100632 }
633
634 throw new IllegalArgumentException("variant signature not found");
635 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100636 }
637
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100638 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100639
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100640 @Override
641 public byte alignment() {
642 return 1;
643 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100644
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100645 @Override
646 public Integer fixedSize() {
647 return 1;
648 }
649
650 @Override
651 public Boolean decode(ByteBuffer byteSlice) {
652 return byteSlice.get() != 0;
653 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100654 }
655
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100656 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100657
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100658 @Override
659 public byte alignment() {
660 return 1;
661 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100662
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100663 @Override
664 public Integer fixedSize() {
665 return 1;
666 }
667
668 @Override
669 public Byte decode(ByteBuffer byteSlice) {
670 return byteSlice.get();
671 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100672 }
673
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100674 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100675
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100676 @Override
677 public byte alignment() {
678 return 2;
679 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100680
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100681 @Override
682 public Integer fixedSize() {
683 return 2;
684 }
685
686 @Override
687 public Short decode(ByteBuffer byteSlice) {
688 return byteSlice.getShort();
689 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100690 }
691
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100692 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100693
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100694 @Override
695 public byte alignment() {
696 return 4;
697 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100698
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100699 @Override
700 public Integer fixedSize() {
701 return 4;
702 }
703
704 @Override
705 public Integer decode(ByteBuffer byteSlice) {
706 return byteSlice.getInt();
707 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100708 }
709
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100710 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100711
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100712 @Override
713 public byte alignment() {
714 return 8;
715 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100716
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100717 @Override
718 public Integer fixedSize() {
719 return 8;
720 }
721
722 @Override
723 public Long decode(ByteBuffer byteSlice) {
724 return byteSlice.getLong();
725 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100726 }
727
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100728 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100729
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100730 @Override
731 public byte alignment() {
732 return 8;
733 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100734
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100735 @Override
736 public Integer fixedSize() {
737 return 8;
738 }
739
740 @Override
741 public Double decode(ByteBuffer byteSlice) {
742 return byteSlice.getDouble();
743 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100744 }
745
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100746 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100747
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100748 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100749
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100750 public StringDecoder(Charset charset) {
751 this.charset = charset;
752 }
753
754 @Override
755 public byte alignment() {
756 return 1;
757 }
758
759 @Override
760 @Nullable
761 Integer fixedSize() {
762 return null;
763 }
764
765 @Override
766 public String decode(ByteBuffer byteSlice) {
767 byteSlice.limit(byteSlice.limit() - 1);
768 return charset.decode(byteSlice).toString();
769 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100770 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100771
772 private class MappingDecoder<U> extends Decoder<U> {
773
774 private final Function<T, U> function;
775
776 public MappingDecoder(Function<T, U> function) {
777 this.function = function;
778 }
779
780 @Override
781 public byte alignment() {
782 return Decoder.this.alignment();
783 }
784
785 @Override
786 public @Nullable Integer fixedSize() {
787 return Decoder.this.fixedSize();
788 }
789
790 @Override
791 public U decode(ByteBuffer byteSlice) {
792 return function.apply(Decoder.this.decode(byteSlice));
793 }
794 }
795
796 private class ByteOrderFixingDecoder extends Decoder<T> {
797
798 private final ByteOrder byteOrder;
799
800 public ByteOrderFixingDecoder(ByteOrder byteOrder) {
801 this.byteOrder = byteOrder;
802 }
803
804 @Override
805 public byte alignment() {
806 return Decoder.this.alignment();
807 }
808
809 @Override
810 public @Nullable Integer fixedSize() {
811 return Decoder.this.fixedSize();
812 }
813
814 @Override
815 public T decode(ByteBuffer byteSlice) {
816 var newByteSlice = byteSlice.duplicate();
817 newByteSlice.order(byteOrder);
818 return Decoder.this.decode(newByteSlice);
819 }
820 }
821
822 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
823 return byteSlice.slice(index, length).order(byteSlice.order());
824 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100825}