blob: 4f2ae107a7f4015d505027c8de43de023ec1e56d [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 Benkard15456c82024-07-20 10:52:54 +02007import static java.lang.Math.max;
8import static java.nio.ByteOrder.BIG_ENDIAN;
9import static java.nio.ByteOrder.LITTLE_ENDIAN;
10import static java.nio.charset.StandardCharsets.UTF_8;
11import static java.util.Objects.requireNonNullElse;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010012
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +020013import com.google.errorprone.annotations.Immutable;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010014import java.io.ByteArrayOutputStream;
15import java.io.IOException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010016import java.lang.reflect.InvocationTargetException;
17import java.lang.reflect.RecordComponent;
18import java.nio.ByteBuffer;
19import java.nio.ByteOrder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010020import java.nio.channels.Channels;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010021import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010022import java.text.ParseException;
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +010023import java.util.*;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010024import java.util.Map.Entry;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010025import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010026import java.util.function.Predicate;
27import java.util.function.UnaryOperator;
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +020028import org.apiguardian.api.API;
29import org.apiguardian.api.API.Status;
30import org.jspecify.annotations.NonNull;
31import org.jspecify.annotations.Nullable;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010032
33/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010034 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010035 *
36 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
37 * the type you wish to decode.
38 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010039 * <p><strong>Example</strong>
40 *
41 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
42 * {@code int}, you can use the following code:
43 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020044 * {@snippet lang="java" :
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010045 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010046 *
47 * var decoder =
48 * Decoder.ofArray(
49 * Decoder.ofStructure(
50 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010051 * Decoder.ofString(UTF_8),
52 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010053 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020054 * byte[] bytes;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010055 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020056 * }
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010057 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010058 * @param <T> the type that the {@link Decoder} can decode.
59 */
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010060@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020061@Immutable
62@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010063public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010064
65 private Decoder() {}
66
67 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010068 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
69 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010070 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
71 * important that the start and end boundaries of the passed byte slice correspond to the actual
72 * start and end of the serialized value. The format does generally not allow for the dynamic
73 * discovery of the end of the data structure.
74 *
75 * @param byteSlice a byte slice holding a serialized GVariant.
76 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010077 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
78 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010079 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010080 */
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +020081 public abstract @NonNull T decode(ByteBuffer byteSlice);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010082
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010083 /**
84 * Encodes a value of type {@code T} into a {@link ByteBuffer} holding a serialized GVariant.
85 *
86 * @param value the value to serialize.
87 * @return a {@link ByteBuffer} holding the serialized value.
88 */
89 public final ByteBuffer encode(T value) {
90 var byteWriter = new ByteWriter();
91 encode(value, byteWriter);
92 return byteWriter.toByteBuffer();
93 }
94
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010095 abstract byte alignment();
96
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010097 abstract @Nullable Integer fixedSize();
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010098
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010099 abstract void encode(T value, ByteWriter byteWriter);
100
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100101 final boolean hasFixedSize() {
102 return fixedSize() != null;
103 }
104
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100105 /**
106 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
107 *
108 * @param byteOrder the byte order to use.
109 * @return a new, decorated {@link Decoder}.
110 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100111 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100112 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100113 }
114
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100115 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100116 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
117 *
Matthias Andreas Benkard716fc322023-12-12 19:13:01 +0100118 * @param decodingFunction the function to apply when decoding.
119 * @param encodingFunction the function to apply when encoding.
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100120 * @return a new, decorated {@link Decoder}.
121 * @see java.util.stream.Stream#map
122 */
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200123 public final <U> Decoder<U> map(
124 Function<@NonNull T, @NonNull U> decodingFunction,
125 Function<@NonNull U, @NonNull T> encodingFunction) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100126 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 *
Matthias Andreas Benkard716fc322023-12-12 19:13:01 +0100132 * @param decodingFunction the function to apply when decoding.
133 * @param encodingFunction the function to apply when encoding.
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100134 * @return a new, decorated {@link Decoder}.
135 * @see java.util.stream.Stream#map
136 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100137 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
138 return new ContramappingDecoder(decodingFunction, encodingFunction);
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100139 }
140
141 /**
142 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
143 * condition on the input {@link ByteBuffer}.
144 *
145 * @param selector the predicate to use to determine the decoder to use.
146 * @return a new {@link Decoder}.
147 */
148 public static <U> Decoder<U> ofPredicate(
149 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
150 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
151 }
152
153 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100154 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100155 *
156 * @param elementDecoder a {@link Decoder} for the elements of the array.
157 * @param <U> the element type.
158 * @return a new {@link Decoder}.
159 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100160 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100161 return new ArrayDecoder<>(elementDecoder);
162 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100163
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100164 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100165 * Creates a {@link Decoder} for a {@code Dictionary} type.
166 *
167 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
168 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
169 * @return a new {@link Decoder}.
170 */
171 public static <K, V> Decoder<Map<K, V>> ofDictionary(
172 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
173 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
174 }
175
176 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100177 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
178 * primitive {@code byte[]} array.
179 *
180 * @return a new {@link Decoder}.
181 */
182 public static Decoder<byte[]> ofByteArray() {
183 return new ByteArrayDecoder();
184 }
185
186 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100187 * Creates a {@link Decoder} for a {@code Maybe} type.
188 *
189 * @param elementDecoder a {@link Decoder} for the contained element.
190 * @param <U> the element type.
191 * @return a new {@link Decoder}.
192 */
193 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
194 return new MaybeDecoder<>(elementDecoder);
195 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100196
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100197 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100198 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100199 *
200 * @param recordType the {@link Record} type that represents the components of the structure.
201 * @param componentDecoders a {@link Decoder} for each component of the structure.
202 * @param <U> the {@link Record} type that represents the components of the structure.
203 * @return a new {@link Decoder}.
204 */
205 public static <U extends Record> Decoder<U> ofStructure(
206 Class<U> recordType, Decoder<?>... componentDecoders) {
207 return new StructureDecoder<>(recordType, componentDecoders);
208 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100209
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100210 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100211 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
212 *
213 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
214 * more convenient.
215 *
216 * @param componentDecoders a {@link Decoder} for each component of the structure.
217 * @return a new {@link Decoder}.
218 */
219 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
220 return new TupleDecoder(componentDecoders);
221 }
222
223 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100224 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
225 * Map.Entry}.
226 *
227 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
228 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
229 * @return a new {@link Decoder}.
230 */
231 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
232 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
233 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
234 }
235
236 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100237 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100238 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100239 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100240 *
241 * <ul>
242 * <li>{@link Boolean}
243 * <li>{@link Byte}
244 * <li>{@link Short}
245 * <li>{@link Integer}
246 * <li>{@link Long}
247 * <li>{@link String}
248 * <li>{@link Optional} (a GVariant {@code Maybe} type)
249 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100250 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100251 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100252 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100253 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100254 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100255 *
256 * @return a new {@link Decoder}.
257 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100258 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100259 return new VariantDecoder();
260 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100261
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100262 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100263 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100264 *
265 * @return a new {@link Decoder}.
266 */
267 public static Decoder<Boolean> ofBoolean() {
268 return new BooleanDecoder();
269 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100270
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100271 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100272 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100273 *
274 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
275 * result of this method.
276 *
277 * @return a new {@link Decoder}.
278 */
279 public static Decoder<Byte> ofByte() {
280 return new ByteDecoder();
281 }
282
283 /**
284 * Creates a {@link Decoder} for the 16-bit {@code short} type.
285 *
286 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
287 * result of this method.
288 *
289 * @return a new {@link Decoder}.
290 */
291 public static Decoder<Short> ofShort() {
292 return new ShortDecoder();
293 }
294
295 /**
296 * Creates a {@link Decoder} for the 32-bit {@code int} type.
297 *
298 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
299 * result of this method.
300 *
301 * @return a new {@link Decoder}.
302 */
303 public static Decoder<Integer> ofInt() {
304 return new IntegerDecoder();
305 }
306
307 /**
308 * Creates a {@link Decoder} for the 64-bit {@code long} type.
309 *
310 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
311 * result of this method.
312 *
313 * @return a new {@link Decoder}.
314 */
315 public static Decoder<Long> ofLong() {
316 return new LongDecoder();
317 }
318
319 /**
320 * Creates a {@link Decoder} for the {@code double} type.
321 *
322 * @return a new {@link Decoder}.
323 */
324 public static Decoder<Double> ofDouble() {
325 return new DoubleDecoder();
326 }
327
328 /**
329 * Creates a {@link Decoder} for the {@link String} type.
330 *
331 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
332 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
333 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100334 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100335 * @return a new {@link Decoder}.
336 */
337 public static Decoder<String> ofString(Charset charset) {
338 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100339 }
340
341 private static int align(int offset, byte alignment) {
342 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
343 }
344
345 private static int getIntN(ByteBuffer byteSlice) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100346 return switch (byteSlice.limit()) {
347 case 0 -> 0;
348 case 1 ->
349 Byte.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).get());
350 case 2 ->
351 Short.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).getShort());
352 case 4 ->
353 byteSlice.order(LITTLE_ENDIAN).getInt();
354 default ->
355 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteSlice.limit()));
356 };
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100357 }
358
359 @SuppressWarnings("java:S3358")
360 private static int byteCount(int n) {
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100361 return n == 0 ? 0 : n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100362 }
363
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100364 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
365 // Determining the framing offset size requires trial and error.
366 int framingOffsetSize;
367 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100368 if (elementsRelativeEnd + (long)framingOffsetSize * framingOffsets.size() >= 1L << (8*framingOffsetSize)) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100369 continue;
370 }
371
372 if (framingOffsetSize > 4) {
373 throw new IllegalArgumentException("too many framing offsets");
374 }
375
376 return framingOffsetSize;
377 }
378 }
379
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100380 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100381
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100382 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100383
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100384 ArrayDecoder(Decoder<U> elementDecoder) {
385 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100386 }
387
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100388 @Override
389 public byte alignment() {
390 return elementDecoder.alignment();
391 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100392
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100393 @Override
394 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100395 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100396 return null;
397 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100398
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100399 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200400 public @NonNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100401 List<U> elements;
402
403 var elementSize = elementDecoder.fixedSize();
404 if (elementSize != null) {
405 // A simple C-style array.
406 elements = new ArrayList<>(byteSlice.limit() / elementSize);
407 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100408 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100409 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100410 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100411 } else if (byteSlice.limit() == 0) {
412 // A degenerate zero-length array of variable width.
413 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100414 } else {
415 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkarddf853ef2023-12-10 20:29:35 +0100416 int framingOffsetSize = max(1, byteCount(byteSlice.limit()));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100417 int lastFramingOffset =
418 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
419 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100420
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100421 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100422 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100423 for (int i = 0; i < elementCount; i++) {
424 int framingOffset =
425 getIntN(
426 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100427 elements.add(
428 elementDecoder.decode(
429 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100430 position = align(framingOffset, alignment());
431 }
432 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100433
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100434 return elements;
435 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100436
437 @Override
438 void encode(List<U> value, ByteWriter byteWriter) {
439 if (elementDecoder.hasFixedSize()) {
440 for (var element : value) {
441 elementDecoder.encode(element, byteWriter);
442 }
443 } else {
444 // Variable-width arrays are encoded with a vector of framing offsets in the end.
445 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
446 int startOffset = byteWriter.position();
447 for (var element : value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100448 // Align the element.
449 var lastRelativeEnd = byteWriter.position() - startOffset;
450 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
451
452 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100453 elementDecoder.encode(element, byteWriter);
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100454
455 // Record the framing offset of the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100456 var relativeEnd = byteWriter.position() - startOffset;
457 framingOffsets.add(relativeEnd);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100458 }
459
460 // Write the framing offsets.
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100461 int framingOffsetSize = max(1, computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets));
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100462 for (var framingOffset : framingOffsets) {
463 byteWriter.writeIntN(framingOffset, framingOffsetSize);
464 }
465 }
466 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100467 }
468
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100469 private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
470
471 private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
472
473 DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
474 this.entryArrayDecoder =
475 new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
476 }
477
478 @Override
479 public byte alignment() {
480 return entryArrayDecoder.alignment();
481 }
482
483 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100484 @Nullable
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100485 public Integer fixedSize() {
486 return entryArrayDecoder.fixedSize();
487 }
488
489 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200490 public @NonNull Map<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100491 List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
Matthias Andreas Benkard2802f2b2023-12-10 15:42:19 +0100492 Map<K, V> result = new LinkedHashMap<>();
493 for (var entry : entries) {
494 result.put(entry.getKey(), entry.getValue());
495 }
496 return result;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100497 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100498
499 @Override
500 void encode(Map<K, V> value, ByteWriter byteWriter) {
501 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
502 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100503 }
504
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100505 private static class ByteArrayDecoder extends Decoder<byte[]> {
506
507 private static final int ELEMENT_SIZE = 1;
508
509 @Override
510 public byte alignment() {
511 return ELEMENT_SIZE;
512 }
513
514 @Override
515 @Nullable
516 Integer fixedSize() {
517 return null;
518 }
519
520 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200521 public byte @NonNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100522 // A simple C-style array.
523 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
524 byteSlice.get(elements);
525 return elements;
526 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100527
528 @Override
529 void encode(byte[] value, ByteWriter byteWriter) {
530 byteWriter.write(value);
531 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100532 }
533
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100534 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
535
536 private final Decoder<U> elementDecoder;
537
538 MaybeDecoder(Decoder<U> elementDecoder) {
539 this.elementDecoder = elementDecoder;
540 }
541
542 @Override
543 public byte alignment() {
544 return elementDecoder.alignment();
545 }
546
547 @Override
548 @Nullable
549 Integer fixedSize() {
550 return null;
551 }
552
553 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200554 public @NonNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100555 if (!byteSlice.hasRemaining()) {
556 return Optional.empty();
557 } else {
558 if (!elementDecoder.hasFixedSize()) {
559 // Remove trailing zero byte.
560 byteSlice.limit(byteSlice.limit() - 1);
561 }
562
563 return Optional.of(elementDecoder.decode(byteSlice));
564 }
565 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100566
567 @Override
568 void encode(Optional<U> value, ByteWriter byteWriter) {
569 if (value.isEmpty()) {
570 return;
571 }
572
573 elementDecoder.encode(value.get(), byteWriter);
574 if (!elementDecoder.hasFixedSize()) {
575 byteWriter.write((byte) 0);
576 }
577 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100578 }
579
580 private static class StructureDecoder<U extends Record> extends Decoder<U> {
581
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100582 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100583 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100584
585 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
586 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100587 if (componentDecoders.length != recordComponents.length) {
588 throw new IllegalArgumentException(
589 "number of decoders (%d) does not match number of structure components (%d)"
590 .formatted(componentDecoders.length, recordComponents.length));
591 }
592
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100593 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100594 this.tupleDecoder = new TupleDecoder(componentDecoders);
595 }
596
597 @Override
598 public byte alignment() {
599 return tupleDecoder.alignment();
600 }
601
602 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100603 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100604 return tupleDecoder.fixedSize();
605 }
606
607 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200608 public @NonNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100609 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
610
611 try {
612 var recordComponentTypes =
613 Arrays.stream(recordType.getRecordComponents())
614 .map(RecordComponent::getType)
615 .toArray(Class<?>[]::new);
616 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
617 return recordConstructor.newInstance(recordConstructorArguments);
618 } catch (NoSuchMethodException
619 | InstantiationException
620 | IllegalAccessException
621 | InvocationTargetException e) {
622 throw new IllegalStateException(e);
623 }
624 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100625
626 @Override
627 void encode(U value, ByteWriter byteWriter) {
628 try {
629 var components = recordType.getRecordComponents();
630 List<Object> componentValues = new ArrayList<>(components.length);
631 for (var component : components) {
632 var accessor = component.getAccessor();
633 var componentValue = accessor.invoke(value);
634 componentValues.add(componentValue);
635 }
636 tupleDecoder.encode(componentValues.toArray(), byteWriter);
637 } catch (IllegalAccessException | InvocationTargetException e) {
638 throw new IllegalStateException(e);
639 }
640 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100641 }
642
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200643 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100644 private static class TupleDecoder extends Decoder<Object[]> {
645
646 private final Decoder<?>[] componentDecoders;
647
648 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100649 this.componentDecoders = componentDecoders;
650 }
651
652 @Override
653 public byte alignment() {
654 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
655 }
656
657 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100658 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100659 int position = 0;
660 for (var componentDecoder : componentDecoders) {
661 var fixedComponentSize = componentDecoder.fixedSize();
662 if (fixedComponentSize == null) {
663 return null;
664 }
665
666 position = align(position, componentDecoder.alignment());
667 position += fixedComponentSize;
668 }
669
670 if (position == 0) {
671 return 1;
672 }
673
674 return align(position, alignment());
675 }
676
677 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200678 public Object @NonNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100679 int framingOffsetSize = byteCount(byteSlice.limit());
680
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100681 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100682
683 int position = 0;
684 int framingOffsetIndex = 0;
685 int componentIndex = 0;
686 for (var componentDecoder : componentDecoders) {
687 position = align(position, componentDecoder.alignment());
688
689 var fixedComponentSize = componentDecoder.fixedSize();
690 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100691 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100692 componentDecoder.decode(
693 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100694 position += fixedComponentSize;
695 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100696 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100697 // The last component never has a framing offset.
698 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100699 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100700 componentDecoder.decode(
701 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100702 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100703 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100704 int framingOffset =
705 getIntN(
706 byteSlice.slice(
707 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
708 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100709 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100710 componentDecoder.decode(
711 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100712 position = framingOffset;
713 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100714 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100715 }
716
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100717 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100718 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100719
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100720 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100721 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100722
723 @Override
724 @SuppressWarnings("unchecked")
725 void encode(Object[] value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100726 // The unit type is encoded as a single zero byte.
727 if (value.length == 0) {
728 byteWriter.write((byte) 0);
729 return;
730 }
731
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100732 int startOffset = byteWriter.position();
733 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
734 for (int i = 0; i < value.length; ++i) {
735 var componentDecoder = (Decoder<Object>) componentDecoders[i];
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100736
737 // Align the element.
738 var lastRelativeEnd = byteWriter.position() - startOffset;
739 byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]);
740
741 // Encode the element.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100742 componentDecoder.encode(value[i], byteWriter);
743
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100744 // Record the framing offset of the element if it is of variable size.
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100745 var fixedComponentSize = componentDecoders[i].fixedSize();
746 if (fixedComponentSize == null && i < value.length - 1) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100747 var relativeEnd = byteWriter.position() - startOffset;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100748 framingOffsets.add(relativeEnd);
749 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100750 }
751
752 // Write the framing offsets in reverse order.
753 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
754 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
755 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
756 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +0100757
758 // Pad the structure to its alignment if it is of fixed size.
759 if (fixedSize() != null) {
760 var lastRelativeEnd = byteWriter.position() - startOffset;
761 byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]);
762 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100763 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100764 }
765
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100766 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
767
768 private final TupleDecoder tupleDecoder;
769
770 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
771 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
772 }
773
774 @Override
775 public byte alignment() {
776 return tupleDecoder.alignment();
777 }
778
779 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100780 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100781 return tupleDecoder.fixedSize();
782 }
783
784 @Override
785 @SuppressWarnings("unchecked")
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200786 public Map.@NonNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100787 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200788 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100789 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100790
791 @Override
792 void encode(Entry<K, V> value, ByteWriter byteWriter) {
793 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
794 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100795 }
796
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100797 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100798
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100799 @Override
800 public byte alignment() {
801 return 8;
802 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100803
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100804 @Override
805 @Nullable
806 Integer fixedSize() {
807 return null;
808 }
809
810 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200811 public @NonNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100812 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
813 if (byteSlice.get(i) != 0) {
814 continue;
815 }
816
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100817 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100818 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100819
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100820 Signature signature;
821 try {
822 signature = Signature.parse(signatureBytes);
823 } catch (ParseException e) {
824 throw new IllegalArgumentException(e);
825 }
826
827 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100828 }
829
830 throw new IllegalArgumentException("variant signature not found");
831 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100832
833 @Override
834 void encode(Variant value, ByteWriter byteWriter) {
835 value.signature().decoder().encode(value.value(), byteWriter);
836 byteWriter.write((byte) 0);
837 byteWriter.write(value.signature().toString().getBytes(UTF_8));
838 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100839 }
840
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100841 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100842
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100843 @Override
844 public byte alignment() {
845 return 1;
846 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100847
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100848 @Override
849 public Integer fixedSize() {
850 return 1;
851 }
852
853 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200854 public @NonNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100855 return byteSlice.get() != 0;
856 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100857
858 @Override
859 void encode(Boolean value, ByteWriter byteWriter) {
860 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
861 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100862 }
863
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100864 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100865
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100866 @Override
867 public byte alignment() {
868 return 1;
869 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100870
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100871 @Override
872 public Integer fixedSize() {
873 return 1;
874 }
875
876 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200877 public @NonNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100878 return byteSlice.get();
879 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100880
881 @Override
882 void encode(Byte value, ByteWriter byteWriter) {
883 byteWriter.write(value);
884 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100885 }
886
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100887 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100888
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100889 @Override
890 public byte alignment() {
891 return 2;
892 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100893
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100894 @Override
895 public Integer fixedSize() {
896 return 2;
897 }
898
899 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200900 public @NonNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100901 return byteSlice.getShort();
902 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100903
904 @Override
905 void encode(Short value, ByteWriter byteWriter) {
906 byteWriter.write(value);
907 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100908 }
909
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100910 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100911
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100912 @Override
913 public byte alignment() {
914 return 4;
915 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100916
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100917 @Override
918 public Integer fixedSize() {
919 return 4;
920 }
921
922 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200923 public @NonNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100924 return byteSlice.getInt();
925 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100926
927 @Override
928 void encode(Integer value, ByteWriter byteWriter) {
929 byteWriter.write(value);
930 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100931 }
932
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100933 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100934
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100935 @Override
936 public byte alignment() {
937 return 8;
938 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100939
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100940 @Override
941 public Integer fixedSize() {
942 return 8;
943 }
944
945 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200946 public @NonNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100947 return byteSlice.getLong();
948 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100949
950 @Override
951 void encode(Long value, ByteWriter byteWriter) {
952 byteWriter.write(value);
953 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100954 }
955
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100956 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100957
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100958 @Override
959 public byte alignment() {
960 return 8;
961 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100962
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100963 @Override
964 public Integer fixedSize() {
965 return 8;
966 }
967
968 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200969 public @NonNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100970 return byteSlice.getDouble();
971 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100972
973 @Override
974 void encode(Double value, ByteWriter byteWriter) {
975 byteWriter.write(value);
976 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100977 }
978
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100979 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100980
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100981 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100982
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100983 public StringDecoder(Charset charset) {
984 this.charset = charset;
985 }
986
987 @Override
988 public byte alignment() {
989 return 1;
990 }
991
992 @Override
993 @Nullable
994 Integer fixedSize() {
995 return null;
996 }
997
998 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +0200999 public @NonNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +01001000 byteSlice.limit(byteSlice.limit() - 1);
1001 return charset.decode(byteSlice).toString();
1002 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001003
1004 @Override
1005 void encode(String value, ByteWriter byteWriter) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001006 byteWriter.write(charset.encode(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001007 byteWriter.write((byte) 0);
1008 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001009 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001010
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001011 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001012 private class MappingDecoder<U> extends Decoder<U> {
1013
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001014 private final Function<@NonNull T, @NonNull U> decodingFunction;
1015 private final Function<@NonNull U, @NonNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001016
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001017 MappingDecoder(
1018 Function<@NonNull T, @NonNull U> decodingFunction,
1019 Function<@NonNull U, @NonNull T> encodingFunction) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001020 this.decodingFunction = decodingFunction;
1021 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001022 }
1023
1024 @Override
1025 public byte alignment() {
1026 return Decoder.this.alignment();
1027 }
1028
1029 @Override
1030 public @Nullable Integer fixedSize() {
1031 return Decoder.this.fixedSize();
1032 }
1033
1034 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001035 public @NonNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001036 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1037 }
1038
1039 @Override
1040 void encode(U value, ByteWriter byteWriter) {
1041 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001042 }
1043 }
1044
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001045 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001046 private class ContramappingDecoder extends Decoder<T> {
1047
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001048 private final UnaryOperator<ByteBuffer> decodingFunction;
1049 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001050
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001051 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1052 this.decodingFunction = decodingFunction;
1053 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001054 }
1055
1056 @Override
1057 public byte alignment() {
1058 return Decoder.this.alignment();
1059 }
1060
1061 @Override
1062 public @Nullable Integer fixedSize() {
1063 return Decoder.this.fixedSize();
1064 }
1065
1066 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001067 public @NonNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001068 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001069 return Decoder.this.decode(transformedBuffer);
1070 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001071
1072 @Override
1073 void encode(T value, ByteWriter byteWriter) {
1074 var innerByteWriter = new ByteWriter();
1075 Decoder.this.encode(value, innerByteWriter);
1076 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001077 byteWriter.write(transformedBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001078 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001079 }
1080
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001081 private class ByteOrderFixingDecoder extends Decoder<T> {
1082
1083 private final ByteOrder byteOrder;
1084
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001085 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001086 this.byteOrder = byteOrder;
1087 }
1088
1089 @Override
1090 public byte alignment() {
1091 return Decoder.this.alignment();
1092 }
1093
1094 @Override
1095 public @Nullable Integer fixedSize() {
1096 return Decoder.this.fixedSize();
1097 }
1098
1099 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001100 public @NonNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001101 var newByteSlice = byteSlice.duplicate();
1102 newByteSlice.order(byteOrder);
1103 return Decoder.this.decode(newByteSlice);
1104 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001105
1106 @Override
1107 protected void encode(T value, ByteWriter byteWriter) {
1108 var newByteWriter = byteWriter.duplicate();
1109 newByteWriter.order(byteOrder);
1110 Decoder.this.encode(value, newByteWriter);
1111 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001112 }
1113
1114 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1115 return byteSlice.slice(index, length).order(byteSlice.order());
1116 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001117
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001118 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001119 private static class PredicateDecoder<U> extends Decoder<U> {
1120
1121 private final Predicate<ByteBuffer> selector;
1122 private final Decoder<U> thenDecoder;
1123 private final Decoder<U> elseDecoder;
1124
1125 PredicateDecoder(
1126 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1127 this.selector = selector;
1128 this.thenDecoder = thenDecoder;
1129 this.elseDecoder = elseDecoder;
1130 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1131 throw new IllegalArgumentException(
1132 "incompatible alignments in predicate branches: then=%d, else=%d"
1133 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1134 }
1135
1136 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1137 throw new IllegalArgumentException(
1138 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001139 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001140 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1141 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001142 }
1143 }
1144
1145 @Override
1146 public byte alignment() {
1147 return thenDecoder.alignment();
1148 }
1149
1150 @Override
1151 public @Nullable Integer fixedSize() {
1152 return thenDecoder.fixedSize();
1153 }
1154
1155 @Override
Matthias Andreas Benkard15456c82024-07-20 10:52:54 +02001156 public @NonNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001157 var b = selector.test(byteSlice);
1158 byteSlice.rewind();
1159 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1160 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001161
1162 @Override
1163 public void encode(U value, ByteWriter byteWriter) {
1164 elseDecoder.encode(value, byteWriter);
1165 }
1166 }
1167
1168 private static class ByteWriter {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001169 private ByteOrder byteOrder = BIG_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001170 private final ByteArrayOutputStream outputStream;
1171
1172 ByteWriter() {
1173 this.outputStream = new ByteArrayOutputStream();
1174 }
1175
1176 private ByteWriter(ByteArrayOutputStream outputStream) {
1177 this.outputStream = outputStream;
1178 }
1179
1180 void write(byte[] bytes) {
1181 outputStream.write(bytes, 0, bytes.length);
1182 }
1183
1184 @SuppressWarnings("java:S2095")
1185 void write(ByteBuffer byteBuffer) {
1186 var channel = Channels.newChannel(outputStream);
1187 try {
1188 channel.write(byteBuffer);
1189 } catch (IOException e) {
1190 // impossible
1191 throw new IllegalStateException(e);
1192 }
1193 }
1194
1195 void write(byte value) {
1196 outputStream.write(value);
1197 }
1198
1199 void write(int value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001200 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001201 }
1202
1203 void write(long value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001204 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001205 }
1206
1207 void write(short value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001208 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001209 }
1210
1211 void write(double value) {
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001212 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001213 }
1214
1215 private void writeIntN(int value, int byteCount) {
1216 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1217 switch (byteCount) {
1218 case 0 -> {}
1219 case 1 ->
1220 byteBuffer.put((byte) value);
1221 case 2 ->
1222 byteBuffer.putShort((short) value);
1223 case 4 ->
1224 byteBuffer.putInt(value);
1225 default ->
1226 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1227 }
Matthias Andreas Benkarde9440b52023-12-10 15:28:16 +01001228 write(byteBuffer.rewind());
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001229 }
1230
1231 ByteWriter duplicate() {
1232 var duplicate = new ByteWriter(outputStream);
1233 duplicate.byteOrder = byteOrder;
1234 return duplicate;
1235 }
1236
1237 ByteBuffer toByteBuffer() {
1238 return ByteBuffer.wrap(outputStream.toByteArray());
1239 }
1240
1241 void order(ByteOrder byteOrder) {
1242 this.byteOrder = byteOrder;
1243 }
1244
1245 int position() {
1246 return outputStream.size();
1247 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001248 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001249}