This is an automated email from the ASF dual-hosted git repository.

tabish121 pushed a commit to branch 1.x
in repository https://gitbox.apache.org/repos/asf/qpid-protonj2.git


The following commit(s) were added to refs/heads/1.x by this push:
     new 7f8ebc11 PROTON-3002 Add additional thread safety fixes to codec 
implementations
7f8ebc11 is described below

commit 7f8ebc11872bdb2b7368ce17245422c751822b07
Author: Timothy Bish <[email protected]>
AuthorDate: Wed Nov 4 18:40:19 2026 -0500

    PROTON-3002 Add additional thread safety fixes to codec implementations
    
    Adds a few more missed thread safety fixes for the encoder and both
    decoder implementations as well as some additional checks that ensure
    descriptive errors are thrown in more cases.
    
    (cherry picked from commit e035e68806296465da009f8083c82cd69fb506fb)
---
 .../protonj2/codec/decoders/ProtonDecoder.java     |  40 ++++++--
 .../codec/decoders/ProtonStreamDecoder.java        |  29 ++++--
 .../protonj2/codec/encoders/ProtonEncoder.java     |   5 +-
 .../codec/UnknownDescribedTypeCodecTest.java       |  66 +++++++++++++
 .../protonj2/codec/decoders/ProtonDecoderTest.java |  10 ++
 .../codec/decoders/ProtonStreamDecoderTest.java    |  10 ++
 .../codec/decoders/TestDescribedTypeDecoder.java   | 102 +++++++++++++++++++++
 7 files changed, 244 insertions(+), 18 deletions(-)

diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
index f5b0a475..c164fc31 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoder.java
@@ -21,8 +21,10 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiFunction;
 
 import org.apache.qpid.protonj2.buffer.ProtonBuffer;
@@ -174,6 +176,7 @@ public class ProtonDecoder implements Decoder {
     // Registry of decoders for described types which are not registered which 
can be updated with a
     // limited number of cached decoders to speed up processing
     private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new ConcurrentHashMap<>();
+    private final AtomicInteger unknownDescribedTypeDecodersSlots = new 
AtomicInteger(UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT);
 
     // Quick access to decoders that handle AMQP types like Transfer, 
Properties etc.
     private final DescribedTypeDecoder<?>[] amqpTypeDecoders = new 
DescribedTypeDecoder[256];
@@ -202,7 +205,7 @@ public class ProtonDecoder implements Decoder {
     private final AbstractSymbolTypeDecoder localSymbol8Decoder;
     private final AbstractSymbolTypeDecoder localSymbol32Decoder;
 
-    private ProtonDecoderState singleThreadedState;
+    private volatile ProtonDecoderState singleThreadedState;
 
     public ProtonDecoder() {
         this(DecoderMode.DEFAULT, null);
@@ -241,6 +244,7 @@ public class ProtonDecoder implements Decoder {
     @Override
     public ProtonDecoderState getCachedDecoderState() {
         ProtonDecoderState state = singleThreadedState;
+
         if (state == null) {
             singleThreadedState = state = newDecoderState();
         }
@@ -337,8 +341,14 @@ public class ProtonDecoder implements Decoder {
                             state.decreaseDepth();
                         }
                     } else {
-                        throw new DecodeException(String.format(
-                            "Cannot decode a type that is using a reserved 
type descriptor: %s", peekNextTypeDecoder(buffer, state).getTypeClass()));
+                        final TypeDecoder<?> typeDecoder = 
peekNextTypeDecoder(buffer, state);
+
+                        if (typeDecoder == null) {
+                            throw ex;
+                        } else {
+                            throw new DecodeException(String.format(
+                                "Cannot decode a type that is using a reserved 
type descriptor: %s", typeDecoder.getTypeClass()));
+                        }
                     }
                 } else {
                     throw new DecodeException("Cannot decode reserved 
descriptor type in SASL mode.");
@@ -346,6 +356,10 @@ public class ProtonDecoder implements Decoder {
             }
         }
 
+        if (descriptor == null) {
+            throw new DecodeException("Expected Descriptor type but read null 
encoding");
+        }
+
         TypeDecoder<?> typeDecoder = describedTypeDecoders.get(descriptor);
         if (typeDecoder == null) {
             typeDecoder = unknownDescribedTypeDecoders.get(descriptor);
@@ -369,15 +383,20 @@ public class ProtonDecoder implements Decoder {
 
     @Override
     public <V> ProtonDecoder 
registerDescribedTypeDecoder(DescribedTypeDecoder<V> decoder) {
-        DescribedTypeDecoder<?> describedTypeDecoder = decoder;
+        Objects.requireNonNull(decoder, "Stream described type decoder cannot 
be null");
+
+        if (decoder.getDescriptorCode() == null || 
decoder.getDescriptorSymbol() == null) {
+            throw new IllegalArgumentException(
+                "DescribedTypeDecoder instance must return both a Symbol and 
an UnsignedLong descriptor value");
+        }
 
         // Cache AMQP type decoders in the quick lookup array.
         if (decoder.getDescriptorCode().compareTo(amqpTypeDecoders.length) < 
0) {
             amqpTypeDecoders[decoder.getDescriptorCode().intValue()] = decoder;
         }
 
-        describedTypeDecoders.put(describedTypeDecoder.getDescriptorCode(), 
describedTypeDecoder);
-        describedTypeDecoders.put(describedTypeDecoder.getDescriptorSymbol(), 
describedTypeDecoder);
+        describedTypeDecoders.put(decoder.getDescriptorCode(), decoder);
+        describedTypeDecoders.put(decoder.getDescriptorSymbol(), decoder);
 
         decoder.decoderRegistered(this);
 
@@ -1033,8 +1052,13 @@ public class ProtonDecoder implements Decoder {
             }
         };
 
-        if (canCache && unknownDescribedTypeDecoders.size() < 
UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT) {
-            unknownDescribedTypeDecoders.putIfAbsent(descriptor, typeDecoder);
+        if (canCache && 
unknownDescribedTypeDecodersSlots.getAndUpdate(available -> available > 0 ? 
available - 1 : available) > 0) {
+            if (unknownDescribedTypeDecodersSlots.getAndDecrement() > 0) {
+                final UnknownDescribedTypeDecoder previous = 
unknownDescribedTypeDecoders.putIfAbsent(descriptor, typeDecoder);
+                if (previous != null) {
+                    unknownDescribedTypeDecodersSlots.incrementAndGet();
+                }
+            }
         }
 
         return typeDecoder;
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
index 7dcc05fe..7fbcc283 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoder.java
@@ -21,11 +21,12 @@ import java.io.InputStream;
 import java.lang.reflect.Array;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.qpid.protonj2.buffer.ProtonBuffer;
 import org.apache.qpid.protonj2.codec.DecodeException;
@@ -172,16 +173,17 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
 
     // Registry of decoders for described types which can be updated with user 
defined
     // decoders as well as the default decoders.
-    private final Map<Object, StreamDescribedTypeDecoder<?>> 
describedTypeDecoders = new HashMap<>();
+    private final Map<Object, StreamDescribedTypeDecoder<?>> 
describedTypeDecoders = new ConcurrentHashMap<>();
 
     // Registry of decoders for described types which are not registered which 
can be updated with a
     // limited number of cached decoders to speed up processing
     private final Map<Object, UnknownDescribedTypeDecoder> 
unknownDescribedTypeDecoders = new ConcurrentHashMap<>();
+    private final AtomicInteger unknownDescribedTypeDecodersSlots = new 
AtomicInteger(UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT);
 
     // Quick access to decoders that handle AMQP types like Transfer, 
Properties etc.
     private final StreamDescribedTypeDecoder<?>[] amqpTypeDecoders = new 
StreamDescribedTypeDecoder[256];
 
-    private ProtonStreamDecoderState singleThreadedState;
+    private volatile ProtonStreamDecoderState singleThreadedState;
 
     // Internal Decoders used to prevent user to access Proton specific 
decoding methods
     private static final Symbol8TypeDecoder symbol8Decoder;
@@ -230,6 +232,7 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
     @Override
     public ProtonStreamDecoderState getCachedDecoderState() {
         ProtonStreamDecoderState state = singleThreadedState;
+
         if (state == null) {
             singleThreadedState = state = newDecoderState();
         }
@@ -362,15 +365,20 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
 
     @Override
     public <V> ProtonStreamDecoder 
registerDescribedTypeDecoder(StreamDescribedTypeDecoder<V> decoder) {
-        StreamDescribedTypeDecoder<?> describedTypeDecoder = decoder;
+        Objects.requireNonNull(decoder, "Stream described type decoder cannot 
be null");
+
+        if (decoder.getDescriptorCode() == null || 
decoder.getDescriptorSymbol() == null) {
+            throw new IllegalArgumentException(
+                "DescribedTypeDecoder instance must return both a Symbol and 
an UnsignedLong descriptor value");
+        }
 
         // Cache AMQP type decoders in the quick lookup array.
         if (decoder.getDescriptorCode().compareTo(amqpTypeDecoders.length) < 
0) {
             amqpTypeDecoders[decoder.getDescriptorCode().intValue()] = decoder;
         }
 
-        describedTypeDecoders.put(describedTypeDecoder.getDescriptorCode(), 
describedTypeDecoder);
-        describedTypeDecoders.put(describedTypeDecoder.getDescriptorSymbol(), 
describedTypeDecoder);
+        describedTypeDecoders.put(decoder.getDescriptorCode(), decoder);
+        describedTypeDecoders.put(decoder.getDescriptorSymbol(), decoder);
 
         return this;
     }
@@ -1016,8 +1024,13 @@ public final class ProtonStreamDecoder implements 
StreamDecoder {
             }
         };
 
-        if (canCache && unknownDescribedTypeDecoders.size() < 
UNKNOWN_DESCRIBED_TYPES_CACHE_LIMIT) {
-            unknownDescribedTypeDecoders.putIfAbsent(descriptor, 
streamTypeDecoder);
+        if (canCache && unknownDescribedTypeDecodersSlots.get() > 0) {
+            if (canCache && 
unknownDescribedTypeDecodersSlots.getAndUpdate(available -> available > 0 ? 
available - 1 : available) > 0) {
+                final UnknownDescribedTypeDecoder previous = 
unknownDescribedTypeDecoders.putIfAbsent(descriptor, streamTypeDecoder);
+                if (previous != null) {
+                    unknownDescribedTypeDecodersSlots.incrementAndGet();
+                }
+            }
         }
 
         return streamTypeDecoder;
diff --git 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
index 8fecf306..9f8ff698 100644
--- 
a/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
+++ 
b/protonj2/src/main/java/org/apache/qpid/protonj2/codec/encoders/ProtonEncoder.java
@@ -106,7 +106,7 @@ public final class ProtonEncoder implements Encoder {
     private static final UnsignedLongTypeEncoder ulongEncoder = 
UnsignedLongTypeEncoder.INSTANCE;
     private static final DeliveryTagEncoder deliveryTagEncoder = 
DeliveryTagEncoder.INSTANCE;
 
-    private ProtonEncoderState singleThreadedState;
+    private volatile ProtonEncoderState singleThreadedState;
 
     private final Map<Class<?>, TypeEncoder<?>> typeEncoders = new 
ConcurrentHashMap<>();
     {
@@ -160,11 +160,12 @@ public final class ProtonEncoder implements Encoder {
     @Override
     public ProtonEncoderState getCachedEncoderState() {
         ProtonEncoderState state = singleThreadedState;
+
         if (state == null) {
             singleThreadedState = state = newEncoderState();
         }
 
-        return singleThreadedState.reset();
+        return state.reset();
     }
 
     @Override
diff --git 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/UnknownDescribedTypeCodecTest.java
 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/UnknownDescribedTypeCodecTest.java
index 9717c163..5b892236 100644
--- 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/UnknownDescribedTypeCodecTest.java
+++ 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/UnknownDescribedTypeCodecTest.java
@@ -650,4 +650,70 @@ public class UnknownDescribedTypeCodecTest extends 
CodecTestSupport {
             assertThrows(DecodeException.class, () -> 
decoder.readObject(buffer.copy(true), decoderState));
         }
     }
+
+    @Test
+    public void testDecodeExceptionThrownForInvalidDescribedTypeDescriptor() 
throws IOException {
+        doTestDecodeExceptionThrownForInvliadDescribefTypeDescriptor(false);
+    }
+
+    @Test
+    public void testDecodeExceptionThrownForInvalidDescribedTypeDescriptorFS() 
throws IOException {
+        doTestDecodeExceptionThrownForInvliadDescribefTypeDescriptor(true);
+    }
+
+    private void 
doTestDecodeExceptionThrownForInvliadDescribefTypeDescriptor(boolean 
fromStream) throws IOException {
+        ProtonBuffer buffer = 
ProtonBufferAllocator.defaultAllocator().allocate();
+
+        buffer.writeByte((byte) 0); // Described Type Indicator
+        buffer.writeByte(EncodingCodes.SMALLINT);
+        buffer.writeByte(NoLocalType.DESCRIPTOR_CODE.byteValue());
+        buffer.writeByte(EncodingCodes.LIST0);
+
+        if (fromStream) {
+            InputStream stream = new 
ProtonBufferInputStream(buffer.copy(true));
+            assertThrows(DecodeException.class, () -> 
streamDecoder.readObject(stream, streamDecoderState));
+        } else {
+            assertThrows(DecodeException.class, () -> 
decoder.readObject(buffer.copy(true), decoderState));
+        }
+
+        if (fromStream) {
+            InputStream stream = new 
ProtonBufferInputStream(buffer.copy(true));
+            assertThrows(DecodeException.class, () -> 
streamDecoder.readNextTypeDecoder(stream, streamDecoderState));
+        } else {
+            assertThrows(DecodeException.class, () -> 
decoder.readNextTypeDecoder(buffer.copy(true), decoderState));
+        }
+    }
+
+    @Test
+    public void 
testDecodeExceptionThrownForInvalidNullDescribedTypeDescriptor() throws 
IOException {
+        
doTestDecodeExceptionThrownForInvalidNullDescribedTypeDescriptor(false);
+    }
+
+    @Test
+    public void 
testDecodeExceptionThrownForInvalidNulDescribedTypeDescriptorFS() throws 
IOException {
+        doTestDecodeExceptionThrownForInvalidNullDescribedTypeDescriptor(true);
+    }
+
+    private void 
doTestDecodeExceptionThrownForInvalidNullDescribedTypeDescriptor(boolean 
fromStream) throws IOException {
+        ProtonBuffer buffer = 
ProtonBufferAllocator.defaultAllocator().allocate();
+
+        buffer.writeByte((byte) 0); // Described Type Indicator
+        buffer.writeByte(EncodingCodes.NULL);
+        buffer.writeByte(NoLocalType.DESCRIPTOR_CODE.byteValue());
+        buffer.writeByte(EncodingCodes.LIST0);
+
+        if (fromStream) {
+            InputStream stream = new 
ProtonBufferInputStream(buffer.copy(true));
+            assertThrows(DecodeException.class, () -> 
streamDecoder.readObject(stream, streamDecoderState));
+        } else {
+            assertThrows(DecodeException.class, () -> 
decoder.readObject(buffer.copy(true), decoderState));
+        }
+
+        if (fromStream) {
+            InputStream stream = new 
ProtonBufferInputStream(buffer.copy(true));
+            assertThrows(DecodeException.class, () -> 
streamDecoder.readNextTypeDecoder(stream, streamDecoderState));
+        } else {
+            assertThrows(DecodeException.class, () -> 
decoder.readNextTypeDecoder(buffer.copy(true), decoderState));
+        }
+    }
 }
diff --git 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoderTest.java
 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoderTest.java
index a965ea7e..83460328 100644
--- 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoderTest.java
+++ 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonDecoderTest.java
@@ -52,6 +52,16 @@ public class ProtonDecoderTest extends CodecTestSupport {
         assertSame(first, decoder.getCachedDecoderState());
     }
 
+    @Test
+    public void 
testDescriptedTypeDecoderRegistrationChecksForNullDescriptors() {
+        assertThrows(IllegalArgumentException.class,
+            () -> decoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(null, null)));
+        assertThrows(IllegalArgumentException.class,
+            () -> decoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(null, new UnsignedLong(0))));
+        assertThrows(IllegalArgumentException.class,
+            () -> decoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(Symbol.getSymbol("test"), null)));
+    }
+
     @Test
     public void testReadNullFromReadObjectForNullEncoding() throws IOException 
{
         ProtonBuffer buffer = 
ProtonBufferAllocator.defaultAllocator().allocate();
diff --git 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoderTest.java
 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoderTest.java
index c5539aa1..2154d568 100644
--- 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoderTest.java
+++ 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/ProtonStreamDecoderTest.java
@@ -55,6 +55,16 @@ public class ProtonStreamDecoderTest extends 
CodecTestSupport {
         assertSame(first, streamDecoder.getCachedDecoderState());
     }
 
+    @Test
+    public void 
testDescriptedTypeDecoderRegistrationChecksForNullDescriptors() {
+        assertThrows(IllegalArgumentException.class,
+            () -> streamDecoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(null, null)));
+        assertThrows(IllegalArgumentException.class,
+            () -> streamDecoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(null, new UnsignedLong(0))));
+        assertThrows(IllegalArgumentException.class,
+            () -> streamDecoder.registerDescribedTypeDecoder(new 
TestDescribedTypeDecoder(Symbol.getSymbol("test"), null)));
+    }
+
     @Test
     public void testReadNullFromReadObjectForNullEncoding() throws IOException 
{
         ProtonBuffer buffer = 
ProtonBufferAllocator.defaultAllocator().allocate();
diff --git 
a/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/TestDescribedTypeDecoder.java
 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/TestDescribedTypeDecoder.java
new file mode 100644
index 00000000..b812896b
--- /dev/null
+++ 
b/protonj2/src/test/java/org/apache/qpid/protonj2/codec/decoders/TestDescribedTypeDecoder.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.protonj2.codec.decoders;
+
+import java.io.InputStream;
+
+import org.apache.qpid.protonj2.buffer.ProtonBuffer;
+import org.apache.qpid.protonj2.codec.DecodeException;
+import org.apache.qpid.protonj2.codec.DecoderState;
+import org.apache.qpid.protonj2.codec.StreamDecoderState;
+import org.apache.qpid.protonj2.types.Symbol;
+import org.apache.qpid.protonj2.types.UnsignedLong;
+
+public final class TestDescribedTypeDecoder extends 
AbstractDescribedTypeDecoder<String> {
+
+    private final Symbol symbolDescriptor;
+    private final UnsignedLong ulongDescriptor;
+
+    public TestDescribedTypeDecoder(Symbol symbol, UnsignedLong ulong) {
+        this.symbolDescriptor = symbol;
+        this.ulongDescriptor = ulong;
+    }
+
+    @Override
+    public String readValue(InputStream stream, StreamDecoderState state) 
throws DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String readValue(ProtonBuffer buffer, DecoderState state) throws 
DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipValue(InputStream stream, StreamDecoderState state) throws 
DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipValue(ProtonBuffer buffer, DecoderState state) throws 
DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] readArrayElements(InputStream stream, StreamDecoderState 
state, int count) throws DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] readArrayElements(ProtonBuffer buffer, DecoderState state, 
int count) throws DecodeException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int readSize(ProtonBuffer buffer, DecoderState state) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isPrimitive() {
+        return false;
+    }
+
+    @Override
+    public boolean isNull() {
+        return false;
+    }
+
+    @Override
+    public boolean isArrayType() {
+        return false;
+    }
+
+    @Override
+    public Class<String> getTypeClass() {
+        return String.class;
+    }
+
+    @Override
+    public Symbol getDescriptorSymbol() {
+        return symbolDescriptor;
+    }
+
+    @Override
+    public UnsignedLong getDescriptorCode() {
+        return ulongDescriptor;
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to