From 194de936c56375345d4677b0c9063517bdec6a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vilius=20Panev=C4=97=C5=BEys?= Date: Thu, 30 Oct 2025 14:37:13 +0200 Subject: [PATCH] Add support for extensible enum decoding --- .../java/net/gcdc/asn1/uper/EnumCoder.java | 40 +++++++++++-------- .../net/gcdc/asn1/uper/EnumCoderTest.java | 37 +++++++++++++++++ 2 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 asn1-uper/src/test/java/net/gcdc/asn1/uper/EnumCoderTest.java diff --git a/asn1-uper/src/main/java/net/gcdc/asn1/uper/EnumCoder.java b/asn1-uper/src/main/java/net/gcdc/asn1/uper/EnumCoder.java index fb30cef..8a3f6dd 100644 --- a/asn1-uper/src/main/java/net/gcdc/asn1/uper/EnumCoder.java +++ b/asn1-uper/src/main/java/net/gcdc/asn1/uper/EnumCoder.java @@ -64,23 +64,31 @@ class EnumCoder implements Decoder, Encoder { AnnotationStore annotations = new AnnotationStore(classOfT.getAnnotations(), extraAnnotations); UperEncoder.logger.debug("ENUM"); - if (UperEncoder.hasExtensionMarker(annotations)) { - boolean extensionPresent = bitbuffer.get(); - UperEncoder.logger.debug("with extension marker, {}", extensionPresent ? "present" : "absent"); - if (extensionPresent) { - throw new UnsupportedOperationException( - "choice extension is not implemented yet"); + T value = null; + if (UperEncoder.hasExtensionMarker(annotations) + && bitbuffer.get()) { // extension present + UperEncoder.logger.debug("with extension marker, extension present"); + boolean isGreaterThan64 = bitbuffer.get(); + // The decoded value is discarded - no way to return an undefined enum value, just consuming the bits. + if (!isGreaterThan64) { + UperEncoder.decodeConstrainedInt(bitbuffer, UperEncoder.newRange(0, uintMax(6), false)); } else { - // We already consumed the bit, keep processing as if there were no extension. + int numOctets = (int) UperEncoder.decodeLengthDeterminant(bitbuffer); + UperEncoder.decodeConstrainedInt(bitbuffer, UperEncoder.newRange(0, uintMax(numOctets * 8), false)); } + } else { + T[] enumValues = classOfT.getEnumConstants(); + int index = (int) UperEncoder.decodeConstrainedInt(bitbuffer, + UperEncoder.newRange(0, enumValues.length - 1, false)); + if (index > enumValues.length - 1) { throw new IllegalArgumentException( + "decoded enum index " + index + " is larger than number of elements (0.." + + enumValues.length + ") in " + classOfT.getName()); } + value = enumValues[index]; } - T[] enumValues = classOfT.getEnumConstants(); - int index = (int) UperEncoder.decodeConstrainedInt(bitbuffer, - UperEncoder.newRange(0, enumValues.length - 1, false)); - if (index > enumValues.length - 1) { throw new IllegalArgumentException( - "decoded enum index " + index + " is larger then number of elements (0.." - + enumValues.length + ") in " + classOfT.getName()); } - T value = enumValues[index]; - return value; } + return value; + } -} \ No newline at end of file + private static int uintMax(int bits) { + return (int) Math.pow(2, bits) - 1; + } +} diff --git a/asn1-uper/src/test/java/net/gcdc/asn1/uper/EnumCoderTest.java b/asn1-uper/src/test/java/net/gcdc/asn1/uper/EnumCoderTest.java new file mode 100644 index 0000000..0f147df --- /dev/null +++ b/asn1-uper/src/test/java/net/gcdc/asn1/uper/EnumCoderTest.java @@ -0,0 +1,37 @@ +package net.gcdc.asn1.uper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import net.gcdc.asn1.datatypes.HasExtensionMarker; + +public class EnumCoderTest { + @HasExtensionMarker + private enum ConstructedType { + CHOICE(1), SEQUENCE(2), SET(3), SEQUENCE_OF(4), SET_OF(5); + + private final int value; + + private ConstructedType(int value) { + this.value = value; + } + } + + @Test + public void extensibleNoExt() { + assertEquals(ConstructedType.SEQUENCE, + UperEncoder.decode(new byte[] {0x10}, ConstructedType.class)); + } + + @Test + public void extensibleExtSmall() { + assertNull(UperEncoder.decode(new byte[] {(byte) 0xA9}, ConstructedType.class)); + } + + @Test + public void extensibleWithLength() { + assertNull(UperEncoder.decode(new byte[] {(byte) 0xC0, 0x50, 0x00}, ConstructedType.class)); + } +}