From 34430180f83a9e52ead4f919731f955bbc3b3b79 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Tue, 14 Dec 2021 20:00:36 +0100 Subject: Decoder: Make anonymous classes into named inner classes. Change-Id: I3150b262512cafe42f139d56c9ecee165da4e1df --- src/main/java/eu/mulk/jgvariant/core/Decoder.java | 663 ++++++++++++---------- 1 file changed, 362 insertions(+), 301 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/src/main/java/eu/mulk/jgvariant/core/Decoder.java index 8134d45..bb479ff 100644 --- a/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -96,67 +96,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder> ofArray(Decoder elementDecoder) { - return new Decoder<>() { - @Override - public byte alignment() { - return elementDecoder.alignment(); - } - - @Override - @Nullable - Integer fixedSize() { - return null; - } - - @Override - public List decode(ByteBuffer byteSlice) { - List elements; - - var elementSize = elementDecoder.fixedSize(); - if (elementSize != null) { - // A simple C-style array. - elements = new ArrayList<>(byteSlice.limit() / elementSize); - for (int i = 0; i < byteSlice.limit(); i += elementSize) { - var element = elementDecoder.decode(byteSlice.slice(i, elementSize)); - elements.add(element); - } - } else { - // An array with aligned elements and a vector of framing offsets in the end. - int framingOffsetSize = byteCount(byteSlice.limit()); - int lastFramingOffset = - getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize)); - int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize; - - elements = new ArrayList<>(elementCount); - int position = 0; - for (int i = 0; i < elementCount; i++) { - int framingOffset = - getIntN( - byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize)); - elements.add( - elementDecoder.decode(byteSlice.slice(position, framingOffset - position))); - position = align(framingOffset, alignment()); - } - } - - return elements; - } - }; - } - - private static int align(int offset, byte alignment) { - return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment); - } - - private static int getIntN(ByteBuffer byteSlice) { - var intBytes = new byte[4]; - byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit())); - return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt(); - } - - @SuppressWarnings("java:S3358") - private static int byteCount(int n) { - return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4; + return new ArrayDecoder<>(elementDecoder); } /** @@ -167,32 +107,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder> ofMaybe(Decoder elementDecoder) { - return new Decoder<>() { - @Override - public byte alignment() { - return elementDecoder.alignment(); - } - - @Override - @Nullable - Integer fixedSize() { - return null; - } - - @Override - public Optional decode(ByteBuffer byteSlice) { - if (!byteSlice.hasRemaining()) { - return Optional.empty(); - } else { - if (!elementDecoder.hasFixedSize()) { - // Remove trailing zero byte. - byteSlice.limit(byteSlice.limit() - 1); - } - - return Optional.of(elementDecoder.decode(byteSlice)); - } - } - }; + return new MaybeDecoder<>(elementDecoder); } /** @@ -205,94 +120,7 @@ public abstract class Decoder { */ public static Decoder ofStructure( Class recordType, Decoder... componentDecoders) { - var recordComponents = recordType.getRecordComponents(); - if (componentDecoders.length != recordComponents.length) { - throw new IllegalArgumentException( - "number of decoders (%d) does not match number of structure components (%d)" - .formatted(componentDecoders.length, recordComponents.length)); - } - - return new Decoder<>() { - @Override - public byte alignment() { - return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1); - } - - @Override - public Integer fixedSize() { - int position = 0; - for (var componentDecoder : componentDecoders) { - var fixedComponentSize = componentDecoder.fixedSize(); - if (fixedComponentSize == null) { - return null; - } - - position = align(position, componentDecoder.alignment()); - position += fixedComponentSize; - } - - if (position == 0) { - return 1; - } - - return align(position, alignment()); - } - - @Override - public U decode(ByteBuffer byteSlice) { - int framingOffsetSize = byteCount(byteSlice.limit()); - - var recordConstructorArguments = new Object[recordComponents.length]; - - int position = 0; - int framingOffsetIndex = 0; - int componentIndex = 0; - for (var componentDecoder : componentDecoders) { - position = align(position, componentDecoder.alignment()); - - var fixedComponentSize = componentDecoder.fixedSize(); - if (fixedComponentSize != null) { - recordConstructorArguments[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, fixedComponentSize)); - position += fixedComponentSize; - } else { - if (componentIndex == recordComponents.length - 1) { - // The last component never has a framing offset. - int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize; - recordConstructorArguments[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, endPosition - position)); - position = endPosition; - } else { - int framingOffset = - getIntN( - byteSlice.slice( - byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize, - framingOffsetSize)); - recordConstructorArguments[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, framingOffset - position)); - position = framingOffset; - ++framingOffsetIndex; - } - } - - ++componentIndex; - } - - try { - var recordComponentTypes = - Arrays.stream(recordType.getRecordComponents()) - .map(RecordComponent::getType) - .toArray(Class[]::new); - var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes); - return recordConstructor.newInstance(recordConstructorArguments); - } catch (NoSuchMethodException - | InstantiationException - | IllegalAccessException - | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - }; + return new StructureDecoder<>(recordType, componentDecoders); } /** @@ -301,24 +129,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofVariant() { - return new Decoder<>() { - @Override - public byte alignment() { - return 8; - } - - @Override - @Nullable - Integer fixedSize() { - return null; - } - - @Override - public Variant decode(ByteBuffer byteSlice) { - // TODO - throw new UnsupportedOperationException("not implemented"); - } - }; + return new VariantDecoder(); } /** @@ -327,22 +138,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofBoolean() { - return new Decoder<>() { - @Override - public byte alignment() { - return 1; - } - - @Override - public Integer fixedSize() { - return 1; - } - - @Override - public Boolean decode(ByteBuffer byteSlice) { - return byteSlice.get() != 0; - } - }; + return new BooleanDecoder(); } /** @@ -354,22 +150,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofByte() { - return new Decoder<>() { - @Override - public byte alignment() { - return 1; - } - - @Override - public Integer fixedSize() { - return 1; - } - - @Override - public Byte decode(ByteBuffer byteSlice) { - return byteSlice.get(); - } - }; + return new ByteDecoder(); } /** @@ -381,22 +162,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofShort() { - return new Decoder<>() { - @Override - public byte alignment() { - return 2; - } - - @Override - public Integer fixedSize() { - return 2; - } - - @Override - public Short decode(ByteBuffer byteSlice) { - return byteSlice.getShort(); - } - }; + return new ShortDecoder(); } /** @@ -408,22 +174,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofInt() { - return new Decoder<>() { - @Override - public byte alignment() { - return 4; - } - - @Override - public Integer fixedSize() { - return 4; - } - - @Override - public Integer decode(ByteBuffer byteSlice) { - return byteSlice.getInt(); - } - }; + return new IntegerDecoder(); } /** @@ -435,22 +186,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofLong() { - return new Decoder<>() { - @Override - public byte alignment() { - return 8; - } - - @Override - public Integer fixedSize() { - return 8; - } - - @Override - public Long decode(ByteBuffer byteSlice) { - return byteSlice.getLong(); - } - }; + return new LongDecoder(); } /** @@ -459,22 +195,7 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofDouble() { - return new Decoder<>() { - @Override - public byte alignment() { - return 8; - } - - @Override - public Integer fixedSize() { - return 8; - } - - @Override - public Double decode(ByteBuffer byteSlice) { - return byteSlice.getDouble(); - } - }; + return new DoubleDecoder(); } /** @@ -486,23 +207,363 @@ public abstract class Decoder { * @return a new {@link Decoder}. */ public static Decoder ofString(Charset charset) { - return new Decoder<>() { - @Override - public byte alignment() { + return new StringDecoder(charset); + } + + private static int align(int offset, byte alignment) { + return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment); + } + + private static int getIntN(ByteBuffer byteSlice) { + var intBytes = new byte[4]; + byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit())); + return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt(); + } + + @SuppressWarnings("java:S3358") + private static int byteCount(int n) { + return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4; + } + + private static class ArrayDecoder extends Decoder> { + + private final Decoder elementDecoder; + + ArrayDecoder(Decoder elementDecoder) { + this.elementDecoder = elementDecoder; + } + + @Override + public byte alignment() { + return elementDecoder.alignment(); + } + + @Override + @Nullable + Integer fixedSize() { + return null; + } + + @Override + public List decode(ByteBuffer byteSlice) { + List elements; + + var elementSize = elementDecoder.fixedSize(); + if (elementSize != null) { + // A simple C-style array. + elements = new ArrayList<>(byteSlice.limit() / elementSize); + for (int i = 0; i < byteSlice.limit(); i += elementSize) { + var element = elementDecoder.decode(byteSlice.slice(i, elementSize)); + elements.add(element); + } + } else { + // An array with aligned elements and a vector of framing offsets in the end. + int framingOffsetSize = byteCount(byteSlice.limit()); + int lastFramingOffset = + getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize)); + int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize; + + elements = new ArrayList<>(elementCount); + int position = 0; + for (int i = 0; i < elementCount; i++) { + int framingOffset = + getIntN( + byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize)); + elements.add(elementDecoder.decode(byteSlice.slice(position, framingOffset - position))); + position = align(framingOffset, alignment()); + } + } + + return elements; + } + } + + private static class MaybeDecoder extends Decoder> { + + private final Decoder elementDecoder; + + MaybeDecoder(Decoder elementDecoder) { + this.elementDecoder = elementDecoder; + } + + @Override + public byte alignment() { + return elementDecoder.alignment(); + } + + @Override + @Nullable + Integer fixedSize() { + return null; + } + + @Override + public Optional decode(ByteBuffer byteSlice) { + if (!byteSlice.hasRemaining()) { + return Optional.empty(); + } else { + if (!elementDecoder.hasFixedSize()) { + // Remove trailing zero byte. + byteSlice.limit(byteSlice.limit() - 1); + } + + return Optional.of(elementDecoder.decode(byteSlice)); + } + } + } + + private static class StructureDecoder extends Decoder { + + private final RecordComponent[] recordComponents; + private final Class recordType; + private final Decoder[] componentDecoders; + + StructureDecoder(Class recordType, Decoder... componentDecoders) { + var recordComponents = recordType.getRecordComponents(); + + if (componentDecoders.length != recordComponents.length) { + throw new IllegalArgumentException( + "number of decoders (%d) does not match number of structure components (%d)" + .formatted(componentDecoders.length, recordComponents.length)); + } + + this.recordComponents = recordComponents; + this.recordType = recordType; + this.componentDecoders = componentDecoders; + } + + @Override + public byte alignment() { + return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1); + } + + @Override + public Integer fixedSize() { + int position = 0; + for (var componentDecoder : componentDecoders) { + var fixedComponentSize = componentDecoder.fixedSize(); + if (fixedComponentSize == null) { + return null; + } + + position = align(position, componentDecoder.alignment()); + position += fixedComponentSize; + } + + if (position == 0) { return 1; } - @Override - @Nullable - Integer fixedSize() { - return null; + return align(position, alignment()); + } + + @Override + public U decode(ByteBuffer byteSlice) { + int framingOffsetSize = byteCount(byteSlice.limit()); + + var recordConstructorArguments = new Object[recordComponents.length]; + + int position = 0; + int framingOffsetIndex = 0; + int componentIndex = 0; + for (var componentDecoder : componentDecoders) { + position = align(position, componentDecoder.alignment()); + + var fixedComponentSize = componentDecoder.fixedSize(); + if (fixedComponentSize != null) { + recordConstructorArguments[componentIndex] = + componentDecoder.decode(byteSlice.slice(position, fixedComponentSize)); + position += fixedComponentSize; + } else { + if (componentIndex == recordComponents.length - 1) { + // The last component never has a framing offset. + int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize; + recordConstructorArguments[componentIndex] = + componentDecoder.decode(byteSlice.slice(position, endPosition - position)); + position = endPosition; + } else { + int framingOffset = + getIntN( + byteSlice.slice( + byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize, + framingOffsetSize)); + recordConstructorArguments[componentIndex] = + componentDecoder.decode(byteSlice.slice(position, framingOffset - position)); + position = framingOffset; + ++framingOffsetIndex; + } + } + + ++componentIndex; } - @Override - public String decode(ByteBuffer byteSlice) { - byteSlice.limit(byteSlice.limit() - 1); - return charset.decode(byteSlice).toString(); + try { + var recordComponentTypes = + Arrays.stream(recordType.getRecordComponents()) + .map(RecordComponent::getType) + .toArray(Class[]::new); + var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes); + return recordConstructor.newInstance(recordConstructorArguments); + } catch (NoSuchMethodException + | InstantiationException + | IllegalAccessException + | InvocationTargetException e) { + throw new IllegalStateException(e); } - }; + } + } + + private static class VariantDecoder extends Decoder { + + @Override + public byte alignment() { + return 8; + } + + @Override + @Nullable + Integer fixedSize() { + return null; + } + + @Override + public Variant decode(ByteBuffer byteSlice) { + // TODO + throw new UnsupportedOperationException("not implemented"); + } + } + + private static class BooleanDecoder extends Decoder { + + @Override + public byte alignment() { + return 1; + } + + @Override + public Integer fixedSize() { + return 1; + } + + @Override + public Boolean decode(ByteBuffer byteSlice) { + return byteSlice.get() != 0; + } + } + + private static class ByteDecoder extends Decoder { + + @Override + public byte alignment() { + return 1; + } + + @Override + public Integer fixedSize() { + return 1; + } + + @Override + public Byte decode(ByteBuffer byteSlice) { + return byteSlice.get(); + } + } + + private static class ShortDecoder extends Decoder { + + @Override + public byte alignment() { + return 2; + } + + @Override + public Integer fixedSize() { + return 2; + } + + @Override + public Short decode(ByteBuffer byteSlice) { + return byteSlice.getShort(); + } + } + + private static class IntegerDecoder extends Decoder { + + @Override + public byte alignment() { + return 4; + } + + @Override + public Integer fixedSize() { + return 4; + } + + @Override + public Integer decode(ByteBuffer byteSlice) { + return byteSlice.getInt(); + } + } + + private static class LongDecoder extends Decoder { + + @Override + public byte alignment() { + return 8; + } + + @Override + public Integer fixedSize() { + return 8; + } + + @Override + public Long decode(ByteBuffer byteSlice) { + return byteSlice.getLong(); + } + } + + private static class DoubleDecoder extends Decoder { + + @Override + public byte alignment() { + return 8; + } + + @Override + public Integer fixedSize() { + return 8; + } + + @Override + public Double decode(ByteBuffer byteSlice) { + return byteSlice.getDouble(); + } + } + + private static class StringDecoder extends Decoder { + + private final Charset charset; + + public StringDecoder(Charset charset) { + this.charset = charset; + } + + @Override + public byte alignment() { + return 1; + } + + @Override + @Nullable + Integer fixedSize() { + return null; + } + + @Override + public String decode(ByteBuffer byteSlice) { + byteSlice.limit(byteSlice.limit() - 1); + return charset.decode(byteSlice).toString(); + } } } -- cgit v1.2.3