TOP

ASN.1/Java Coding Services

Applies to: ASN.1/Java v8.7

Using Coding Services

The coding services provide support for the various ASN.1 encoding rules. The com.oss.asn1 package contains a single subclass of Coder that concretely implements each encoding rule.

The following sets of ASN.1 encoding rules are supported:

ASN.1 Encoding Rule Concrete Implementation SOED runtime TOED runtime
BER - Basic encoding rules BERCoder yes yes
CER - Canonical encoding rules CERCoder yes no
DER - Distinguished encoding rules DERCoder yes yes
JSON - JSON encoding rules JSONCoder yes* yes
OER - Octet encoding rules OERCoder yes yes
COER - Canonical Octet encoding rules COERCoder yes yes
PER - Packed (Aligned, Canonical Aligned, Unaligned, or Canonical Unaligned) encoding rules PERAlignedCoder
PERUnalignedCoder
CPERAlignedCoder
CPERUnalignedCoder
yes yes
XER - XML encoding rules XERCoder yes yes
CXER - Canonical XML encoding rules CXERCoder yes yes
E-XER - Extended XML encoding rules EXERCoder yes no

*The JSON encoding rules are supported in the SOED runtime in version 7.0 and later.

The Coder class

The API for accessing the coding services is defined in the com.oss.asn1.Coder class. The Coder class defines methods for encoding, decoding, and setting the various runtime options.

Encoding and Decoding PDU Objects

Methods for encoding a PDU into an OutputStream or a ByteBuffer

public void encode (AbstractData pdu, OutputStream sink) throws
                   EncodeNotSupportedException, EncodeFailedException;
public void encode (AbstractData pdu, ByteBuffer sink) throws
                   EncodeNotSupportedException, EncodeFailedException;

Parameters

pdu
An instance of a class generated by the ASN.1 compiler. When the pdu parameter does not support the encode operation, an EncodeNotSupportedException is thrown.
sink
An instance of any class that extends either java.io.OutputStream for the first method, or java.nio.ByteBuffer for the second method.

The data contained within pdu is encoded according to the ASN.1 encoding rules implemented by the Coder and is written to sink.

When the encode operation fails, an EncodeFailedException is thrown.

Important Note

The PER encoding rules specify that default values must not be encoded. However, by specifying a value that is the default one, you instruct the PER encoder to ignore the rules and encode the value. By omitting a value, when it corresponds to the default, you instruct the PER encoder to not encode the value. In either case, the PER-encoded message is properly structured.

Methods for decoding a PDU from an InputStream or ByteBuffer

public AbstractData decode (InputStream source, AbstractData pdu)
           throws DecodeNotSupportedException, DecodeFailedException;
public AbstractData decode (ByteBuffer source, AbstractData pdu)
           throws DecodeNotSupportedException, DecodeFailedException;

Parameters

pdu
Is either an instance of a class generated by the ASN.1 compiler, or null which, for BER, CER, DER, XER, CXER or E-XER, instructs the decoder to determine from the tag which PDU to decode (null may not be used for decoding PER since there are no tags present). For XER and CXER there are exceptions when null cannot be used. Make sure the isPDUDetectionAvailable() method of the coder returns true.
source
An instance of a class that extends either java.io.OutputStream for the first method, or java.nio.ByteBuffer for the second method.

When the pdu parameter does not support the decode operation, the DecodeNotSupportedException is thrown.

The data contained within source is decoded according to the ASN.1 encoding rules that are implemented by the Coder and are used to populate pdu.

When the decode operation fails, a DecodeFailedException is thrown.

To save the size/offset information of the encoded PDU components when decoding, the following method can be used (available in the SOED runtime only).

public AbstractDataWithPositions decodeWithPositions(
       InputStream source, AbstractData sink)
       throws DecodeFailedException, DecodeNotSuportedException;

The size/offset information specifies the bit-field position of the encoded components within the PDU encoding.

The decodeWithPositions() method is similar to that of the decode() method, however its return type is AbstractDataWithPositions. The AbstractDataWithPositions class is a container that wraps an AbstractData decoded value object and an AbstractDataPositions object and that represents the offset/size information of various components of the decoded value.

The AbstractDataWithPositions class provides getter methods for the decoded value, the positions of its components in the encoded data, and the entire PDU size in bits.

Partially decode the encoded data (available in the TOED runtime only)

public final void decodePartial(InputStream source, ContentHandler handler)
       throws DecodeNotSupportedException, DecodeFailedException
public final void decodePartial(ByteBuffer source, ContentHandler handler)
       throws DecodeNotSupportedException, DecodeFailedException

Parameters

handler
An instance of a class that extends the PartialContentHandler abstract class generated by the ASN.1 compiler and implements callback methods.
source
An instance of a class that extends either java.io.OutputStream for the first method, or java.nio.ByteBuffer for the second method.

During partial decoding, when the decoder encounters a field to which the OSS.DataCallback or OSS.InfoCallback compiler directive is applied, it invokes the specified callback methods (provided by the user).

See Also

Partial Decoding

Decoding with the PDU helper API

When the -helperAPI pdudecoder command line option is specified, the ASN.1/Java compiler generates a PDU class and PDU_ID enum class as inner classes of an ASN.1/Java project class. In application code, the new classes can be referenced as <project>.PDU and <project>.PDU_ID, where <project> is the name of a project class.

The PDU_ID enum class enumerates all ASN.1 PDU types that are defined in the current project. The enum class instance names are the same as the PDU type names.

Example

public enum PDU_ID {
      PDU_1,
      PDU_2,
      PDU_3,
      ...
}

The PDU class serves as a container for a decoded PDU type value and an instance of the PDU_ID enum that identifies the decoded value's type.

The PDU class instances are created by decoding. The PDU class defines public static PDU decode(input, coder) methods for all suitable encoding rule options specified on the compiler command line with the -helperAPI pdudecoder option. For example, when the -ber -helperAPI pdudecoder options are used, the PDU class contains methods to decode a PDU from the BER input:

public static PDU decode(InputStream source, BERCoder coder)
    throws DecodeFailedException;
public static PDU decode(java.nio.ByteBuffer source, BERCoder coder).
    throws DecodeFailedException;

With proper source and coder parameters, calling the above decode() methods will create a new instance of the PDU class that is populated with the decoded value of one of the project's PDU types and the PDU_ID enum instance that identifies the type.

For each of the project's PDU types there is a compiler-generated getter method to access the decoded PDU values inside the container. The getter method's signature uses the following pattern:

public <PDU_type_name> get<PDU_type_name>();

Here is an incomplete code fragment that illustrates usage of the PDU class:

import com.oss.asn1.*;
import java.io.*;
import project.Project;
import project.Project.PDU;
import project.Project.PDU_ID.*;


InputStream source;
// Assuming project is initialized and
// proper java.io.InputStream source is prepared
...
BERCoder coder = Project.getBERCoder();
Project.PDU pdu;

try {
    pdu = Project.PDU.decode(source, coder)
    // Assuming existence of process(Type) methods for each
    // PDU type
    switch (pdu.getTypeID()) {
        case PDU_1:
           process(pdu.getPDU_1());
        case PDU_2:
           process(pdu.getPDU_2());
        case PDU_3:
           process(pdu.getPDU_3());
        ...

    }
} catch (DecodeFailedException ex) {
    System.out.println("Failed to decode PDU: " + ex);
}

Version Information

The -helperAPI pdudecoder option is available in version 7.0 and later.

Coding Service Options

Debugging Output

You can enable or disable debugging (tracing) output during encode and decode operations.

Methods

public void enableEncoderDebugging();
public void disableEncoderDebugging();

public void enableDecoderDebugging();
public void disableDecoderDebugging();

Remarks

Turning on the debugging feature does not automatically enable printing of exception stack traces. To enable this, you need to define the tracer.exceptiontrace system property. For example, if you are running an application with Sun JVM, use the following command-line argument:

-Dtracer.exceptiontrace=on

Constraint Enforcement

You can enable or disable constraint enforcement during encode and decode operations.

Methods

public void enableEncoderConstraints()
public void disableEncoderConstraints()

public void enableDecoderConstraints()
public void disableDecoderConstraints()

Remarks

When PER is used, encoding can fail due to a constraint violation even when runtime constraint checking is disabled.

Definite/Indefinite Length Encoding

You can specify whether the BERCoder should use Definite Length Encoding or Indefinite Length Encoding during encode operations. These two options are mutually exclusive, and are only relevant when using the Basic Encoding Rules with the SOED runtime. The TOED runtime BER encoder currently supports only definite-length encoding.

Methods

public void useDefiniteLengthEncoding()
public void useIndefiniteLengthEncoding()(SOED only)
public void usingDefiniteLengthEncoding()

Remarks

When invoked on a Coder that is not an instance of BERCoder, the methods are silently ignored.

Checking whether automatic detection is possible

You can check whether it is possible to automatically detect the type of the incoming PDU from the bits on the wire.

Method

public boolean isPDUDetectionAvailable()

Remarks

To make sure you can pass a null sink argument, call this API before coder.decode(). This is useful when you need to decode a PDU without prior knowledge of its type. For example:

Coder coder = MyProject.getBERCoder();
AbstractData pdu = null;

if (coder.isPDUDetectionAvailable())
    pdu = coder.decode(encoding, null);
else
System.out.println("Automatic detection of incoming PDU is not available.");

This method returns true if the coder can automatically detect the type of incoming PDU from the bits on the wire. Currently, this method returns true when:

  • The Coder is either BERCoder, CERCoder or DERCoder.
  • The ASN1Project, associated with the coder, was compiled with the -uniquepdu compiler option and no "C0101E: Duplicate PDU tag: ..." error message was issued during the compilation.
  • The Coder is either XERCoder or CXERCoder or EXERCoder.
  • All top level types have different names.

To check if a particular type is a PDU, use the isPDU() method of AbstractData.

Allowing/Preventing Deferred Decoding

You can prevent deferred decoding of components marked with the DeferDecoding directive. The coder ignores the directive and fully decodes the components.

You can allow deferred decoding of components marked with the DeferDecoding directive. Deferred decoding is enabled by default, so you can use this method if the Coder class was disabled.

Methods

public void disableDeferredDecoding()
public void enableDeferredDecoding()

Remarks

Note that these methods have no effect on PERAlignedCoder(), PERUnalignedCoder(), CPERAlignedCoder(), CPERUnalignedCoder(), XERCoder(), CXERCoder, or EXERCoder because deferred decoding of a component is not supported for the Packed Encoding Rules or the XML Encoding Rules.

Automatic Encoding/Decoding

You can enable or disable automatic encoding or decoding of OpenTypes. When automatic encoding is enabled, the coder automatically encodes any instance of an OpenType with a component relation constraint or a type constraint applied.

Methods

public void enableAutomaticEncoding()
public void disableAutomaticEncoding()

public void enableAutomaticDecoding()
public void disableAutomaticDecoding()

public void enableContainedValueEncoding()
public void enableContainedValueDecoding()

public void disableContainedValueEncoding()
public void disableContainedValueDecoding()

Use the four latter methods when your ASN.1 definition contains Contents Constraints. Contained values carried within a BIT STRING or OCTET STRING with a contents constraint can be automatically encoded and decoded providing the encoding rules specified by the ENCODED BY clause are supported by the OSS runtime library and automatic encoding and decoding is enabled.

The enableContainedValueDecoding() has an overloaded version.

public void enableContainedValueDecoding(boolean preserveEncoding)

It enables automatic decoding of contained values and, when the preserveEncoding parameter is set to true, instructs the decoder to keep the original bits/octets of a BIT STRING or OCTET STRING and present them with the decoded contained value.

When the automatic decoding of contained values is enabled with the enableContainedValueDecoding() method without an argument, the original bits/octets are discarded and only the decoded contained value is presented.

This feature is supported in the SOED runtime only. For compatibility, the TOED runtime defines a similar method but currently it has no effect.

Remarks

Unless you explicitly enable automatic encoding/decoding, only manual encoding/decoding is possible.

In order for the decoder to figure out what type to expect in an open type slot, the ASN.1 syntax must specify a component relation constraint.

To help you better understand the benefit of automatic encoding and decoding, see the Java FAQ section of the OSS website (requires a customer ID and password). The example demonstrates automatic decoding when the PDU contains numerous open types at various nesting levels. It first seeks to automatically decode open types that are nested in the complex NBAP message and then goes on to demonstrate how much extra code you would need instead to manually decode these nested OpenTypes.

Relaxed Decoding

Methods

public void enableRelaxedDecoding()
public void disableRelaxedDecoding()

Remarks

Use the enableRelaxedDecoding method when your application needs to process third party generated encodings that are not compliant to the ASN.1 standard. When the deviation is not severe and the decoder can parse non-standard encodings, you can instruct the decoder to ignore these errors and continue decoding. This method causes the decoder to silently ignore certain inconsistencies in the input encoding.

By default, relaxed decoding mode is disabled.

Here is a list of decoding errors that are ignored when the relaxed decoding mode is set:

Errors in BER encoding:

  • Needlessly long encoding of INTEGER or ENUMERATED (more than 9 leading '0'or '1' bits).
  • Primitive encoding of a BIT STRING has the length of zero.
  • Encoding of an OID has the length of zero.
  • Non-innermost TL pair in the encoding of NULL, BOOLEAN, INTEGER or ENUMERATED indicates indefinite length form of encoding but the tag lacks the "constructed" bit set.
  • Needlessly long encoding of the exponent.

Errors in PER encoding:

  • More than 7 padding bits in the inner encoding that is delimited by the length determinant (encoding of extensions and nested open types).
  • Needlessly long encoding of INTEGER (more than 7 leading '0' bits in the encoding of the constrained INTEGER, more than 8 '0' or '1' leading bits in the encoding of the unconstrained INTEGER).
  • Encoding of the OID with a total length of zero.
  • Unconstrained length determinant less than 128 but encoded in two octets (10XXXXXX XXXXXXXX).
  • Leading bits of the unconstrained length determinant that indicate fragmentation, but the remaining six bits have a value other than 0..4.
  • An encoding of an extensible SEQUENCE or SET with the extension bit turned on but the extension preamble that follows the encoding of root components specifies a zero extension count.
  • Needlessly long encoding of the exponent of the REAL value.
  • A mantissa in the encoding of a binary REAL that is not normalized (has more than 7 zero leading bits or its least significant bit is zero).
  • A normally small number is encoded in multiple octets but has a value that is less than 64.
  • A normally small length that is encoded in multiple octets but has the value that is less than or equal to 64.

Rich Exception Handling

You can enable or disable the ability to retrieve partially decoded PDU data in the exception object.

Methods

public void enableRichDecoderExceptions();
public void disableRichDecoderExceptions();

See Also

Allowing/Preventing Strict Decoding

Methods

enableStrictDecoding()
disableStrictDecoding()

Remarks

This feature is available only with the PER Aligned version of the Coder.

When the enableStrictDecoding() method is used, during decoding the application checks the padding bits while it tries to skip them. If one or more padding bits are non-zero, the application issues an exception message: "0263E: Non-zero padding bit was detected".

When this method is used while decoding data, the application will catch any DecodeFailedExceptions.

By default, strict decoding mode is disabled. When this feature is disabled, the application silently skips the padding bits without detecting issuing any exception. This feature is not supported for the TOED runtime.

Controlling whitespace for XML and JSON encodings

You can control the whitespace generated by the XER, E-XER and JSON encoders (available for SOED and TOED).

Methods

public void enableCompact();
public void disableCompact();

The enableCompact() method disables whitespace generation. When this method is called, compact mode is enabled: the encoding is written as a single line of text with no whitespace.

The disableCompact() method adds whitespace to improve readability. When this method is called, newlines are inserted and indentation is applied.

Remarks

By default, compact mode is disabled. The XER, E-XER, and JSON encoders generate whitespace to improve readability. Text encodings are generated on multiple lines with a two space indent by default. This indent is increased by two spaces for each indentation level.

You can also control the whitespace generated by the XER and JSON encoders using the following methods:

Methods

public void setIndentWidth(int width);
public int  getIndentWidth();

The setIndentWidth(int width) method controls indentation. The width parameter specifies the number of spaces added to the existing indent for the next indentation level.

Example

{
   "name":"Casey",
   "team":"Mudville Nine",
   "age":32,
   "position":"left field",
   "handedness":"ambidextrous",
   "batting-average":{
     "base":2,
     "value":0.25
   }
}

The default indentation width is 2. When the indentation width is set to 10 (by calling the coder.setIndentWidth(10) method), the following output is generated:

{
           "name":"Casey",
           "team":"Mudville Nine",
           "age":32,
           "position":"left field",
           "handedness":"ambidextrous",
           "batting-average":{
                     "base":2,
                     "value":0.25
           }
}

The getIndentWidth() method returns the current indentation width setting.

Trace output format

The com.oss.asn1.Coder class defines eight methods to control the destination and the format of the diagnostic output. This class is used in conjunction with the com.oss.util.ASN1PrintWriter class.

Controlling the destination of the diagnostic output

These methods get or set the destination for the diagnostic output generated by the encoder and decoder.

Methods

    public void setEncoderDebugOut (PrintWriter out);
    public PrintWriter getEncoderDebugOut ();
    public void setDecoderDebugOut  (PrintWriter out);
    public PrintWriter getDecoderDebugOut ();

Example

If your application is multi-threaded and the debug lines are printed from multiple threads, the whole diagnostic output on System.out (the default destination for the diagnostic output) will look confusing because the lines printed by one thread will intermix in random order with the lines printed by another thread. To make the output deterministic, you can associate a StringWriter with each thread and capture its diagnostic output in the string buffer. Then print the accumulated debug lines immediately after the thread completes the encode or decode operation.

Remarks

Because the ASN1PrintWriter inherits from the java.util.PrintWriter, you can use it to print trace output as well. You can create one instance of ASN1PrintWriter and use it to capture the diagnostic output from the application (including the diagnostic trace output and other messages that the application code prints explicitly).

Controlling the format of the diagnostic output

These methods get or set various parameters that affect the format of the diagnostic output. However, instead of providing the individual method to get or set each parameter (truncation limit, indent width) the parameters are passed or retrieved as java.util.Properties.

Methods

    public Properties getEncoderDebugProperties ();
    public void setEncoderDebugPropeties (Properties params);
    public Properties getDecoderDebugProperties ();
    public void setDecoderDebugProperties (Properties params);

Remarks

Currently, the following properties are supported by the runtime:

oss.tracer.TruncationLimit=number

Specifies how the lengthy content trace data is truncated. When the value is set to "0", truncation is disabled.

oss.tracer.IndentWidth=number

For better readability, certain lines in the diagnostic output are indented to the right. For example, in BER diagnostic output indentation is used to reflect the nested TLV structure. This property specifies the number of print positions in one indentation level.

oss.tracer.ExceptionTrace=on|off

Setting this property to "on" causes the exception stack trace to be included in the diagnostic output.

Along with applying the set of debugging properties to the individual instance of com.oss.asn1.Coder, you can also set default values using the -D switch on the JVM command-line:

java -Doss.tracer.TruncationLimit=0 -Doss.tracer.ExceptionTrace=on MyApp

In this case the diagnostic output produced by every encode or decode operation will never truncate the content trace and will include the exception stack trace in the output by default.

Example

The first example constructs the Properties object and applies it to a particular instance of com.oss.asn1.Coder. The encoder is instructed that the lengthy content trace is never truncated and that the diagnostic output should include the exception stack trace if any exception occurs during the encode operation. Instead of printing to System.out, the whole diagnostic output is captured in the string buffer.

    /* Construct the settings inline */
    StringWriter stringBuffer = new StringWriter();
    ASN1PrintWriter debugOut = new ASN1PrintWriter(stringBuffer);
    Properties params = new Properties();
    params.setProperty("oss.tracer.TruncationLimit", "0");
    params.setProperty("oss.tracer.ExceptionTrace", "on");


    Coder coder = MyProject.getBERCoder();
    coder.setDebugOut(debugOut);

    coder.setEncoderDebugProperties(params);

Alternatively, the diagnostic output settings for decoding can be loaded from the disk file:

    /* Read the settings from the disk file */
    Properties params = new Properties();
    params.load(new FileInputStream("debug.settings"));
    coder.setDecoderDebugProperties(params);

The sample contents of the debug.settings file is as follows:

    oss.trace.TruncationLimit=40
    oss.trace.IndentWidth=1
    oss.trace.ExceptionTrace=on

In this case, the settings are loaded from the debug.settings disk file. The decoder is instructed to truncate the content trace that is longer than 40 characters. The indent width setting is decreased to 1, which makes the output more compact. And the diagnostic output is again set to include the stack trace of an exception that could occur during the decode operation.

Checking constraints

The encoder and decoder can be configured to automatically invoke constraint checking on decoded data after it was decoded or before encoding it. Currently this feature is available only for the SOED runtime.

When you disable automatic constraint checking by the encoder/decoder you can still invoke the constraint checker directly. To do so, use the isValid() method of the AbstractData class (defined below) or the validate() method of the Coder class.

Method

public boolean isValid() throws ValidateFailedException;

The method returns true if the instance of the AbstractData subclass that invoked it satisfies its ASN.1 subtype constraints. If there is a constraint violation, the ValidateFailedException is thrown. You can analyze the message text from the ValidateFailedException to determine which field within the data value caused the constraint violation.

Remarks

We recommend that you use the isValid() method when you need to perform constraint checking directly and not using the automatic constraint checking feature of the encoder/decoder.

The Coder class also provides a method for constraint checking:

public int validate(AbstractData data) throws
             ValidateNotSupportedException, ValidateFailedException;

The data parameter is an instance of an ASN.1 compiler for Java generated class.

The validate() method returns 0 if the data value satisfies its ASN.1 type constraints. Otherwise, the return value is non-zero.

We do not recommend using the validate() method. It is provided only for compatibility purposes with applications built with the obsolete JNI version of the OSS ASN.1 Tools for Java. It is marked as deprecated in the current API.

See Also

Constraint Enforcement

Default Encoding Rules

A coding service abstraction layer is generated into the Project class. If your ASN.1 specification requires a single encoding rule, use the following factory method to create instances of the default Coder implementation (you will not need to modify your code if the default encoding rules for your application change):

Method

public Coder getDefaultCoder()

Remarks

When you compile without any encoding rule option, getDefaultCoder() returns an instance of BERCoder. When you compile with a single encoding rule option such as, for example, -per, the getDefaultCoder() method returns an instance of the Coder class for that encoding rule. When you compile with several encoding rule options, for example, -per and -xer, the getDefaultCoder() returns a Coder instance according to the following order: -ber, -der, -per, -cper, -uper, -cuper, -cer, -xer, -cxer, -exer. That is, for -per -xer, the method returns an instance of the PERAlignedCoder class.

Specific Encoding Rules

Methods specific to the individual ASN.1 encoding rules are also generated into the Project class. Encoding rule-specific methods are generated for support of the encoding rules by the Java command line.

Methods

public BERCoder getBERCoder()
public BERCoder getCERCoder()
public DERCoder getDERCoder()
public JSONCoder getJSONCoder()
public OERCoder getOERCoder()
public COERCoder getCOERCoder()
public PERAlignedCoder getPERAlignedCoder()
public PERUnalignedCoder getPERUnalignedCoder()
public CPERAlignedCoder getCPERAlignedCoder()
public CPERUnalignedCoder getCPERUnalignedCoder()
public XERCoder getXERCoder()
public CXERCoder getCXERCoder()
public EXERCoder getEXERCoder()

The encoding rule-specific methods are useful when you must employ more than one encoding rule in an application.

Example

Coder coder = MyProject.getDefaultCoder();

// encode
try {
    MyPDU outPDU = new MyPDU(...);
    FileOutputStream mySink = new FileOutputStream("myPDU.enc");
    coder.enableEncoderDebugging(); //print debugging output to console
    coder.disableEncoderConstaints(); //disable constraint checking
    coder.encode(outPDU, mySink);
} catch(EncodeNotSupportedException e) {
  // Encode is not supported
} catch(EncodeFailedException e) {
  // the encode() operation failed

}

// decode
try {
    FileInputStream mySource = new FileInputStream("myPDU.enc");
    coder.disableDecoderDebugging(); // disable debugging output
    coder.enableDecoderConstaints(); // enable constraint checking
    MyPDU inPDU = (MyPDU)coder.decode(mySource, new MyPDU());
} catch(DecodeNotSupportedException e) {
  // Decode is not supported
} catch(DecodeFailedException e) {
  // the decode() operation failed
}

Using the StorageManager for huge values (SOED only)

You can handle the allocation of external memory (for example, files) for encoding to or decoding from.

Methods

getStorageManager

public StorageManager getStorageManager()

Gets the current setting of the StorageManager and returns an instance of the StorageManager to allocate the external storage.

setStorageManager

public void setStorageManager(StorageManager storageManager)

You instruct the runtime to use your customized storage manager instead of the default one. Make sure that the object can be accessed by concurrent threads (is thread safe).

The storageManager parameter is the instance of the class that implements the StorageManager interface.

See Also

ValueInFile, Storage interface, huge values.

How to Access a Partially Decoded PDU (SOED only)

Example

The following example illustrates a nested value.

Name ::= SEQUENCE {
    first UTF8String,
    last UTF8String
}

Info ::= SEQUENCE {
    name Name,
    age INTEGER,
    children SEQUENCE OF Name
}

When a decoding error occurs during decoding of the last field of the second element of children and the BER coder is used, the following application code is present:

    try {
        AbstractData value = coder.decode(source, new Info());
    } catch (DecodeFailedException e) {
        AbstractData badValue = e.getDecodedValue();
  if (badValue != null) {
            // Examine the data
            if (badValue.getName() != null)
        // examine 'name' of 'badValue'
      ...
        }
        ...
    }

badValue will contain a partially decoded PDU where name and age are fully decoded and children contains two elements. The first element is fully decoded and the second element has the last field equal to null.

Note that the runtime does not identify why a particular component is missing (or why the getter method returns null). This might happen when the component is past the location where the decoding error was thrown (and has not been decoded because of a decoding error) or when the component is optional and was omitted from the encoding.

A partially decoded PDU that is returned via the DecodeFailedException may contain values of primitive types: BOOLEAN, INTEGER, ENUMERATED, REAL, NULL, BIT STRING, OCTET STRING, character strings, OBJECT IDENTIFIER, GeneralizedTime and UTCTime, but will never contain partially decoded primitive values. For scalar types, a "partially decoded" value does not make sense. In other words, if the decoding error occurs in the middle of the string, the whole contents of the string is discarded (and is not inserted in the AbstractData that is aggregated in the decoder exception), even octets that were read from the input before the DecoderError was thrown.

Remarks

Partial decoding is not supported for SET OF or SEQUENCE OF with the ValueInFile directive applied. If a decoding error occurs in the middle of a huge SET OF or SEQUENCE OF, the whole value is discarded and is not returned in the decoder exception. If you are interested in additions to what is decoded, contact Technical Support <support@oss.com>.

See Also

getDecodedValue()

Padding Bits in PER

PER encoding pads out the PDU to the next whole octet. For example, if the encoded data is 110 bits (13 octets and 6 bits), two zero bits are appended to create a full octet boundary. The com.oss.asn1.getNumberOfPaddingBits method is useful when you need to know how much of the PDU is actual data (for 3GPP layer 2 applications, for example).

Method

public int getNumberOfPaddingBits()

This method returns the following:

  • For BER, CER or DER: 0.
  • For XER or CXER: 0.
  • For E-XER: 0.
  • For PER: the number of padding bits added by the previous call to the encoder.

PER decoder performance tuning via system properties

The PER decoder performance can be enhanced with two performance tuning parameters that can be set by the Java system properties. The parameters affect the internal buffering strategy used when decoding large byte arrays. The affected ASN.1 types are OCTET STRING, BIT STRING and open type.

While encoding, large amounts of content are split into a sequence of 64 KB fragments for PER, so the receiving party needs to reconstruct the original content by merging the fragments. PER does not provide a length determinant that would precede the fragmented content to specify its total length in bytes. It is not possible to allocate a buffer that could hold all the fragments, so a buffer reallocation scheme needs to be applied.

By default, the PER decoder allocates a new buffer that is expanded by a fragment size each time the decoder sees a new fragment. Content decoded so far is copied into the new buffer and merged with the octets of the new fragment. The old buffer is discarded. Due to the frequent reallocations and copying, this scheme can create a performance bottleneck when decoding large content. The PER decoder tunable parameters are used to avoid this possible performance issue. The number of buffer reallocations can be kept to a minimum by specifying:

  • An initial buffer size so that all or most of the fragments could fit into a single buffer.
  • The buffer size increment strategy as other than simply growing by a fragment size.

The com.oss.per.in.bufsize property causes the PER decoder to allocate a byte array of a specified size and use it as the initial buffer for every decoding of fragmented content of the OCTET STRING, BIT STRING, or open type. Once allocated, the buffer is kept as part of the PER coder object and is reused. The syntax of the com.oss.per.in.bufsize property is "nnnn[K|k|M|m]", where "nnnn" is an integer that specifies the buffer size in bytes, KB or MB. The optional letter, "K" or "M", appended to the end of the integer value designates whether "nnnn" is in KB or MB.

The com.oss.per.in.incr property specifies a buffer size increment that is used when resizing a fragmented content decode buffer for the OCTET STRING, BIT STRING, or open type. The syntax of the com.oss.per.in.incr property specifies an absolute increment in bytes, KB, or MB, or a relative increment as a percent of the current buffer size. The syntax of the property is "nnnn[K|k|M|m|%]", which is the same as "bufsize", except that the ending symbol, "%", specifies the relative increment.

The above properties are queried by the PERCoder object at construction time by the System.getProperty() method. Properties can be specified by the "-D" command-line option or by calling the System.setProperty() method. With the latter choice, it is possible to create PERCoder objects that use different settings. The properties apply to both the Aligned and Unaligned PER coders.

Example

System.setProperty("com.oss.per.in.bufsize", "4M");
Coder coder1 = <Project>.getPERAlignedCoder();

System.setProperty("com.oss.per.in.bufsize", "2M");
System.setProperty("com.oss.per.in.incr", "1M");
Coder coder2 = <Project>.getPERAlignedCoder();

The coder1 object is configured to use a 4 MB initial buffer for decoding the fragmented byte array content, while the coder2 object will start decoding with a 2 MB buffer and will allocate new buffers with a 1 MB size increment.


Using XER and E-XER

The XML Encoding Rules (XER) describe a simple way to encode ASN.1 abstract values using XML. These encoding rules are useful for an ASN.1 application that exchanges values with XML applications.

The Extended XML Encoding Rules (E-XER) support additional encoder options, and take account of encoding instructions that specify variations of the XER encodings to support specific styles of XML documents. There are many aspects of an XML representation of data (using XML attributes instead of child elements, or using whitespace-delimited lists, for example) whose use is a matter of style and XML designer choice. If a type defined in an ASN.1 specification is encoded by XER, then there is a single fixed style used for the XML representation, with no user control of stylistic features; E-XER allows you to apply this kind of control. This enables ASN.1 to provide a complete XML Schema language, such as DTDs or W3C XML Schema.

To learn more about the encoding instructions that can be used by E-XER, see the E-XER white paper.

E-XER and character-encodable types

A character-encodable type is an ASN.1 type containing values that can serve as XML attribute values. A character is an atomic unit of text as specified by ISO/IEC 10646. Legal characters are tab, carriage return, line feed, and the legal characters of Unicode and ISO/IEC 10646.

Certain E-XER encoding instructions can be assigned only to character-encodable ASN.1 types, for example, the ATTRIBUTE encoding instruction. Similarly, the LIST encoding instruction can be applied only to SET OF or SEQUENCE OF types if all type components are character-encodable, and the USE-UNION encoding instruction can be applied only to CHOICE types if all alternatives of CHOICE are character-encodable.

You must specify either a Single Value Constraint or a Permitted Alphabet Constraint for character-encodable types. When other constraints are present and the -exer compiler option is specified, the compiler issues an error. To ignore the error, use the -ignoreError compiler option.

It is recommended to define a separate character-encodable type, so that the character-encodable constraint definition is not repeated too many times in your ASN.1 syntax. For example, you can create another XMLCompatibleString type as shown below, and use this type in your ASN.1 syntax when a character-encodable type is needed.

Example

XMLCompatibleString ::= UTF8String (FROM(
        {0, 0, 0, 9} |
        {0, 0, 0, 10} |
        {0, 0, 0, 13} |
        {0, 0, 0, 32} .. {0, 0, 215, 255} |
        {0, 0, 224, 0} .. {0, 0, 255, 253} |
        {0, 1, 0, 0} .. {0, 16, 255, 253}))

Attr ::= XMLCompatibleString

Color ::= XMLCompatibleString ("red" | "blue" | "green")

S ::= [LIST] SEQUENCE OF Color

C ::= [USE-UNION] CHOICE {
    c1  [TAG: 0] XMLCompatibleString,
    c2  [TAG: 1] XMLCompatibleString,
    c3  [TAG: 2] XMLCompatibleString,
    c4  [TAG: 3] XMLCompatibleString
}

E-XER Restrictions

The following features are not supported when using E-XER:

  • The -lean compiler option or the Lean Encoder/Decoder
  • BIT STRING and OCTET STRING types with a CONTAINING clause
  • The -relaySafe compiler option
  • The ValueInFile compiler directive
  • The DeferDecoding compiler directive
  • Tracing with EXERCoder

If you are interested in any of these features for E-XER, contact Sales <info@oss.com>.

Customizing with the UserClass directive

You can customize XML encodings specifying the UserClass directive with user-defined classes that implement the XERCoderInterface. The Coder class provides methods for enabling and disabling this feature.

Methods

public void enableXERCoderInterface();
public void disableXERCoderInterface();

When the feature is enabled, the encoder checks if the component being encoded implements XERCoderInterface. If it does, the encoder invokes the encode() method defined in the component rather than the default method defined in the XML coder class.

So user-defined XML can be put into the target output stream instead of the standard encoding. Note that because these user-defined encodings are non-standard they cannot be decoded by the standard decoder.

See Also

XERCoderInterface


Using JSON (JER)

The JSON Encoding Rules (JER) describe a simple way to encode ASN.1 abstract values using JSON. These encoding rules are useful for an ASN.1 application that exchanges values with JSON applications.

Customizing with the UserClass directive

You can customize JSON encodings specifying the UserClass directive with user-defined classes that implement the JSONCoderInterface. The Coder class provides methods for enabling and disabling this feature.

Methods

public void enableJSONCoderInterface();
public void disableJSONCoderInterface();

When the feature is enabled, the encoder checks if the component being encoded implements JSONCoderInterface. If it does, the encoder invokes the encode() method defined in the component rather than the default method defined in the JSON coder class.

So user-defined JSON can be put into the target output stream instead of the standard encoding. Note that because these user-defined encodings are non-standard they cannot be decoded by the standard decoder.

See Also

XERCoderInterface


Converting between different transfer syntaxes

You can convert encoded data between different sets of encoding rules used to represent the data. For example, you can convert between BER and PER, PER and XER, etc. To do so, the data must be decoded using the input coder object and re-encoded using the output coder object. For example, to convert BER-encoded data into aligned PER-encoded data, you can use the decode() method of the BERCoder class, and then the encode() method of the PERAlignedCoder class. Alternatively, you can use the convert() method of the EncodingRuleConvertor class for a one-step conversion.

OSS provides five helper classes for encoding rule conversions:

  • EncodingRuleConvertor, which provides conversions between arbitrary encoding rules.
  • Binary2XMLConvertor, which provides conversions from binary encoding rules (BER, CER, DER, PER) to XML encoding rules (XER, E-XER).
  • XML2BinaryConvertor classes, which provide conversions from XML encoding rules (XER, E-XER) to binary encoding rules (BER, CER, DER, PER).
  • Binary2JSONConvertor, which provides conversions from binary encoding rules (BER, CER, DER, PER) to JSON encoding rules.
  • JSON2BinaryConvertor classes, which provide conversions from JSON encoding rules to binary encoding rules (BER, CER, DER, PER).

EncodingRuleConvertor class

Use the EncodingRuleConvertor class to convert between two encoding rules. This class extends the Coder class. Objects of the class are constructed from two Coder objects: decoder and encoder.

Definition

public class EncodingRuleConvertor extends Coder {
    // Public Constructors
    public        EncodingRuleConvertor(Coder decoder, Coder encoder)

    // Public Instance Methods
	public void convert(InputStream source, OutputStream sink,
						AbstractData pdu) throws DecodeFailedException,
													EncodeFailedException
	public Coder getDecoder()
	public void setDecoder(Coder decoder)
	public Coder getEncoder()
	public void setEncoder(Coder encoder)
}

Methods

Constructors Description
EncodingRuleConvertor(Coder decoder, Coder encoder)
Creates an EncodingRuleConvertor object and associates it with the two encoding rules.
Parameters:
decoder
Coder object used to decode from an input stream.
encoder
Coder object used to encode into an output stream.
Instance Methods Description
convert((InputStream source, OutputStream sink, AbstractData pdu)
Performs the conversion. The method decodes the source stream containing the pdu value and re-encodes it into the sink stream. If pdu detection is available for the input coder object, then the pdu parameter can be set to null.
Parameters:
source
Input stream.
sink
Output stream.
pdu
The PDU instance that will receive the decoded data.
Throws:
DecodeFailedException
Thrown if the decode operation fails.
EncodeFailedException
Thrown if the encode operation fails.
Coder getDecoder()
Returns the Coder used for decoding.

Returns: input decoder
setDecoder(Coder decoder)
Sets the Coder used for decoding.

Parameters: decoder - input decoder
Coder getEncoder()
Returns the Coder used for encoding.

Returns: output decoder
setEncoder(Coder encoder)
Sets the Coder used for encoding.

Parameters: encoder - input encoder

Binary2XMLConvertor and XML2BinaryConvertor classes

The Binary2XMLConvertor and XML2BinaryConvertor classes are extensions of EncodingRuleConvertor. The classes provide type-safe constructors for configuring binary to XML and XML to binary conversions.

Definition

public class Binary2XMLConvertor extends EncodingRuleConvertor {

// Public Constructors
public   Binary2XMLConvertor(BinaryCoder decoder, XMLCoder encoder)

// Public Methods
public void setDecoder(BinaryCoder decoder);
public void setEncoder(XMLCoder encoder);
}

public class XML2BinaryConvertor extends EncodingRuleConvertor {

// Public Constructors
public   XML2BinaryConvertor(XMLCoder decoder, BinaryCoder encoder)

// Public Methods
public void setDecoder(XMLCoder decoder);
public void setEncoder(BinaryCoder encoder);
}

The XMLCoder and BinaryCoder are interfaces used to distinguish XML and binary coder classes.

The coder objects that can be used as BinaryCoder typed parameters are: BERCoder, CERCoder, DERCoder, AlignedPERCoder, and UnalignedPERCoder.

The coder objects that can be used as XMLCoder typed parameters are: XERCoder, CXERCoder, and EXERCoder.

Converting from XML to binary

To convert from XML to binary, use the XML2BinaryConvertor class.

Example

The following step by step example shows you how to convert from an XML encoding (E-XER) to binary (Aligned PER).

First, create input and output stream objects. You decode from a file and encode into a memory byte array.

InputStream source = new FileInputStream("sample.xml");
OutputStream sink = new ByteArrayOutputStream();

Then create XML2BinaryConvertor with two coders EXERCoder decoder and AlignedPER encoder obtained from the same "project":

XML2BinaryConvertor convertor = new

XML2BinaryConvertor(project.getEXERCoder(),
project.getAlignedPERCoder());

Configure the convertor for the desired decoder and encoder options.

convertor.enableAutomaticDecoding();
convertor.enableDecoderConstraints();
convertor.enableEncoderDebugging();

Invoke the convertor's convert() method. The input stream should contain the XML encoding for the MyPDU class.

try {
		convertor.convert(source, sink, new MyPDU());
} catch (DecodeFailedException e) {
		System.out.println("Decode failed.");
} catch (EncodeFailedException e) {
		System.out.println("Encode failed.");
}

Binary2JSONConvertor and JSON2BinaryConvertor classes

The Binary2JSONConvertor and JSON2BinaryConvertor classes are extensions of EncodingRuleConvertor. The classes provide type-safe constructors for configuring binary to JSON and JSON to binary conversions.

Definition

public class Binary2JSONConvertor extends EncodingRuleConvertor {

// Public Constructors
public   Binary2JSONConvertor(BinaryCoder decoder, JSONCoder encoder)

// Public Methods
public void setDecoder(BinaryCoder decoder);
public void setEncoder(JSONCoder encoder);
}

public class JSON2BinaryConvertor extends EncodingRuleConvertor {

// Public Constructors
public   JSON2BinaryConvertor(JSONCoder decoder, BinaryCoder encoder)

// Public Methods
public void setDecoder(JSONCoder decoder);
public void setEncoder(BinaryCoder encoder);
}

The JSONCoder and BinaryCoder are interfaces used to distinguish JSON and binary coder classes.

The coder objects that can be used as BinaryCoder typed parameters are: BERCoder, CERCoder, DERCoder, AlignedPERCoder, and UnalignedPERCoder.

The coder object that can be used as a JSONCoder typed parameter is: JSONCoder.

Converting from JSON to binary

To convert from JSON to binary, use the JSON2BinaryConvertor class.

Example

The following step by step example shows you how to convert from a JSON encoding (JSON) to binary (Aligned PER).

First, create input and output stream objects. You decode from a file and encode into a memory byte array.

InputStream source = new FileInputStream("sample.json");
OutputStream sink = new ByteArrayOutputStream();

Then create JSON2BinaryConvertor with two coders JSONCoder decoder and AlignedPER encoder obtained from the same "project":

JSON2BinaryConvertor convertor = new

JSON2BinaryConvertor(project.getJSONCoder(),
project.getAlignedPERCoder());

Configure the convertor for the desired decoder and encoder options.

convertor.enableAutomaticDecoding();
convertor.enableDecoderConstraints();
convertor.enableEncoderDebugging();

Invoke the convertor's convert() method. The input stream should contain the JSON encoding for the MyPDU class.

try {
		convertor.convert(source, sink, new MyPDU());
} catch (DecodeFailedException e) {
		System.out.println("Decode failed.");
} catch (EncodeFailedException e) {
		System.out.println("Encode failed.");
}

The Java Lean Encoder/Decoder (JLED)

The lean runtime is designed for use in production environments.

If your application does not require the full range of functions provided by the oss.jar runtime library, consider using the lean version of the runtime, osslean.jar located in the lib subdirectory of the installation. The lean runtime is half the size of the oss.jar and runs up to 20% faster.

Note that the lean runtime does not include support for the following functions provided by the oss.jar runtime:

  • The following ASN.1 types: REAL, EMBEDDED PDV, EXTERNAL, CHARACTER STRING, TIME, DATE, DATE-TIME, DURATION, TIME-OF-DAY, UniversalString and UTF8String with UNBOUNDED or UNIVERSALSTRING directives.
  • Runtime constraint checking - the isValid() method of the AbstractData class always returns true.
  • Contents constraints (the lean runtime does not support universal classes, like ContainingBit or OctetString, and does not support code in the coders for automatic coding of the contained value). When -lean is specified, the compiler-generated classes ignore the contents constraints and extend the BitString and the OctetString universal classes.
  • ASN.1 value notation printing - the toString() method of the AbstractData class always returns super.toString().
  • The ValueInFile compiler directive.
  • Indefinite length form encoding in the BER encoder, which implies no support for Canonical Encoding Rules (CER). The BER decoder can handle indefinite length form encodings.
  • No debugging output is generated by the encoder or decoder.
  • Partial data decoding (rich decoder exceptions).
  • Encoding value position feature.

It is recommended that you compile using the -lean command-line option. The option helps you save space by excluding constraint and debugging information, which is not needed when using the lean runtime. Also, when compiling with the -lean option, the ASN.1 compiler will issue warning messages if your .asn input contains types or directives that are not supported by the lean runtime. Note that when the -lean option is used with osslean.jar, the -soed compiler option must also be used.

To use the lean runtime you, replace the path to oss.jar in your CLASSPATH setting with the path to the osslean.jar file.

See Also

-lean


The JIA API class library

JIA API stands for Java InterActive Application Programming Interface. It is a separate runtime library that allows manipulation of ASN.1 data in a generic way. The JIA API class library requires the SOED runtime.

The OSS ASN.1/Java Tools assumes that your application code knows the type of the value that you wish to modify, instantiate, or examine. However, certain applications cannot predict the type, and need generic access to data, for example:

  • Generic browsers that need to display, edit, or validate an abstract value without prior knowledge of its type.
  • Learning tools that demonstrate encoding rules (you can interactively enter a value and examine its encoding in another window).
  • LAN analyzers, where you can interactively decode the value and see its contents in another window.

Additionally, certain applications can instantiate a value to provide the name of its ASN.1 type rather than the Java class name, or access a field of a SEQUENCE or SET that provides the field name.

The JIA API also provides the ASN.1 parser so that you can read values in ASCII format. Every self-contained valid ASN.1 value notation is parsable. Self-contained means that each value does not reference other parts of the ASN.1 schema. So for example, a BOOLEAN value can be parsed if it is "TRUE", "FALSE", "T", "F", "0" or "1" (case insensitive). An OCTET STRING is an Hstring ('12345'H) or a Bstring ('1010101'B). For a CHOICE value, the parsable input is 'selector : value'.

See Also

-jiaapi

You can find the JIA API class library and the documentation in the jiaapi.zip that resides in the root directory of the installation. To use the JIA API, unzip the jiaapi.zip at the location of your choice and append the jiaapi.jar to your CLASSPATH.


Saving encoded value positions when decoding

When using the BER, DER, CER and PER encoding rules, you can obtain encoded value offset or size information from the decoder. This feature is available with the SOED runtime and is not available for XER, CXER, or E-XER.

A PDU usually consists of several components. Each encoded value component occupies a bit-field inside the entire PDU encoding. During decoding, the offset/size information of each bit-field can be collected by the decoder and stored in an AbstractDataPositions table object. When decoding is complete, an application can retrieve the offset/size information of the PDU components from the table object using either the JIAAPI or (with some limitations) directly, using a decoded component reference.

The decodeWithPositions() method of the com.oss.asn1.Coder class is used to receive offset/size information from the decoder in addition to the decoded value. The method's signature is the same as that of decode() except. Its return type is an AbstractDataWithPositions object.

public AbstractDataWithPositions decodeWIthPositions (InputStream source, AbstractData pdu)
           throws DecodeNotSupportedException, DecodeFailedException;

Parameters

pdu
An instance of a class generated by the compiler, or null. When pdu is null and BER, CER, DER, XER, CXER or E-XER is used, it instructs the decoder to determine from the tag which PDU to decode (null cannot be used for decoding PER since there are no tags present). For XER and CXER there are exceptions when null cannot be used. Make sure the isPDUDetectionAvailable() method of the coder returns true.
source
An instance of a class that ultimately extends java.io.InputStream.

NOTE: Position information is not collected by the XER, CXER, and E-XER coders. The decodeWithPositions() method returns an empty positions table when invoked for the above encoding rules.

The AbstractDataWithPositions class is a container that wraps an AbstractData decoded value object and an AbstractDataPositions object.

The AbstractDataWithPositions class provides getter methods for the decoded value, its components' positions in the encoded data, and the entire PDU size in bits.

public class AbstractDataWithPositions {
     public AbstractData getDecodedValue();
     public AbstractDataPositions getPositions();
     public int getSize();
}

The position of individual fields can be obtained from the AbstractDataPositions object. An individual field's offset/size information is represented in bits by an instance of the com.oss.asn1.Position class. The instance also indicates whether the field is primitive or constructed in the case of BER/DER/CER encoded fields.

public class Position {
    public Position(int offset, int size);
    public int getOffset();
    public int getSize();
    public boolean isConstructed();
}

See Also

AbstractDataWithPositions, AbstractDataPositions, and Position classes.

Offset/size information returned by the BER/DER/CER decoder

The ASN.1 Basic Encoding Rules (BER), as well as DER and CER, use tag-length-value (TLV) triplets to encode values. The tag part identifies the value type and form of the encoding. The length part specifies the length of the value (content) part. All the parts are encoded into an integral number of octets.

BER encodings can be primitive (the content octets directly represent the value) or constructed (the content octets contain other nested TLVs).

  • Encodings of BOOLEAN, INTEGER, ENUMERATED, and REAL types are always primitive.
  • Encodings of BIT STRING, OCTET STRING, and character string types can be primitive or constructed, depending on the encoder's choice. In the latter case, the string value is split into several fragments.
  • Encodings of SEQUENCE, SET, SEQUENCE OF, and SET OF types are always constructed.
  • Encodings of CHOICE types or open types can be primitive or constructed, depending on the value chosen or carried by the open type.

The offset/size information returned by the BER/DER/CER decoder is specified in bits (instead of bytes) to be aligned with the PER decoder.

For both primitive and constructed encodings, the value of the offset points to the beginning of the value part of the TLV. For open type values, the offset points to the beginning of the tag. The constructed encodings are indicated by a boolean flag that can be queried by the isConstructed() method of the com.oss.asn1.Position class. The size indicates the value length specified in bits. In the case of a constructed encoding, the size also includes all the tag-length octets of any nested TLVs.

Offset/size information returned by the PER decoder

In PER encodings, component values are represented by bit-fields. The PER ALIGNED encoder may insert padding bits as necessary to ensure that a particular bit-field starts at an octet boundary. An offset/size returned by the PER decoder delimits a bit-field that corresponds to a value.

The padding bits inserted by the PER ALIGNED encoder are not treated as part of a bit-field, and are not included in the bit field's size.

The exact format of the bits delimited by the offset/size depends on the type of the value and is specified by the ITU-T X.691 - "Specification of Packed Encoding Rules (PER)" standard.

Example

  • The BOOLEAN value is represented by a single bit (the size returned is always 1).
  • The bits for INTEGER types may contain an extensibility bit (if the type was defined as extensible), followed by a length-determinant (if the type definition suggests that representation of different values may require a variable number of bits), and then followed by the bits representing the binary value of the integer number.
  • The bits for ENUMERATED types may contain an extensibility bit (if the type was defined as extensible) followed by the packed binary representation of the enumerator.
  • The bits for REAL types contain a length-determinant followed by the binary representation of the floating point number, as specified by DER/CER.
  • The bits for BIT STRING or OCTET STRING types may contain an extensibility bit (if the type was defined as extensible), followed by a length-determinant (if the type definition does not constrain all values to have the same length), and then followed by the bits of the BIT STRING or octets of the OCTET STRING.
  • The bits for restricted character strings may contain an extensibility bit (if the type was defined as extensible), followed by a length-determinant (if the type definition does not constrain all values to have the same length), and then followed by the bits representing the packed characters of the character string.
  • For complex types, such as SEQUENCE or SET, the bit-field delimited by the offset/size includes
    • The extensibility bit (if the type was defined as extensible).
    • The bit-mask specifying the presence of the optional components (if the type defines any).
    • The bits representing each individual field of the SEQUENCE or SET.
    • The bit-mask of variable-size length specifying the presence of individual extension fields (for the extensible types).
    • The bits representing each individual extension field.
  • The bits for SEQUENCE OF/SET OF types may contain an extensibility bit (if the type was defined as extensible), followed by a length-determinant (if the type definition does not constrain all values to have the same number of elements), and then followed by the bits representing each individual element of the SET OF or the SEQUENCE OF type.

NOTE: For complex types the padding bits added by the PER ALIGNED encoder to align bit-fields of nested components are treated as a part of the enclosing value, and are included in the bits delimited by the offset/size for the SEQUENCE/SET/SEQUENCE OF/SET OF types. The PER decoder returns each offset/size in such a way that if the bits delimited by the offset/size are presented to the PER decoder again, it should be able to decode them into an abstract value.

Getting offset/size data via the JIAAPI

The Java Interpretive ASN.1 API (JIAAPI) allows manipulation of the decoded data in a generic way without prior knowledge of the data type. Individual data elements are handled via instances of the JIA_IE (IE stands for "Information Element") class. To get the position information via the JIAAPI, the "root" IE must be initialized from an AbstractDataWithPositions object obtained by the decodeWithPositions() method of the coder class. A JIA_IE factory method accepts the data with positions.

    public static JIA_IE getInstanceOf(AbstractDataWithPositions value);

Positions of individual information elements in the encoded data stream can then be obtained with the getPosition() method of the JIA_IE class.

public Position getPosition()

The method returns a reference to the Position object, or null if the position information for the element is not available. null will also be returned if the root IE was created using a plain AbstractData object, not an AbstractDataWithPositions object.

Example

The following example shows how to decode a PDU and print the encoded data positions of its components. For simplicity, exception handling is not included in this example.

import com.oss.asn1.*;
import com.oss.jiaapi.*;
import baseball.*;
import baseball.bcas.*;

...

AbstractDataWithPositions decoded = coder.decodeWithPositions(
source, new BBCard());

JIA_IE_Collection_IE root = (JIA_IE_Collection_IE)JIA_IE.getInstanceOF(decoded);
int numComponents = root.getNumberOfComponents();

for (int i = 0; i < numComponents; i++) {
JIA_Component comp = root.getComponent(i);
	 if (comp.isPresent()) {
	     JIA_IE ie = comp.getIE(false);
	     Position pos = ie.getPosition();
	     if (pos != null)
		 System.out.println("Component " + comp.getName() +
		     " was encoded at offset " + pos.getOffset() +
		     " size " + pos.getSize());
	     else
		 System.out.println("Position not available for " +
		     comp.getName());
}
}

Limited functionality without using the JIAAPI

You can access the position information using the ASN.1 type specific API. Because the AbstractDataPositions tables store the positions of all the AbstractData components created while decoding a PDU, you can to retrieve the position information directly from the positions table using an AbstractData component reference.

An AbstractDataPositions table can be queried for an arbitrary AbstractData component position as follows:

AbstractDataPositions positions = decoded.getPositions();
AbstractData data = ...;

Position pos = positions.get(data);

The above method has limited functionality and it does not work in some cases. For instance, when complex type components are mapped to the Java primitive types, int, boolean and double, their AbstractData representations are not available. Second, multiple AbstractData components of the ASN.1 ENUMERATED type can be represented by the same object reference, so there is no one-to-one correspondence between the AbstractData references and the encoded data positions. The above get() method will return null if the table contains more than one row that corresponds to the data parameter.

Here is a fragment from the example in the previous section, modified for this method:

     printPosition(positions.get(card.getName()), "name");
     printPosition(positions.get(card.getTeam()), "team");
     // ERROR! the getAge() method return type is "int"
     printPosition(positions.get(card.getAge()), "age");
     printPosition(positions.get(card.getPosition()), "position");
     // ATTENTION! the following line can report the position not available
     printPosition(positions.get(card.getHandedness()), "handedness");
     printPosition(positions.get(card.getBatting_average()), "batting_average");

Limitations of the encoded value positions feature

Automatic decoding of outer-level encodings

The PER encoder fragments large binary data (such as open types, OCTET STRING, or BIT STRING with contents constraint imposed) into 64K, 32K or 16K chunks, where each chunk is prefixed with a length-determinant. Therefore it is possible that a bit-field representing a field nested in an open type is not contiguous, but has a length-determinant from the outer-level encoding in the middle. This case will not be supported by the PER decoder. If the encoding of a component crosses the chunk boundary, no position information will be returned for such a field.

Example

A ::= SEQUENCE {
   f1 INTEGER,
   f2 TYPE-IDENTIFIER.&Type (B)
}

B ::= SEQUENCE {
   f1 OBJECT IDENTIFIER,
   f2 TYPE-IDENTIFIER.&Type(C)
}
C ::= SEQUENCE {
    f1 OCTET STRING,
    f2 INTEGER
}

If the length of f1 (OCTET STRING) in the value of C is large (< 16K), the bits representing C.f2 in the value of A may have a length-determinant that belongs to A.f2 in the middle. The PER decoder will not be able to return the offset/size information of the C.f1 field in this case, and will return null to the calling application.

Manual decoding of outer level encodings

This limitation applies to the position information returned for the values that were manually decoded from an open type, OCTET STRING or BIT STRING with contents constraint imposed.

Example

A ::= SEQUENCE {
   f1 INTEGER,
   f2 TYPE-IDENTIFIER.&Type
}

or

B ::= SEQUENCE {
   f1 INTEGER,
   f2 OCTET STRING CONTAINING INTEGER
}

Values of such types are usually decoded in two steps. First, the outer value is decoded. If the encoding of A.f2 or B.f2 is fragmented, the fragments are concatenated and any length-determinants are stripped at this step. Then the decoder is invoked for the second time with the input stream containing the A.f2 or B.f2 octets.

There will not be a way to pass the position information for A.f2 or B.f2 to the second invocation of the decoder. Therefore, the offset/size for the values decoded from A.f2 or B.f2 are relative to A.f2/B.f2, not from the initial buffer that carried the encoding of A or B.

See Also

SOED versus TOED


Partial Decoding

The partial decoding feature enables decoding of particular fields from input messages while the remaining fields are ignored. Using the partial decoding feature, users of the OSS ASN.1 Tools for Java are able to

  • Extract data without writing code to access deeply nested fields of complex PDUs.
  • Reduce the memory footprint of applications that contain minimal information in their PDUs.

The decodePartial() method of a Coder object does not return the decoded PDU value. Instead, it invokes user-defined callback methods when decoding each field marked by the OSS.DataCallback or OSS.InfoCallback compiler directive and, optionally, passes the decoded field value to it. Your callback method can analyze the decoded value, and by setting the return code, instructs the decoder to either terminate or continue decoding.

Remarks

The partial decoding feature requires the TOED runtime (-toed compiler option).

Only the BER, DER, PER, UPER, CPER, CUPER, OER, and COER binary encoding rules support partial decoding.

Example

For the following ASN.1 syntax, the ZIP code nested within homeAddress will be extracted while the ZIP code nested within the company address is ignored:

M DEFINITIONS AUTOMATIC TAGS ::= BEGIN
  Subscriber ::= SEQUENCE {
    name VisibleString,
    company Company,
    homeAddress Address
  }
  Company ::= SEQUENCE {
    name VisibleString,
    address Address
  }
  Address ::= SEQUENCE {
    zipcode INTEGER( 0..99999 ),
    addressline VisibleString( SIZE (1..64) )
  }
END

First, apply the following directives and compile the specification with the -toed option and either the -enablePartialDecoding or -partialDecodeOnly option.

--<OSS.DataCallback M.Address.zipcode "myZipcode">--
--<OSS.InfoCallback M.Subscriber.homeAddress "homeAddressField">--

The options instruct the compiler to generate the PartialContentHandler class with the following abstract callback method declarations:

public abstract Response beginMyZipcode();
public abstract Response endMyZipcode(com.oss.asn1.INTEGER data);
public abstract Response beginHomeAddressField();
public abstract Response endHomeAddressField();

The com.oss.asn1.ContentHandler.Response Java enum class defines possible return codes from the callback methods:

CONTINUE - Continue with normal processing.
SKIP   -   If returned from a "begin" callback, skip to the end of the field, suppressing further
           callbacks that could occur inside the field, including the "end" field callback.
           If returned from an "end" callback, skip to the end of the PDU, consuming all the encoded
           stream data without any further callback invocations.
ABORT   -  Terminate partial decode procedure and return to the caller immediately.

Then, define the subclass of the generated PartialContentHandler and implement the abstract methods as follows:

public class MyPartialContentHandler extends PartialContentHandler {

  private boolean _insideHomeAddress = false;

  public Response beginHomeAddressField()
  {
    _insideHomeAddress = true;
    return Response.CONTINUE;
  }
  public Response endHomeAddressField()
  {
    return Response.SKIP;
  }
  public Response beginMyZipcode()
  {
    return _insideHomeAddress ? Response.CONTINUE : Response.SKIP;
  }
  public Response endMyZipcode(INTEGER value)
  {
    // Consume ZIP code
    // ...
    return Response.CONTINUE;
  }
  public MyPartialContentHandler(AbstractData contentType)
  {
    super(contentType);
  }
}

Finally, invoke the partial decode operation as follows:

Subscriber pduType = new Subscriber();
coder.decodePartial(source, new MyPartialContentHandler(pduType));

Alternatively, you can apply OSS.InfoCallback to Subscriber.company rather than Subscriber.homeAddress:

--<OSS.InfoCallback M.Subscriber.company "company">--

Define the content handler as follows:

public class MyPartialContentHandler extends PartialContentHandler
{
  public Response beginCompany(String name)
  {
    return Response.SKIP;
  }
  public Response endCompany(String name)
  {
    return Response.CONTINUE;
  }
  public Response beginMyZipcode()
  {
    return Response.CONTINUE;
  }
  public Response endMyZipcode(INTEGER value)
  {
  // Consume ZIP code
  // ...
  return Response.CONTINUE;
  }

  public MyPartialContentHandler(AbstractData contentType)
  {
    super(contentType);
  }
}

You would get the same result: the homeAddress ZIP code is extracted and the company address ZIP code is ignored.

See Also

Note: Partial decoding is a chargeable feature in non-evaluation licenses. Contact Sales to obtain pricing information.


Detecting Unknown Extensions

The "boolean isUnknownExtensionFound()" method of the com.oss.asn1.Coder class checks whether the last PDU decoded by the decode() method contained at least one unknown SEQUENCE, SET, or CHOICE type extension. The presence of unknown extensions indicates that the sender is using a newer version of the ASN.1 specification.


This documentation applies to the OSS® ASN.1 Tools for Java release 8.7 and later.

Copyright © 2024 OSS Nokalva, Inc. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording or otherwise, without the prior permission of OSS Nokalva, Inc.
Every distributed copy of the OSS® ASN.1 Tools for Java is associated with a specific license and related unique license number. That license determines, among other things, what functions of the OSS ASN.1 Tools for Java are available to you.