blob: 60cef3e78f7ac77f84aa3c644450ab5601f7db9d [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 *
119 * @param function the function to apply.
120 * @return a new, decorated {@link Decoder}.
121 * @see java.util.stream.Stream#map
122 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100123 public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
124 return new MappingDecoder<>(decodingFunction, encodingFunction);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100125 }
126
127 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100128 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
129 *
130 * @param function the function to apply.
131 * @return a new, decorated {@link Decoder}.
132 * @see java.util.stream.Stream#map
133 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100134 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
135 return new ContramappingDecoder(decodingFunction, encodingFunction);
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100136 }
137
138 /**
139 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
140 * condition on the input {@link ByteBuffer}.
141 *
142 * @param selector the predicate to use to determine the decoder to use.
143 * @return a new {@link Decoder}.
144 */
145 public static <U> Decoder<U> ofPredicate(
146 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
147 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
148 }
149
150 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100151 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100152 *
153 * @param elementDecoder a {@link Decoder} for the elements of the array.
154 * @param <U> the element type.
155 * @return a new {@link Decoder}.
156 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100157 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100158 return new ArrayDecoder<>(elementDecoder);
159 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100160
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100161 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100162 * Creates a {@link Decoder} for a {@code Dictionary} type.
163 *
164 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
165 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
166 * @return a new {@link Decoder}.
167 */
168 public static <K, V> Decoder<Map<K, V>> ofDictionary(
169 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
170 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
171 }
172
173 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100174 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
175 * primitive {@code byte[]} array.
176 *
177 * @return a new {@link Decoder}.
178 */
179 public static Decoder<byte[]> ofByteArray() {
180 return new ByteArrayDecoder();
181 }
182
183 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100184 * Creates a {@link Decoder} for a {@code Maybe} type.
185 *
186 * @param elementDecoder a {@link Decoder} for the contained element.
187 * @param <U> the element type.
188 * @return a new {@link Decoder}.
189 */
190 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
191 return new MaybeDecoder<>(elementDecoder);
192 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100193
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100194 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100195 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100196 *
197 * @param recordType the {@link Record} type that represents the components of the structure.
198 * @param componentDecoders a {@link Decoder} for each component of the structure.
199 * @param <U> the {@link Record} type that represents the components of the structure.
200 * @return a new {@link Decoder}.
201 */
202 public static <U extends Record> Decoder<U> ofStructure(
203 Class<U> recordType, Decoder<?>... componentDecoders) {
204 return new StructureDecoder<>(recordType, componentDecoders);
205 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100206
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100207 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100208 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
209 *
210 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
211 * more convenient.
212 *
213 * @param componentDecoders a {@link Decoder} for each component of the structure.
214 * @return a new {@link Decoder}.
215 */
216 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
217 return new TupleDecoder(componentDecoders);
218 }
219
220 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100221 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
222 * Map.Entry}.
223 *
224 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
225 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
226 * @return a new {@link Decoder}.
227 */
228 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
229 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
230 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
231 }
232
233 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100234 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100235 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100236 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100237 *
238 * <ul>
239 * <li>{@link Boolean}
240 * <li>{@link Byte}
241 * <li>{@link Short}
242 * <li>{@link Integer}
243 * <li>{@link Long}
244 * <li>{@link String}
245 * <li>{@link Optional} (a GVariant {@code Maybe} type)
246 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100247 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100248 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100249 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100250 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100251 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100252 *
253 * @return a new {@link Decoder}.
254 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100255 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100256 return new VariantDecoder();
257 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100258
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100259 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100260 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100261 *
262 * @return a new {@link Decoder}.
263 */
264 public static Decoder<Boolean> ofBoolean() {
265 return new BooleanDecoder();
266 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100267
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100268 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100269 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100270 *
271 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
272 * result of this method.
273 *
274 * @return a new {@link Decoder}.
275 */
276 public static Decoder<Byte> ofByte() {
277 return new ByteDecoder();
278 }
279
280 /**
281 * Creates a {@link Decoder} for the 16-bit {@code short} type.
282 *
283 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
284 * result of this method.
285 *
286 * @return a new {@link Decoder}.
287 */
288 public static Decoder<Short> ofShort() {
289 return new ShortDecoder();
290 }
291
292 /**
293 * Creates a {@link Decoder} for the 32-bit {@code int} type.
294 *
295 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
296 * result of this method.
297 *
298 * @return a new {@link Decoder}.
299 */
300 public static Decoder<Integer> ofInt() {
301 return new IntegerDecoder();
302 }
303
304 /**
305 * Creates a {@link Decoder} for the 64-bit {@code long} type.
306 *
307 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
308 * result of this method.
309 *
310 * @return a new {@link Decoder}.
311 */
312 public static Decoder<Long> ofLong() {
313 return new LongDecoder();
314 }
315
316 /**
317 * Creates a {@link Decoder} for the {@code double} type.
318 *
319 * @return a new {@link Decoder}.
320 */
321 public static Decoder<Double> ofDouble() {
322 return new DoubleDecoder();
323 }
324
325 /**
326 * Creates a {@link Decoder} for the {@link String} type.
327 *
328 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
329 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
330 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100331 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100332 * @return a new {@link Decoder}.
333 */
334 public static Decoder<String> ofString(Charset charset) {
335 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100336 }
337
338 private static int align(int offset, byte alignment) {
339 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
340 }
341
342 private static int getIntN(ByteBuffer byteSlice) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100343 return switch (byteSlice.limit()) {
344 case 0 -> 0;
345 case 1 ->
346 Byte.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).get());
347 case 2 ->
348 Short.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).getShort());
349 case 4 ->
350 byteSlice.order(LITTLE_ENDIAN).getInt();
351 default ->
352 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteSlice.limit()));
353 };
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100354 }
355
356 @SuppressWarnings("java:S3358")
357 private static int byteCount(int n) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100358 return n == 0 ? 0 : n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100359 }
360
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100361 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
362 // Determining the framing offset size requires trial and error.
363 int framingOffsetSize;
364 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100365 if (elementsRelativeEnd + (long)framingOffsetSize * framingOffsets.size() >= 1L << (8*framingOffsetSize)) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100366 continue;
367 }
368
369 if (framingOffsetSize > 4) {
370 throw new IllegalArgumentException("too many framing offsets");
371 }
372
373 return framingOffsetSize;
374 }
375 }
376
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100377 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100378
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100379 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100380
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100381 ArrayDecoder(Decoder<U> elementDecoder) {
382 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100383 }
384
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100385 @Override
386 public byte alignment() {
387 return elementDecoder.alignment();
388 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100389
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100390 @Override
391 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100392 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100393 return null;
394 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100395
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100396 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100397 public @NotNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100398 List<U> elements;
399
400 var elementSize = elementDecoder.fixedSize();
401 if (elementSize != null) {
402 // A simple C-style array.
403 elements = new ArrayList<>(byteSlice.limit() / elementSize);
404 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100405 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100406 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100407 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100408 } else if (byteSlice.limit() == 0) {
409 // A degenerate zero-length array of variable width.
410 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100411 } else {
412 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100413 int framingOffsetSize = max(1, byteCount(byteSlice.limit()));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100414 int lastFramingOffset =
415 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
416 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100417
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100418 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100419 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100420 for (int i = 0; i < elementCount; i++) {
421 int framingOffset =
422 getIntN(
423 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100424 elements.add(
425 elementDecoder.decode(
426 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100427 position = align(framingOffset, alignment());
428 }
429 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100430
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100431 return elements;
432 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100433
434 @Override
435 void encode(List<U> value, ByteWriter byteWriter) {
436 if (elementDecoder.hasFixedSize()) {
437 for (var element : value) {
438 elementDecoder.encode(element, byteWriter);
439 }
440 } else {
441 // Variable-width arrays are encoded with a vector of framing offsets in the end.
442 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
443 int startOffset = byteWriter.position();
444 for (var element : value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100445 // Align the element.
446 var lastRelativeEnd = byteWriter.position() - startOffset;
447 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
448
449 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100450 elementDecoder.encode(element, byteWriter);
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100451
452 // Record the framing offset of the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100453 var relativeEnd = byteWriter.position() - startOffset;
454 framingOffsets.add(relativeEnd);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100455 }
456
457 // Write the framing offsets.
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100458 int framingOffsetSize = max(1, computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets));
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100459 for (var framingOffset : framingOffsets) {
460 byteWriter.writeIntN(framingOffset, framingOffsetSize);
461 }
462 }
463 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100464 }
465
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100466 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
467
468 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
469
470 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
471 this.entryArrayDecoder =
472 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
473 }
474
475 @Override
476 public byte alignment() {
477 return entryArrayDecoder.alignment();
478 }
479
480 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100481 @Nullable
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100482 public Integer fixedSize() {
483 return entryArrayDecoder.fixedSize();
484 }
485
486 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100487 public @NotNull Map<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100488 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +0100489 Map<K, V> result = new LinkedHashMap<>();
490 for (var entry : entries) {
491 result.put(entry.getKey(), entry.getValue());
492 }
493 return result;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100494 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100495
496 @Override
497 void encode(Map<K, V> value, ByteWriter byteWriter) {
498 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
499 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100500 }
501
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100502 private static class ByteArrayDecoder extends Decoder<byte[]> {
503
504 private static final int ELEMENT_SIZE = 1;
505
506 @Override
507 public byte alignment() {
508 return ELEMENT_SIZE;
509 }
510
511 @Override
512 @Nullable
513 Integer fixedSize() {
514 return null;
515 }
516
517 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100518 public byte @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100519 // A simple C-style array.
520 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
521 byteSlice.get(elements);
522 return elements;
523 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100524
525 @Override
526 void encode(byte[] value, ByteWriter byteWriter) {
527 byteWriter.write(value);
528 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100529 }
530
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100531 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
532
533 private final Decoder<U> elementDecoder;
534
535 MaybeDecoder(Decoder<U> elementDecoder) {
536 this.elementDecoder = elementDecoder;
537 }
538
539 @Override
540 public byte alignment() {
541 return elementDecoder.alignment();
542 }
543
544 @Override
545 @Nullable
546 Integer fixedSize() {
547 return null;
548 }
549
550 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100551 public @NotNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100552 if (!byteSlice.hasRemaining()) {
553 return Optional.empty();
554 } else {
555 if (!elementDecoder.hasFixedSize()) {
556 // Remove trailing zero byte.
557 byteSlice.limit(byteSlice.limit() - 1);
558 }
559
560 return Optional.of(elementDecoder.decode(byteSlice));
561 }
562 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100563
564 @Override
565 void encode(Optional<U> value, ByteWriter byteWriter) {
566 if (value.isEmpty()) {
567 return;
568 }
569
570 elementDecoder.encode(value.get(), byteWriter);
571 if (!elementDecoder.hasFixedSize()) {
572 byteWriter.write((byte) 0);
573 }
574 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100575 }
576
577 private static class StructureDecoder<U extends Record> extends Decoder<U> {
578
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100579 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100580 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100581
582 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
583 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100584 if (componentDecoders.length != recordComponents.length) {
585 throw new IllegalArgumentException(
586 "number of decoders (%d) does not match number of structure components (%d)"
587 .formatted(componentDecoders.length, recordComponents.length));
588 }
589
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100590 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100591 this.tupleDecoder = new TupleDecoder(componentDecoders);
592 }
593
594 @Override
595 public byte alignment() {
596 return tupleDecoder.alignment();
597 }
598
599 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100600 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100601 return tupleDecoder.fixedSize();
602 }
603
604 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100605 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100606 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
607
608 try {
609 var recordComponentTypes =
610 Arrays.stream(recordType.getRecordComponents())
611 .map(RecordComponent::getType)
612 .toArray(Class<?>[]::new);
613 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
614 return recordConstructor.newInstance(recordConstructorArguments);
615 } catch (NoSuchMethodException
616 | InstantiationException
617 | IllegalAccessException
618 | InvocationTargetException e) {
619 throw new IllegalStateException(e);
620 }
621 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100622
623 @Override
624 void encode(U value, ByteWriter byteWriter) {
625 try {
626 var components = recordType.getRecordComponents();
627 List<Object> componentValues = new ArrayList<>(components.length);
628 for (var component : components) {
629 var accessor = component.getAccessor();
630 var componentValue = accessor.invoke(value);
631 componentValues.add(componentValue);
632 }
633 tupleDecoder.encode(componentValues.toArray(), byteWriter);
634 } catch (IllegalAccessException | InvocationTargetException e) {
635 throw new IllegalStateException(e);
636 }
637 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100638 }
639
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200640 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100641 private static class TupleDecoder extends Decoder<Object[]> {
642
643 private final Decoder<?>[] componentDecoders;
644
645 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100646 this.componentDecoders = componentDecoders;
647 }
648
649 @Override
650 public byte alignment() {
651 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
652 }
653
654 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100655 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100656 int position = 0;
657 for (var componentDecoder : componentDecoders) {
658 var fixedComponentSize = componentDecoder.fixedSize();
659 if (fixedComponentSize == null) {
660 return null;
661 }
662
663 position = align(position, componentDecoder.alignment());
664 position += fixedComponentSize;
665 }
666
667 if (position == 0) {
668 return 1;
669 }
670
671 return align(position, alignment());
672 }
673
674 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100675 public Object @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100676 int framingOffsetSize = byteCount(byteSlice.limit());
677
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100678 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100679
680 int position = 0;
681 int framingOffsetIndex = 0;
682 int componentIndex = 0;
683 for (var componentDecoder : componentDecoders) {
684 position = align(position, componentDecoder.alignment());
685
686 var fixedComponentSize = componentDecoder.fixedSize();
687 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100688 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100689 componentDecoder.decode(
690 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100691 position += fixedComponentSize;
692 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100693 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100694 // The last component never has a framing offset.
695 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100696 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100697 componentDecoder.decode(
698 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100699 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100700 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100701 int framingOffset =
702 getIntN(
703 byteSlice.slice(
704 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
705 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100706 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100707 componentDecoder.decode(
708 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100709 position = framingOffset;
710 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100711 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100712 }
713
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100714 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100715 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100716
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100717 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100718 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100719
720 @Override
721 @SuppressWarnings("unchecked")
722 void encode(Object[] value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100723 // The unit type is encoded as a single zero byte.
724 if (value.length == 0) {
725 byteWriter.write((byte) 0);
726 return;
727 }
728
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100729 int startOffset = byteWriter.position();
730 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
731 for (int i = 0; i < value.length; ++i) {
732 var componentDecoder = (Decoder<Object>) componentDecoders[i];
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100733
734 // Align the element.
735 var lastRelativeEnd = byteWriter.position() - startOffset;
736 byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]);
737
738 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100739 componentDecoder.encode(value[i], byteWriter);
740
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100741 // Record the framing offset of the element if it is of variable size.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100742 var fixedComponentSize = componentDecoders[i].fixedSize();
743 if (fixedComponentSize == null && i < value.length - 1) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100744 var relativeEnd = byteWriter.position() - startOffset;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100745 framingOffsets.add(relativeEnd);
746 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100747 }
748
749 // Write the framing offsets in reverse order.
750 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
751 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
752 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
753 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100754
755 // Pad the structure to its alignment if it is of fixed size.
756 if (fixedSize() != null) {
757 var lastRelativeEnd = byteWriter.position() - startOffset;
758 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
759 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100760 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100761 }
762
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100763 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
764
765 private final TupleDecoder tupleDecoder;
766
767 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
768 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
769 }
770
771 @Override
772 public byte alignment() {
773 return tupleDecoder.alignment();
774 }
775
776 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100777 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100778 return tupleDecoder.fixedSize();
779 }
780
781 @Override
782 @SuppressWarnings("unchecked")
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100783 public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100784 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200785 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100786 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100787
788 @Override
789 void encode(Entry<K, V> value, ByteWriter byteWriter) {
790 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
791 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100792 }
793
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100794 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100795
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100796 @Override
797 public byte alignment() {
798 return 8;
799 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100800
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100801 @Override
802 @Nullable
803 Integer fixedSize() {
804 return null;
805 }
806
807 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100808 public @NotNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100809 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
810 if (byteSlice.get(i) != 0) {
811 continue;
812 }
813
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100814 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100815 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100816
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100817 Signature signature;
818 try {
819 signature = Signature.parse(signatureBytes);
820 } catch (ParseException e) {
821 throw new IllegalArgumentException(e);
822 }
823
824 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100825 }
826
827 throw new IllegalArgumentException("variant signature not found");
828 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100829
830 @Override
831 void encode(Variant value, ByteWriter byteWriter) {
832 value.signature().decoder().encode(value.value(), byteWriter);
833 byteWriter.write((byte) 0);
834 byteWriter.write(value.signature().toString().getBytes(UTF_8));
835 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100836 }
837
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100838 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100839
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100840 @Override
841 public byte alignment() {
842 return 1;
843 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100844
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100845 @Override
846 public Integer fixedSize() {
847 return 1;
848 }
849
850 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100851 public @NotNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100852 return byteSlice.get() != 0;
853 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100854
855 @Override
856 void encode(Boolean value, ByteWriter byteWriter) {
857 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
858 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100859 }
860
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100861 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100862
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100863 @Override
864 public byte alignment() {
865 return 1;
866 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100867
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100868 @Override
869 public Integer fixedSize() {
870 return 1;
871 }
872
873 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100874 public @NotNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100875 return byteSlice.get();
876 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100877
878 @Override
879 void encode(Byte value, ByteWriter byteWriter) {
880 byteWriter.write(value);
881 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100882 }
883
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100884 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100885
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100886 @Override
887 public byte alignment() {
888 return 2;
889 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100890
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100891 @Override
892 public Integer fixedSize() {
893 return 2;
894 }
895
896 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100897 public @NotNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100898 return byteSlice.getShort();
899 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100900
901 @Override
902 void encode(Short value, ByteWriter byteWriter) {
903 byteWriter.write(value);
904 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100905 }
906
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100907 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100908
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100909 @Override
910 public byte alignment() {
911 return 4;
912 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100913
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100914 @Override
915 public Integer fixedSize() {
916 return 4;
917 }
918
919 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100920 public @NotNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100921 return byteSlice.getInt();
922 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100923
924 @Override
925 void encode(Integer value, ByteWriter byteWriter) {
926 byteWriter.write(value);
927 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100928 }
929
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100930 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100931
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100932 @Override
933 public byte alignment() {
934 return 8;
935 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100936
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100937 @Override
938 public Integer fixedSize() {
939 return 8;
940 }
941
942 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100943 public @NotNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100944 return byteSlice.getLong();
945 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100946
947 @Override
948 void encode(Long value, ByteWriter byteWriter) {
949 byteWriter.write(value);
950 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100951 }
952
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100953 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100954
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100955 @Override
956 public byte alignment() {
957 return 8;
958 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100959
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100960 @Override
961 public Integer fixedSize() {
962 return 8;
963 }
964
965 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100966 public @NotNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100967 return byteSlice.getDouble();
968 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100969
970 @Override
971 void encode(Double value, ByteWriter byteWriter) {
972 byteWriter.write(value);
973 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100974 }
975
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100976 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100977
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100978 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100979
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100980 public StringDecoder(Charset charset) {
981 this.charset = charset;
982 }
983
984 @Override
985 public byte alignment() {
986 return 1;
987 }
988
989 @Override
990 @Nullable
991 Integer fixedSize() {
992 return null;
993 }
994
995 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100996 public @NotNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100997 byteSlice.limit(byteSlice.limit() - 1);
998 return charset.decode(byteSlice).toString();
999 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001000
1001 @Override
1002 void encode(String value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001003 byteWriter.write(charset.encode(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001004 byteWriter.write((byte) 0);
1005 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001006 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001007
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001008 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001009 private class MappingDecoder<U> extends Decoder<U> {
1010
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001011 private final Function<@NotNull T, @NotNull U> decodingFunction;
1012 private final Function<@NotNull U, @NotNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001013
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001014 MappingDecoder(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
1015 this.decodingFunction = decodingFunction;
1016 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001017 }
1018
1019 @Override
1020 public byte alignment() {
1021 return Decoder.this.alignment();
1022 }
1023
1024 @Override
1025 public @Nullable Integer fixedSize() {
1026 return Decoder.this.fixedSize();
1027 }
1028
1029 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001030 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001031 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1032 }
1033
1034 @Override
1035 void encode(U value, ByteWriter byteWriter) {
1036 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001037 }
1038 }
1039
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001040 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001041 private class ContramappingDecoder extends Decoder<T> {
1042
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001043 private final UnaryOperator<ByteBuffer> decodingFunction;
1044 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001045
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001046 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1047 this.decodingFunction = decodingFunction;
1048 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001049 }
1050
1051 @Override
1052 public byte alignment() {
1053 return Decoder.this.alignment();
1054 }
1055
1056 @Override
1057 public @Nullable Integer fixedSize() {
1058 return Decoder.this.fixedSize();
1059 }
1060
1061 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001062 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001063 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001064 return Decoder.this.decode(transformedBuffer);
1065 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001066
1067 @Override
1068 void encode(T value, ByteWriter byteWriter) {
1069 var innerByteWriter = new ByteWriter();
1070 Decoder.this.encode(value, innerByteWriter);
1071 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001072 byteWriter.write(transformedBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001073 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001074 }
1075
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001076 private class ByteOrderFixingDecoder extends Decoder<T> {
1077
1078 private final ByteOrder byteOrder;
1079
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001080 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001081 this.byteOrder = byteOrder;
1082 }
1083
1084 @Override
1085 public byte alignment() {
1086 return Decoder.this.alignment();
1087 }
1088
1089 @Override
1090 public @Nullable Integer fixedSize() {
1091 return Decoder.this.fixedSize();
1092 }
1093
1094 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001095 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001096 var newByteSlice = byteSlice.duplicate();
1097 newByteSlice.order(byteOrder);
1098 return Decoder.this.decode(newByteSlice);
1099 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001100
1101 @Override
1102 protected void encode(T value, ByteWriter byteWriter) {
1103 var newByteWriter = byteWriter.duplicate();
1104 newByteWriter.order(byteOrder);
1105 Decoder.this.encode(value, newByteWriter);
1106 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001107 }
1108
1109 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1110 return byteSlice.slice(index, length).order(byteSlice.order());
1111 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001112
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001113 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001114 private static class PredicateDecoder<U> extends Decoder<U> {
1115
1116 private final Predicate<ByteBuffer> selector;
1117 private final Decoder<U> thenDecoder;
1118 private final Decoder<U> elseDecoder;
1119
1120 PredicateDecoder(
1121 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1122 this.selector = selector;
1123 this.thenDecoder = thenDecoder;
1124 this.elseDecoder = elseDecoder;
1125 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1126 throw new IllegalArgumentException(
1127 "incompatible alignments in predicate branches: then=%d, else=%d"
1128 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1129 }
1130
1131 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1132 throw new IllegalArgumentException(
1133 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001134 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001135 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1136 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001137 }
1138 }
1139
1140 @Override
1141 public byte alignment() {
1142 return thenDecoder.alignment();
1143 }
1144
1145 @Override
1146 public @Nullable Integer fixedSize() {
1147 return thenDecoder.fixedSize();
1148 }
1149
1150 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001151 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001152 var b = selector.test(byteSlice);
1153 byteSlice.rewind();
1154 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1155 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001156
1157 @Override
1158 public void encode(U value, ByteWriter byteWriter) {
1159 elseDecoder.encode(value, byteWriter);
1160 }
1161 }
1162
1163 private static class ByteWriter {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001164 private ByteOrder byteOrder = BIG_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001165 private final ByteArrayOutputStream outputStream;
1166
1167 ByteWriter() {
1168 this.outputStream = new ByteArrayOutputStream();
1169 }
1170
1171 private ByteWriter(ByteArrayOutputStream outputStream) {
1172 this.outputStream = outputStream;
1173 }
1174
1175 void write(byte[] bytes) {
1176 outputStream.write(bytes, 0, bytes.length);
1177 }
1178
1179 @SuppressWarnings("java:S2095")
1180 void write(ByteBuffer byteBuffer) {
1181 var channel = Channels.newChannel(outputStream);
1182 try {
1183 channel.write(byteBuffer);
1184 } catch (IOException e) {
1185 // impossible
1186 throw new IllegalStateException(e);
1187 }
1188 }
1189
1190 void write(byte value) {
1191 outputStream.write(value);
1192 }
1193
1194 void write(int value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001195 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001196 }
1197
1198 void write(long value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001199 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001200 }
1201
1202 void write(short value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001203 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001204 }
1205
1206 void write(double value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001207 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001208 }
1209
1210 private void writeIntN(int value, int byteCount) {
1211 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1212 switch (byteCount) {
1213 case 0 -> {}
1214 case 1 ->
1215 byteBuffer.put((byte) value);
1216 case 2 ->
1217 byteBuffer.putShort((short) value);
1218 case 4 ->
1219 byteBuffer.putInt(value);
1220 default ->
1221 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1222 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001223 write(byteBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001224 }
1225
1226 ByteWriter duplicate() {
1227 var duplicate = new ByteWriter(outputStream);
1228 duplicate.byteOrder = byteOrder;
1229 return duplicate;
1230 }
1231
1232 ByteBuffer toByteBuffer() {
1233 return ByteBuffer.wrap(outputStream.toByteArray());
1234 }
1235
1236 void order(ByteOrder byteOrder) {
1237 this.byteOrder = byteOrder;
1238 }
1239
1240 int position() {
1241 return outputStream.size();
1242 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001243 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001244}