blob: f605b09c0491b17bc580920195b2f61306d69d2e [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
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01007import static java.lang.Math.max;
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01008import static java.nio.ByteOrder.BIG_ENDIAN;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01009import static java.nio.ByteOrder.LITTLE_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010010import static java.nio.charset.StandardCharsets.UTF_8;
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020011import static java.util.Objects.requireNonNullElse;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010012import static java.util.stream.Collectors.toMap;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010013
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020014import com.google.errorprone.annotations.Immutable;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010015
16import java.io.ByteArrayOutputStream;
17import java.io.IOException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010018import java.lang.reflect.InvocationTargetException;
19import java.lang.reflect.RecordComponent;
20import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010022import java.nio.channels.Channels;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010023import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010024import java.text.ParseException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010025import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.List;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010028import java.util.Map;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010029import java.util.Map.Entry;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010030import java.util.Objects;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010031import java.util.Optional;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010032import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010033import java.util.function.Predicate;
34import java.util.function.UnaryOperator;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010035
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010036import org.apiguardian.api.API;
37import org.apiguardian.api.API.Status;
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010038import org.jetbrains.annotations.NotNull;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010039import org.jetbrains.annotations.Nullable;
40
41/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010042 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010043 *
44 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
45 * the type you wish to decode.
46 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010047 * <p><strong>Example</strong>
48 *
49 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
50 * {@code int}, you can use the following code:
51 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020052 * {@snippet lang="java" :
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010053 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010054 *
55 * var decoder =
56 * Decoder.ofArray(
57 * Decoder.ofStructure(
58 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010059 * Decoder.ofString(UTF_8),
60 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010061 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020062 * byte[] bytes;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010063 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020064 * }
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010065 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010066 * @param <T> the type that the {@link Decoder} can decode.
67 */
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010068@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020069@Immutable
70@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010071public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010072
73 private Decoder() {}
74
75 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010076 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
77 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010078 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
79 * important that the start and end boundaries of the passed byte slice correspond to the actual
80 * start and end of the serialized value. The format does generally not allow for the dynamic
81 * discovery of the end of the data structure.
82 *
83 * @param byteSlice a byte slice holding a serialized GVariant.
84 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010085 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
86 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010087 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010088 */
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010089 public abstract @NotNull T decode(ByteBuffer byteSlice);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010090
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010091 /**
92 * Encodes a value of type {@code T} into a {@link ByteBuffer} holding a serialized GVariant.
93 *
94 * @param value the value to serialize.
95 * @return a {@link ByteBuffer} holding the serialized value.
96 */
97 public final ByteBuffer encode(T value) {
98 var byteWriter = new ByteWriter();
99 encode(value, byteWriter);
100 return byteWriter.toByteBuffer();
101 }
102
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100103 abstract byte alignment();
104
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100105 abstract @Nullable Integer fixedSize();
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100106
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100107 abstract void encode(T value, ByteWriter byteWriter);
108
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100109 final boolean hasFixedSize() {
110 return fixedSize() != null;
111 }
112
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100113 /**
114 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
115 *
116 * @param byteOrder the byte order to use.
117 * @return a new, decorated {@link Decoder}.
118 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100119 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100120 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100121 }
122
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100123 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100124 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
125 *
126 * @param function the function to apply.
127 * @return a new, decorated {@link Decoder}.
128 * @see java.util.stream.Stream#map
129 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100130 public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
131 return new MappingDecoder<>(decodingFunction, encodingFunction);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100132 }
133
134 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100135 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
136 *
137 * @param function the function to apply.
138 * @return a new, decorated {@link Decoder}.
139 * @see java.util.stream.Stream#map
140 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100141 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
142 return new ContramappingDecoder(decodingFunction, encodingFunction);
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100143 }
144
145 /**
146 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
147 * condition on the input {@link ByteBuffer}.
148 *
149 * @param selector the predicate to use to determine the decoder to use.
150 * @return a new {@link Decoder}.
151 */
152 public static <U> Decoder<U> ofPredicate(
153 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
154 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
155 }
156
157 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100158 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100159 *
160 * @param elementDecoder a {@link Decoder} for the elements of the array.
161 * @param <U> the element type.
162 * @return a new {@link Decoder}.
163 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100164 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100165 return new ArrayDecoder<>(elementDecoder);
166 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100167
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100168 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100169 * Creates a {@link Decoder} for a {@code Dictionary} type.
170 *
171 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
172 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
173 * @return a new {@link Decoder}.
174 */
175 public static <K, V> Decoder<Map<K, V>> ofDictionary(
176 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
177 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
178 }
179
180 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100181 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
182 * primitive {@code byte[]} array.
183 *
184 * @return a new {@link Decoder}.
185 */
186 public static Decoder<byte[]> ofByteArray() {
187 return new ByteArrayDecoder();
188 }
189
190 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100191 * Creates a {@link Decoder} for a {@code Maybe} type.
192 *
193 * @param elementDecoder a {@link Decoder} for the contained element.
194 * @param <U> the element type.
195 * @return a new {@link Decoder}.
196 */
197 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
198 return new MaybeDecoder<>(elementDecoder);
199 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100200
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100201 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100202 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100203 *
204 * @param recordType the {@link Record} type that represents the components of the structure.
205 * @param componentDecoders a {@link Decoder} for each component of the structure.
206 * @param <U> the {@link Record} type that represents the components of the structure.
207 * @return a new {@link Decoder}.
208 */
209 public static <U extends Record> Decoder<U> ofStructure(
210 Class<U> recordType, Decoder<?>... componentDecoders) {
211 return new StructureDecoder<>(recordType, componentDecoders);
212 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100213
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100214 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100215 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
216 *
217 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
218 * more convenient.
219 *
220 * @param componentDecoders a {@link Decoder} for each component of the structure.
221 * @return a new {@link Decoder}.
222 */
223 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
224 return new TupleDecoder(componentDecoders);
225 }
226
227 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100228 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
229 * Map.Entry}.
230 *
231 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
232 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
233 * @return a new {@link Decoder}.
234 */
235 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
236 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
237 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
238 }
239
240 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100241 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100242 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100243 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100244 *
245 * <ul>
246 * <li>{@link Boolean}
247 * <li>{@link Byte}
248 * <li>{@link Short}
249 * <li>{@link Integer}
250 * <li>{@link Long}
251 * <li>{@link String}
252 * <li>{@link Optional} (a GVariant {@code Maybe} type)
253 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100254 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100255 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100256 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100257 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100258 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100259 *
260 * @return a new {@link Decoder}.
261 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100262 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100263 return new VariantDecoder();
264 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100265
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100266 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100267 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100268 *
269 * @return a new {@link Decoder}.
270 */
271 public static Decoder<Boolean> ofBoolean() {
272 return new BooleanDecoder();
273 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100274
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100275 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100276 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100277 *
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<Byte> ofByte() {
284 return new ByteDecoder();
285 }
286
287 /**
288 * Creates a {@link Decoder} for the 16-bit {@code short} 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<Short> ofShort() {
296 return new ShortDecoder();
297 }
298
299 /**
300 * Creates a {@link Decoder} for the 32-bit {@code int} type.
301 *
302 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
303 * result of this method.
304 *
305 * @return a new {@link Decoder}.
306 */
307 public static Decoder<Integer> ofInt() {
308 return new IntegerDecoder();
309 }
310
311 /**
312 * Creates a {@link Decoder} for the 64-bit {@code long} type.
313 *
314 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
315 * result of this method.
316 *
317 * @return a new {@link Decoder}.
318 */
319 public static Decoder<Long> ofLong() {
320 return new LongDecoder();
321 }
322
323 /**
324 * Creates a {@link Decoder} for the {@code double} type.
325 *
326 * @return a new {@link Decoder}.
327 */
328 public static Decoder<Double> ofDouble() {
329 return new DoubleDecoder();
330 }
331
332 /**
333 * Creates a {@link Decoder} for the {@link String} type.
334 *
335 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
336 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
337 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100338 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100339 * @return a new {@link Decoder}.
340 */
341 public static Decoder<String> ofString(Charset charset) {
342 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100343 }
344
345 private static int align(int offset, byte alignment) {
346 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
347 }
348
349 private static int getIntN(ByteBuffer byteSlice) {
350 var intBytes = new byte[4];
351 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
352 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
353 }
354
355 @SuppressWarnings("java:S3358")
356 private static int byteCount(int n) {
357 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
358 }
359
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100360 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
361 // Determining the framing offset size requires trial and error.
362 int framingOffsetSize;
363 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
364 if (elementsRelativeEnd + framingOffsetSize* framingOffsets.size() >= 1 << (8*framingOffsetSize)) {
365 continue;
366 }
367
368 if (framingOffsetSize > 4) {
369 throw new IllegalArgumentException("too many framing offsets");
370 }
371
372 return framingOffsetSize;
373 }
374 }
375
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100376 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100377
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100378 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100379
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100380 ArrayDecoder(Decoder<U> elementDecoder) {
381 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100382 }
383
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100384 @Override
385 public byte alignment() {
386 return elementDecoder.alignment();
387 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100388
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100389 @Override
390 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100391 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100392 return null;
393 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100394
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100395 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100396 public @NotNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100397 List<U> elements;
398
399 var elementSize = elementDecoder.fixedSize();
400 if (elementSize != null) {
401 // A simple C-style array.
402 elements = new ArrayList<>(byteSlice.limit() / elementSize);
403 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100404 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100405 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100406 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100407 } else if (byteSlice.limit() == 0) {
408 // A degenerate zero-length array of variable width.
409 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100410 } else {
411 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100412 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100413 int lastFramingOffset =
414 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
415 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100416
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100417 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100418 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100419 for (int i = 0; i < elementCount; i++) {
420 int framingOffset =
421 getIntN(
422 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100423 elements.add(
424 elementDecoder.decode(
425 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100426 position = align(framingOffset, alignment());
427 }
428 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100429
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100430 return elements;
431 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100432
433 @Override
434 void encode(List<U> value, ByteWriter byteWriter) {
435 if (elementDecoder.hasFixedSize()) {
436 for (var element : value) {
437 elementDecoder.encode(element, byteWriter);
438 }
439 } else {
440 // Variable-width arrays are encoded with a vector of framing offsets in the end.
441 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
442 int startOffset = byteWriter.position();
443 for (var element : value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100444 // Align the element.
445 var lastRelativeEnd = byteWriter.position() - startOffset;
446 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
447
448 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100449 elementDecoder.encode(element, byteWriter);
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100450
451 // Record the framing offset of the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100452 var relativeEnd = byteWriter.position() - startOffset;
453 framingOffsets.add(relativeEnd);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100454 }
455
456 // Write the framing offsets.
457 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
458 for (var framingOffset : framingOffsets) {
459 byteWriter.writeIntN(framingOffset, framingOffsetSize);
460 }
461 }
462 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100463 }
464
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100465 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
466
467 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
468
469 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
470 this.entryArrayDecoder =
471 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
472 }
473
474 @Override
475 public byte alignment() {
476 return entryArrayDecoder.alignment();
477 }
478
479 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100480 @Nullable
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100481 public Integer fixedSize() {
482 return entryArrayDecoder.fixedSize();
483 }
484
485 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100486 public @NotNull Map<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100487 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
488 return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
489 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100490
491 @Override
492 void encode(Map<K, V> value, ByteWriter byteWriter) {
493 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
494 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100495 }
496
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100497 private static class ByteArrayDecoder extends Decoder<byte[]> {
498
499 private static final int ELEMENT_SIZE = 1;
500
501 @Override
502 public byte alignment() {
503 return ELEMENT_SIZE;
504 }
505
506 @Override
507 @Nullable
508 Integer fixedSize() {
509 return null;
510 }
511
512 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100513 public byte @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100514 // A simple C-style array.
515 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
516 byteSlice.get(elements);
517 return elements;
518 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100519
520 @Override
521 void encode(byte[] value, ByteWriter byteWriter) {
522 byteWriter.write(value);
523 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100524 }
525
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100526 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
527
528 private final Decoder<U> elementDecoder;
529
530 MaybeDecoder(Decoder<U> elementDecoder) {
531 this.elementDecoder = elementDecoder;
532 }
533
534 @Override
535 public byte alignment() {
536 return elementDecoder.alignment();
537 }
538
539 @Override
540 @Nullable
541 Integer fixedSize() {
542 return null;
543 }
544
545 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100546 public @NotNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100547 if (!byteSlice.hasRemaining()) {
548 return Optional.empty();
549 } else {
550 if (!elementDecoder.hasFixedSize()) {
551 // Remove trailing zero byte.
552 byteSlice.limit(byteSlice.limit() - 1);
553 }
554
555 return Optional.of(elementDecoder.decode(byteSlice));
556 }
557 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100558
559 @Override
560 void encode(Optional<U> value, ByteWriter byteWriter) {
561 if (value.isEmpty()) {
562 return;
563 }
564
565 elementDecoder.encode(value.get(), byteWriter);
566 if (!elementDecoder.hasFixedSize()) {
567 byteWriter.write((byte) 0);
568 }
569 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100570 }
571
572 private static class StructureDecoder<U extends Record> extends Decoder<U> {
573
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100574 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100575 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100576
577 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
578 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100579 if (componentDecoders.length != recordComponents.length) {
580 throw new IllegalArgumentException(
581 "number of decoders (%d) does not match number of structure components (%d)"
582 .formatted(componentDecoders.length, recordComponents.length));
583 }
584
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100585 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100586 this.tupleDecoder = new TupleDecoder(componentDecoders);
587 }
588
589 @Override
590 public byte alignment() {
591 return tupleDecoder.alignment();
592 }
593
594 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100595 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100596 return tupleDecoder.fixedSize();
597 }
598
599 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100600 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100601 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
602
603 try {
604 var recordComponentTypes =
605 Arrays.stream(recordType.getRecordComponents())
606 .map(RecordComponent::getType)
607 .toArray(Class<?>[]::new);
608 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
609 return recordConstructor.newInstance(recordConstructorArguments);
610 } catch (NoSuchMethodException
611 | InstantiationException
612 | IllegalAccessException
613 | InvocationTargetException e) {
614 throw new IllegalStateException(e);
615 }
616 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100617
618 @Override
619 void encode(U value, ByteWriter byteWriter) {
620 try {
621 var components = recordType.getRecordComponents();
622 List<Object> componentValues = new ArrayList<>(components.length);
623 for (var component : components) {
624 var accessor = component.getAccessor();
625 var componentValue = accessor.invoke(value);
626 componentValues.add(componentValue);
627 }
628 tupleDecoder.encode(componentValues.toArray(), byteWriter);
629 } catch (IllegalAccessException | InvocationTargetException e) {
630 throw new IllegalStateException(e);
631 }
632 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100633 }
634
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200635 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100636 private static class TupleDecoder extends Decoder<Object[]> {
637
638 private final Decoder<?>[] componentDecoders;
639
640 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100641 this.componentDecoders = componentDecoders;
642 }
643
644 @Override
645 public byte alignment() {
646 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
647 }
648
649 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100650 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100651 int position = 0;
652 for (var componentDecoder : componentDecoders) {
653 var fixedComponentSize = componentDecoder.fixedSize();
654 if (fixedComponentSize == null) {
655 return null;
656 }
657
658 position = align(position, componentDecoder.alignment());
659 position += fixedComponentSize;
660 }
661
662 if (position == 0) {
663 return 1;
664 }
665
666 return align(position, alignment());
667 }
668
669 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100670 public Object @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100671 int framingOffsetSize = byteCount(byteSlice.limit());
672
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100673 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100674
675 int position = 0;
676 int framingOffsetIndex = 0;
677 int componentIndex = 0;
678 for (var componentDecoder : componentDecoders) {
679 position = align(position, componentDecoder.alignment());
680
681 var fixedComponentSize = componentDecoder.fixedSize();
682 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100683 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100684 componentDecoder.decode(
685 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100686 position += fixedComponentSize;
687 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100688 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100689 // The last component never has a framing offset.
690 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100691 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100692 componentDecoder.decode(
693 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100694 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100695 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100696 int framingOffset =
697 getIntN(
698 byteSlice.slice(
699 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
700 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100701 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100702 componentDecoder.decode(
703 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100704 position = framingOffset;
705 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100706 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100707 }
708
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100709 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100710 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100711
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100712 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100713 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100714
715 @Override
716 @SuppressWarnings("unchecked")
717 void encode(Object[] value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100718 // The unit type is encoded as a single zero byte.
719 if (value.length == 0) {
720 byteWriter.write((byte) 0);
721 return;
722 }
723
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100724 int startOffset = byteWriter.position();
725 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
726 for (int i = 0; i < value.length; ++i) {
727 var componentDecoder = (Decoder<Object>) componentDecoders[i];
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100728
729 // Align the element.
730 var lastRelativeEnd = byteWriter.position() - startOffset;
731 byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]);
732
733 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100734 componentDecoder.encode(value[i], byteWriter);
735
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100736 // Record the framing offset of the element if it is of variable size.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100737 var fixedComponentSize = componentDecoders[i].fixedSize();
738 if (fixedComponentSize == null && i < value.length - 1) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100739 var relativeEnd = byteWriter.position() - startOffset;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100740 framingOffsets.add(relativeEnd);
741 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100742 }
743
744 // Write the framing offsets in reverse order.
745 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
746 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
747 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
748 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100749
750 // Pad the structure to its alignment if it is of fixed size.
751 if (fixedSize() != null) {
752 var lastRelativeEnd = byteWriter.position() - startOffset;
753 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
754 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100755 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100756 }
757
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100758 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
759
760 private final TupleDecoder tupleDecoder;
761
762 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
763 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
764 }
765
766 @Override
767 public byte alignment() {
768 return tupleDecoder.alignment();
769 }
770
771 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100772 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100773 return tupleDecoder.fixedSize();
774 }
775
776 @Override
777 @SuppressWarnings("unchecked")
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100778 public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100779 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200780 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100781 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100782
783 @Override
784 void encode(Entry<K, V> value, ByteWriter byteWriter) {
785 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
786 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100787 }
788
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100789 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100790
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100791 @Override
792 public byte alignment() {
793 return 8;
794 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100795
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100796 @Override
797 @Nullable
798 Integer fixedSize() {
799 return null;
800 }
801
802 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100803 public @NotNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100804 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
805 if (byteSlice.get(i) != 0) {
806 continue;
807 }
808
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100809 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100810 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100811
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100812 Signature signature;
813 try {
814 signature = Signature.parse(signatureBytes);
815 } catch (ParseException e) {
816 throw new IllegalArgumentException(e);
817 }
818
819 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100820 }
821
822 throw new IllegalArgumentException("variant signature not found");
823 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100824
825 @Override
826 void encode(Variant value, ByteWriter byteWriter) {
827 value.signature().decoder().encode(value.value(), byteWriter);
828 byteWriter.write((byte) 0);
829 byteWriter.write(value.signature().toString().getBytes(UTF_8));
830 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100831 }
832
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100833 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100834
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100835 @Override
836 public byte alignment() {
837 return 1;
838 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100839
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100840 @Override
841 public Integer fixedSize() {
842 return 1;
843 }
844
845 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100846 public @NotNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100847 return byteSlice.get() != 0;
848 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100849
850 @Override
851 void encode(Boolean value, ByteWriter byteWriter) {
852 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
853 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100854 }
855
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100856 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100857
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100858 @Override
859 public byte alignment() {
860 return 1;
861 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100862
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100863 @Override
864 public Integer fixedSize() {
865 return 1;
866 }
867
868 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100869 public @NotNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100870 return byteSlice.get();
871 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100872
873 @Override
874 void encode(Byte value, ByteWriter byteWriter) {
875 byteWriter.write(value);
876 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100877 }
878
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100879 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100880
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100881 @Override
882 public byte alignment() {
883 return 2;
884 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100885
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100886 @Override
887 public Integer fixedSize() {
888 return 2;
889 }
890
891 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100892 public @NotNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100893 return byteSlice.getShort();
894 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100895
896 @Override
897 void encode(Short value, ByteWriter byteWriter) {
898 byteWriter.write(value);
899 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100900 }
901
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100902 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100903
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100904 @Override
905 public byte alignment() {
906 return 4;
907 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100908
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100909 @Override
910 public Integer fixedSize() {
911 return 4;
912 }
913
914 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100915 public @NotNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100916 return byteSlice.getInt();
917 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100918
919 @Override
920 void encode(Integer value, ByteWriter byteWriter) {
921 byteWriter.write(value);
922 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100923 }
924
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100925 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100926
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100927 @Override
928 public byte alignment() {
929 return 8;
930 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100931
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100932 @Override
933 public Integer fixedSize() {
934 return 8;
935 }
936
937 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100938 public @NotNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100939 return byteSlice.getLong();
940 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100941
942 @Override
943 void encode(Long value, ByteWriter byteWriter) {
944 byteWriter.write(value);
945 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100946 }
947
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100948 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100949
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100950 @Override
951 public byte alignment() {
952 return 8;
953 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100954
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100955 @Override
956 public Integer fixedSize() {
957 return 8;
958 }
959
960 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100961 public @NotNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100962 return byteSlice.getDouble();
963 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100964
965 @Override
966 void encode(Double value, ByteWriter byteWriter) {
967 byteWriter.write(value);
968 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100969 }
970
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100971 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100972
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100973 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100974
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100975 public StringDecoder(Charset charset) {
976 this.charset = charset;
977 }
978
979 @Override
980 public byte alignment() {
981 return 1;
982 }
983
984 @Override
985 @Nullable
986 Integer fixedSize() {
987 return null;
988 }
989
990 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100991 public @NotNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100992 byteSlice.limit(byteSlice.limit() - 1);
993 return charset.decode(byteSlice).toString();
994 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100995
996 @Override
997 void encode(String value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100998 byteWriter.write(charset.encode(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100999 byteWriter.write((byte) 0);
1000 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001001 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001002
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001003 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001004 private class MappingDecoder<U> extends Decoder<U> {
1005
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001006 private final Function<@NotNull T, @NotNull U> decodingFunction;
1007 private final Function<@NotNull U, @NotNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001008
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001009 MappingDecoder(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
1010 this.decodingFunction = decodingFunction;
1011 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001012 }
1013
1014 @Override
1015 public byte alignment() {
1016 return Decoder.this.alignment();
1017 }
1018
1019 @Override
1020 public @Nullable Integer fixedSize() {
1021 return Decoder.this.fixedSize();
1022 }
1023
1024 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001025 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001026 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1027 }
1028
1029 @Override
1030 void encode(U value, ByteWriter byteWriter) {
1031 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001032 }
1033 }
1034
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001035 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001036 private class ContramappingDecoder extends Decoder<T> {
1037
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001038 private final UnaryOperator<ByteBuffer> decodingFunction;
1039 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001040
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001041 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1042 this.decodingFunction = decodingFunction;
1043 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001044 }
1045
1046 @Override
1047 public byte alignment() {
1048 return Decoder.this.alignment();
1049 }
1050
1051 @Override
1052 public @Nullable Integer fixedSize() {
1053 return Decoder.this.fixedSize();
1054 }
1055
1056 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001057 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001058 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001059 return Decoder.this.decode(transformedBuffer);
1060 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001061
1062 @Override
1063 void encode(T value, ByteWriter byteWriter) {
1064 var innerByteWriter = new ByteWriter();
1065 Decoder.this.encode(value, innerByteWriter);
1066 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001067 byteWriter.write(transformedBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001068 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001069 }
1070
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001071 private class ByteOrderFixingDecoder extends Decoder<T> {
1072
1073 private final ByteOrder byteOrder;
1074
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001075 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001076 this.byteOrder = byteOrder;
1077 }
1078
1079 @Override
1080 public byte alignment() {
1081 return Decoder.this.alignment();
1082 }
1083
1084 @Override
1085 public @Nullable Integer fixedSize() {
1086 return Decoder.this.fixedSize();
1087 }
1088
1089 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001090 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001091 var newByteSlice = byteSlice.duplicate();
1092 newByteSlice.order(byteOrder);
1093 return Decoder.this.decode(newByteSlice);
1094 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001095
1096 @Override
1097 protected void encode(T value, ByteWriter byteWriter) {
1098 var newByteWriter = byteWriter.duplicate();
1099 newByteWriter.order(byteOrder);
1100 Decoder.this.encode(value, newByteWriter);
1101 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001102 }
1103
1104 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1105 return byteSlice.slice(index, length).order(byteSlice.order());
1106 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001107
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001108 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001109 private static class PredicateDecoder<U> extends Decoder<U> {
1110
1111 private final Predicate<ByteBuffer> selector;
1112 private final Decoder<U> thenDecoder;
1113 private final Decoder<U> elseDecoder;
1114
1115 PredicateDecoder(
1116 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1117 this.selector = selector;
1118 this.thenDecoder = thenDecoder;
1119 this.elseDecoder = elseDecoder;
1120 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1121 throw new IllegalArgumentException(
1122 "incompatible alignments in predicate branches: then=%d, else=%d"
1123 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1124 }
1125
1126 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1127 throw new IllegalArgumentException(
1128 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001129 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001130 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1131 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001132 }
1133 }
1134
1135 @Override
1136 public byte alignment() {
1137 return thenDecoder.alignment();
1138 }
1139
1140 @Override
1141 public @Nullable Integer fixedSize() {
1142 return thenDecoder.fixedSize();
1143 }
1144
1145 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001146 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001147 var b = selector.test(byteSlice);
1148 byteSlice.rewind();
1149 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1150 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001151
1152 @Override
1153 public void encode(U value, ByteWriter byteWriter) {
1154 elseDecoder.encode(value, byteWriter);
1155 }
1156 }
1157
1158 private static class ByteWriter {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001159 private ByteOrder byteOrder = BIG_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001160 private final ByteArrayOutputStream outputStream;
1161
1162 ByteWriter() {
1163 this.outputStream = new ByteArrayOutputStream();
1164 }
1165
1166 private ByteWriter(ByteArrayOutputStream outputStream) {
1167 this.outputStream = outputStream;
1168 }
1169
1170 void write(byte[] bytes) {
1171 outputStream.write(bytes, 0, bytes.length);
1172 }
1173
1174 @SuppressWarnings("java:S2095")
1175 void write(ByteBuffer byteBuffer) {
1176 var channel = Channels.newChannel(outputStream);
1177 try {
1178 channel.write(byteBuffer);
1179 } catch (IOException e) {
1180 // impossible
1181 throw new IllegalStateException(e);
1182 }
1183 }
1184
1185 void write(byte value) {
1186 outputStream.write(value);
1187 }
1188
1189 void write(int value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001190 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001191 }
1192
1193 void write(long value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001194 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001195 }
1196
1197 void write(short value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001198 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001199 }
1200
1201 void write(double value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001202 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001203 }
1204
1205 private void writeIntN(int value, int byteCount) {
1206 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1207 switch (byteCount) {
1208 case 0 -> {}
1209 case 1 ->
1210 byteBuffer.put((byte) value);
1211 case 2 ->
1212 byteBuffer.putShort((short) value);
1213 case 4 ->
1214 byteBuffer.putInt(value);
1215 default ->
1216 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1217 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001218 write(byteBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001219 }
1220
1221 ByteWriter duplicate() {
1222 var duplicate = new ByteWriter(outputStream);
1223 duplicate.byteOrder = byteOrder;
1224 return duplicate;
1225 }
1226
1227 ByteBuffer toByteBuffer() {
1228 return ByteBuffer.wrap(outputStream.toByteArray());
1229 }
1230
1231 void order(ByteOrder byteOrder) {
1232 this.byteOrder = byteOrder;
1233 }
1234
1235 int position() {
1236 return outputStream.size();
1237 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001238 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001239}