blob: 74d569d962607bef7a97146d73d26af4e4516032 [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 Benkard91dbd742022-10-17 19:38:56 +02007import com.google.errorprone.annotations.Immutable;
Matthias Andreas Benkarda7c474d2023-12-11 20:12:04 +01008import org.apiguardian.api.API;
9import org.apiguardian.api.API.Status;
10import org.jetbrains.annotations.NotNull;
11import org.jetbrains.annotations.Nullable;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010012
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010015import java.lang.reflect.InvocationTargetException;
16import java.lang.reflect.RecordComponent;
17import java.nio.ByteBuffer;
18import java.nio.ByteOrder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010019import java.nio.channels.Channels;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010020import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010021import java.text.ParseException;
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +010022import java.util.*;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010023import java.util.Map.Entry;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010024import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010025import java.util.function.Predicate;
26import java.util.function.UnaryOperator;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010027
Matthias Andreas Benkarda7c474d2023-12-11 20:12:04 +010028import static java.lang.Math.max;
29import static java.nio.ByteOrder.BIG_ENDIAN;
30import static java.nio.ByteOrder.LITTLE_ENDIAN;
31import static java.nio.charset.StandardCharsets.UTF_8;
32import static java.util.Objects.requireNonNullElse;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010033
34/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010035 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010036 *
37 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
38 * the type you wish to decode.
39 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010040 * <p><strong>Example</strong>
41 *
42 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
43 * {@code int}, you can use the following code:
44 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020045 * {@snippet lang="java" :
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010046 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010047 *
48 * var decoder =
49 * Decoder.ofArray(
50 * Decoder.ofStructure(
51 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010052 * Decoder.ofString(UTF_8),
53 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010054 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020055 * byte[] bytes;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010056 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020057 * }
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010058 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010059 * @param <T> the type that the {@link Decoder} can decode.
60 */
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010061@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020062@Immutable
63@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010064public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010065
66 private Decoder() {}
67
68 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010069 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
70 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010071 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
72 * important that the start and end boundaries of the passed byte slice correspond to the actual
73 * start and end of the serialized value. The format does generally not allow for the dynamic
74 * discovery of the end of the data structure.
75 *
76 * @param byteSlice a byte slice holding a serialized GVariant.
77 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010078 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
79 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010080 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010081 */
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010082 public abstract @NotNull T decode(ByteBuffer byteSlice);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010083
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010084 /**
85 * Encodes a value of type {@code T} into a {@link ByteBuffer} holding a serialized GVariant.
86 *
87 * @param value the value to serialize.
88 * @return a {@link ByteBuffer} holding the serialized value.
89 */
90 public final ByteBuffer encode(T value) {
91 var byteWriter = new ByteWriter();
92 encode(value, byteWriter);
93 return byteWriter.toByteBuffer();
94 }
95
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010096 abstract byte alignment();
97
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010098 abstract @Nullable Integer fixedSize();
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010099
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100100 abstract void encode(T value, ByteWriter byteWriter);
101
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100102 final boolean hasFixedSize() {
103 return fixedSize() != null;
104 }
105
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100106 /**
107 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
108 *
109 * @param byteOrder the byte order to use.
110 * @return a new, decorated {@link Decoder}.
111 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100112 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100113 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100114 }
115
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100116 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100117 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
118 *
Matthias Andreas Benkard716fc322023-12-12 19:13:01 +0100119 * @param decodingFunction the function to apply when decoding.
120 * @param encodingFunction the function to apply when encoding.
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100121 * @return a new, decorated {@link Decoder}.
122 * @see java.util.stream.Stream#map
123 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100124 public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
125 return new MappingDecoder<>(decodingFunction, encodingFunction);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100126 }
127
128 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100129 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
130 *
Matthias Andreas Benkard716fc322023-12-12 19:13:01 +0100131 * @param decodingFunction the function to apply when decoding.
132 * @param encodingFunction the function to apply when encoding.
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100133 * @return a new, decorated {@link Decoder}.
134 * @see java.util.stream.Stream#map
135 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100136 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
137 return new ContramappingDecoder(decodingFunction, encodingFunction);
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100138 }
139
140 /**
141 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
142 * condition on the input {@link ByteBuffer}.
143 *
144 * @param selector the predicate to use to determine the decoder to use.
145 * @return a new {@link Decoder}.
146 */
147 public static <U> Decoder<U> ofPredicate(
148 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
149 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
150 }
151
152 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100153 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100154 *
155 * @param elementDecoder a {@link Decoder} for the elements of the array.
156 * @param <U> the element type.
157 * @return a new {@link Decoder}.
158 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100159 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100160 return new ArrayDecoder<>(elementDecoder);
161 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100162
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100163 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100164 * Creates a {@link Decoder} for a {@code Dictionary} type.
165 *
166 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
167 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
168 * @return a new {@link Decoder}.
169 */
170 public static <K, V> Decoder<Map<K, V>> ofDictionary(
171 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
172 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
173 }
174
175 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100176 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
177 * primitive {@code byte[]} array.
178 *
179 * @return a new {@link Decoder}.
180 */
181 public static Decoder<byte[]> ofByteArray() {
182 return new ByteArrayDecoder();
183 }
184
185 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100186 * Creates a {@link Decoder} for a {@code Maybe} type.
187 *
188 * @param elementDecoder a {@link Decoder} for the contained element.
189 * @param <U> the element type.
190 * @return a new {@link Decoder}.
191 */
192 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
193 return new MaybeDecoder<>(elementDecoder);
194 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100195
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100196 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100197 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100198 *
199 * @param recordType the {@link Record} type that represents the components of the structure.
200 * @param componentDecoders a {@link Decoder} for each component of the structure.
201 * @param <U> the {@link Record} type that represents the components of the structure.
202 * @return a new {@link Decoder}.
203 */
204 public static <U extends Record> Decoder<U> ofStructure(
205 Class<U> recordType, Decoder<?>... componentDecoders) {
206 return new StructureDecoder<>(recordType, componentDecoders);
207 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100208
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100209 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100210 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
211 *
212 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
213 * more convenient.
214 *
215 * @param componentDecoders a {@link Decoder} for each component of the structure.
216 * @return a new {@link Decoder}.
217 */
218 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
219 return new TupleDecoder(componentDecoders);
220 }
221
222 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100223 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
224 * Map.Entry}.
225 *
226 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
227 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
228 * @return a new {@link Decoder}.
229 */
230 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
231 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
232 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
233 }
234
235 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100236 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100237 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100238 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100239 *
240 * <ul>
241 * <li>{@link Boolean}
242 * <li>{@link Byte}
243 * <li>{@link Short}
244 * <li>{@link Integer}
245 * <li>{@link Long}
246 * <li>{@link String}
247 * <li>{@link Optional} (a GVariant {@code Maybe} type)
248 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100249 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100250 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100251 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100252 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100253 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100254 *
255 * @return a new {@link Decoder}.
256 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100257 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100258 return new VariantDecoder();
259 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100260
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100261 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100262 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100263 *
264 * @return a new {@link Decoder}.
265 */
266 public static Decoder<Boolean> ofBoolean() {
267 return new BooleanDecoder();
268 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100269
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100270 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100271 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100272 *
273 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
274 * result of this method.
275 *
276 * @return a new {@link Decoder}.
277 */
278 public static Decoder<Byte> ofByte() {
279 return new ByteDecoder();
280 }
281
282 /**
283 * Creates a {@link Decoder} for the 16-bit {@code short} type.
284 *
285 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
286 * result of this method.
287 *
288 * @return a new {@link Decoder}.
289 */
290 public static Decoder<Short> ofShort() {
291 return new ShortDecoder();
292 }
293
294 /**
295 * Creates a {@link Decoder} for the 32-bit {@code int} type.
296 *
297 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
298 * result of this method.
299 *
300 * @return a new {@link Decoder}.
301 */
302 public static Decoder<Integer> ofInt() {
303 return new IntegerDecoder();
304 }
305
306 /**
307 * Creates a {@link Decoder} for the 64-bit {@code long} type.
308 *
309 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
310 * result of this method.
311 *
312 * @return a new {@link Decoder}.
313 */
314 public static Decoder<Long> ofLong() {
315 return new LongDecoder();
316 }
317
318 /**
319 * Creates a {@link Decoder} for the {@code double} type.
320 *
321 * @return a new {@link Decoder}.
322 */
323 public static Decoder<Double> ofDouble() {
324 return new DoubleDecoder();
325 }
326
327 /**
328 * Creates a {@link Decoder} for the {@link String} type.
329 *
330 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
331 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
332 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100333 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100334 * @return a new {@link Decoder}.
335 */
336 public static Decoder<String> ofString(Charset charset) {
337 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100338 }
339
340 private static int align(int offset, byte alignment) {
341 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
342 }
343
344 private static int getIntN(ByteBuffer byteSlice) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100345 return switch (byteSlice.limit()) {
346 case 0 -> 0;
347 case 1 ->
348 Byte.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).get());
349 case 2 ->
350 Short.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).getShort());
351 case 4 ->
352 byteSlice.order(LITTLE_ENDIAN).getInt();
353 default ->
354 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteSlice.limit()));
355 };
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100356 }
357
358 @SuppressWarnings("java:S3358")
359 private static int byteCount(int n) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100360 return n == 0 ? 0 : n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100361 }
362
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100363 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
364 // Determining the framing offset size requires trial and error.
365 int framingOffsetSize;
366 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100367 if (elementsRelativeEnd + (long)framingOffsetSize * framingOffsets.size() >= 1L << (8*framingOffsetSize)) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100368 continue;
369 }
370
371 if (framingOffsetSize > 4) {
372 throw new IllegalArgumentException("too many framing offsets");
373 }
374
375 return framingOffsetSize;
376 }
377 }
378
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100379 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100380
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100381 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100382
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100383 ArrayDecoder(Decoder<U> elementDecoder) {
384 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100385 }
386
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100387 @Override
388 public byte alignment() {
389 return elementDecoder.alignment();
390 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100391
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100392 @Override
393 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100394 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100395 return null;
396 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100397
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100398 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100399 public @NotNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100400 List<U> elements;
401
402 var elementSize = elementDecoder.fixedSize();
403 if (elementSize != null) {
404 // A simple C-style array.
405 elements = new ArrayList<>(byteSlice.limit() / elementSize);
406 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100407 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100408 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100409 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100410 } else if (byteSlice.limit() == 0) {
411 // A degenerate zero-length array of variable width.
412 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100413 } else {
414 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100415 int framingOffsetSize = max(1, byteCount(byteSlice.limit()));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100416 int lastFramingOffset =
417 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
418 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100419
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100420 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100421 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100422 for (int i = 0; i < elementCount; i++) {
423 int framingOffset =
424 getIntN(
425 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100426 elements.add(
427 elementDecoder.decode(
428 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100429 position = align(framingOffset, alignment());
430 }
431 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100432
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100433 return elements;
434 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100435
436 @Override
437 void encode(List<U> value, ByteWriter byteWriter) {
438 if (elementDecoder.hasFixedSize()) {
439 for (var element : value) {
440 elementDecoder.encode(element, byteWriter);
441 }
442 } else {
443 // Variable-width arrays are encoded with a vector of framing offsets in the end.
444 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
445 int startOffset = byteWriter.position();
446 for (var element : value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100447 // Align the element.
448 var lastRelativeEnd = byteWriter.position() - startOffset;
449 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
450
451 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100452 elementDecoder.encode(element, byteWriter);
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100453
454 // Record the framing offset of the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100455 var relativeEnd = byteWriter.position() - startOffset;
456 framingOffsets.add(relativeEnd);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100457 }
458
459 // Write the framing offsets.
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100460 int framingOffsetSize = max(1, computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets));
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100461 for (var framingOffset : framingOffsets) {
462 byteWriter.writeIntN(framingOffset, framingOffsetSize);
463 }
464 }
465 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100466 }
467
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100468 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
469
470 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
471
472 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
473 this.entryArrayDecoder =
474 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
475 }
476
477 @Override
478 public byte alignment() {
479 return entryArrayDecoder.alignment();
480 }
481
482 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100483 @Nullable
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100484 public Integer fixedSize() {
485 return entryArrayDecoder.fixedSize();
486 }
487
488 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100489 public @NotNull Map<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100490 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +0100491 Map<K, V> result = new LinkedHashMap<>();
492 for (var entry : entries) {
493 result.put(entry.getKey(), entry.getValue());
494 }
495 return result;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100496 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100497
498 @Override
499 void encode(Map<K, V> value, ByteWriter byteWriter) {
500 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
501 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100502 }
503
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100504 private static class ByteArrayDecoder extends Decoder<byte[]> {
505
506 private static final int ELEMENT_SIZE = 1;
507
508 @Override
509 public byte alignment() {
510 return ELEMENT_SIZE;
511 }
512
513 @Override
514 @Nullable
515 Integer fixedSize() {
516 return null;
517 }
518
519 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100520 public byte @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100521 // A simple C-style array.
522 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
523 byteSlice.get(elements);
524 return elements;
525 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100526
527 @Override
528 void encode(byte[] value, ByteWriter byteWriter) {
529 byteWriter.write(value);
530 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100531 }
532
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100533 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
534
535 private final Decoder<U> elementDecoder;
536
537 MaybeDecoder(Decoder<U> elementDecoder) {
538 this.elementDecoder = elementDecoder;
539 }
540
541 @Override
542 public byte alignment() {
543 return elementDecoder.alignment();
544 }
545
546 @Override
547 @Nullable
548 Integer fixedSize() {
549 return null;
550 }
551
552 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100553 public @NotNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100554 if (!byteSlice.hasRemaining()) {
555 return Optional.empty();
556 } else {
557 if (!elementDecoder.hasFixedSize()) {
558 // Remove trailing zero byte.
559 byteSlice.limit(byteSlice.limit() - 1);
560 }
561
562 return Optional.of(elementDecoder.decode(byteSlice));
563 }
564 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100565
566 @Override
567 void encode(Optional<U> value, ByteWriter byteWriter) {
568 if (value.isEmpty()) {
569 return;
570 }
571
572 elementDecoder.encode(value.get(), byteWriter);
573 if (!elementDecoder.hasFixedSize()) {
574 byteWriter.write((byte) 0);
575 }
576 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100577 }
578
579 private static class StructureDecoder<U extends Record> extends Decoder<U> {
580
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100581 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100582 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100583
584 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
585 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100586 if (componentDecoders.length != recordComponents.length) {
587 throw new IllegalArgumentException(
588 "number of decoders (%d) does not match number of structure components (%d)"
589 .formatted(componentDecoders.length, recordComponents.length));
590 }
591
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100592 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100593 this.tupleDecoder = new TupleDecoder(componentDecoders);
594 }
595
596 @Override
597 public byte alignment() {
598 return tupleDecoder.alignment();
599 }
600
601 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100602 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100603 return tupleDecoder.fixedSize();
604 }
605
606 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100607 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100608 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
609
610 try {
611 var recordComponentTypes =
612 Arrays.stream(recordType.getRecordComponents())
613 .map(RecordComponent::getType)
614 .toArray(Class<?>[]::new);
615 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
616 return recordConstructor.newInstance(recordConstructorArguments);
617 } catch (NoSuchMethodException
618 | InstantiationException
619 | IllegalAccessException
620 | InvocationTargetException e) {
621 throw new IllegalStateException(e);
622 }
623 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100624
625 @Override
626 void encode(U value, ByteWriter byteWriter) {
627 try {
628 var components = recordType.getRecordComponents();
629 List<Object> componentValues = new ArrayList<>(components.length);
630 for (var component : components) {
631 var accessor = component.getAccessor();
632 var componentValue = accessor.invoke(value);
633 componentValues.add(componentValue);
634 }
635 tupleDecoder.encode(componentValues.toArray(), byteWriter);
636 } catch (IllegalAccessException | InvocationTargetException e) {
637 throw new IllegalStateException(e);
638 }
639 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100640 }
641
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200642 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100643 private static class TupleDecoder extends Decoder<Object[]> {
644
645 private final Decoder<?>[] componentDecoders;
646
647 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100648 this.componentDecoders = componentDecoders;
649 }
650
651 @Override
652 public byte alignment() {
653 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
654 }
655
656 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100657 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100658 int position = 0;
659 for (var componentDecoder : componentDecoders) {
660 var fixedComponentSize = componentDecoder.fixedSize();
661 if (fixedComponentSize == null) {
662 return null;
663 }
664
665 position = align(position, componentDecoder.alignment());
666 position += fixedComponentSize;
667 }
668
669 if (position == 0) {
670 return 1;
671 }
672
673 return align(position, alignment());
674 }
675
676 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100677 public Object @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100678 int framingOffsetSize = byteCount(byteSlice.limit());
679
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100680 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100681
682 int position = 0;
683 int framingOffsetIndex = 0;
684 int componentIndex = 0;
685 for (var componentDecoder : componentDecoders) {
686 position = align(position, componentDecoder.alignment());
687
688 var fixedComponentSize = componentDecoder.fixedSize();
689 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100690 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100691 componentDecoder.decode(
692 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100693 position += fixedComponentSize;
694 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100695 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100696 // The last component never has a framing offset.
697 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100698 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100699 componentDecoder.decode(
700 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100701 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100702 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100703 int framingOffset =
704 getIntN(
705 byteSlice.slice(
706 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
707 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100708 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100709 componentDecoder.decode(
710 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100711 position = framingOffset;
712 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100713 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100714 }
715
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100716 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100717 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100718
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100719 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100720 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100721
722 @Override
723 @SuppressWarnings("unchecked")
724 void encode(Object[] value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100725 // The unit type is encoded as a single zero byte.
726 if (value.length == 0) {
727 byteWriter.write((byte) 0);
728 return;
729 }
730
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100731 int startOffset = byteWriter.position();
732 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
733 for (int i = 0; i < value.length; ++i) {
734 var componentDecoder = (Decoder<Object>) componentDecoders[i];
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100735
736 // Align the element.
737 var lastRelativeEnd = byteWriter.position() - startOffset;
738 byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]);
739
740 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100741 componentDecoder.encode(value[i], byteWriter);
742
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100743 // Record the framing offset of the element if it is of variable size.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100744 var fixedComponentSize = componentDecoders[i].fixedSize();
745 if (fixedComponentSize == null && i < value.length - 1) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100746 var relativeEnd = byteWriter.position() - startOffset;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100747 framingOffsets.add(relativeEnd);
748 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100749 }
750
751 // Write the framing offsets in reverse order.
752 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
753 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
754 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
755 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100756
757 // Pad the structure to its alignment if it is of fixed size.
758 if (fixedSize() != null) {
759 var lastRelativeEnd = byteWriter.position() - startOffset;
760 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
761 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100762 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100763 }
764
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100765 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
766
767 private final TupleDecoder tupleDecoder;
768
769 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
770 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
771 }
772
773 @Override
774 public byte alignment() {
775 return tupleDecoder.alignment();
776 }
777
778 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100779 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100780 return tupleDecoder.fixedSize();
781 }
782
783 @Override
784 @SuppressWarnings("unchecked")
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100785 public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100786 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200787 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100788 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100789
790 @Override
791 void encode(Entry<K, V> value, ByteWriter byteWriter) {
792 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
793 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100794 }
795
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100796 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100797
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100798 @Override
799 public byte alignment() {
800 return 8;
801 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100802
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100803 @Override
804 @Nullable
805 Integer fixedSize() {
806 return null;
807 }
808
809 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100810 public @NotNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100811 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
812 if (byteSlice.get(i) != 0) {
813 continue;
814 }
815
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100816 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100817 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100818
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100819 Signature signature;
820 try {
821 signature = Signature.parse(signatureBytes);
822 } catch (ParseException e) {
823 throw new IllegalArgumentException(e);
824 }
825
826 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100827 }
828
829 throw new IllegalArgumentException("variant signature not found");
830 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100831
832 @Override
833 void encode(Variant value, ByteWriter byteWriter) {
834 value.signature().decoder().encode(value.value(), byteWriter);
835 byteWriter.write((byte) 0);
836 byteWriter.write(value.signature().toString().getBytes(UTF_8));
837 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100838 }
839
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100840 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100841
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100842 @Override
843 public byte alignment() {
844 return 1;
845 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100846
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100847 @Override
848 public Integer fixedSize() {
849 return 1;
850 }
851
852 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100853 public @NotNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100854 return byteSlice.get() != 0;
855 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100856
857 @Override
858 void encode(Boolean value, ByteWriter byteWriter) {
859 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
860 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100861 }
862
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100863 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100864
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100865 @Override
866 public byte alignment() {
867 return 1;
868 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100869
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100870 @Override
871 public Integer fixedSize() {
872 return 1;
873 }
874
875 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100876 public @NotNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100877 return byteSlice.get();
878 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100879
880 @Override
881 void encode(Byte value, ByteWriter byteWriter) {
882 byteWriter.write(value);
883 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100884 }
885
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100886 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100887
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100888 @Override
889 public byte alignment() {
890 return 2;
891 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100892
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100893 @Override
894 public Integer fixedSize() {
895 return 2;
896 }
897
898 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100899 public @NotNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100900 return byteSlice.getShort();
901 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100902
903 @Override
904 void encode(Short value, ByteWriter byteWriter) {
905 byteWriter.write(value);
906 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100907 }
908
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100909 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100910
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100911 @Override
912 public byte alignment() {
913 return 4;
914 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100915
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100916 @Override
917 public Integer fixedSize() {
918 return 4;
919 }
920
921 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100922 public @NotNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100923 return byteSlice.getInt();
924 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100925
926 @Override
927 void encode(Integer value, ByteWriter byteWriter) {
928 byteWriter.write(value);
929 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100930 }
931
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100932 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100933
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100934 @Override
935 public byte alignment() {
936 return 8;
937 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100938
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100939 @Override
940 public Integer fixedSize() {
941 return 8;
942 }
943
944 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100945 public @NotNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100946 return byteSlice.getLong();
947 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100948
949 @Override
950 void encode(Long value, ByteWriter byteWriter) {
951 byteWriter.write(value);
952 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100953 }
954
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100955 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100956
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100957 @Override
958 public byte alignment() {
959 return 8;
960 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100961
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100962 @Override
963 public Integer fixedSize() {
964 return 8;
965 }
966
967 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100968 public @NotNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100969 return byteSlice.getDouble();
970 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100971
972 @Override
973 void encode(Double value, ByteWriter byteWriter) {
974 byteWriter.write(value);
975 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100976 }
977
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100978 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100979
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100980 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100981
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100982 public StringDecoder(Charset charset) {
983 this.charset = charset;
984 }
985
986 @Override
987 public byte alignment() {
988 return 1;
989 }
990
991 @Override
992 @Nullable
993 Integer fixedSize() {
994 return null;
995 }
996
997 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100998 public @NotNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100999 byteSlice.limit(byteSlice.limit() - 1);
1000 return charset.decode(byteSlice).toString();
1001 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001002
1003 @Override
1004 void encode(String value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001005 byteWriter.write(charset.encode(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001006 byteWriter.write((byte) 0);
1007 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001008 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001009
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001010 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001011 private class MappingDecoder<U> extends Decoder<U> {
1012
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001013 private final Function<@NotNull T, @NotNull U> decodingFunction;
1014 private final Function<@NotNull U, @NotNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001015
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001016 MappingDecoder(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
1017 this.decodingFunction = decodingFunction;
1018 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001019 }
1020
1021 @Override
1022 public byte alignment() {
1023 return Decoder.this.alignment();
1024 }
1025
1026 @Override
1027 public @Nullable Integer fixedSize() {
1028 return Decoder.this.fixedSize();
1029 }
1030
1031 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001032 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001033 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1034 }
1035
1036 @Override
1037 void encode(U value, ByteWriter byteWriter) {
1038 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001039 }
1040 }
1041
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001042 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001043 private class ContramappingDecoder extends Decoder<T> {
1044
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001045 private final UnaryOperator<ByteBuffer> decodingFunction;
1046 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001047
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001048 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1049 this.decodingFunction = decodingFunction;
1050 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001051 }
1052
1053 @Override
1054 public byte alignment() {
1055 return Decoder.this.alignment();
1056 }
1057
1058 @Override
1059 public @Nullable Integer fixedSize() {
1060 return Decoder.this.fixedSize();
1061 }
1062
1063 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001064 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001065 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001066 return Decoder.this.decode(transformedBuffer);
1067 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001068
1069 @Override
1070 void encode(T value, ByteWriter byteWriter) {
1071 var innerByteWriter = new ByteWriter();
1072 Decoder.this.encode(value, innerByteWriter);
1073 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001074 byteWriter.write(transformedBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001075 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001076 }
1077
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001078 private class ByteOrderFixingDecoder extends Decoder<T> {
1079
1080 private final ByteOrder byteOrder;
1081
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001082 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001083 this.byteOrder = byteOrder;
1084 }
1085
1086 @Override
1087 public byte alignment() {
1088 return Decoder.this.alignment();
1089 }
1090
1091 @Override
1092 public @Nullable Integer fixedSize() {
1093 return Decoder.this.fixedSize();
1094 }
1095
1096 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001097 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001098 var newByteSlice = byteSlice.duplicate();
1099 newByteSlice.order(byteOrder);
1100 return Decoder.this.decode(newByteSlice);
1101 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001102
1103 @Override
1104 protected void encode(T value, ByteWriter byteWriter) {
1105 var newByteWriter = byteWriter.duplicate();
1106 newByteWriter.order(byteOrder);
1107 Decoder.this.encode(value, newByteWriter);
1108 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001109 }
1110
1111 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1112 return byteSlice.slice(index, length).order(byteSlice.order());
1113 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001114
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001115 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001116 private static class PredicateDecoder<U> extends Decoder<U> {
1117
1118 private final Predicate<ByteBuffer> selector;
1119 private final Decoder<U> thenDecoder;
1120 private final Decoder<U> elseDecoder;
1121
1122 PredicateDecoder(
1123 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1124 this.selector = selector;
1125 this.thenDecoder = thenDecoder;
1126 this.elseDecoder = elseDecoder;
1127 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1128 throw new IllegalArgumentException(
1129 "incompatible alignments in predicate branches: then=%d, else=%d"
1130 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1131 }
1132
1133 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1134 throw new IllegalArgumentException(
1135 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001136 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001137 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1138 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001139 }
1140 }
1141
1142 @Override
1143 public byte alignment() {
1144 return thenDecoder.alignment();
1145 }
1146
1147 @Override
1148 public @Nullable Integer fixedSize() {
1149 return thenDecoder.fixedSize();
1150 }
1151
1152 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001153 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001154 var b = selector.test(byteSlice);
1155 byteSlice.rewind();
1156 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1157 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001158
1159 @Override
1160 public void encode(U value, ByteWriter byteWriter) {
1161 elseDecoder.encode(value, byteWriter);
1162 }
1163 }
1164
1165 private static class ByteWriter {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001166 private ByteOrder byteOrder = BIG_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001167 private final ByteArrayOutputStream outputStream;
1168
1169 ByteWriter() {
1170 this.outputStream = new ByteArrayOutputStream();
1171 }
1172
1173 private ByteWriter(ByteArrayOutputStream outputStream) {
1174 this.outputStream = outputStream;
1175 }
1176
1177 void write(byte[] bytes) {
1178 outputStream.write(bytes, 0, bytes.length);
1179 }
1180
1181 @SuppressWarnings("java:S2095")
1182 void write(ByteBuffer byteBuffer) {
1183 var channel = Channels.newChannel(outputStream);
1184 try {
1185 channel.write(byteBuffer);
1186 } catch (IOException e) {
1187 // impossible
1188 throw new IllegalStateException(e);
1189 }
1190 }
1191
1192 void write(byte value) {
1193 outputStream.write(value);
1194 }
1195
1196 void write(int value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001197 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001198 }
1199
1200 void write(long value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001201 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001202 }
1203
1204 void write(short value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001205 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001206 }
1207
1208 void write(double value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001209 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001210 }
1211
1212 private void writeIntN(int value, int byteCount) {
1213 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1214 switch (byteCount) {
1215 case 0 -> {}
1216 case 1 ->
1217 byteBuffer.put((byte) value);
1218 case 2 ->
1219 byteBuffer.putShort((short) value);
1220 case 4 ->
1221 byteBuffer.putInt(value);
1222 default ->
1223 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1224 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001225 write(byteBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001226 }
1227
1228 ByteWriter duplicate() {
1229 var duplicate = new ByteWriter(outputStream);
1230 duplicate.byteOrder = byteOrder;
1231 return duplicate;
1232 }
1233
1234 ByteBuffer toByteBuffer() {
1235 return ByteBuffer.wrap(outputStream.toByteArray());
1236 }
1237
1238 void order(ByteOrder byteOrder) {
1239 this.byteOrder = byteOrder;
1240 }
1241
1242 int position() {
1243 return outputStream.size();
1244 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001245 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001246}