blob: 3beb24735d677817e80767a79ce7d9ee3a842a8b [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 Benkard44df94e2021-12-30 18:43:33 +010018import java.util.Objects;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010019import java.util.Optional;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010020import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010021import java.util.function.Predicate;
22import java.util.function.UnaryOperator;
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010023import org.apiguardian.api.API;
24import org.apiguardian.api.API.Status;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010025import org.jetbrains.annotations.Nullable;
26
27/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010028 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010029 *
30 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
31 * the type you wish to decode.
32 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010033 * <p><strong>Example</strong>
34 *
35 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
36 * {@code int}, you can use the following code:
37 *
38 * <pre>{@code
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010039 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010040 *
41 * var decoder =
42 * Decoder.ofArray(
43 * Decoder.ofStructure(
44 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010045 * Decoder.ofString(UTF_8),
46 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010047 *
48 * byte[] bytes = ...;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010049 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010050 * }</pre>
51 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010052 * @param <T> the type that the {@link Decoder} can decode.
53 */
54@SuppressWarnings("java:S1610")
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010055@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010056public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010057
58 private Decoder() {}
59
60 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010061 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
62 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010063 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
64 * important that the start and end boundaries of the passed byte slice correspond to the actual
65 * start and end of the serialized value. The format does generally not allow for the dynamic
66 * discovery of the end of the data structure.
67 *
68 * @param byteSlice a byte slice holding a serialized GVariant.
69 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010070 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
71 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010072 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010073 */
74 public abstract T decode(ByteBuffer byteSlice);
75
76 abstract byte alignment();
77
78 @Nullable
79 abstract Integer fixedSize();
80
81 final boolean hasFixedSize() {
82 return fixedSize() != null;
83 }
84
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010085 /**
86 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
87 *
88 * @param byteOrder the byte order to use.
89 * @return a new, decorated {@link Decoder}.
90 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010091 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +010092 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010093 }
94
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010095 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010096 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
97 *
98 * @param function the function to apply.
99 * @return a new, decorated {@link Decoder}.
100 * @see java.util.stream.Stream#map
101 */
102 public final <U> Decoder<U> map(Function<T, U> function) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100103 return new MappingDecoder<>(function);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100104 }
105
106 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100107 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
108 *
109 * @param function the function to apply.
110 * @return a new, decorated {@link Decoder}.
111 * @see java.util.stream.Stream#map
112 */
113 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> function) {
114 return new ContramappingDecoder(function);
115 }
116
117 /**
118 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
119 * condition on the input {@link ByteBuffer}.
120 *
121 * @param selector the predicate to use to determine the decoder to use.
122 * @return a new {@link Decoder}.
123 */
124 public static <U> Decoder<U> ofPredicate(
125 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
126 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
127 }
128
129 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100130 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100131 *
132 * @param elementDecoder a {@link Decoder} for the elements of the array.
133 * @param <U> the element type.
134 * @return a new {@link Decoder}.
135 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100136 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100137 return new ArrayDecoder<>(elementDecoder);
138 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100139
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100140 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100141 * Creates a {@link Decoder} for a {@code Dictionary} type.
142 *
143 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
144 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
145 * @return a new {@link Decoder}.
146 */
147 public static <K, V> Decoder<Map<K, V>> ofDictionary(
148 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
149 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
150 }
151
152 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100153 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
154 * primitive {@code byte[]} array.
155 *
156 * @return a new {@link Decoder}.
157 */
158 public static Decoder<byte[]> ofByteArray() {
159 return new ByteArrayDecoder();
160 }
161
162 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100163 * Creates a {@link Decoder} for a {@code Maybe} type.
164 *
165 * @param elementDecoder a {@link Decoder} for the contained element.
166 * @param <U> the element type.
167 * @return a new {@link Decoder}.
168 */
169 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
170 return new MaybeDecoder<>(elementDecoder);
171 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100172
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100173 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100174 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100175 *
176 * @param recordType the {@link Record} type that represents the components of the structure.
177 * @param componentDecoders a {@link Decoder} for each component of the structure.
178 * @param <U> the {@link Record} type that represents the components of the structure.
179 * @return a new {@link Decoder}.
180 */
181 public static <U extends Record> Decoder<U> ofStructure(
182 Class<U> recordType, Decoder<?>... componentDecoders) {
183 return new StructureDecoder<>(recordType, componentDecoders);
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 a {@code Structure} type, decoding into a {@link List}.
188 *
189 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
190 * more convenient.
191 *
192 * @param componentDecoders a {@link Decoder} for each component of the structure.
193 * @return a new {@link Decoder}.
194 */
195 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
196 return new TupleDecoder(componentDecoders);
197 }
198
199 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100200 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
201 * Map.Entry}.
202 *
203 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
204 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
205 * @return a new {@link Decoder}.
206 */
207 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
208 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
209 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
210 }
211
212 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100213 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100214 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100215 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100216 *
217 * <ul>
218 * <li>{@link Boolean}
219 * <li>{@link Byte}
220 * <li>{@link Short}
221 * <li>{@link Integer}
222 * <li>{@link Long}
223 * <li>{@link String}
224 * <li>{@link Optional} (a GVariant {@code Maybe} type)
225 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100226 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100227 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100228 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100229 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100230 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100231 *
232 * @return a new {@link Decoder}.
233 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100234 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100235 return new VariantDecoder();
236 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100237
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100238 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100239 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100240 *
241 * @return a new {@link Decoder}.
242 */
243 public static Decoder<Boolean> ofBoolean() {
244 return new BooleanDecoder();
245 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100246
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100247 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100248 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100249 *
250 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
251 * result of this method.
252 *
253 * @return a new {@link Decoder}.
254 */
255 public static Decoder<Byte> ofByte() {
256 return new ByteDecoder();
257 }
258
259 /**
260 * Creates a {@link Decoder} for the 16-bit {@code short} type.
261 *
262 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
263 * result of this method.
264 *
265 * @return a new {@link Decoder}.
266 */
267 public static Decoder<Short> ofShort() {
268 return new ShortDecoder();
269 }
270
271 /**
272 * Creates a {@link Decoder} for the 32-bit {@code int} type.
273 *
274 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
275 * result of this method.
276 *
277 * @return a new {@link Decoder}.
278 */
279 public static Decoder<Integer> ofInt() {
280 return new IntegerDecoder();
281 }
282
283 /**
284 * Creates a {@link Decoder} for the 64-bit {@code long} type.
285 *
286 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
287 * result of this method.
288 *
289 * @return a new {@link Decoder}.
290 */
291 public static Decoder<Long> ofLong() {
292 return new LongDecoder();
293 }
294
295 /**
296 * Creates a {@link Decoder} for the {@code double} type.
297 *
298 * @return a new {@link Decoder}.
299 */
300 public static Decoder<Double> ofDouble() {
301 return new DoubleDecoder();
302 }
303
304 /**
305 * Creates a {@link Decoder} for the {@link String} type.
306 *
307 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
308 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
309 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100310 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100311 * @return a new {@link Decoder}.
312 */
313 public static Decoder<String> ofString(Charset charset) {
314 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100315 }
316
317 private static int align(int offset, byte alignment) {
318 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
319 }
320
321 private static int getIntN(ByteBuffer byteSlice) {
322 var intBytes = new byte[4];
323 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
324 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
325 }
326
327 @SuppressWarnings("java:S3358")
328 private static int byteCount(int n) {
329 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
330 }
331
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100332 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100333
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100334 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100335
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100336 ArrayDecoder(Decoder<U> elementDecoder) {
337 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100338 }
339
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100340 @Override
341 public byte alignment() {
342 return elementDecoder.alignment();
343 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100344
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100345 @Override
346 @Nullable
347 Integer fixedSize() {
348 return null;
349 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100350
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100351 @Override
352 public List<U> decode(ByteBuffer byteSlice) {
353 List<U> elements;
354
355 var elementSize = elementDecoder.fixedSize();
356 if (elementSize != null) {
357 // A simple C-style array.
358 elements = new ArrayList<>(byteSlice.limit() / elementSize);
359 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100360 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100361 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100362 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100363 } else if (byteSlice.limit() == 0) {
364 // A degenerate zero-length array of variable width.
365 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100366 } else {
367 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100368 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100369 int lastFramingOffset =
370 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
371 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100372
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100373 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100374 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100375 for (int i = 0; i < elementCount; i++) {
376 int framingOffset =
377 getIntN(
378 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100379 elements.add(
380 elementDecoder.decode(
381 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100382 position = align(framingOffset, alignment());
383 }
384 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100385
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100386 return elements;
387 }
388 }
389
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100390 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
391
392 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
393
394 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
395 this.entryArrayDecoder =
396 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
397 }
398
399 @Override
400 public byte alignment() {
401 return entryArrayDecoder.alignment();
402 }
403
404 @Override
405 public Integer fixedSize() {
406 return entryArrayDecoder.fixedSize();
407 }
408
409 @Override
410 public Map<K, V> decode(ByteBuffer byteSlice) {
411 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
412 return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
413 }
414 }
415
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100416 private static class ByteArrayDecoder extends Decoder<byte[]> {
417
418 private static final int ELEMENT_SIZE = 1;
419
420 @Override
421 public byte alignment() {
422 return ELEMENT_SIZE;
423 }
424
425 @Override
426 @Nullable
427 Integer fixedSize() {
428 return null;
429 }
430
431 @Override
432 public byte[] decode(ByteBuffer byteSlice) {
433 // A simple C-style array.
434 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
435 byteSlice.get(elements);
436 return elements;
437 }
438 }
439
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100440 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
441
442 private final Decoder<U> elementDecoder;
443
444 MaybeDecoder(Decoder<U> elementDecoder) {
445 this.elementDecoder = elementDecoder;
446 }
447
448 @Override
449 public byte alignment() {
450 return elementDecoder.alignment();
451 }
452
453 @Override
454 @Nullable
455 Integer fixedSize() {
456 return null;
457 }
458
459 @Override
460 public Optional<U> decode(ByteBuffer byteSlice) {
461 if (!byteSlice.hasRemaining()) {
462 return Optional.empty();
463 } else {
464 if (!elementDecoder.hasFixedSize()) {
465 // Remove trailing zero byte.
466 byteSlice.limit(byteSlice.limit() - 1);
467 }
468
469 return Optional.of(elementDecoder.decode(byteSlice));
470 }
471 }
472 }
473
474 private static class StructureDecoder<U extends Record> extends Decoder<U> {
475
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100476 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100477 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100478
479 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
480 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100481 if (componentDecoders.length != recordComponents.length) {
482 throw new IllegalArgumentException(
483 "number of decoders (%d) does not match number of structure components (%d)"
484 .formatted(componentDecoders.length, recordComponents.length));
485 }
486
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100487 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100488 this.tupleDecoder = new TupleDecoder(componentDecoders);
489 }
490
491 @Override
492 public byte alignment() {
493 return tupleDecoder.alignment();
494 }
495
496 @Override
497 public Integer fixedSize() {
498 return tupleDecoder.fixedSize();
499 }
500
501 @Override
502 public U decode(ByteBuffer byteSlice) {
503 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
504
505 try {
506 var recordComponentTypes =
507 Arrays.stream(recordType.getRecordComponents())
508 .map(RecordComponent::getType)
509 .toArray(Class<?>[]::new);
510 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
511 return recordConstructor.newInstance(recordConstructorArguments);
512 } catch (NoSuchMethodException
513 | InstantiationException
514 | IllegalAccessException
515 | InvocationTargetException e) {
516 throw new IllegalStateException(e);
517 }
518 }
519 }
520
521 private static class TupleDecoder extends Decoder<Object[]> {
522
523 private final Decoder<?>[] componentDecoders;
524
525 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100526 this.componentDecoders = componentDecoders;
527 }
528
529 @Override
530 public byte alignment() {
531 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
532 }
533
534 @Override
535 public Integer fixedSize() {
536 int position = 0;
537 for (var componentDecoder : componentDecoders) {
538 var fixedComponentSize = componentDecoder.fixedSize();
539 if (fixedComponentSize == null) {
540 return null;
541 }
542
543 position = align(position, componentDecoder.alignment());
544 position += fixedComponentSize;
545 }
546
547 if (position == 0) {
548 return 1;
549 }
550
551 return align(position, alignment());
552 }
553
554 @Override
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100555 public Object[] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100556 int framingOffsetSize = byteCount(byteSlice.limit());
557
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100558 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100559
560 int position = 0;
561 int framingOffsetIndex = 0;
562 int componentIndex = 0;
563 for (var componentDecoder : componentDecoders) {
564 position = align(position, componentDecoder.alignment());
565
566 var fixedComponentSize = componentDecoder.fixedSize();
567 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100568 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100569 componentDecoder.decode(
570 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100571 position += fixedComponentSize;
572 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100573 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100574 // The last component never has a framing offset.
575 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100576 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100577 componentDecoder.decode(
578 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100579 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100580 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100581 int framingOffset =
582 getIntN(
583 byteSlice.slice(
584 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
585 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100586 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100587 componentDecoder.decode(
588 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100589 position = framingOffset;
590 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100591 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100592 }
593
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100594 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100595 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100596
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100597 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100598 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100599 }
600
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100601 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
602
603 private final TupleDecoder tupleDecoder;
604
605 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
606 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
607 }
608
609 @Override
610 public byte alignment() {
611 return tupleDecoder.alignment();
612 }
613
614 @Override
615 public Integer fixedSize() {
616 return tupleDecoder.fixedSize();
617 }
618
619 @Override
620 @SuppressWarnings("unchecked")
621 public Map.Entry<K, V> decode(ByteBuffer byteSlice) {
622 Object[] components = tupleDecoder.decode(byteSlice);
623 return new SimpleImmutableEntry<>((K) components[0], (V) components[1]);
624 }
625 }
626
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100627 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100628
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100629 @Override
630 public byte alignment() {
631 return 8;
632 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100633
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100634 @Override
635 @Nullable
636 Integer fixedSize() {
637 return null;
638 }
639
640 @Override
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100641 public Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100642 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
643 if (byteSlice.get(i) != 0) {
644 continue;
645 }
646
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100647 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100648 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100649
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100650 Signature signature;
651 try {
652 signature = Signature.parse(signatureBytes);
653 } catch (ParseException e) {
654 throw new IllegalArgumentException(e);
655 }
656
657 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100658 }
659
660 throw new IllegalArgumentException("variant signature not found");
661 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100662 }
663
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100664 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100665
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100666 @Override
667 public byte alignment() {
668 return 1;
669 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100670
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100671 @Override
672 public Integer fixedSize() {
673 return 1;
674 }
675
676 @Override
677 public Boolean decode(ByteBuffer byteSlice) {
678 return byteSlice.get() != 0;
679 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100680 }
681
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100682 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100683
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100684 @Override
685 public byte alignment() {
686 return 1;
687 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100688
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100689 @Override
690 public Integer fixedSize() {
691 return 1;
692 }
693
694 @Override
695 public Byte decode(ByteBuffer byteSlice) {
696 return byteSlice.get();
697 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100698 }
699
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100700 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100701
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100702 @Override
703 public byte alignment() {
704 return 2;
705 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100706
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100707 @Override
708 public Integer fixedSize() {
709 return 2;
710 }
711
712 @Override
713 public Short decode(ByteBuffer byteSlice) {
714 return byteSlice.getShort();
715 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100716 }
717
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100718 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100719
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100720 @Override
721 public byte alignment() {
722 return 4;
723 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100724
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100725 @Override
726 public Integer fixedSize() {
727 return 4;
728 }
729
730 @Override
731 public Integer decode(ByteBuffer byteSlice) {
732 return byteSlice.getInt();
733 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100734 }
735
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100736 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100737
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100738 @Override
739 public byte alignment() {
740 return 8;
741 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100742
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100743 @Override
744 public Integer fixedSize() {
745 return 8;
746 }
747
748 @Override
749 public Long decode(ByteBuffer byteSlice) {
750 return byteSlice.getLong();
751 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100752 }
753
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100754 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100755
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100756 @Override
757 public byte alignment() {
758 return 8;
759 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100760
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100761 @Override
762 public Integer fixedSize() {
763 return 8;
764 }
765
766 @Override
767 public Double decode(ByteBuffer byteSlice) {
768 return byteSlice.getDouble();
769 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100770 }
771
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100772 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100773
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100774 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100775
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100776 public StringDecoder(Charset charset) {
777 this.charset = charset;
778 }
779
780 @Override
781 public byte alignment() {
782 return 1;
783 }
784
785 @Override
786 @Nullable
787 Integer fixedSize() {
788 return null;
789 }
790
791 @Override
792 public String decode(ByteBuffer byteSlice) {
793 byteSlice.limit(byteSlice.limit() - 1);
794 return charset.decode(byteSlice).toString();
795 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100796 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100797
798 private class MappingDecoder<U> extends Decoder<U> {
799
800 private final Function<T, U> function;
801
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100802 MappingDecoder(Function<T, U> function) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100803 this.function = function;
804 }
805
806 @Override
807 public byte alignment() {
808 return Decoder.this.alignment();
809 }
810
811 @Override
812 public @Nullable Integer fixedSize() {
813 return Decoder.this.fixedSize();
814 }
815
816 @Override
817 public U decode(ByteBuffer byteSlice) {
818 return function.apply(Decoder.this.decode(byteSlice));
819 }
820 }
821
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100822 private class ContramappingDecoder extends Decoder<T> {
823
824 private final UnaryOperator<ByteBuffer> function;
825
826 ContramappingDecoder(UnaryOperator<ByteBuffer> function) {
827 this.function = function;
828 }
829
830 @Override
831 public byte alignment() {
832 return Decoder.this.alignment();
833 }
834
835 @Override
836 public @Nullable Integer fixedSize() {
837 return Decoder.this.fixedSize();
838 }
839
840 @Override
841 public T decode(ByteBuffer byteSlice) {
842 var transformedBuffer = function.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
843 return Decoder.this.decode(transformedBuffer);
844 }
845 }
846
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100847 private class ByteOrderFixingDecoder extends Decoder<T> {
848
849 private final ByteOrder byteOrder;
850
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100851 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100852 this.byteOrder = byteOrder;
853 }
854
855 @Override
856 public byte alignment() {
857 return Decoder.this.alignment();
858 }
859
860 @Override
861 public @Nullable Integer fixedSize() {
862 return Decoder.this.fixedSize();
863 }
864
865 @Override
866 public T decode(ByteBuffer byteSlice) {
867 var newByteSlice = byteSlice.duplicate();
868 newByteSlice.order(byteOrder);
869 return Decoder.this.decode(newByteSlice);
870 }
871 }
872
873 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
874 return byteSlice.slice(index, length).order(byteSlice.order());
875 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100876
877 private static class PredicateDecoder<U> extends Decoder<U> {
878
879 private final Predicate<ByteBuffer> selector;
880 private final Decoder<U> thenDecoder;
881 private final Decoder<U> elseDecoder;
882
883 PredicateDecoder(
884 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
885 this.selector = selector;
886 this.thenDecoder = thenDecoder;
887 this.elseDecoder = elseDecoder;
888 if (thenDecoder.alignment() != elseDecoder.alignment()) {
889 throw new IllegalArgumentException(
890 "incompatible alignments in predicate branches: then=%d, else=%d"
891 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
892 }
893
894 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
895 throw new IllegalArgumentException(
896 "incompatible sizes in predicate branches: then=%s, else=%s"
897 .formatted(thenDecoder.fixedSize(), elseDecoder.fixedSize()));
898 }
899 }
900
901 @Override
902 public byte alignment() {
903 return thenDecoder.alignment();
904 }
905
906 @Override
907 public @Nullable Integer fixedSize() {
908 return thenDecoder.fixedSize();
909 }
910
911 @Override
912 public U decode(ByteBuffer byteSlice) {
913 var b = selector.test(byteSlice);
914 byteSlice.rewind();
915 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
916 }
917 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100918}