blob: 9362487761843f5caa322bf410fed4e572c31623 [file] [log] [blame]
Matthias Andreas Benkardb5d657a2022-02-03 21:14:30 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01005package eu.mulk.jgvariant.core;
6
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01007import static java.lang.Math.max;
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01008import static java.nio.ByteOrder.BIG_ENDIAN;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01009import static java.nio.ByteOrder.LITTLE_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010010import static java.nio.charset.StandardCharsets.UTF_8;
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020011import static java.util.Objects.requireNonNullElse;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010012import static java.util.stream.Collectors.toMap;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010013
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020014import com.google.errorprone.annotations.Immutable;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010015
16import java.io.ByteArrayOutputStream;
17import java.io.IOException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010018import java.lang.reflect.InvocationTargetException;
19import java.lang.reflect.RecordComponent;
20import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010022import java.nio.channels.Channels;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010023import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010024import java.text.ParseException;
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +010025import java.util.*;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010026import java.util.Map.Entry;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010027import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010028import java.util.function.Predicate;
29import java.util.function.UnaryOperator;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010030
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010031import org.apiguardian.api.API;
32import org.apiguardian.api.API.Status;
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010033import org.jetbrains.annotations.NotNull;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010034import org.jetbrains.annotations.Nullable;
35
36/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010037 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010038 *
39 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
40 * the type you wish to decode.
41 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010042 * <p><strong>Example</strong>
43 *
44 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
45 * {@code int}, you can use the following code:
46 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020047 * {@snippet lang="java" :
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010048 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010049 *
50 * var decoder =
51 * Decoder.ofArray(
52 * Decoder.ofStructure(
53 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010054 * Decoder.ofString(UTF_8),
55 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010056 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020057 * byte[] bytes;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010058 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020059 * }
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010060 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010061 * @param <T> the type that the {@link Decoder} can decode.
62 */
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010063@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020064@Immutable
65@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010066public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010067
68 private Decoder() {}
69
70 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010071 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
72 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010073 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
74 * important that the start and end boundaries of the passed byte slice correspond to the actual
75 * start and end of the serialized value. The format does generally not allow for the dynamic
76 * discovery of the end of the data structure.
77 *
78 * @param byteSlice a byte slice holding a serialized GVariant.
79 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010080 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
81 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010082 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010083 */
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010084 public abstract @NotNull T decode(ByteBuffer byteSlice);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010085
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010086 /**
87 * Encodes a value of type {@code T} into a {@link ByteBuffer} holding a serialized GVariant.
88 *
89 * @param value the value to serialize.
90 * @return a {@link ByteBuffer} holding the serialized value.
91 */
92 public final ByteBuffer encode(T value) {
93 var byteWriter = new ByteWriter();
94 encode(value, byteWriter);
95 return byteWriter.toByteBuffer();
96 }
97
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010098 abstract byte alignment();
99
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100100 abstract @Nullable Integer fixedSize();
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100101
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100102 abstract void encode(T value, ByteWriter byteWriter);
103
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100104 final boolean hasFixedSize() {
105 return fixedSize() != null;
106 }
107
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100108 /**
109 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
110 *
111 * @param byteOrder the byte order to use.
112 * @return a new, decorated {@link Decoder}.
113 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100114 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100115 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100116 }
117
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100118 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100119 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
120 *
121 * @param function the function to apply.
122 * @return a new, decorated {@link Decoder}.
123 * @see java.util.stream.Stream#map
124 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100125 public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
126 return new MappingDecoder<>(decodingFunction, encodingFunction);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100127 }
128
129 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100130 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
131 *
132 * @param function the function to apply.
133 * @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) {
345 var intBytes = new byte[4];
346 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
347 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
348 }
349
350 @SuppressWarnings("java:S3358")
351 private static int byteCount(int n) {
352 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
353 }
354
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100355 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
356 // Determining the framing offset size requires trial and error.
357 int framingOffsetSize;
358 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
359 if (elementsRelativeEnd + framingOffsetSize* framingOffsets.size() >= 1 << (8*framingOffsetSize)) {
360 continue;
361 }
362
363 if (framingOffsetSize > 4) {
364 throw new IllegalArgumentException("too many framing offsets");
365 }
366
367 return framingOffsetSize;
368 }
369 }
370
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100371 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100372
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100373 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100374
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100375 ArrayDecoder(Decoder<U> elementDecoder) {
376 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100377 }
378
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100379 @Override
380 public byte alignment() {
381 return elementDecoder.alignment();
382 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100383
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100384 @Override
385 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100386 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100387 return null;
388 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100389
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100390 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100391 public @NotNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100392 List<U> elements;
393
394 var elementSize = elementDecoder.fixedSize();
395 if (elementSize != null) {
396 // A simple C-style array.
397 elements = new ArrayList<>(byteSlice.limit() / elementSize);
398 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100399 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100400 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100401 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100402 } else if (byteSlice.limit() == 0) {
403 // A degenerate zero-length array of variable width.
404 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100405 } else {
406 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100407 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100408 int lastFramingOffset =
409 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
410 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100411
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100412 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100413 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100414 for (int i = 0; i < elementCount; i++) {
415 int framingOffset =
416 getIntN(
417 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100418 elements.add(
419 elementDecoder.decode(
420 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100421 position = align(framingOffset, alignment());
422 }
423 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100424
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100425 return elements;
426 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100427
428 @Override
429 void encode(List<U> value, ByteWriter byteWriter) {
430 if (elementDecoder.hasFixedSize()) {
431 for (var element : value) {
432 elementDecoder.encode(element, byteWriter);
433 }
434 } else {
435 // Variable-width arrays are encoded with a vector of framing offsets in the end.
436 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
437 int startOffset = byteWriter.position();
438 for (var element : value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100439 // Align the element.
440 var lastRelativeEnd = byteWriter.position() - startOffset;
441 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
442
443 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100444 elementDecoder.encode(element, byteWriter);
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100445
446 // Record the framing offset of the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100447 var relativeEnd = byteWriter.position() - startOffset;
448 framingOffsets.add(relativeEnd);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100449 }
450
451 // Write the framing offsets.
452 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
453 for (var framingOffset : framingOffsets) {
454 byteWriter.writeIntN(framingOffset, framingOffsetSize);
455 }
456 }
457 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100458 }
459
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100460 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
461
462 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
463
464 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
465 this.entryArrayDecoder =
466 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
467 }
468
469 @Override
470 public byte alignment() {
471 return entryArrayDecoder.alignment();
472 }
473
474 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100475 @Nullable
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100476 public Integer fixedSize() {
477 return entryArrayDecoder.fixedSize();
478 }
479
480 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100481 public @NotNull Map<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100482 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +0100483 Map<K, V> result = new LinkedHashMap<>();
484 for (var entry : entries) {
485 result.put(entry.getKey(), entry.getValue());
486 }
487 return result;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100488 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100489
490 @Override
491 void encode(Map<K, V> value, ByteWriter byteWriter) {
492 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
493 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100494 }
495
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100496 private static class ByteArrayDecoder extends Decoder<byte[]> {
497
498 private static final int ELEMENT_SIZE = 1;
499
500 @Override
501 public byte alignment() {
502 return ELEMENT_SIZE;
503 }
504
505 @Override
506 @Nullable
507 Integer fixedSize() {
508 return null;
509 }
510
511 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100512 public byte @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100513 // A simple C-style array.
514 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
515 byteSlice.get(elements);
516 return elements;
517 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100518
519 @Override
520 void encode(byte[] value, ByteWriter byteWriter) {
521 byteWriter.write(value);
522 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100523 }
524
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100525 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
526
527 private final Decoder<U> elementDecoder;
528
529 MaybeDecoder(Decoder<U> elementDecoder) {
530 this.elementDecoder = elementDecoder;
531 }
532
533 @Override
534 public byte alignment() {
535 return elementDecoder.alignment();
536 }
537
538 @Override
539 @Nullable
540 Integer fixedSize() {
541 return null;
542 }
543
544 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100545 public @NotNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100546 if (!byteSlice.hasRemaining()) {
547 return Optional.empty();
548 } else {
549 if (!elementDecoder.hasFixedSize()) {
550 // Remove trailing zero byte.
551 byteSlice.limit(byteSlice.limit() - 1);
552 }
553
554 return Optional.of(elementDecoder.decode(byteSlice));
555 }
556 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100557
558 @Override
559 void encode(Optional<U> value, ByteWriter byteWriter) {
560 if (value.isEmpty()) {
561 return;
562 }
563
564 elementDecoder.encode(value.get(), byteWriter);
565 if (!elementDecoder.hasFixedSize()) {
566 byteWriter.write((byte) 0);
567 }
568 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100569 }
570
571 private static class StructureDecoder<U extends Record> extends Decoder<U> {
572
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100573 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100574 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100575
576 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
577 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100578 if (componentDecoders.length != recordComponents.length) {
579 throw new IllegalArgumentException(
580 "number of decoders (%d) does not match number of structure components (%d)"
581 .formatted(componentDecoders.length, recordComponents.length));
582 }
583
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100584 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100585 this.tupleDecoder = new TupleDecoder(componentDecoders);
586 }
587
588 @Override
589 public byte alignment() {
590 return tupleDecoder.alignment();
591 }
592
593 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100594 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100595 return tupleDecoder.fixedSize();
596 }
597
598 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100599 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100600 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
601
602 try {
603 var recordComponentTypes =
604 Arrays.stream(recordType.getRecordComponents())
605 .map(RecordComponent::getType)
606 .toArray(Class<?>[]::new);
607 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
608 return recordConstructor.newInstance(recordConstructorArguments);
609 } catch (NoSuchMethodException
610 | InstantiationException
611 | IllegalAccessException
612 | InvocationTargetException e) {
613 throw new IllegalStateException(e);
614 }
615 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100616
617 @Override
618 void encode(U value, ByteWriter byteWriter) {
619 try {
620 var components = recordType.getRecordComponents();
621 List<Object> componentValues = new ArrayList<>(components.length);
622 for (var component : components) {
623 var accessor = component.getAccessor();
624 var componentValue = accessor.invoke(value);
625 componentValues.add(componentValue);
626 }
627 tupleDecoder.encode(componentValues.toArray(), byteWriter);
628 } catch (IllegalAccessException | InvocationTargetException e) {
629 throw new IllegalStateException(e);
630 }
631 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100632 }
633
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200634 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100635 private static class TupleDecoder extends Decoder<Object[]> {
636
637 private final Decoder<?>[] componentDecoders;
638
639 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100640 this.componentDecoders = componentDecoders;
641 }
642
643 @Override
644 public byte alignment() {
645 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
646 }
647
648 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100649 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100650 int position = 0;
651 for (var componentDecoder : componentDecoders) {
652 var fixedComponentSize = componentDecoder.fixedSize();
653 if (fixedComponentSize == null) {
654 return null;
655 }
656
657 position = align(position, componentDecoder.alignment());
658 position += fixedComponentSize;
659 }
660
661 if (position == 0) {
662 return 1;
663 }
664
665 return align(position, alignment());
666 }
667
668 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100669 public Object @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100670 int framingOffsetSize = byteCount(byteSlice.limit());
671
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100672 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100673
674 int position = 0;
675 int framingOffsetIndex = 0;
676 int componentIndex = 0;
677 for (var componentDecoder : componentDecoders) {
678 position = align(position, componentDecoder.alignment());
679
680 var fixedComponentSize = componentDecoder.fixedSize();
681 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100682 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100683 componentDecoder.decode(
684 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100685 position += fixedComponentSize;
686 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100687 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100688 // The last component never has a framing offset.
689 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
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, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100693 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100694 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100695 int framingOffset =
696 getIntN(
697 byteSlice.slice(
698 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
699 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100700 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100701 componentDecoder.decode(
702 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100703 position = framingOffset;
704 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100705 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100706 }
707
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100708 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100709 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100710
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100711 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100712 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100713
714 @Override
715 @SuppressWarnings("unchecked")
716 void encode(Object[] value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100717 // The unit type is encoded as a single zero byte.
718 if (value.length == 0) {
719 byteWriter.write((byte) 0);
720 return;
721 }
722
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100723 int startOffset = byteWriter.position();
724 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
725 for (int i = 0; i < value.length; ++i) {
726 var componentDecoder = (Decoder<Object>) componentDecoders[i];
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100727
728 // Align the element.
729 var lastRelativeEnd = byteWriter.position() - startOffset;
730 byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]);
731
732 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100733 componentDecoder.encode(value[i], byteWriter);
734
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100735 // Record the framing offset of the element if it is of variable size.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100736 var fixedComponentSize = componentDecoders[i].fixedSize();
737 if (fixedComponentSize == null && i < value.length - 1) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100738 var relativeEnd = byteWriter.position() - startOffset;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100739 framingOffsets.add(relativeEnd);
740 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100741 }
742
743 // Write the framing offsets in reverse order.
744 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
745 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
746 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
747 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100748
749 // Pad the structure to its alignment if it is of fixed size.
750 if (fixedSize() != null) {
751 var lastRelativeEnd = byteWriter.position() - startOffset;
752 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
753 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100754 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100755 }
756
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100757 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
758
759 private final TupleDecoder tupleDecoder;
760
761 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
762 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
763 }
764
765 @Override
766 public byte alignment() {
767 return tupleDecoder.alignment();
768 }
769
770 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100771 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100772 return tupleDecoder.fixedSize();
773 }
774
775 @Override
776 @SuppressWarnings("unchecked")
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100777 public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100778 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200779 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100780 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100781
782 @Override
783 void encode(Entry<K, V> value, ByteWriter byteWriter) {
784 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
785 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100786 }
787
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100788 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100789
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100790 @Override
791 public byte alignment() {
792 return 8;
793 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100794
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100795 @Override
796 @Nullable
797 Integer fixedSize() {
798 return null;
799 }
800
801 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100802 public @NotNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100803 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
804 if (byteSlice.get(i) != 0) {
805 continue;
806 }
807
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100808 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100809 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100810
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100811 Signature signature;
812 try {
813 signature = Signature.parse(signatureBytes);
814 } catch (ParseException e) {
815 throw new IllegalArgumentException(e);
816 }
817
818 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100819 }
820
821 throw new IllegalArgumentException("variant signature not found");
822 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100823
824 @Override
825 void encode(Variant value, ByteWriter byteWriter) {
826 value.signature().decoder().encode(value.value(), byteWriter);
827 byteWriter.write((byte) 0);
828 byteWriter.write(value.signature().toString().getBytes(UTF_8));
829 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100830 }
831
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100832 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100833
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100834 @Override
835 public byte alignment() {
836 return 1;
837 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100838
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100839 @Override
840 public Integer fixedSize() {
841 return 1;
842 }
843
844 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100845 public @NotNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100846 return byteSlice.get() != 0;
847 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100848
849 @Override
850 void encode(Boolean value, ByteWriter byteWriter) {
851 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
852 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100853 }
854
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100855 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100856
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100857 @Override
858 public byte alignment() {
859 return 1;
860 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100861
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100862 @Override
863 public Integer fixedSize() {
864 return 1;
865 }
866
867 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100868 public @NotNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100869 return byteSlice.get();
870 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100871
872 @Override
873 void encode(Byte value, ByteWriter byteWriter) {
874 byteWriter.write(value);
875 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100876 }
877
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100878 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100879
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100880 @Override
881 public byte alignment() {
882 return 2;
883 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100884
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100885 @Override
886 public Integer fixedSize() {
887 return 2;
888 }
889
890 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100891 public @NotNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100892 return byteSlice.getShort();
893 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100894
895 @Override
896 void encode(Short value, ByteWriter byteWriter) {
897 byteWriter.write(value);
898 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100899 }
900
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100901 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100902
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100903 @Override
904 public byte alignment() {
905 return 4;
906 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100907
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100908 @Override
909 public Integer fixedSize() {
910 return 4;
911 }
912
913 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100914 public @NotNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100915 return byteSlice.getInt();
916 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100917
918 @Override
919 void encode(Integer value, ByteWriter byteWriter) {
920 byteWriter.write(value);
921 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100922 }
923
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100924 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100925
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100926 @Override
927 public byte alignment() {
928 return 8;
929 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100930
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100931 @Override
932 public Integer fixedSize() {
933 return 8;
934 }
935
936 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100937 public @NotNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100938 return byteSlice.getLong();
939 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100940
941 @Override
942 void encode(Long value, ByteWriter byteWriter) {
943 byteWriter.write(value);
944 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100945 }
946
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100947 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100948
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100949 @Override
950 public byte alignment() {
951 return 8;
952 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100953
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100954 @Override
955 public Integer fixedSize() {
956 return 8;
957 }
958
959 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100960 public @NotNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100961 return byteSlice.getDouble();
962 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100963
964 @Override
965 void encode(Double value, ByteWriter byteWriter) {
966 byteWriter.write(value);
967 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100968 }
969
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100970 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100971
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100972 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100973
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100974 public StringDecoder(Charset charset) {
975 this.charset = charset;
976 }
977
978 @Override
979 public byte alignment() {
980 return 1;
981 }
982
983 @Override
984 @Nullable
985 Integer fixedSize() {
986 return null;
987 }
988
989 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100990 public @NotNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100991 byteSlice.limit(byteSlice.limit() - 1);
992 return charset.decode(byteSlice).toString();
993 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100994
995 @Override
996 void encode(String value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100997 byteWriter.write(charset.encode(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100998 byteWriter.write((byte) 0);
999 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001000 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001001
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001002 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001003 private class MappingDecoder<U> extends Decoder<U> {
1004
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001005 private final Function<@NotNull T, @NotNull U> decodingFunction;
1006 private final Function<@NotNull U, @NotNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001007
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001008 MappingDecoder(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
1009 this.decodingFunction = decodingFunction;
1010 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001011 }
1012
1013 @Override
1014 public byte alignment() {
1015 return Decoder.this.alignment();
1016 }
1017
1018 @Override
1019 public @Nullable Integer fixedSize() {
1020 return Decoder.this.fixedSize();
1021 }
1022
1023 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001024 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001025 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1026 }
1027
1028 @Override
1029 void encode(U value, ByteWriter byteWriter) {
1030 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001031 }
1032 }
1033
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001034 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001035 private class ContramappingDecoder extends Decoder<T> {
1036
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001037 private final UnaryOperator<ByteBuffer> decodingFunction;
1038 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001039
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001040 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1041 this.decodingFunction = decodingFunction;
1042 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001043 }
1044
1045 @Override
1046 public byte alignment() {
1047 return Decoder.this.alignment();
1048 }
1049
1050 @Override
1051 public @Nullable Integer fixedSize() {
1052 return Decoder.this.fixedSize();
1053 }
1054
1055 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001056 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001057 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001058 return Decoder.this.decode(transformedBuffer);
1059 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001060
1061 @Override
1062 void encode(T value, ByteWriter byteWriter) {
1063 var innerByteWriter = new ByteWriter();
1064 Decoder.this.encode(value, innerByteWriter);
1065 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001066 byteWriter.write(transformedBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001067 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001068 }
1069
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001070 private class ByteOrderFixingDecoder extends Decoder<T> {
1071
1072 private final ByteOrder byteOrder;
1073
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001074 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001075 this.byteOrder = byteOrder;
1076 }
1077
1078 @Override
1079 public byte alignment() {
1080 return Decoder.this.alignment();
1081 }
1082
1083 @Override
1084 public @Nullable Integer fixedSize() {
1085 return Decoder.this.fixedSize();
1086 }
1087
1088 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001089 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001090 var newByteSlice = byteSlice.duplicate();
1091 newByteSlice.order(byteOrder);
1092 return Decoder.this.decode(newByteSlice);
1093 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001094
1095 @Override
1096 protected void encode(T value, ByteWriter byteWriter) {
1097 var newByteWriter = byteWriter.duplicate();
1098 newByteWriter.order(byteOrder);
1099 Decoder.this.encode(value, newByteWriter);
1100 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001101 }
1102
1103 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1104 return byteSlice.slice(index, length).order(byteSlice.order());
1105 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001106
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001107 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001108 private static class PredicateDecoder<U> extends Decoder<U> {
1109
1110 private final Predicate<ByteBuffer> selector;
1111 private final Decoder<U> thenDecoder;
1112 private final Decoder<U> elseDecoder;
1113
1114 PredicateDecoder(
1115 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1116 this.selector = selector;
1117 this.thenDecoder = thenDecoder;
1118 this.elseDecoder = elseDecoder;
1119 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1120 throw new IllegalArgumentException(
1121 "incompatible alignments in predicate branches: then=%d, else=%d"
1122 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1123 }
1124
1125 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1126 throw new IllegalArgumentException(
1127 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001128 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001129 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1130 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001131 }
1132 }
1133
1134 @Override
1135 public byte alignment() {
1136 return thenDecoder.alignment();
1137 }
1138
1139 @Override
1140 public @Nullable Integer fixedSize() {
1141 return thenDecoder.fixedSize();
1142 }
1143
1144 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001145 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001146 var b = selector.test(byteSlice);
1147 byteSlice.rewind();
1148 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1149 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001150
1151 @Override
1152 public void encode(U value, ByteWriter byteWriter) {
1153 elseDecoder.encode(value, byteWriter);
1154 }
1155 }
1156
1157 private static class ByteWriter {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001158 private ByteOrder byteOrder = BIG_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001159 private final ByteArrayOutputStream outputStream;
1160
1161 ByteWriter() {
1162 this.outputStream = new ByteArrayOutputStream();
1163 }
1164
1165 private ByteWriter(ByteArrayOutputStream outputStream) {
1166 this.outputStream = outputStream;
1167 }
1168
1169 void write(byte[] bytes) {
1170 outputStream.write(bytes, 0, bytes.length);
1171 }
1172
1173 @SuppressWarnings("java:S2095")
1174 void write(ByteBuffer byteBuffer) {
1175 var channel = Channels.newChannel(outputStream);
1176 try {
1177 channel.write(byteBuffer);
1178 } catch (IOException e) {
1179 // impossible
1180 throw new IllegalStateException(e);
1181 }
1182 }
1183
1184 void write(byte value) {
1185 outputStream.write(value);
1186 }
1187
1188 void write(int value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001189 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001190 }
1191
1192 void write(long value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001193 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001194 }
1195
1196 void write(short value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001197 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001198 }
1199
1200 void write(double value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001201 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001202 }
1203
1204 private void writeIntN(int value, int byteCount) {
1205 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1206 switch (byteCount) {
1207 case 0 -> {}
1208 case 1 ->
1209 byteBuffer.put((byte) value);
1210 case 2 ->
1211 byteBuffer.putShort((short) value);
1212 case 4 ->
1213 byteBuffer.putInt(value);
1214 default ->
1215 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1216 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001217 write(byteBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001218 }
1219
1220 ByteWriter duplicate() {
1221 var duplicate = new ByteWriter(outputStream);
1222 duplicate.byteOrder = byteOrder;
1223 return duplicate;
1224 }
1225
1226 ByteBuffer toByteBuffer() {
1227 return ByteBuffer.wrap(outputStream.toByteArray());
1228 }
1229
1230 void order(ByteOrder byteOrder) {
1231 this.byteOrder = byteOrder;
1232 }
1233
1234 int position() {
1235 return outputStream.size();
1236 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001237 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001238}