blob: a28d79246914d12aae63ce3f4a4a156b7f990947 [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 Benkard261532a2021-12-12 20:09:27 +01008import static java.nio.ByteOrder.LITTLE_ENDIAN;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01009import static java.nio.charset.StandardCharsets.UTF_8;
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020010import static java.util.Objects.requireNonNullElse;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010011import static java.util.stream.Collectors.toMap;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010012
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020013import com.google.errorprone.annotations.Immutable;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010014
15import java.io.ByteArrayOutputStream;
16import java.io.IOException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010017import java.lang.reflect.InvocationTargetException;
18import java.lang.reflect.RecordComponent;
19import java.nio.ByteBuffer;
20import java.nio.ByteOrder;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010021import java.nio.channels.Channels;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010022import java.nio.charset.Charset;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010023import java.text.ParseException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010024import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.List;
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +010027import java.util.Map;
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +010028import java.util.Map.Entry;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010029import java.util.Objects;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010030import java.util.Optional;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010031import java.util.function.Function;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +010032import java.util.function.Predicate;
33import java.util.function.UnaryOperator;
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010034
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010035import org.apiguardian.api.API;
36import org.apiguardian.api.API.Status;
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010037import org.jetbrains.annotations.NotNull;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010038import org.jetbrains.annotations.Nullable;
39
40/**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +010041 * Type class for decodable types.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010042 *
43 * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for
44 * the type you wish to decode.
45 *
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010046 * <p><strong>Example</strong>
47 *
48 * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and
49 * {@code int}, you can use the following code:
50 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020051 * {@snippet lang="java" :
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010052 * record ExampleRecord(String s, int i) {}
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010053 *
54 * var decoder =
55 * Decoder.ofArray(
56 * Decoder.ofStructure(
57 * ExampleRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010058 * Decoder.ofString(UTF_8),
59 * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010060 *
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020061 * byte[] bytes;
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010062 * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes));
Matthias Andreas Benkard0239d322022-04-15 20:21:37 +020063 * }
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +010064 *
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010065 * @param <T> the type that the {@link Decoder} can decode.
66 */
Matthias Andreas Benkard25b7f902021-12-17 06:02:11 +010067@API(status = Status.EXPERIMENTAL)
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +020068@Immutable
69@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010070public abstract class Decoder<T> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010071
72 private Decoder() {}
73
74 /**
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010075 * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}.
76 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +010077 * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is
78 * important that the start and end boundaries of the passed byte slice correspond to the actual
79 * start and end of the serialized value. The format does generally not allow for the dynamic
80 * discovery of the end of the data structure.
81 *
82 * @param byteSlice a byte slice holding a serialized GVariant.
83 * @return the deserialized value.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010084 * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested
85 * data.
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010086 * @throws IllegalArgumentException if the serialized GVariant is ill-formed
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010087 */
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +010088 public abstract @NotNull T decode(ByteBuffer byteSlice);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010089
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010090 /**
91 * Encodes a value of type {@code T} into a {@link ByteBuffer} holding a serialized GVariant.
92 *
93 * @param value the value to serialize.
94 * @return a {@link ByteBuffer} holding the serialized value.
95 */
96 public final ByteBuffer encode(T value) {
97 var byteWriter = new ByteWriter();
98 encode(value, byteWriter);
99 return byteWriter.toByteBuffer();
100 }
101
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100102 abstract byte alignment();
103
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100104 abstract @Nullable Integer fixedSize();
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100105
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100106 abstract void encode(T value, ByteWriter byteWriter);
107
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100108 final boolean hasFixedSize() {
109 return fixedSize() != null;
110 }
111
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100112 /**
113 * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it.
114 *
115 * @param byteOrder the byte order to use.
116 * @return a new, decorated {@link Decoder}.
117 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100118 public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100119 return new ByteOrderFixingDecoder(byteOrder);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100120 }
121
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100122 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100123 * Creates a new {@link Decoder} from an existing one by applying a function to the result.
124 *
125 * @param function the function to apply.
126 * @return a new, decorated {@link Decoder}.
127 * @see java.util.stream.Stream#map
128 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100129 public final <U> Decoder<U> map(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
130 return new MappingDecoder<>(decodingFunction, encodingFunction);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100131 }
132
133 /**
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100134 * Creates a new {@link Decoder} from an existing one by applying a function to the input.
135 *
136 * @param function the function to apply.
137 * @return a new, decorated {@link Decoder}.
138 * @see java.util.stream.Stream#map
139 */
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100140 public final Decoder<T> contramap(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
141 return new ContramappingDecoder(decodingFunction, encodingFunction);
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +0100142 }
143
144 /**
145 * Creates a new {@link Decoder} that delegates to one of two other {@link Decoder}s based on a
146 * condition on the input {@link ByteBuffer}.
147 *
148 * @param selector the predicate to use to determine the decoder to use.
149 * @return a new {@link Decoder}.
150 */
151 public static <U> Decoder<U> ofPredicate(
152 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
153 return new PredicateDecoder<>(selector, thenDecoder, elseDecoder);
154 }
155
156 /**
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100157 * Creates a {@link Decoder} for an {@code Array} type.
Matthias Andreas Benkard4c32c392021-12-12 21:23:53 +0100158 *
159 * @param elementDecoder a {@link Decoder} for the elements of the array.
160 * @param <U> the element type.
161 * @return a new {@link Decoder}.
162 */
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100163 public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100164 return new ArrayDecoder<>(elementDecoder);
165 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100166
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100167 /**
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100168 * Creates a {@link Decoder} for a {@code Dictionary} type.
169 *
170 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
171 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
172 * @return a new {@link Decoder}.
173 */
174 public static <K, V> Decoder<Map<K, V>> ofDictionary(
175 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
176 return new DictionaryDecoder<>(keyDecoder, valueDecoder);
177 }
178
179 /**
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100180 * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
181 * primitive {@code byte[]} array.
182 *
183 * @return a new {@link Decoder}.
184 */
185 public static Decoder<byte[]> ofByteArray() {
186 return new ByteArrayDecoder();
187 }
188
189 /**
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100190 * Creates a {@link Decoder} for a {@code Maybe} type.
191 *
192 * @param elementDecoder a {@link Decoder} for the contained element.
193 * @param <U> the element type.
194 * @return a new {@link Decoder}.
195 */
196 public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
197 return new MaybeDecoder<>(elementDecoder);
198 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100199
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100200 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100201 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100202 *
203 * @param recordType the {@link Record} type that represents the components of the structure.
204 * @param componentDecoders a {@link Decoder} for each component of the structure.
205 * @param <U> the {@link Record} type that represents the components of the structure.
206 * @return a new {@link Decoder}.
207 */
208 public static <U extends Record> Decoder<U> ofStructure(
209 Class<U> recordType, Decoder<?>... componentDecoders) {
210 return new StructureDecoder<>(recordType, componentDecoders);
211 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100212
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100213 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100214 * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}.
215 *
216 * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and
217 * more convenient.
218 *
219 * @param componentDecoders a {@link Decoder} for each component of the structure.
220 * @return a new {@link Decoder}.
221 */
222 public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) {
223 return new TupleDecoder(componentDecoders);
224 }
225
226 /**
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100227 * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
228 * Map.Entry}.
229 *
230 * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
231 * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
232 * @return a new {@link Decoder}.
233 */
234 public static <K, V> Decoder<Map.Entry<K, V>> ofDictionaryEntry(
235 Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
236 return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
237 }
238
239 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100240 * Creates a {@link Decoder} for the {@link Variant} type.
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100241 *
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100242 * <p>The contained {@link Object} can be of one of the following types:
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100243 *
244 * <ul>
245 * <li>{@link Boolean}
246 * <li>{@link Byte}
247 * <li>{@link Short}
248 * <li>{@link Integer}
249 * <li>{@link Long}
250 * <li>{@link String}
251 * <li>{@link Optional} (a GVariant {@code Maybe} type)
252 * <li>{@link List} (a GVariant array)
Matthias Andreas Benkarda66b73d2021-12-18 19:15:52 +0100253 * <li>{@code Object[]} (a GVariant structure)
Matthias Andreas Benkardd6a25d12021-12-28 01:13:58 +0100254 * <li>{@link java.util.Map} (a dictionary)
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100255 * <li>{@link java.util.Map.Entry} (a dictionary entry)
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100256 * <li>{@link Variant} (a nested variant)
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100257 * </ul>
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100258 *
259 * @return a new {@link Decoder}.
260 */
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100261 public static Decoder<Variant> ofVariant() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100262 return new VariantDecoder();
263 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100264
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100265 /**
Matthias Andreas Benkardb50fcd02021-12-16 20:17:20 +0100266 * Creates a {@link Decoder} for the {@code boolean} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100267 *
268 * @return a new {@link Decoder}.
269 */
270 public static Decoder<Boolean> ofBoolean() {
271 return new BooleanDecoder();
272 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100273
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100274 /**
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100275 * Creates a {@link Decoder} for the 8-bit {@code byte} type.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100276 *
277 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
278 * result of this method.
279 *
280 * @return a new {@link Decoder}.
281 */
282 public static Decoder<Byte> ofByte() {
283 return new ByteDecoder();
284 }
285
286 /**
287 * Creates a {@link Decoder} for the 16-bit {@code short} type.
288 *
289 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
290 * result of this method.
291 *
292 * @return a new {@link Decoder}.
293 */
294 public static Decoder<Short> ofShort() {
295 return new ShortDecoder();
296 }
297
298 /**
299 * Creates a {@link Decoder} for the 32-bit {@code int} type.
300 *
301 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
302 * result of this method.
303 *
304 * @return a new {@link Decoder}.
305 */
306 public static Decoder<Integer> ofInt() {
307 return new IntegerDecoder();
308 }
309
310 /**
311 * Creates a {@link Decoder} for the 64-bit {@code long} type.
312 *
313 * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
314 * result of this method.
315 *
316 * @return a new {@link Decoder}.
317 */
318 public static Decoder<Long> ofLong() {
319 return new LongDecoder();
320 }
321
322 /**
323 * Creates a {@link Decoder} for the {@code double} type.
324 *
325 * @return a new {@link Decoder}.
326 */
327 public static Decoder<Double> ofDouble() {
328 return new DoubleDecoder();
329 }
330
331 /**
332 * Creates a {@link Decoder} for the {@link String} type.
333 *
334 * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
335 * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
336 *
Matthias Andreas Benkard3f13b662021-12-18 18:57:37 +0100337 * @param charset the {@link Charset} the string is encoded in.
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100338 * @return a new {@link Decoder}.
339 */
340 public static Decoder<String> ofString(Charset charset) {
341 return new StringDecoder(charset);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100342 }
343
344 private static int align(int offset, byte alignment) {
345 return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
346 }
347
348 private static int getIntN(ByteBuffer byteSlice) {
349 var intBytes = new byte[4];
350 byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
351 return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
352 }
353
354 @SuppressWarnings("java:S3358")
355 private static int byteCount(int n) {
356 return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
357 }
358
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100359 private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) {
360 // Determining the framing offset size requires trial and error.
361 int framingOffsetSize;
362 for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) {
363 if (elementsRelativeEnd + framingOffsetSize* framingOffsets.size() >= 1 << (8*framingOffsetSize)) {
364 continue;
365 }
366
367 if (framingOffsetSize > 4) {
368 throw new IllegalArgumentException("too many framing offsets");
369 }
370
371 return framingOffsetSize;
372 }
373 }
374
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100375 private static class ArrayDecoder<U> extends Decoder<List<U>> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100376
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100377 private final Decoder<U> elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100378
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100379 ArrayDecoder(Decoder<U> elementDecoder) {
380 this.elementDecoder = elementDecoder;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100381 }
382
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100383 @Override
384 public byte alignment() {
385 return elementDecoder.alignment();
386 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100387
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100388 @Override
389 @Nullable
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100390 public Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100391 return null;
392 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100393
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100394 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100395 public @NotNull List<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100396 List<U> elements;
397
398 var elementSize = elementDecoder.fixedSize();
399 if (elementSize != null) {
400 // A simple C-style array.
401 elements = new ArrayList<>(byteSlice.limit() / elementSize);
402 for (int i = 0; i < byteSlice.limit(); i += elementSize) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100403 var element = elementDecoder.decode(slicePreservingOrder(byteSlice, i, elementSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100404 elements.add(element);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100405 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100406 } else if (byteSlice.limit() == 0) {
407 // A degenerate zero-length array of variable width.
408 elements = List.of();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100409 } else {
410 // An array with aligned elements and a vector of framing offsets in the end.
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100411 int framingOffsetSize = byteCount(byteSlice.limit());
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100412 int lastFramingOffset =
413 getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
414 int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100415
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100416 elements = new ArrayList<>(elementCount);
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100417 int position = 0;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100418 for (int i = 0; i < elementCount; i++) {
419 int framingOffset =
420 getIntN(
421 byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100422 elements.add(
423 elementDecoder.decode(
424 slicePreservingOrder(byteSlice, position, framingOffset - position)));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100425 position = align(framingOffset, alignment());
426 }
427 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100428
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100429 return elements;
430 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100431
432 @Override
433 void encode(List<U> value, ByteWriter byteWriter) {
434 if (elementDecoder.hasFixedSize()) {
435 for (var element : value) {
436 elementDecoder.encode(element, byteWriter);
437 }
438 } else {
439 // Variable-width arrays are encoded with a vector of framing offsets in the end.
440 ArrayList<Integer> framingOffsets = new ArrayList<>(value.size());
441 int startOffset = byteWriter.position();
442 for (var element : value) {
443 elementDecoder.encode(element, byteWriter);
444 var relativeEnd = byteWriter.position() - startOffset;
445 framingOffsets.add(relativeEnd);
446
447 // Align the next element.
448 byteWriter.write(new byte[align(relativeEnd, alignment()) - relativeEnd]);
449 }
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);
483 return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
484 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100485
486 @Override
487 void encode(Map<K, V> value, ByteWriter byteWriter) {
488 entryArrayDecoder.encode(value.entrySet().stream().toList(), byteWriter);
489 }
Matthias Andreas Benkard9a6c8ed2021-12-28 01:00:22 +0100490 }
491
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100492 private static class ByteArrayDecoder extends Decoder<byte[]> {
493
494 private static final int ELEMENT_SIZE = 1;
495
496 @Override
497 public byte alignment() {
498 return ELEMENT_SIZE;
499 }
500
501 @Override
502 @Nullable
503 Integer fixedSize() {
504 return null;
505 }
506
507 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100508 public byte @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100509 // A simple C-style array.
510 byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
511 byteSlice.get(elements);
512 return elements;
513 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100514
515 @Override
516 void encode(byte[] value, ByteWriter byteWriter) {
517 byteWriter.write(value);
518 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100519 }
520
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100521 private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
522
523 private final Decoder<U> elementDecoder;
524
525 MaybeDecoder(Decoder<U> elementDecoder) {
526 this.elementDecoder = elementDecoder;
527 }
528
529 @Override
530 public byte alignment() {
531 return elementDecoder.alignment();
532 }
533
534 @Override
535 @Nullable
536 Integer fixedSize() {
537 return null;
538 }
539
540 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100541 public @NotNull Optional<U> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100542 if (!byteSlice.hasRemaining()) {
543 return Optional.empty();
544 } else {
545 if (!elementDecoder.hasFixedSize()) {
546 // Remove trailing zero byte.
547 byteSlice.limit(byteSlice.limit() - 1);
548 }
549
550 return Optional.of(elementDecoder.decode(byteSlice));
551 }
552 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100553
554 @Override
555 void encode(Optional<U> value, ByteWriter byteWriter) {
556 if (value.isEmpty()) {
557 return;
558 }
559
560 elementDecoder.encode(value.get(), byteWriter);
561 if (!elementDecoder.hasFixedSize()) {
562 byteWriter.write((byte) 0);
563 }
564 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100565 }
566
567 private static class StructureDecoder<U extends Record> extends Decoder<U> {
568
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100569 private final Class<U> recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100570 private final TupleDecoder tupleDecoder;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100571
572 StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
573 var recordComponents = recordType.getRecordComponents();
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100574 if (componentDecoders.length != recordComponents.length) {
575 throw new IllegalArgumentException(
576 "number of decoders (%d) does not match number of structure components (%d)"
577 .formatted(componentDecoders.length, recordComponents.length));
578 }
579
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100580 this.recordType = recordType;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100581 this.tupleDecoder = new TupleDecoder(componentDecoders);
582 }
583
584 @Override
585 public byte alignment() {
586 return tupleDecoder.alignment();
587 }
588
589 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100590 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100591 return tupleDecoder.fixedSize();
592 }
593
594 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100595 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100596 Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice);
597
598 try {
599 var recordComponentTypes =
600 Arrays.stream(recordType.getRecordComponents())
601 .map(RecordComponent::getType)
602 .toArray(Class<?>[]::new);
603 var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
604 return recordConstructor.newInstance(recordConstructorArguments);
605 } catch (NoSuchMethodException
606 | InstantiationException
607 | IllegalAccessException
608 | InvocationTargetException e) {
609 throw new IllegalStateException(e);
610 }
611 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100612
613 @Override
614 void encode(U value, ByteWriter byteWriter) {
615 try {
616 var components = recordType.getRecordComponents();
617 List<Object> componentValues = new ArrayList<>(components.length);
618 for (var component : components) {
619 var accessor = component.getAccessor();
620 var componentValue = accessor.invoke(value);
621 componentValues.add(componentValue);
622 }
623 tupleDecoder.encode(componentValues.toArray(), byteWriter);
624 } catch (IllegalAccessException | InvocationTargetException e) {
625 throw new IllegalStateException(e);
626 }
627 }
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100628 }
629
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200630 @SuppressWarnings("Immutable")
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100631 private static class TupleDecoder extends Decoder<Object[]> {
632
633 private final Decoder<?>[] componentDecoders;
634
635 TupleDecoder(Decoder<?>... componentDecoders) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100636 this.componentDecoders = componentDecoders;
637 }
638
639 @Override
640 public byte alignment() {
641 return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
642 }
643
644 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100645 public @Nullable Integer fixedSize() {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100646 int position = 0;
647 for (var componentDecoder : componentDecoders) {
648 var fixedComponentSize = componentDecoder.fixedSize();
649 if (fixedComponentSize == null) {
650 return null;
651 }
652
653 position = align(position, componentDecoder.alignment());
654 position += fixedComponentSize;
655 }
656
657 if (position == 0) {
658 return 1;
659 }
660
661 return align(position, alignment());
662 }
663
664 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100665 public Object @NotNull [] decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100666 int framingOffsetSize = byteCount(byteSlice.limit());
667
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100668 var objects = new Object[componentDecoders.length];
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100669
670 int position = 0;
671 int framingOffsetIndex = 0;
672 int componentIndex = 0;
673 for (var componentDecoder : componentDecoders) {
674 position = align(position, componentDecoder.alignment());
675
676 var fixedComponentSize = componentDecoder.fixedSize();
677 if (fixedComponentSize != null) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100678 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100679 componentDecoder.decode(
680 slicePreservingOrder(byteSlice, position, fixedComponentSize));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100681 position += fixedComponentSize;
682 } else {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100683 if (componentIndex == componentDecoders.length - 1) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100684 // The last component never has a framing offset.
685 int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100686 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100687 componentDecoder.decode(
688 slicePreservingOrder(byteSlice, position, endPosition - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100689 position = endPosition;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100690 } else {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100691 int framingOffset =
692 getIntN(
693 byteSlice.slice(
694 byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
695 framingOffsetSize));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100696 objects[componentIndex] =
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100697 componentDecoder.decode(
698 slicePreservingOrder(byteSlice, position, framingOffset - position));
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100699 position = framingOffset;
700 ++framingOffsetIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100701 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100702 }
703
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100704 ++componentIndex;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100705 }
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100706
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100707 return objects;
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100708 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100709
710 @Override
711 @SuppressWarnings("unchecked")
712 void encode(Object[] value, ByteWriter byteWriter) {
713 int startOffset = byteWriter.position();
714 ArrayList<Integer> framingOffsets = new ArrayList<>(value.length);
715 for (int i = 0; i < value.length; ++i) {
716 var componentDecoder = (Decoder<Object>) componentDecoders[i];
717 componentDecoder.encode(value[i], byteWriter);
718
719 var relativeEnd = byteWriter.position() - startOffset;
720
721 var fixedComponentSize = componentDecoders[i].fixedSize();
722 if (fixedComponentSize == null && i < value.length - 1) {
723 framingOffsets.add(relativeEnd);
724 }
725
726 // Align the next element.
727 byteWriter.write(new byte[align(relativeEnd, alignment()) - relativeEnd]);
728 }
729
730 // Write the framing offsets in reverse order.
731 int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets);
732 for (int i = framingOffsets.size() - 1; i >= 0; --i) {
733 byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize);
734 }
735 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100736 }
737
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100738 private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
739
740 private final TupleDecoder tupleDecoder;
741
742 DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
743 this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
744 }
745
746 @Override
747 public byte alignment() {
748 return tupleDecoder.alignment();
749 }
750
751 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100752 public @Nullable Integer fixedSize() {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100753 return tupleDecoder.fixedSize();
754 }
755
756 @Override
757 @SuppressWarnings("unchecked")
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100758 public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100759 Object[] components = tupleDecoder.decode(byteSlice);
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200760 return Map.entry((K) components[0], (V) components[1]);
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100761 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100762
763 @Override
764 void encode(Entry<K, V> value, ByteWriter byteWriter) {
765 tupleDecoder.encode(new Object[] {value.getKey(), value.getValue()}, byteWriter);
766 }
Matthias Andreas Benkardcd924f62021-12-28 00:46:06 +0100767 }
768
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100769 private static class VariantDecoder extends Decoder<Variant> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100770
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100771 @Override
772 public byte alignment() {
773 return 8;
774 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100775
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100776 @Override
777 @Nullable
778 Integer fixedSize() {
779 return null;
780 }
781
782 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100783 public @NotNull Variant decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100784 for (int i = byteSlice.limit() - 1; i >= 0; --i) {
785 if (byteSlice.get(i) != 0) {
786 continue;
787 }
788
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100789 var dataBytes = slicePreservingOrder(byteSlice, 0, i);
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100790 var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100791
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100792 Signature signature;
793 try {
794 signature = Signature.parse(signatureBytes);
795 } catch (ParseException e) {
796 throw new IllegalArgumentException(e);
797 }
798
799 return new Variant(signature, signature.decoder().decode(dataBytes));
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100800 }
801
802 throw new IllegalArgumentException("variant signature not found");
803 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100804
805 @Override
806 void encode(Variant value, ByteWriter byteWriter) {
807 value.signature().decoder().encode(value.value(), byteWriter);
808 byteWriter.write((byte) 0);
809 byteWriter.write(value.signature().toString().getBytes(UTF_8));
810 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100811 }
812
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100813 private static class BooleanDecoder extends Decoder<Boolean> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100814
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100815 @Override
816 public byte alignment() {
817 return 1;
818 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100819
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100820 @Override
821 public Integer fixedSize() {
822 return 1;
823 }
824
825 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100826 public @NotNull Boolean decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100827 return byteSlice.get() != 0;
828 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100829
830 @Override
831 void encode(Boolean value, ByteWriter byteWriter) {
832 byteWriter.write(Boolean.TRUE.equals(value) ? (byte) 1 : (byte) 0);
833 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100834 }
835
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100836 private static class ByteDecoder extends Decoder<Byte> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100837
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100838 @Override
839 public byte alignment() {
840 return 1;
841 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100842
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100843 @Override
844 public Integer fixedSize() {
845 return 1;
846 }
847
848 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100849 public @NotNull Byte decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100850 return byteSlice.get();
851 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100852
853 @Override
854 void encode(Byte value, ByteWriter byteWriter) {
855 byteWriter.write(value);
856 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100857 }
858
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100859 private static class ShortDecoder extends Decoder<Short> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100860
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100861 @Override
862 public byte alignment() {
863 return 2;
864 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100865
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100866 @Override
867 public Integer fixedSize() {
868 return 2;
869 }
870
871 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100872 public @NotNull Short decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100873 return byteSlice.getShort();
874 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100875
876 @Override
877 void encode(Short value, ByteWriter byteWriter) {
878 byteWriter.write(value);
879 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100880 }
881
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100882 private static class IntegerDecoder extends Decoder<Integer> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100883
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100884 @Override
885 public byte alignment() {
886 return 4;
887 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100888
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100889 @Override
890 public Integer fixedSize() {
891 return 4;
892 }
893
894 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100895 public @NotNull Integer decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100896 return byteSlice.getInt();
897 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100898
899 @Override
900 void encode(Integer value, ByteWriter byteWriter) {
901 byteWriter.write(value);
902 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100903 }
904
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100905 private static class LongDecoder extends Decoder<Long> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100906
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100907 @Override
908 public byte alignment() {
909 return 8;
910 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100911
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100912 @Override
913 public Integer fixedSize() {
914 return 8;
915 }
916
917 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100918 public @NotNull Long decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100919 return byteSlice.getLong();
920 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100921
922 @Override
923 void encode(Long value, ByteWriter byteWriter) {
924 byteWriter.write(value);
925 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100926 }
927
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100928 private static class DoubleDecoder extends Decoder<Double> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100929
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100930 @Override
931 public byte alignment() {
932 return 8;
933 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100934
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100935 @Override
936 public Integer fixedSize() {
937 return 8;
938 }
939
940 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100941 public @NotNull Double decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100942 return byteSlice.getDouble();
943 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100944
945 @Override
946 void encode(Double value, ByteWriter byteWriter) {
947 byteWriter.write(value);
948 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100949 }
950
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100951 private static class StringDecoder extends Decoder<String> {
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100952
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100953 private final Charset charset;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100954
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100955 public StringDecoder(Charset charset) {
956 this.charset = charset;
957 }
958
959 @Override
960 public byte alignment() {
961 return 1;
962 }
963
964 @Override
965 @Nullable
966 Integer fixedSize() {
967 return null;
968 }
969
970 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +0100971 public @NotNull String decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard34430182021-12-14 20:00:36 +0100972 byteSlice.limit(byteSlice.limit() - 1);
973 return charset.decode(byteSlice).toString();
974 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100975
976 @Override
977 void encode(String value, ByteWriter byteWriter) {
978 byteWriter.write(charset.encode(value));
979 byteWriter.write((byte) 0);
980 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100981 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100982
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +0200983 @SuppressWarnings("Immutable")
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100984 private class MappingDecoder<U> extends Decoder<U> {
985
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100986 private final Function<@NotNull T, @NotNull U> decodingFunction;
987 private final Function<@NotNull U, @NotNull T> encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100988
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100989 MappingDecoder(Function<@NotNull T, @NotNull U> decodingFunction, Function<@NotNull U, @NotNull T> encodingFunction) {
990 this.decodingFunction = decodingFunction;
991 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +0100992 }
993
994 @Override
995 public byte alignment() {
996 return Decoder.this.alignment();
997 }
998
999 @Override
1000 public @Nullable Integer fixedSize() {
1001 return Decoder.this.fixedSize();
1002 }
1003
1004 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001005 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001006 return decodingFunction.apply(Decoder.this.decode(byteSlice));
1007 }
1008
1009 @Override
1010 void encode(U value, ByteWriter byteWriter) {
1011 Decoder.this.encode(encodingFunction.apply(value), byteWriter);
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001012 }
1013 }
1014
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001015 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001016 private class ContramappingDecoder extends Decoder<T> {
1017
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001018 private final UnaryOperator<ByteBuffer> decodingFunction;
1019 private final UnaryOperator<ByteBuffer> encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001020
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001021 ContramappingDecoder(UnaryOperator<ByteBuffer> decodingFunction, UnaryOperator<ByteBuffer> encodingFunction) {
1022 this.decodingFunction = decodingFunction;
1023 this.encodingFunction = encodingFunction;
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001024 }
1025
1026 @Override
1027 public byte alignment() {
1028 return Decoder.this.alignment();
1029 }
1030
1031 @Override
1032 public @Nullable Integer fixedSize() {
1033 return Decoder.this.fixedSize();
1034 }
1035
1036 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001037 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001038 var transformedBuffer = decodingFunction.apply(byteSlice.asReadOnlyBuffer().order(byteSlice.order()));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001039 return Decoder.this.decode(transformedBuffer);
1040 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001041
1042 @Override
1043 void encode(T value, ByteWriter byteWriter) {
1044 var innerByteWriter = new ByteWriter();
1045 Decoder.this.encode(value, innerByteWriter);
1046 var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer());
1047 byteWriter.write(transformedBuffer);
1048 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001049 }
1050
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001051 private class ByteOrderFixingDecoder extends Decoder<T> {
1052
1053 private final ByteOrder byteOrder;
1054
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001055 ByteOrderFixingDecoder(ByteOrder byteOrder) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001056 this.byteOrder = byteOrder;
1057 }
1058
1059 @Override
1060 public byte alignment() {
1061 return Decoder.this.alignment();
1062 }
1063
1064 @Override
1065 public @Nullable Integer fixedSize() {
1066 return Decoder.this.fixedSize();
1067 }
1068
1069 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001070 public @NotNull T decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001071 var newByteSlice = byteSlice.duplicate();
1072 newByteSlice.order(byteOrder);
1073 return Decoder.this.decode(newByteSlice);
1074 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001075
1076 @Override
1077 protected void encode(T value, ByteWriter byteWriter) {
1078 var newByteWriter = byteWriter.duplicate();
1079 newByteWriter.order(byteOrder);
1080 Decoder.this.encode(value, newByteWriter);
1081 }
Matthias Andreas Benkard6f993f72021-12-27 22:40:14 +01001082 }
1083
1084 private static ByteBuffer slicePreservingOrder(ByteBuffer byteSlice, int index, int length) {
1085 return byteSlice.slice(index, length).order(byteSlice.order());
1086 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001087
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001088 @SuppressWarnings("Immutable")
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001089 private static class PredicateDecoder<U> extends Decoder<U> {
1090
1091 private final Predicate<ByteBuffer> selector;
1092 private final Decoder<U> thenDecoder;
1093 private final Decoder<U> elseDecoder;
1094
1095 PredicateDecoder(
1096 Predicate<ByteBuffer> selector, Decoder<U> thenDecoder, Decoder<U> elseDecoder) {
1097 this.selector = selector;
1098 this.thenDecoder = thenDecoder;
1099 this.elseDecoder = elseDecoder;
1100 if (thenDecoder.alignment() != elseDecoder.alignment()) {
1101 throw new IllegalArgumentException(
1102 "incompatible alignments in predicate branches: then=%d, else=%d"
1103 .formatted(thenDecoder.alignment(), elseDecoder.alignment()));
1104 }
1105
1106 if (!Objects.equals(thenDecoder.fixedSize(), elseDecoder.fixedSize())) {
1107 throw new IllegalArgumentException(
1108 "incompatible sizes in predicate branches: then=%s, else=%s"
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001109 .formatted(
Matthias Andreas Benkard91dbd742022-10-17 19:38:56 +02001110 requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
1111 requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001112 }
1113 }
1114
1115 @Override
1116 public byte alignment() {
1117 return thenDecoder.alignment();
1118 }
1119
1120 @Override
1121 public @Nullable Integer fixedSize() {
1122 return thenDecoder.fixedSize();
1123 }
1124
1125 @Override
Matthias Andreas Benkard9006e702022-03-01 13:43:50 +01001126 public @NotNull U decode(ByteBuffer byteSlice) {
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001127 var b = selector.test(byteSlice);
1128 byteSlice.rewind();
1129 return b ? thenDecoder.decode(byteSlice) : elseDecoder.decode(byteSlice);
1130 }
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01001131
1132 @Override
1133 public void encode(U value, ByteWriter byteWriter) {
1134 elseDecoder.encode(value, byteWriter);
1135 }
1136 }
1137
1138 private static class ByteWriter {
1139 private ByteOrder byteOrder = ByteOrder.nativeOrder();
1140 private final ByteArrayOutputStream outputStream;
1141
1142 ByteWriter() {
1143 this.outputStream = new ByteArrayOutputStream();
1144 }
1145
1146 private ByteWriter(ByteArrayOutputStream outputStream) {
1147 this.outputStream = outputStream;
1148 }
1149
1150 void write(byte[] bytes) {
1151 outputStream.write(bytes, 0, bytes.length);
1152 }
1153
1154 @SuppressWarnings("java:S2095")
1155 void write(ByteBuffer byteBuffer) {
1156 var channel = Channels.newChannel(outputStream);
1157 try {
1158 channel.write(byteBuffer);
1159 } catch (IOException e) {
1160 // impossible
1161 throw new IllegalStateException(e);
1162 }
1163 }
1164
1165 void write(byte value) {
1166 outputStream.write(value);
1167 }
1168
1169 void write(int value) {
1170 write(ByteBuffer.allocate(4).order(byteOrder).putInt(value));
1171 }
1172
1173 void write(long value) {
1174 write(ByteBuffer.allocate(8).order(byteOrder).putLong(value));
1175 }
1176
1177 void write(short value) {
1178 write(ByteBuffer.allocate(2).order(byteOrder).putShort(value));
1179 }
1180
1181 void write(double value) {
1182 write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value));
1183 }
1184
1185 private void writeIntN(int value, int byteCount) {
1186 var byteBuffer = ByteBuffer.allocate(byteCount).order(LITTLE_ENDIAN);
1187 switch (byteCount) {
1188 case 0 -> {}
1189 case 1 ->
1190 byteBuffer.put((byte) value);
1191 case 2 ->
1192 byteBuffer.putShort((short) value);
1193 case 4 ->
1194 byteBuffer.putInt(value);
1195 default ->
1196 throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount));
1197 }
1198 write(byteBuffer);
1199 }
1200
1201 ByteWriter duplicate() {
1202 var duplicate = new ByteWriter(outputStream);
1203 duplicate.byteOrder = byteOrder;
1204 return duplicate;
1205 }
1206
1207 ByteBuffer toByteBuffer() {
1208 return ByteBuffer.wrap(outputStream.toByteArray());
1209 }
1210
1211 void order(ByteOrder byteOrder) {
1212 this.byteOrder = byteOrder;
1213 }
1214
1215 int position() {
1216 return outputStream.size();
1217 }
Matthias Andreas Benkard44df94e2021-12-30 18:43:33 +01001218 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001219}