From e9440b54b5442c3b5ef7bffa936152ebbc7b7173 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sun, 10 Dec 2023 15:28:16 +0100 Subject: Add Decoder#encode roundtrip tests and fix the bugs discovered. Change-Id: I21447306d9fc7768e07fafe5bed1d92a3eb42e53 --- .../main/java/eu/mulk/jgvariant/core/Decoder.java | 52 +++++++++++++++------- 1 file changed, 36 insertions(+), 16 deletions(-) (limited to 'jgvariant-core/src/main/java/eu') diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java index a28d792..f605b09 100644 --- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -5,6 +5,7 @@ package eu.mulk.jgvariant.core; import static java.lang.Math.max; +import static java.nio.ByteOrder.BIG_ENDIAN; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNullElse; @@ -440,12 +441,16 @@ public abstract class Decoder { ArrayList framingOffsets = new ArrayList<>(value.size()); int startOffset = byteWriter.position(); for (var element : value) { + // Align the element. + var lastRelativeEnd = byteWriter.position() - startOffset; + byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]); + + // Encode the element. elementDecoder.encode(element, byteWriter); + + // Record the framing offset of the element. var relativeEnd = byteWriter.position() - startOffset; framingOffsets.add(relativeEnd); - - // Align the next element. - byteWriter.write(new byte[align(relativeEnd, alignment()) - relativeEnd]); } // Write the framing offsets. @@ -710,21 +715,30 @@ public abstract class Decoder { @Override @SuppressWarnings("unchecked") void encode(Object[] value, ByteWriter byteWriter) { + // The unit type is encoded as a single zero byte. + if (value.length == 0) { + byteWriter.write((byte) 0); + return; + } + int startOffset = byteWriter.position(); ArrayList framingOffsets = new ArrayList<>(value.length); for (int i = 0; i < value.length; ++i) { var componentDecoder = (Decoder) componentDecoders[i]; - componentDecoder.encode(value[i], byteWriter); - var relativeEnd = byteWriter.position() - startOffset; + // Align the element. + var lastRelativeEnd = byteWriter.position() - startOffset; + byteWriter.write(new byte[align(lastRelativeEnd, componentDecoder.alignment()) - lastRelativeEnd]); + + // Encode the element. + componentDecoder.encode(value[i], byteWriter); + // Record the framing offset of the element if it is of variable size. var fixedComponentSize = componentDecoders[i].fixedSize(); if (fixedComponentSize == null && i < value.length - 1) { + var relativeEnd = byteWriter.position() - startOffset; framingOffsets.add(relativeEnd); } - - // Align the next element. - byteWriter.write(new byte[align(relativeEnd, alignment()) - relativeEnd]); } // Write the framing offsets in reverse order. @@ -732,6 +746,12 @@ public abstract class Decoder { for (int i = framingOffsets.size() - 1; i >= 0; --i) { byteWriter.writeIntN(framingOffsets.get(i), framingOffsetSize); } + + // Pad the structure to its alignment if it is of fixed size. + if (fixedSize() != null) { + var lastRelativeEnd = byteWriter.position() - startOffset; + byteWriter.write(new byte[align(lastRelativeEnd, alignment()) - lastRelativeEnd]); + } } } @@ -975,7 +995,7 @@ public abstract class Decoder { @Override void encode(String value, ByteWriter byteWriter) { - byteWriter.write(charset.encode(value)); + byteWriter.write(charset.encode(value).rewind()); byteWriter.write((byte) 0); } } @@ -1044,7 +1064,7 @@ public abstract class Decoder { var innerByteWriter = new ByteWriter(); Decoder.this.encode(value, innerByteWriter); var transformedBuffer = encodingFunction.apply(innerByteWriter.toByteBuffer()); - byteWriter.write(transformedBuffer); + byteWriter.write(transformedBuffer.rewind()); } } @@ -1136,7 +1156,7 @@ public abstract class Decoder { } private static class ByteWriter { - private ByteOrder byteOrder = ByteOrder.nativeOrder(); + private ByteOrder byteOrder = BIG_ENDIAN; private final ByteArrayOutputStream outputStream; ByteWriter() { @@ -1167,19 +1187,19 @@ public abstract class Decoder { } void write(int value) { - write(ByteBuffer.allocate(4).order(byteOrder).putInt(value)); + write(ByteBuffer.allocate(4).order(byteOrder).putInt(value).rewind()); } void write(long value) { - write(ByteBuffer.allocate(8).order(byteOrder).putLong(value)); + write(ByteBuffer.allocate(8).order(byteOrder).putLong(value).rewind()); } void write(short value) { - write(ByteBuffer.allocate(2).order(byteOrder).putShort(value)); + write(ByteBuffer.allocate(2).order(byteOrder).putShort(value).rewind()); } void write(double value) { - write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value)); + write(ByteBuffer.allocate(8).order(byteOrder).putDouble(value).rewind()); } private void writeIntN(int value, int byteCount) { @@ -1195,7 +1215,7 @@ public abstract class Decoder { default -> throw new IllegalArgumentException("invalid byte count: %d".formatted(byteCount)); } - write(byteBuffer); + write(byteBuffer.rewind()); } ByteWriter duplicate() { -- cgit v1.2.3