TOP

ASN.1/C Runtime Advanced Topics

The advanced topics covered in this section include the following:

Memory, File, Sockets, and PDU Handling

PDU encoding or decoding requires extensive use of memory to store the source and destination data buffers. An encoded PDU is always stored as a single stream of bytes, while an unencoded (decoded) PDU is allocated in memory as C-representation of its individual fields (a combination of structures, arrays, and linked lists).

Depending on the PDU and memory size, your performance needs, and your choice of encoder or decoder runtime (SOED or TOED), the following options are available:

  • Store unencoded and encoded PDUs in conventional memory (available for SOED and TOED).
  • Store individual fields (large arrays) of unencoded PDUs in files (available for SOED).
  • Stream the encoded PDUs to/from files or sockets (available for SOED).

Additionally, you can combine the options mentioned above. For example, you can encode from memory to a file or decode from a socket to memory, while storing a large OCTET STRING field (of the un-decoded PDU) in a temporary file. Also, you can decode PDUs that start in a memory buffer and continue in a file or a socket. Note that only the SOED library supports files and sockets.

Handling PDUs in Conventional Memory (TOED and SOED)

As illustrated in the following diagram, the encoder or decoder allocates and deallocates memory through low level functions contained in the OssGlobal structure (by default: malloc(), realloc(), free()).

To optimize memory use, consider the following options:

  • Use the default encoder or decoder memory handling to dynamically allocate output PDU buffers (use ossFreePDU() and ossFreeBuf() to deallocate).
  • Preallocate output PDU buffers manually (reuse preallocated buffers for better performance).
  • Use OSS.NOCOPY to mark certain PDU fields (strings). Thus, the decoder will treat them as pointers and will not copy large chunks of memory from the encoded PDU to the decoded one.
  • Override default memory functions with OSS specialized memory manager (see ossCreateMemoryHandle()). This is available for SOED.
  • Customize default memory functions by providing your own low level implementation (see ossSetUserMallocFreeRealloc()).
SOED Runtime Memory

SOED Runtime Memory

Handling Unencoded PDU Fields in Files (SOED)

In general, unencoded PDUs reside in memory, where each field is mapped to a certain C representation. However, to save memory, you can redirect fields (large streams of bytes) to a file. To ensure correct C-representation and handling, mark the fields with OSS.OBJHANDLE | OSS.NOCOPY at ASN.1 compile time, then use ossMarkObj() to bound to a file. Note that to add support for the files, you must link with the File Memory Manager library.

Handling Encoded PDUs in Files and Sockets (SOED)

Encoded PDUs are represented as streams of bytes, thus they can be streamed directly to or from files or sockets during encoding or decoding to save memory.

The ossEncode() and ossDecode() functions inherit the behavior of the send() and recv() socket functions. For example, when a socket is closed by the remote side and a subsequent send() on the local side does not report an error, ossEncode() cannot detect the closed socket; instead, it accepts the undeliverable packet. To tune the socket, to send or receive additional messages using socket functions, use the socket handler.

NOTE: The ossDetermineMessageLength(), ossCompress(), and ossUnCompress() functions do not support encoded PDUs streamed to or from files.

Binding data to a file/socket

Binding data to a file/socket

Building and Linking

To use the selected PDU handling method, link with the corresponding management library. There are four libraries available:

  • Conventional memory manager
  • File manager
  • Socket manager
  • OSAK architecture manager

Additionally, you can develop your own management library.

On Windows, you can link either statically or dynamically (DLL). The latter implements the explicit linking (late binding), thus the desired library is loaded at runtime, as follows: during the initialization, the encoder/decoder always loads the ossdmem.dll (conventional memory manager). Then, if the application needs a different PDU handling. To replace the default library, call ossLoadMemoryManager() and specify a different DLL (ossfmem.dll or osssmem.dll for files and sockets, accordingly, provided by OSS).

When linking statically, link ossdmgmt.obj/ossfmgmt.obj/osssmgmt.obj (default/file/socket library) with soeddefa.lib, or ossdmgmd.obj/ossfmgmd.obj/osssmgmd.obj (default/file/socket library) with soeddemd.lib.

Memory Handle API

The OSS ASN.1 runtime supports two modes of memory allocation during encoding and decoding.

In user allocation mode you provide to the OSS runtime a buffer sufficient to hold the data. For encoding it must be large enough to accommodate the encoded message; for decoding it must be large enough to accommodate the decoded PDU struct. This mode is faster than OSS-allocated mode, but you need to know how large a buffer is needed. If your buffer is too small, your call to ossEncode() or ossDecode() fails and returns an error code.

In OSS allocation mode the OSS runtime allocates the buffer, and if the size is insufficient, automatically allocates more. It will continue allocating memory, as needed, so ossEncode() and ossDecode() cannot fail because the buffer is too small.

Using Memory Handles

The default for OSS allocation mode is to allocate little pieces, which has the advantage of not wasting space, but the disadvantage of having to call the system's memory allocation function (malloc) many times. You can instead use the Memory Handle API to request that the OSS runtime allocate memory in larger chunks, and to fill its need for buffer space from within those larger chunks.

A memory handle is a linked list of typically large memory blocks, where you choose the size. Rather than call malloc, the OSS runtime allocates pieces of memory from these blocks. You can also use this technique when working with helper macros, the helper list API, or when manually allocating values using the ossGetMemory() function.

The Memory Handle API is a set of functions to activate, control and terminate the memory handle mode. It employs ossSetUserMallocFreeRealloc() to override the default allocation (malloc), reallocation (realloc), and deallocation (free) functions with its own functions to maintain a chain of memory blocks.

As a result, you cannot use the ossSetUserMallocFreeRealloc() function when working with memory handles (you cannot override memory handle-specific allocation functions with your own).

Memory needed by ossEncode(), ossDecode(), or any other OSS API function is allocated from the same memory handle. You can allocate several memory handles for different parts of the program, for example, one handle to store the encoded data and another to store the decoded value.

Advantages of using the Memory Handle API

With the Memory Handle API, you can optimize dynamic memory freeing and reuse. Since the memory is hosted by a single 'handle' structure as a list of blocks, it's possible to free it quickly.

The OSS decoder creates a tree-like C structure in memory. This tree can become quite intricate, and were it not for the Memory Handle feature, quite scattered. To deallocate a PDU, you would call the ossFreePDU() function to traverse the PDU tree and calls a system freer for each block of memory; this is time consuming. On the other hand, with memory handles, a single ossDeleteMemoryHandle() call deallocates the memory handle and the entire chain of memory blocks hosted by this handle.

In an even more efficient manner, you can release the memory without deallocating it by calling ossCleanMemoryHandle(), which instead of deallocating the memory blocks simply marks them as unused. The memory from these blocks can now be reused, for instance, by subsequent ossDecode() function calls. You can thus reuse the same memory for all your decodes, allocating it only once, that is, before the initial ossDecode() call.

Bearing in mind that user allocation mode is always fastest, we present some simple performance statistics for encoding and decoding the PersonnelRecord sample PDU in OSS allocation mode. For encoding without the benefit of memory handles, the TOED BER is 35% slower than user allocation mode; with memory handles, only 10% slower. The performance improvement in decoding is much more pronounced. Without memory handles, the TOED BER decoder is 300% slower, while with memory handles, only 22% slower.

Function Overview

To use the memory handle API, start by calling the ossCreateMemoryHandle() API function. It returns a pointer to the allocated handle or NULL in case of failure.

If several memory handles are in use, one of them should be set as "active" for a given instance of the global OSS environment variable by calling the ossSetActiveMemoryHandle() function. To retrieve the active memory handle pointer, call the ossGetActiveMemoryHandle() API function. The function returns NULL if memory handle mode is not in use.

To release memory, you can use ossCleanMemoryHandle() or ossDeleteMemoryHandle().

ossCleanMemoryHandle() – does not delete the handle itself nor does it deallocate the memory blocks attached to the handle (the memory releasing system function is not called). Instead, it marks all memory in these blocks as unused. As a result, subsequent attempts to allocate memory from the same handle will reuse the same memory blocks without having to allocate new ones with the system memory allocator (a decreased number of dynamic memory allocations per API function call makes the application work faster).

ossDeleteMemoryHandle() - permanently deallocates the memory handle and all its memory blocks. The handle pointer becomes unusable. This function should be called only once immediately before the ossterm() function. In all other cases, call the ossCleanMemoryHandle().

Memory usage statistics are available to help you tune the size of the buffer blocks and the number of handles. You can access the information by calling ossGetMemoryHandleStat().

For details on individual memory handle functions, see:

Examples

NOTE: You can find a complete working sample test program in the OSS ASN.1 installation directory, samples/advanced/memory_handles.

Reusing memory when an OSS API function is being run in a loop

In this example, a memory handle is allocated only once. It is reset by ossCleanMemoryHandle() at the end of each iteration and is permanently deleted before ossterm().

    struct ossMemoryHandle    *hdl;

    if ((hdl = ossCreateMemoryHandle(world, CHUNK_LENGTH))) {
        while (loop) {
            rc = ossDecode(world, &pdunum, &buf, &value);

            /* any actions with 'value' */
            ..........................

            /* ossCleanMemoryHandle resets the memory */
            ossCleanMemoryHandle(world, hdl);
        }
    } else
        /* handle the error case - could not allocate a handle */
        ..........................

    ossDeleteMemoryHandle(world, hdl); /* delete 'hdl' to prevent a memory leak */
    ossterm(world); 

Working simultaneously with several messages

Suppose you need to work with two decoded messages separately at the same time - this means that the messages should reside in different memory handles. The scenario illustrated below is as follows:

  1. Decode the first message
  2. Decode the second message
  3. Free the first decoded message
  4. Free the second message.
    Struct ossMemoryHandle *hdl1, *hdl2;

    if (hdl1 = ossCreateMemoryHandle(world, CHUNK_LENGTH)) {

        rc = ossDecode(world, &pdunum1, &buf1, &value1);

        /* any actions with 'value1' */
        ..........................
    } else
        /* handle the error case - could not allocate a handle #1 */
        ..........................
        
    if (hdl2 = ossCreateMemoryHandle(world, CHUNK_LENGTH)) {

        rc = ossDecode(world, &pdunum2, &buf2, &value2);
        /* any actions with 'value2' */
        ..........................
    } else
        /* handle the error case - could not allocate a handle #2 */
        ..........................
    
    ossDeleteMemoryHandle(hdl1);
        /* any actions with 'value2', 'value1' being already freed */
    ossDeleteMemoryHandle(hdl2);

Manually modifying the decoded message

Here is how you use the same memory handle for decoding as well as for manually allocating memory after decoding. Suppose you decode a message for the following PDU:

S ::= SEQUENCE {
  s1 INTEGER,
  s2 SEQUENCE OF OCTET STRING OPTIONAL
}

If the decoder creates a value that in ASN.1 value notation is:

value S ::= { s1 200 }

and the task is to add a value for s2 so that the value of S looks like:/p>

value S ::= { s1 200, s2 {'112233'H} }

The application code illustrates how this can be done.

    S *s = NULL;

    if (ossCreateMemoryHandle(world, CHUNK_LENGTH)) {

        rc = ossDecode(world, &pdunum, &buf, &s);
        ......................

        if (s && !s->s2)
            /* The function oss_S_s3 will allocate memory by
               calling ossGetMemory(); memory is allocated from the
               same active memory handle that was used by the
               decoder; same does the macro oss__OctStr_copy() */
            s->s2 = oss_S_s2(world);
            oss_S_s2_append(world, s->s2, 
                oss__OctStr_copy(world, (unsigned char *)"\x11\x22\x33", 3));

        /* any further actions with 's' */
        ..........................

        /* ossFreePDU will call ossCleanMemoryHandle */
        ossDeleteMemoryHandle(world, ossGetActiveMemoryHandle(world));
    }

Cleanup when total amount of allocated memory is too large

This section illustrates how to free memory used by the handle when the amount of memory allocated by all its nodes (total) exceeds 1 MB. When the size of the messages being decoded is too large, the current active handle may allocate huge chunks; to avoid using system resources, the application may return the memory to the system immediately after decoding these messages by using the reset_handle() function:

#define SUCCESS     0
#define ERR_STATS   1
#define ERR_DELETE  2
#define ERR_CREATE  3
#define ERR_CLEAN   4
#define ERR_ACTIVE  5

static int        reset_handle(OssGlobal *world)
{
    struct ossMemoryHandle  *curr = ossGetActiveMemoryHandle(world);
    OssMemoryHandleStat      stat;

    size_t            nBytes, nBlocks;

    if (curr) {
        if (ossGetMemoryHandleStat(world, curr, &stat, OSS_MH_BRIEF))
            /* could not get the statistics */
            return ERR_STATS;

        if (stat.system_allocated > 1024*1024 || stat.nodes > 32) {
            /* It's time to free the memory at the system level */
            if (ossDeleteMemoryHandle(world, curr))
                /* could not delete the handle */
                return ERR_DELETE;
            if (!ossCreateMemoryHandle(world, CHUNK_LENGTH))
                /* could not create a handle */
                return ERR_CREATE;
        } else
            /* Release the memory at the OSS Memory Manager level */
            if (ossCleanMemoryHandle(world, curr))
                /* could not reuse the handle */
                return ERR_CLEAN;
   } else
	 /* active memory handle is NULL */
	 return ERR_ACTIVE;

   return SUCCESS;
}
 
..........................

int main (void)
{
    ..........................

    if (ossCreateMemoryHandle(world, CHUNK_LENGTH)) {

        while (loop) {
            rc = ossDecode(world, &pdunum, &buf, &value);

            /* any actions with 'value' */
            ..........................

            if (reset_handle(world)) {
                /* handle the error - could not reset the handle */
                ..........................
                break;
            }
        }
    } else
        /* handle the error case - could not allocate a handle */
        ..........................
}

Encoder/Decoder Implementation Details

The encoder/decoder has the following implementation details and limitations:

DEFAULT Values in PER/BER

The rules of PER state that a value which takes the default value must be omitted from the encoding. That is, for

abc INTEGER DEFAULT 5

if abc is set to 6, the PER encoder should encode it, but if it is set to 5, the encoder should mark it absent. Since such checking to see whether the value is set to the default value hinders performance, OSS chooses to enable it only when the STRICT_PER_ENCODING_OF_DEFAULT_VALUES runtime flag is set (see ossSetEncodingFlags()) and the OSS_STRICT_PER_ENCODING_OF_DEFAULT_VALUES macro is used at C compile time.

Without an explicit setting of STRICT_PER_ENCODING_OF_DEFAULT_VALUES and DOSS_STRICT_PER_ENCODING_OF_DEFAULT_VALUES, the encoder will encode default values whenever the present bit is set, for example, for abc as defined above, the compiler generates

unsigned char   bit_mask;
#define  abc_present 0x80
int      abc;

If you set the abc_present bit in bit_mask, abc will be encoded; if you set abc_present to 0, abc will not be encoded.

If a DEFAULT field is marked present, the BER encoder always encodes its value.

DER Encoding SET OF Matching the DEFAULT Value

If the value for a SET OF type is the same as the specified DEFAULT value, the DER encoder will not recognize the match between the two values if the order of occurrence of the SET OF elements differs. For example, for a definition: some-field SET OF INTEGER DEFAULT {3, 2, 1}, if the value presented for encoding is {1, 2, 3}, theoretically, the encoder should not encode that value because it is the same as the default one. However, it encodes the value as if the two were not equal. This is done to simplify the comparison algorithm, thereby reducing its size and increasing its speed. In practice, this restriction does not pose a problem with X.509 or any other ASN.1 known DER specification, since they do not define such defaults.

UTC Time in DER

The encoder cannot determine whether a time is UTC (Coordinated Universal Time), therefore you must ensure that GeneralizedTime values are UTC time. Set the field utc in the GeneralizedTime structure to TRUE, which indicates a UTC time.

PER Encoder/Decoder (ALIGNED and UNALIGNED)

The OSS.OBJHANDLE | OSS.NOCOPY directive is not supported.

Lean Encoder/Decoder (LED)

LED has no support for the following:

The Lean encoder does not support Canonical Encoding Rules (CER). However, the Lean decoder supports decoding of a CER encoding, since CER is a valid subset of BER.

The Lean encoder supports the DEFINITE length form of BER encodings. The Lean decoder supports all valid BER encodings including those that are in the INDEFINITE form.

The LED runtime library ignores the OSS_TRAPPING/NOTRAPPING runtime flags.

The Lean encoder/decoder does not support partial decoding. The partial decoding feature is supported only for the Time Optimized encoder/decoder (TOED).

Time-Optimized Encoder/Decoder (TOED)

See the following limitations:

  • Minimal constraint checking.
  • Limited trace capabilities. To instruct the TOED to construct error messages accessible via ossGetErrMsg(), set the DOSSDEBUG=2 C compiler flag.

To use information object handling functions (ossAddInfoObject(), ossGetInfoObject(), ossGetInfoObjectSet(), ossRemoveInfoObject()), ASN.1-compile your input specification with -autoEncDec and -toed. To make these functions available at runtime, C-compile the produced code file with -DOSS_INFOOBJ_API defined.

To automatically encode/decode open types, see the following procedure:

  1. ASN.1-compile your input specification with -autoEncDec and -toed specified.
  2. Set the AUTOMATIC_ENCDEC flag during runtime (via ossSetFlags()).

Currently, in the TOED implementation of automatic encoding/decoding of open types, a SET OF or SEQUENCE OF type cannot be referenced by a component relation constraint that is used to determine the base type of the open type.

The encoder supports only the DEFINITE length form of BER encodings. However, the Time-optimized decoder supports all valid BER encodings including those that are in the INDEFINITE form.

Certain forms of invalid encodings are silently ignored by the decoder. However, the encoder will encode (or re-encode) such values (upon request) in a format that conforms to the official encoding rules standard.

TOED does not check PATTERN constraints.

In the case of a BIT STRING with a named bit list represented in POINTER-ed VARYING form, TOED PER decoder allocates the number of bytes the value consists of for the decoded value, and not the number of bytes that the value type occupies.

The encoder does not support Canonical Encoding Rules (CER). However, the decoder supports decoding of a CER encoding because CER is a valid subset of BER.

TOED does not support user defined constraints (CONSTRAINED BY). Also, it does not support PER Encoding Analyzer functions: ossPrintPER() or ossPrintXPER(). They are compatible only with the SOED libraries.

Space-Optimized encoder/decoder (SOED)

When the SOED encoder/decoder is in use, partial decoding is not supported. The partial decoding feature is supported only for the Time Optimized encoder/decoder (TOED).

XER Encoding/Decoding

See the following limitations:

  • The runtime flags DEBUGPDU, IGNORE_DEFER_DECODING, DONT_FREE_ENCODED, and IGNORE_PDU_TAG are not supported.
  • Node values for the OBJECT IDENTIFIER type with an ENCODED representation and for the RELATIVE-OID type should not exceed the maximum 64-bit unsigned INTEGER (18446744073709551615). This applies to all encoder/decoder libraries (SOED/TOED/LED), CXER, and E-XER.
  • The OSS.OBJHANDLE | OSS.NOCOPY directive is supported for Any and Open types.

E-XER Encoding/Decoding

See the following limitations:

  • The OSS.OBJHANDLE | OSS.NOCOPY directive is not supported.
  • The OSS EXTENDED-XER runtime supports only certain C representations for the OER utility fields of the following encoding instructions.
E-XER instruction ASN.1 definition
ANY-ATTRIBUTES
SEQUENCE OF UTF8String
ANY-ELEMENT
UTF8String
EMBED-VALUES
SEQUENCE OF UTF8String
USE-ORDER
SEQUENCE OF ENUMERATED
USE-QNAME
pair of UTF8String fields

The representation of SEQUENCE OF must be in an UNBOUNDED or LINKED (the default for the ASN.1/C Compiler) or DLINKED-PLUS (the default for the -helperNames compiler option). UTF8String must be either null terminated (the default for SOED and TOED) or UNBOUNDED(the default for LEAN).

The decoder does not perform automatic decoding of types with component relation constraints that reference:

  • SEQUENCE OF fields of EMBED-VALUES or USE-ORDER encoding instructions.
  • Fields with ANY-ELEMENT or ANY-ATTRIBUTES encoding instructions.

When the control table is produced with the -noConstrain option, certain CHOICE types with the USE-UNION instruction may not be decoded correctly. This might occur when the constraint information is needed to identify the union alternative. See the following example:

C ::= [USE-UNION] CHOICE {
   t1 IA5String (FROM ("a".."z")),
   t2 INTEGER (1..MAX),
   t3 BOOLEAN }

The following instance:

<C>0</C> 

will be decoded as:

v C ::= t1 : "0"

instead of:

v C ::= t3 : FALSE

OER/C-OER Encoder/Decoder

When the OER/C-OER encoder/decoder is in use, the following ASN.1 compiler directives are not supported: ASN1.DeferDecoding, OSS.OBJHANDLE(OSS.NOCOPY).

ossPrintPDU() and NULL Characters

The ossPrintPDU() function does not support printing of character string values which have the NULL character embedded in the string value. This limitation applies only to ossPrintPDU(). The encoder and decoder correctly handle NULL characters embedded in character string values even when generating tracing output.

Encoding Compression

When the USE_COMPRESSION flag is set, the encoder/decoder library will ignore OSS.NOCOPY and ASN1.DeferDecoding. To use these directives with the compression function, you must explicitly call ossCompress() after ossEncode(), and ossUncompress() before ossDecode().

SOED with default memory manager and LED support compression. TOED does not support compression.

Constraint Checker

Encoder/decoder ignores single value or value range constraint violations for SET OF values for all encoding rules.

In the following example, value v is successfully encoded and decoded to avoid a potential performance hit that might occur during verification of such constraints:

Test DEFINITIONS ::= BEGIN
   Setof ::= SET ({1, 2}) OF INTEGER
   v Setof ::= {1}
END

C Compiler Macros for Shared (exported) User Functions

OSS provides global #defined macros that you can include in your function prototypes, function definitions, and function pointers. These macros are defined in ossasn1.h and their meaning depends on the platform. Use these macros to:

  • Declare user or user-visible OSS functions and function pointers with the correct calling convention specifiers, such as __cdecl or __stdcall on a Microsoft Win32 platform, since all OSS runtime libraries are built using the default (__cdecl) calling convention.
  • Allow user-declared functions to be contained in shared libraries on platforms that allow this sharing, such as a DLL in Microsoft Windows.
  • Facilitate the recompilation process of your application when you switch platforms, or to force the __stdcall calling convention by using the -Gz C compiler option.

Consider these six global macros:

  • DLL_ENTRY, used in function prototypes
  • DLL_ENTRY_FDEF, used in function definitions
  • DLL_ENTRY_FPTR, used in function pointer declarations
  • CDECL_ENTRY, used in function prototypes
  • CDECL_ENTRY_FDEF, used in function definitions
  • CDECL_ENTRY_FPTR, used in function pointer declarations

The function eopenIn() takes three arguments. Here is the function prototype:

unsigned char* DLL_ENTRY eopenIn(OssGlobal *world, void *lock, size_t length);

Here is the function definition:

unsigned char* DLL_ENTRY_FDEF eopenIn(OssGlobal *world, void *lock, size_t length);
{
/* Function body */
}

Here is the function pointer for eopenIn():

unsigned char *(DLL_ENTRY_FPTR eopenInp)(void *, void *, size_t);

The following example shows how macros are used to declare three callback functions and function pointers passed to the ossSetUserMallocFreeRealloc() and ossGetUserMallocFreeRealloc() OSS API functions to set up a custom memory allocator:

/* The user functions prototypes should look like: */
void * CDECL_ENTRY myMalloc(OssGlobal *world, size_t size);
void * CDECL_ENTRY myRealloc(OssGlobal *world, void *buf, size_t size);
void CDECL_ENTRY myFree(OssGlobal *world, void *buf);
int main() {
struct ossGlobal w, *world = &w;
/* The function pointers should look like: */
void *(CDECL_ENTRY_FPTR *oldMalloc)(OssGlobal *world, size_t size);
void *(CDECL_ENTRY_FPTR *oldRealloc)(OssGlobal *world, void *buf, size_t size);
void (CDECL_ENTRY_FPTR *oldFree)(OssGlobal *world, void *buf);
ossinit(world, oss_data);
/* Save original allocation functions */
ossGetUserMallocFreeRealloc(world, &oldMalloc, &oldFree, &oldRealloc);
/* Set new ones */
ossSetUserMallocFreeRealloc(world, myMalloc, myFree, myRealloc);
/* Restore original allocation functions */
ossSetUserMallocFreeRealloc(world, oldMalloc, oldFree, oldRealloc);
ossterm(world);
}
/* The user functions definitions should look like: */
void * CDECL_ENTRY_FDEF myMalloc(OssGlobal *world, size_t size)
{
/* Function body */
}
void * CDECL_ENTRY_FDEF myRealloc(OssGlobal *world, void *buf, size_t size)
{
/* Function body */
}
void CDECL_ENTRY_FDEF myFree(OssGlobal *world, void *buf)
{
/* Function body */
}

OSS Global Structure

The global environment variable is passed to almost every API function. Aside from the variables documented in this section, you should not explicitly modify any other field in OssGlobal, because it might generate unpredictable results.

typedef struct OssGlobal {...};

You can access (set/get) the following fields in this structure:

Fields

mallocp | freep | reallocp
Function pointers used to perform memory allocation, freeing, or reallocation by all functions in this API. By default, they point to their C counterparts: malloc()| free()| realloc(). You can change the pointers according to your own low-level memory functions.
asn1chop
Number of bytes written during tracing or printing by ossEncode(), ossDecode() or ossPrintPDU(). When asn1chop is set to a zero, the strings will not be truncated. By default, asn1chop is equal to 35.
ossblock
Maximum number of bytes that the encoder/decoder can request in a single call to the memory manager. When ossblock is set to a zero, (the default), no restriction is imposed. When ossblock is set to a non-zero value, you must ensure that your C data types will fit into the memory allocated for them.
ossprefx
Minimum number of bytes in the OSAK buffer memory manager, that ossEncode() must reserve in front of the first byte of encoded data (zero, by default). These reserved bytes can be used by other service entities or protocol layers to prefix additional protocol information to the encoded data.
asn1out
Output stream to which trace is written (used by asn1prnt). By default, it is set to stdout. Change it directly or via ossOpenTraceFile() to reference a different output stream or file. Alternatively, you can save the trace output to a file by calling ossOpenTraceWindow() and selecting the window "Save" menu item.
asn1prnt
Prints diagnostic trace data (when DEBUGPDU flag is set). It is called by the ossPrintPDU(), ossPrintHex(), ossPrint(). By default, asn1prnt points to fprintf() and you can set it to point to your own print function (matching the fprintf() signature). This field is not in use when tracing information is written to a tracing window (after a call to ossOpenTraceWindow()). If you are using the OSS encoder/decoder library with Windows, set asn1out whenever you set asn1prnt (otherwise, the application might crash if the OSS libraries and your application use different versions of the C runtime, for instance, when you use Visual Studio 2005 along with an encoder/decoder library built with Visual Studio 6).
userVar
Used for arbitrary data (see samples\osstest.c). It is recommended that you set the userVar field after ossinit() call. However, you can preserve its content during ossinit() by setting a global flag _ossSetGlobalUserVar to zero.

Open Type Structures

typedef struct OpenType {
   int pduNum;
   long length; /* length of encoded */
   void *encoded;
   void *decoded;
# ifdef OSS_OPENTYPE_HAS_USERFIELD
   void *userField;
# endif
} OpenType;
typedef struct 
OpenTypeExtended {
   int pduNum;
   long length; /* length of encoded */
   void *encoded;
   void *decoded;
   unsigned long byteOffset;
   unsigned short bitOffset; /* Used only for PER */
   unsigned short residualBits; /* Used only for PER */
#ifdef 
OSS_OPENTYPE_HAS_USERFIELD
   void *userField;
#endif 
} OpenTypeExtended;

The ASN.1 compiler generates OpenType structures when Information Objects are defined in a way that renders ambiguous the exact type intended for a certain field. OpenType is generated for elements of CHOICE, SEQUENCE, and SET types that have either the ASN1.DeferDecoding or OSS.ENCODABLE directive applied to them. OpenTypeExtended is generated for ASN.1 types marked with ASN1.DeferDecoding and OSS.OBJHANDLE/OSS.NOCOPY to represent data which can reside in "external" memory, such as a file, whose decoding is to be deferred.

Fields

pduNum
Compiler-generated PDU ID of the decoded structure/value.
decoded
Compiler-generated PDU structure/value.
length
Length of the encoded value.
encoded
Encoded value returned by the encoder.
byteOffset
Byte offset of the decoder's input file at which the open type begins.
bitOffset and residualBits
Reserved for future use.
userField
Generated only if you compile your ASN.1 schema with the -extendOpenType compiler option. Note that this field will not be encoded or decoded with the rest of the open type. The decoder sets this field to NULL upon successfully completing a decode operation.

pduNum and decoded fields are used only if the AUTOMATIC_ENCDEC flag is set (see the ossSetFlags()).

When the AUTOMATIC_ENCDEC flag is set, the length and encoded fields should be set to NULL before calling the encoder; while the decoder sets these fields to NULL upon completing a successful automatic decode.

When the AUTOMATIC_ENCDEC flag is not set, the pduNum and decoded fields must be set to NULL, while the encoded and length fields must reference the encoded data and its length, respectively.

To automatically encode or decode OpenType values, the open type declaration must contain a component relation constraint applied to it, such as the "({SupportedAttributes}{@type})" in the following:

AttributeTypeAndValue ::= SEQUENCE {
   type ATTRIBUTE.&id ({SupportedAttributes}),
   value ATTRIBUTE.&Type ({SupportedAttributes}{@type})
}

Currently, for the TOED implementation of automatic encoding/decoding of open types, a SET OF or SEQUENCE OF type may not be referenced by a component relation constraint that is used to determine the base type of the open type.

C Compiler Macros for Controlling Performance/Memory Footprint when Using TOED

Code files generated for TOED contain a number of features that are either enabled or disabled by default and can be adjusted for performance and/or the memory footprint by a set of C compiler macros. This is done via -D compiler option (-DOSSDEBUG).

IGNORE_DEFER_DECODING_SUPPORTED
Used with IGNORE_DEFER_DECODING runtime flag. By default, this flag has no effect in the program. That is, open types created with the ASN1.DeferDecoding directive will not be automatically encoded or decoded.
OSSDEBUG
Affects the verbosity of TOED diagnostic messages returned by ossGetErrMsg(). Can have the following levels:
  • OSSDEBUG=0, no error message is constructed, and only an error code is returned.
  • OSSDEBUG=1, generates a brief error message along with the error code.
  • OSSDEBUG=2 (or greater), returns an error message, the context (which component type was processed), and the error code.
  • -test automatically sets the OSSDEBUG level to 3. The OSSDEBUG value must not be set to a value which is greater than two, because the effect of such values is reserved for possible future use.
OSSPRINT
Enables implementation of the ossPrintPDU() function in TOED-based programs (as in SOED). By default, this function prints a special message and returns zero. The -test compiler option enables OSSPRINT automatically.
OSS_COMPARE_VALUE | OSS_COPY_VALUE
Enables implementation of ossCmpValue() | ossCpyValue() in TOED-based programs (as in SOED). By default, these functions have no effect and return UNIMPLEMENTED. The -test compiler option enables them automatically.
OSS_DO_NOT_CHECK_POINTERS
Disables the check for NULL pointers within the input C structures of the TOED encoder. By default, this check is performed to protect the encoder from a bad input value.
OSS_INFOOBJ_API
Enables the implementation of object handling functions (ossAddInfoObject(), ossGetInfoObject(), ossGetInfoObjectSet(), ossRemoveInfoObject()) in TOED-based programs. By default, these functions have no effect at run time (they do not modify the object set).
OSS_NO_NESTING_CONTROL
Disables nesting control in TOED-based programs. Nesting control is a security mechanism used in decoders to prevent a possible stack overflow triggered by a specially crafted message. OSS run-time libraries provide protection from two types of nesting overflows: for constructed string encodings and for constructed types. They are enabled by default.
OSS_NO_STRICT_ENCODING_DECODING_CHECKING
Partially disables support for the STRICT_ENCODING_DECODING_RULES runtime flag in TOED-based programs. By default, this flag performs the checks for STRICT_ENCODING_DECODING_RULES. When the code file is C-compiled with the OSS_NO_STRICT_ENCODING_DECODING_CHECKING macro, the following checks are disabled:
  • Incorrect fragmentation of constructed types in the PER encoding
  • Zero length extensions in the PER encoding
  • Zero length PER encodings
  • Inconsistent length determinant of SET OF/SEQUENCE OF types in the BER encoding
  • Container match of Contents Constraint
Note that you cannot cancel the effect of the STRICT_ENCODING_DECODING_RULES runtime flag with the OSS_NO_STRICT_ENCODING_DECODING_CHECKING macro, because only a part of the performed checks is conducted by the generated code file. The other part is conducted by the TOED runtime; to disable these checks make sure you do not use (clear) the STRICT_ENCODING_DECODING_RULES runtime flag in your application code.
OSS_NO_STRICT_EXTENSION_CONTROL
Disables the check for the absence of mandatory (non-optional) fields after the extension marker in the input PDU in TOED-based programs. By default, the check is performed.
OSS_SET_USERFIELDS_TO_ZERO
Instructs the TOED decoder to initialize all user fields in SEQUENCE or SET types with zero. User fields are locally defined fields that are significant only to a particular application. They are produced when the appropriate field in the constructed type is marked with the OSS.USERFIELD directive. By default, the decoder does not modify the user fields. This is useful when you keep application specific data in user fields and that must persist between several calls to the decoder. However, this behavior produces an incompletely filled C-value when you don't initialize the user fields prior to the decoding. An incompletely filled C-value can be a cause for memory problems. For example, you can purify warnings when the entire value is copied with the ossCpyValue() function.
OSS_STRICT_PER_ENCODING_OF_DEFAULT_VALUES
Provides support for the STRICT_PER_ENCODING_OF_DEFAULT_VALUES runtime flag in TOED-based programs (as in SOED). By default, this flag has no effect on the program. That is, the PER encoder produces a non-empty encoding for components defined with a DEFAULT value even when the value to be encoded is the default value.
OSSNOFREEPDU
Disables implementation of the ossFreePDU() function in TOED-based programs, thus decreasing the size of the final application. If you decode PDUs into a user provided buffer only, or do not decode at all (the -encodeonly option is specified), you must define OSSNOFREEPDU.
OSS_SKIP_UNKNOWN_CONTENT_SUPPORTED
Enables support for the OSS_SKIP_UNKNOWN_CONTENT runtime flag in the E-XER TOED-based programs. By default, this code is disabled by the C preprocessor to reduce the size of your program and to achieve slightly better performance (the difference is in the range of 5 to 10% for size and speed). By default, the decoder issues errors if unknown elements or attributes are present in the input XML message.

PER Encoding Analyzer API

Format

ossPrintPER() ossPrintXPER()
int ossPrintPER(OssGlobal *world,
            int            *pdunum,
            OssBuf         *input,
            void          **output,
            long            flags,
            UserPrintPer userPrintPer); 
int ossPrintXPER(struct ossGlobal *world,
            int                   *pdunum,
            OssBuf                *input,
            void                 **output,
            long                   flags,
            UserPrintPer userPrintPer)

Description

The ossPrintPER() and ossPrintXPER() functions take a PER encoded PDU from "input" and print to "world->asn1out" a well-commented description of the encoded data. The generated output identifies the name and value of each component of the PER-encoded message, the exact location within the input buffer of the value, its length, the number of pad bits, how extension addition values are constructed, and so on. The functions support both variants of PER, ALIGNED and UNALIGNED.

The "flags" argument allows you to customize the generated output.

The "userPrintPer" argument allows you to customize the printout and analyze the PER encodings by accepting as input your own print function.

NOTE: The OSS PER Encoding Analyzer (PrintPER) is available for common platforms like Windows, Linux, and Solaris, and might not be available for your embedded system port. If you are interested in PrintPER for your platform, contact Sales ‹info@oss.com›.

The ossPrintPER() function prints a description of the PER encoded data in valid ASN.1 value notation along with additional comments.

The ossPrintXPER() function prints a description of the PER encoded data as a valid XML document (see http://www.w3.org/XML/), according to a predefined schema in DTD form (to learn how a valid DTD file appears, see the ossprintxper.dtd file).

Example

The following example shows the output of the ossPrintPER() and ossPrintXPER() functions. The output may vary depending on the "flags" setting.

ASN.1 type and value definition:

Foo ::= SEQUENCE {
   a    INTEGER (250..253) OPTIONAL,
   ...,
   [[
    b    NumericString (SIZE(3)),
    c    INTEGER
   ]],
   ...,
   d     BOOLEAN OPTIONAL
}

value Foo ::= { a  253,  b  "123", c 20000, d TRUE }

value is encoded in PER ALIGNED (shown in hexadecimal) as: FC040523 40024E20.

ossPrintPER() output:

value Foo ::= 
   {
   --extension flag: <.1>
   --preamble: <11>
   a 253,
   --contents: <11>
   d TRUE,
   --contents: <1>
   --extension count: <00.00000>
   --extension preamble: <1>
   --[[
     --padding: <00>
     --extension length: <.00000101>
     b "123",
      --contents: <.0010 0011 .0100>
     c 20000
      --padding: <0000>
	  --length: <.00000010>
	  --contents: <.01001110 .00100000>
   --]]
}
--TOTAL LENGTH: 8,0 

ossPrintXPER() output:

<PDU name="Foo">
  <Value type="seqset">
    <Details>
      <metadata type="TYPE INFORMATION">SEQUENCE</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0"/>
      <encoding type="extension flag">[.1]</encoding>
      <encoding type="preamble">
        [11]
        <description>bit #0 = 1: 'a' is present</description>
        <description>bit #1 = 1: 'd' is present</description>
      </encoding>
    </Details>
    <Field name="a">
      <Value>
        253
        <Details>
          <metadata type="TYPE INFORMATION">INTEGER (250..253)
                                                        OPTIONAL</metadata>
          <metadata type="FULL NAME">a</metadata>
          <metadata type="LEVEL NUMBER">1.1</metadata>
          <position offset="0,3" length="0,2"/>  
          <encoding type="contents">[11]</encoding>
        </Details>
      </Value>
    </Field>
    <Field name="d">
      <Value>
        TRUE
        <Details>
          <metadata type="TYPE INFORMATION">BOOLEAN OPTIONAL</metadata>
          <metadata type="FULL NAME">d</metadata>
          <metadata type="LEVEL NUMBER">1.2</metadata>
          <position offset="0,5" length="0,1"/>
          <encoding type="contents">[1]</encoding>
        </Details>
      </Value>
    </Field>
    <Extensions>
      <ExtensionsPreamble>
        <encoding type="extension preamble size flag">
          [0]
          <description>(preamble size is EQUAL or LESS than 64)</description>
        </encoding>
        <encoding type="extension preamble length">
          [0.00000]
          <description>(decoded as 1)</description>
        </encoding>
        <encoding type="extension preamble">
          [1]
          <description>bit #0 = 1: version brackets that contain: 'b' and 'c'
                                                     are present</description>
        </encoding>
      </ExtensionsPreamble>
      <Extension brackets="yes">
        <ExtensionHeader>
          <encoding type="padding">[00]</encoding>
          <encoding type="extension length">
            [.00000101]
            <description>(decoded as 5)</description>
          </encoding>
        </ExtensionHeader>
        <Field name="b">
          <Value>
            "123"
            <Details>
              <metadata type="TYPE INFORMATION">NumericString
                                                     (SIZE(3))</metadata>
              <metadata type="FULL NAME">b</metadata>
              <metadata type="LEVEL NUMBER">1.3</metadata>
              <position offset="3,0" length="1,4"/>
              <encoding type="contents">[.0010 0011 .0100]</encoding>
            </Details>
          </Value>
        </Field>
        <Field name="c">
          <Value>
            20000
            <Details>
              <metadata type="TYPE INFORMATION">INTEGER</metadata>
              <metadata type="FULL NAME">c</metadata>
              <metadata type="LEVEL NUMBER">1.4</metadata>
              <position offset="4,4" length="3,4"/>
              <encoding type="padding">[0000]</encoding>
              <encoding type="length">
                [.00000010]
                <description>(decoded as 2)</description>
              </encoding>
              <encoding type="contents">[.01001110 .00100000]</encoding>
            </Details>
          </Value>
        </Field>
      </Extension>
    </Extensions>
  </Value>
  <TotalLength bytecount="8"/>
</PDU>

Conventions

The ossPrintPER() function generates ASN.1 comments.

The ossPrintXPER() function generates comments that are represented as XML tags and attributes.

The comments act as a bridge between the decoded PDU (the "top" level) and its PER encoding (the "low" level). Although some of the comments may seem superfluous (for example, offsets or absolute references), they make the output more complete. There are two main groups of comments:

  • Comments that provide a "bridge" for the PER encoding.
  • Comments that are considered to be "additional information", and that do not contain any reference to the encoding.

The first group of comments is best explained by understanding how the encoder works. The encoded data is divided into logical groups of bits. Each group has its own particular significance. The encoding of ASN.1 values may contain any number of groups, depending on the type of the value. The ossPrintPER() | ossPrintXPER() function recognizes the groups during decoding and prints its description and contents as comments.

The ossPrintPER() function prints the comments in the following format:

--<name_of_group>: <bits_in_encoding> 

name_of_group is the name of a classification of groups of bits listed below.

bits_in_encoding shows the content of the current group. The content is taken from a real encoding and can be presented in two possible formats described below.

The first format is a pure binary format. The content of the group of bits is shown as a sequence of bits enclosed in less-than ("<") and greater-than (">") signs.

The second format is a combination of hex and binary, where full bytes are written in hex format and leading and trailing bits are written between "<" and ">" signs.

Example

a) <1010.1 11111 11.000>       - "binary" format;
b) <1010>.ff<.000>             - "hex/binary" format;

The period (".") indicates the boundary between whole bytes. The space (" "), in "binary" format, indicates the boundary between logical parts of data in the encoding.

The ossPrintXPER() function prints groups of bits in the following format:

<encoding type="name_of_group">[bits_in_encoding]</encoding>

In the hex/binary format, ossPrintXPER() prints full bytes in hex format, and leading and trailing bits between brackets ("[" and "]"), and not angle brackets, because the latter are reserved symbols in XML.

The following section describes the bit groups:

"contents"
The bits define an encoded value that cannot be divided into smaller groups (except for TIME types, when the OSS_NO_TIME_DETAILS flag is set).
"length"
The bits define the length of the block that follows.
"padding"
The padding bits are inserted occasionally to restore octet alignment. However, this does not affect the data that is decoded. Note that the bits are inserted only in the PER ALIGNED variant. In the UNALIGNED variant, the "PDU padding" bits and the "trailing" bits are inserted.
"extension length"
This type is similar to the length type; however, the "extension length" type defines a length of an extension addition that follows.
"extension range"
Contains one bit that is used whenever extension markers are used. It determines if the value is within the range of the extension root or not.
"choice index"
The bits define the index of choice selected.
"PDU padding"
This type is similar to the "padding" type; however, the PDU padding type is used to restore the octet alignment and appears after the encoded PDU. It is supported for PER ALIGNED and UNALIGNED variants.
"type of real number encoding"
Contains bits that define a type of REAL number encoding. This type and the following three types are used only with REAL type encoding.
"exponent length"
Contains an encoded length of the exponent.
"exponent"
Contains an encoded exponent.
"mantissa"
Contains an encoded mantissa.
"unknown extension"
Contains a reference to a skipped unknown extension.
"extension flag"
Contains one bit that is used only for encoding extensible constructed types. The bit is set to TRUE when extension additions are present.
"extension count"
Contains a number of extension additions that are present in the constructed type that is decoded.
"extension preamble"
Contains a bit mask of the extension additions present in the constructed type that is decoded.
"preamble"
Contains a bit mask of optional fields present in the constructed type that is decoded.
"trailing bits"
This type is similar to the "PDU padding" and the "padding" types; however, the "trailing bits" type restores octet alignment, and the padding appears after a block that corresponds to an extension addition.

For more information, see the X.6911 standards.

The second group of comments, namely "additional information" comments, are printed when the corresponding flags are specified and appear in capital letters. The additional information helps you understand the structure of the encoded output, and is especially useful when decoding a complicated PDU. Also, the comments are useful when parsing an output or searching for any particular fields. The following types are considered "additional information" comments:

  • The offset and the length
  • The absolute reference and the level number
  • The information about the ASN.1 type notation
  • The "useful" comments related to parts of the encoding like "length", "preamble", "choice index", "extension count". The comments describe the meaning of bits.

Example

The following example shows the generated output of the ossPrintPER() that contains "additional information" comments. Note that the ASN.1 declarations and the encoding rules are the same as in the first example:

value Foo ::=
{
   --TYPE INFORMATION: SEQUENCE
   --LEVEL NUMBER: 1
   --OFFSET: 0,0
   --extension flag: <.1>
   --preamble: <11>
     --bit #0 = 1: 'a' is present
     --bit #1 = 1: 'd' is present
   a 253,
     --TYPE INFORMATION: INTEGER (250..253) OPTIONAL
     --FULL NAME: a
     --LEVEL NUMBER: 1.1
     --OFFSET: 0,3; LENGTH: 0,2
     --contents: <11>
   d TRUE,
     --TYPE INFORMATION: BOOLEAN OPTIONAL
     --FULL NAME: d
     --LEVEL NUMBER: 1.2
     --OFFSET: 0,5; LENGTH: 0,1
     --contents: <1>
   --extension count: <00.00000> (decoded as 1)
   --extension preamble: <1>
     --bit #0 = 1: version brackets that contain:
       --'b'
       --'c'
     --is present
   --[[
     --padding: <00>
     --extension length: <.00000101> (decoded as 5)
     b "123",
       --TYPE INFORMATION: NumericString (SIZE(3))
       --FULL NAME: b
       --LEVEL NUMBER: 1.3
       --OFFSET: 3,0; LENGTH: 1,4
       --contents: <.0010 0011 .0100>
     c 20000
       --TYPE INFORMATION: INTEGER
       --FULL NAME: c
       --LEVEL NUMBER: 1.4
       --OFFSET: 4,4; LENGTH: 3,4
       --padding: <0000>
       --length: <.00000010> (decoded as 2)
       --contents: <.01001110 .00100000>
     --]]
   }
   --TOTAL LENGTH: 8,0

The output does not change when using the ossPrintXPER() function (see the first example), because this function always produces additional details within its output.

The type of "additional information" is determined based on the following:

  • The offset shows the first bit of a part of an encoding corresponding to a particular ASN.1 value. The offset value is equal to the number of bits already read.
  • The length shows a length (in bits) of the above part. Note that the length is considered "unknown" for constructed types.

The following example shows how the absolute reference and level number are counted.

SamplePDU ::= SET {
   a  SEQUENCE OF BOOLEAN, 
   b  SEQUENCE {
   c  BOOLEAN
   }
}

value SamplePDU ::= { a { TRUE }, b { c TRUE }}

value is encoded in PER ALIGNED into two bytes: 01C0.

ossPrintPER() ossPrintXPER()
	value SamplePDU ::= 
	{
	  --TYPE INFORMATION: SET
	  --OFFSET: 0,0
	  --LEVEL NUMBER: 1
	  a
	  {
	    --TYPE INFORMATION: SEQUENCE OF 
	    --OFFSET: 0.0
	    --FULL NAME: a
	    --LEVEL NUMBER: 1.1
	    --length: <.00000001> (decoded as 1)
	     TRUE
	      --TYPE INFORMATION: BOOLEAN 
	      --OFFSET: 1,0; LENGTH: 0,1 
	      --FULL NAME: a.[0]
	      --LEVEL NUMBER: 1.1.1
	      --contents: <.1>
	  },
	  b
	  {
	    --TYPE INFORMATION: SEQUENCE
	    --OFFSET: 1,1
	    --FULL NAME: b
	    --LEVEL NUMBER: 1.2
	    c TRUE
	      --TYPE INFORMATION: BOOLEAN 
	      --OFFSET: 1,1; LENGTH: 0,1 
	      --FULL NAME: b.c
	      --LEVEL NUMBER: 1.2.1
	      --contents: <1>
	  }
	}
	--PDU padding: <000000>
	--TOTAL LENGTH: 2,0
<PDU name="SamplePDU">
  <Value type="seqset">
    <Details>
      <metadata type="TYPE INFORMATION">SET</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0"/>
    </Details>
    <Field name="a">
      <Value type="seqsetof">
        <Details>
          <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
          <metadata type="FULL NAME">a</metadata>
          <metadata type="LEVEL NUMBER">1.1</metadata>
          <position offset="0,0"/>
          <encoding type="length">
            [.00000001]
            <description>(decoded as 1)</description>
          </encoding>
        </Details>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">a.[0]</metadata>
            <metadata type="LEVEL NUMBER">1.1.1</metadata>
            <position offset="1,0" length="0,1"/>
            <encoding type="contents">[.1]</encoding>
          </Details>
        </Value>
      </Value>
    </Field>
    <Field name="b">
      <Value type="seqset">
        <Details>
          <metadata type="TYPE INFORMATION">SEQUENCE</metadata>
          <metadata type="FULL NAME">b</metadata>
          <metadata type="LEVEL NUMBER">1.2</metadata>
          <position offset="1,1"/>
        </Details>
        <Field name="c">
          <Value>
            TRUE
            <Details>
              <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
              <metadata type="FULL NAME">b.c</metadata>
              <metadata type="LEVEL NUMBER">1.2.1</metadata>
              <position offset="1,1" length="0,1"/>
              <encoding type="contents">[1]</encoding>
            </Details>
          </Value>
        </Field>
      </Value>
    </Field>
  </Value>
  <encoding type="PDU padding">[000000]</encoding>
  <TotalLength bytecount="2"/>
</PDU>

Note that "FULL NAME" and "LEVEL NUMBER" are increased when a constructed type is decoded.

The information about the ASN.1 type notation that is printed as "TYPE INFORMATION" is not equivalent to the primary ASN.1 declaration. Only general information is printed about base constraints and default values. This information helps you understand how a value of an actual ASN.1 type is decoded.

The "useful" comments can be divided into two groups:

  • A preamble description
  • A presentation of certain groups of bits that define any value that can be shown as an integer number

The following example is taken from the above ossPrintPER() and ossPrintXPER() output:

ossPrintPER() ossPrintXPER()
--extension preamble: <1>
--bit #0 = 1: version brackets that contain:
--'b'
--'c'
--are present
<encoding type="extension preamble">
          [1]
<description>bit #0 = 1: version brackets that contain:
           'b'and 'c' are present</description>
</encoding>

Note that these comments are printed only for the "preamble" and the "extension preamble" bits.

The second group of "useful" comments, namely a presentation of certain groups of bits that define any values that can be shown as an integer number, contains the following: "length", "extension length", "choice index", "exponent length", "extension count". Here is an output fragment generated by ossPrintPER() and ossPrintXPER():

ossPrintPER() ossPrintXPER()
c 20000
     --TYPE INFORMATION: INTEGER
     --FULL NAME: c
     --LEVEL NUMBER: 1.4
     --OFFSET: 4,4; LENGTH: 3,4
     --padding: <0000>
     --length: <.00000010> (decoded as 2) <--
     --contents: <.01001110 .00100000>
<Field name="c">
    <Value>
       20000
        <Details>
        <metadata type="TYPE INFORMATION">INTEGER</metadata>
        <metadata type="FULL NAME">c</metadata>
        <metadata type="LEVEL NUMBER">1.4</metadata>
        <position offset="4,4" length="3,4"/>
        <encoding type="padding">[0000]</encoding>
        <encoding type="length">
        [.00000010]
        <description>(decoded as 2)</description>
        </encoding>
        <encoding  type="contents">[.01001110.00100000]</encoding>
       </Details>
     </Value>
   </Field>

Arguments

"world"
The type is OssGlobal. It is the first argument for all OSS API functions. For a detailed description, see the OSSAPI.TXT file.
"pdunum"
Instructs the ossPrintPER() | ossPrintXPER() function to decode and print a certain PDU. The ASN.1 compiler generates a manifest constant that uniquely identifies each PDU. The "pdunum" argument must be one of the manifest constants. A PDU is any ASN.1 typereference that is not referenced in the definition of another typereference, or any typereference that you explicitly mark as a PDU using the PDU directive.
"input"
Address of a variable of type OssBuf, which identifies the address and length of the data to be decoded and printed. If the ASN.1 compiler option -compat decoderUpdatesInputAddress is specified, the OssBuf field length is modified by ossPrintPER() | ossPrintXPER() to reflect the number of bytes in the input buffer that remain to be decoded. The OssBuf field "value" is modified to point to the byte immediately after the last value that was decoded. This description applies to the default memory manager, the "length" and "value" fields may be set differently if you are using a different memory manager, such as the file memory manager.
"output"
Address of a pointer where the ossPrintPER() | ossPrintXPER() will place the address of the decoded data. If the address pointed to by output is NULL, the ossPrintPER() | ossPrintXPER() will allocate all memory required to hold the decoded data, otherwise it will use the preallocated buffer identified by the address pointed to by "output". If you preallocate the output buffer (in which case "*output" is non-NULL) prior to calling ossPrintPER() | ossPrintXPER(), then you must call ossSetDecodingLength() to set the length of the buffer pointed to by *output. In general, this argument is similar to the one used for the ossDecode() function; however, ossDecode() does not allow output to be NULL. If output is NULL, ossPrintPER() | ossPrintXPER() will print a description of the encoded data without storing the decoded data into the output area.
"userPrintPer"
Allows you to provide your own print function to format and print the data.
"flags"
Determines how to format the description of the encoded data. The valid flags are listed below:
OSS_ASN_ONLY
OSS_HEXBYTES
OSS_SEPARATE_ASN
OSS_NOBRACES
OSS_PRINT_ABSREF
OSS_PRINT_OFFSET
OSS_PRINT_NUMBERS
OSS_PRINT_TYPE_INFO
OSS_PRINT_COMMENTS
OSS_NOPRINT
OSS_PRINT_XML_HEADER                (added in version 2.0)
OSS_PRE_2_0_COMPAT                  (added in version 2.0)
OSS_NO_TIME_DETAILS                 (added in version 8.4)
OSS_NO_CONTAINED_TYPE               (added in version 8.4)
OSS_NO_CONTAINED_TYPE_TRACE         (added in version 8.4)
OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS (added in version 10.1)
OSS_PRE_10_1_COMPAT                 (added in version 10.1)

The flags argument does not have default settings. The following ASN.1 notation shows the impact of each flag:

Foo2 ::= SET {
   a  SEQUENCE { 
       b [1] BOOLEAN OPTIONAL,
       c [2] BOOLEAN
       }
}

value Foo2 ::= { a { c TRUE}}

value is encoded in PER Aligned (shown in hexadecimal) as: 40 (into one byte).

The default output generated by ossPrintPER() and ossPrintXPER() for this encoding is:

ossPrintPER() ossPrintXPER()
value Foo2 ::= 
{
   a
   {
   --preamble: <.0>
   c TRUE
   --contents: <1>
   }
}

--PDU padding: <000000>
<PDU name="Foo2">
  <Value type="seqset">
    <Details>
      <metadata type="TYPE INFORMATION">SET</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0"/>
    </Details>
    <Field name="a">
      <Value type="seqset">
        <Details>
          <metadata type="TYPE INFORMATION">SEQUENCE</metadata>
          <metadata type="FULL NAME">a</metadata>
          <metadata type="LEVEL NUMBER">1.1</metadata>
          <position offset="0,0"/>
          <encoding type="preamble">
            [.0]
            <description>bit #0 = 0: 'b' is absent</description>
          </encoding>
        </Details>
        <Field name="c">
          <Value>
               TRUE
            <Details>
              <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
              <metadata type="FULL NAME">a.c</metadata>
              <metadata type="LEVEL NUMBER">1.1.1</metadata>
              <position offset="0,1" length="0,1"/>
              <encoding type="contents">[1]</encoding>
            </Details>
          </Value>
        </Field>
      </Value>
    </Field>
  </Value>
  <encoding type="PDU padding">[000000]</encoding>
  <TotalLength bytecount="1"/>
</PDU> 

Note that the OSS_ASN_ONLY and OSS_SEPARATE_ASN flags are always removed, and the OSS_NOBRACES, OSS_PRINT_ABSREF, OSS_PRINT_OFFSET, OSS_PRINT_NUMBERS, OSS_PRINT_TYPE_INFO, and OSS_PRINT_COMMENTS flags are always added by default. Only the OSS_HEXBYTES, OSS_NOPRINT, OSS_PRE_2_0_COMPAT, OSS_NO_TIME_DETAILS, OSS_PRE_10_1_COMPAT, and OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS flags affect the ossPrintXPER() output.

The following table describes the available flags.

Flag Description
OSS_ASN_ONLY
Prints only decoded values, without a hex/binary representation of the encoded data and without truncated elements of SEQUENCE OF or SET OF. For example:
value Foo2 ::= 
{
   a
   {
      c TRUE
   }
 }
Note that all details about the encoding are skipped. This flag does not affect the ossPrintXPER() output.

OSS_HEXBYTES
Prints encoded data in hex/binary format rather than pure binary format. Considering the above notation, note that this flag has no impact on the output:
value Foo2 ::= 
{
   a
   {
   --preamble: <.0>
   c TRUE
   --contents: <1>
   }
}
--PDU padding: <000000>
The following example shows the impact of the flag:
Foo3 ::= INTEGER
value Foo3 = 100
Output generated by ossPrintXPER(), when OSS_HEXBYTES is set:
value Foo3 ::= 100
--length: .01
--contents: .64

and without OSS_HEXBYTES:

value Foo3 ::= 100
--length: <.00000001>
--contents: <.01100100>
Output generated by ossPrintXPER() when the OSS_HEXBYTES flag is set:
<PDU name="Foo3">
  <Value>
    100
    <Details>
      <metadata type="TYPE INFORMATION">INTEGER</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0" length="2,0"/>
      <encoding type="length">
        .01
        <description>(decoded as 1)</description>
      </encoding>
      <encoding type="contents">.64</encoding>
    </Details>
  </Value>
  <TotalLength bytecount="2"/>
</PDU>
Note that when the OSS_HEXBYTES flag is specified, full bytes are written in hex format, and leading and trailing bits are written between "<" and ">" (between "[" and "]" for ossPrintXPER). The period (".") marks the boundary between whole bytes, and the space (" ") marks the boundary between the logical parts of the data in the encoding.
OSS_SEPARATE_ASN
Prints an additional line after each block related to each ASN.1 value.
OSS_NOBRACES
Will not print braces in the description (in general, braces are printed for constructed types). If this flag is specified, the printed information will not be in valid ASN.1 value notation. This flag does not affect the ossPrintXPER() output. For example:
value Foo2 ::= 
a
--preamble: <.0>
c TRUE
--contents: <1>
OSS_PRINT_ABSREF
Prints the names of the fields of decoded values with the fully qualified reference (name1.name2.name3...). This flag does not affect the ossPrintXPER() output.
value Foo2 ::=
{
a
   {
   --FULL NAME: a
   --preamble: <.0>
   c TRUE
      --FULL NAME: a.c
      --contents: <1>
   }
}
--PDU padding: <000000>
OSS_PRINT_OFFSET
Prints the length and offset of the encoded data corresponding to the ASN.1 value that is decoded. Note that length is considered as unknown for constructive types (SET, SEQUENCE, CHOICE, SET OF, and SEQUENCE OF). This flag does not affect the ossPrintXPER() output. For example:
value Foo2 ::= 
{
 --OFFSET: 0,0
 a
   {
    --OFFSET: 0.0
    --preamble: <.0>
    c TRUE
    --OFFSET: 0,1; LENGTH: 0,1
    --contents: <1>
   }
}
--PDU padding: <000000>
--TOTAL LENGTH: 1,0
OSS_PRINT_NUMBERS
Prints an additional structurized level number to identify each logical component part of the output. This flag does not affect the ossPrintXPER() output. For example:
 
value Foo2 ::= 
{
--LEVEL NUMBER: 1
a
   {
   --LEVEL NUMBER: 1.1
   --preamble: <.0>
   c TRUE
   --LEVEL NUMBER: 1.1.1
   --contents: <1>
   }
}
--PDU padding: <000000>
OSS_PRINT_TYPE_INFO
Prints information about the ASN.1 type notation. For example:
value Foo2 ::= 
{
--TYPE INFORMATION: SET
a
   {
   --TYPE INFORMATION: SEQUENCE
   --preamble: <.0>
   c TRUE
   --TYPE INFORMATION: BOOLEAN
   --contents: <1>
   }
}
--PDU padding: <000000>
The second example is based on the following ASN.1 declaration:
Foo ::= SEQUENCE {
a    INTEGER (250..253) OPTIONAL,
...,
[[
    b   NumericString (SIZE(3)) DEFAULT "000",
    c   INTEGER DEFAULT 0
]],
...,
    d   BOOLEAN OPTIONAL
}

value Foo ::= { a  253,  b  "123", c 20000, d TRUE }
value is decoded with PER ALIGNED into 8 bytes: F80805C8 D0024E20.
The output generated by the ossPrintPER() is:
value Foo ::= 
{
--TYPE INFORMATION: SEQUENCE
--extension flag: <.1>
--preamble: <1>
a 253,
--TYPE INFORMATION: INTEGER (250..253)
--contents: <11>
d TRUE,
--TYPE INFORMATION: BOOLEAN OPTIONAL
   --contents: <1>
   --extension count: <000.0000>
   --extension preamble: <1>
   --[[
   --padding: <000>
   --extension length: <.00000101>
   --preamble: <.11>
   b "123",
   --TYPE INFORMATION: NumericString (SIZE(3)) DEFAULT "000"
   --contents: <0010 00.11 0100>
   c 20000
   --TYPE INFORMATION: INTEGER DEFAULT 0
   --padding: <00>
   --length: <.00000010>
   --contents: <.01001110 .00100000>
   --]]
}
This flag does not affect the ossPrintXPER() output.
OSS_PRINT_COMMENTS
Prints "useful" comments. For example:
value Foo2 ::= 
{
   a	
   {
   --preamble: <.0>
   -bit #0 = 0: 'b' is absent
   c TRUE
   --contents: <1>
   }
}

--PDU padding: <000000>
The second example is based on the following ASN.1 declaration:

Foo ::= SEQUENCE {
a    INTEGER (250..253) OPTIONAL,
...,
[[
    b    NumericString (SIZE(3)) DEFAULT "000",
    c    INTEGER DEFAULT 0
]],
...,
    d   BOOLEAN OPTIONAL
}

value Foo ::= { a  253,  b  "123", c 20000, d TRUE }
This value is decoded with PER UNALIGNED into 8 bytes: F80805C8 D0024E20.
The output generated by the ossPrintPER() is:
value Foo ::= 
{
  --extension flag: <.1>
  --preamble: <1>
  --bit #0 = 1: 'd' is present
  a 253,
  --contents: <11>
  d TRUE,
  --contents: <1>
  --extension count: <000.0000> (decoded as 1)
  --extension preamble: <1>
  --bit #0 = 1: version brackets that contain:
  --'b'
  --'c'
  --is present
  --[[
  --extension length: <000.00101> (decoded as 5)
  --preamble: <11>
  --bit #0 = 1: 'b' is present
  --bit #1 = 1: 'c' is present
  b "123",
  --contents: <0.010 0011 0.100>
  c 20000
  --length: <00000.010> (decoded as 2)
  --contents: <01001.110 00100.000>
  --trailing bits: <00>
  --]]
}
--PDU padding: <000>
This flag does not affect the ossPrintXPER() output.
OSS_NOPRINT
When specified, ossPrintPER() | ossPrintXPER() will not print anything. Instead, the data will be prepared in a special format. This flag is useful when you define your own API function to print or/and analyze the encoded data.

OSS_PRINT_XML_HEADER
Generates the following header into the produced XML document:
<?xml version="1.0"?>
<!DOCTYPE PDU SYSTEM "ossprintxper.dtd">
This flag does not affect the ossPrintPER() output.
OSS_PRE_2_0_COMPAT
Disables the passing of post-v1.x records to the userPrintPer() callback function. If this flag is set, the behavior of ossPrintPER() is the same as that of version 1.0. In other words, this flag provides backward compatibility for applications written using the v1.0 userPrintPer() records with a version 2.0 ossPrintPER() library.
NOTE: Starting with version 8.4, the flag affects TIME types as follows: the only record passed is for a TIME value with "typeId" OSS_ASN1_TYPE_PPR. The record has "timeEncId" and "decodedContent" fields. It contains the simplest record with encodedContent.
For example:
MIX ::= TIME ((SETTINGS "Year=Basic") | (SETTINGS "Year=Proleptic"))

        value MIX ::= "23:59,99999+12:59"

000.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
    addition->typeInformation: "TIME"
    addition->optional:        No
001.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_MIXED
    typeName:                  "MIX"
    addition->typeInformation: "TIME"
    addition->optional:        No
    decodedContent:            "23:59.99999+12:59"
    encodedContent:            78 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   78 bits
        }
      }
002.typeId:                    OSS_ADDITIONS_TYPE_PPR
    timeEncId:                 OSS_TEK_NOTIME
    encodedContent:            2 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_PDU_PADDING_TYPE_PPR
          encodedContent:   2 bits
        }
      }

NOTE: Starting with version 8.4, the flag is effective for types with a contents constraint applied (the CONTAINING keyword is present) as follows: the only record passed is for a CONTAINING type value with "typeId" OSS_ASN1_TYPE_PPR and with "decodedContent" corresponding to the value of an OCTET/BIT STRING with CONTAINING. No records with information about the contained type value encoding are passed.
Example:

T ::= BIT STRING (CONTAINING INTEGER)
v T ::= CONTAINING 33

000.typeId                   : OSS_ASN1_TYPE_PPR
  decodedContent             : '00000001 00100001'B
  typeName                   : "T"
  encodedContent             : .10 ...
  length                     : 24
  numberOfSimplestRecord     : 2
  simplestRecords            : 
    {                          
      {                          
        typeId                     : OSS_LENGTH_TYPE_PPR
        length                     : 8
        encodedContent             : .10 ...
        addition->comments         : "(decoded as 16)"
      },                         
      {                          
        typeId                     : OSS_CONTENTS_TYPE_PPR
        length                     : 16
        encodedContent             : .01 ...
      }                          
    }                          
  bitsPerContentUnit         : 8
  offset                     : 0
  addition->typeInformation  : "BIT STRING (CONTAINING ...)"
OSS_NO_TIME_DETAILS
Disables the passing of the inner records that corresponds to the internal structure of the TIME type defined by Amendment 2 to ITU-T Rec. X.691:2002. Only the records of OSS_TIME8601_BEGIN_PPR and OSS_TIME8601_FINISHED_PPR types are passed to the user. The decoded value ("decodedContent") and the whole TIME value encoding ("encodedContent") are passed in the OSS_TIME8601_FINISHED_PPR record. This flag is useful to reduce the number of passed records.
For example:
MIX ::= TIME ((SETTINGS "Year=Basic") | (SETTINGS "Year=Proleptic"))

        value MIX ::= "23:59,99999+12:59"

000.typeId:                    OSS_PDU_BEGIN_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
001.typeId:                    OSS_TIME8601_BEGIN_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
    addition->typeInformation: "TIME"
    addition->optional:        No
002.typeId:                    OSS_TIME8601_FINISHED_PPR
    timeEncId:                 OSS_TEK_MIXED
    typeName:                  "MIX"
    addition->typeInformation: "TIME"
    addition->optional:        No
    decodedContent:            "23:59.99999+12:59"
    encodedContent:            78 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   78 bits
        }
      }
003.typeId:                    OSS_ADDITIONS_TYPE_PPR
    timeEncId:                 OSS_TEK_NOTIME
    encodedContent:            2 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_PDU_PADDING_TYPE_PPR
          encodedContent:   2 bits
        }
      }
004.typeId:                    OSS_TOTAL_LENGTH_PPR
    timeEncId:                 OSS_TEK_NOTIME
005.typeId:                    OSS_PDU_END_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
OSS_NO_CONTAINED_TYPE
Is used for types with a contents constraint applied. It disables the printing of contained type information. Only records of OSS_CONTAINING_TYPE_BEGIN_PPR, OSS_ASN1_TYPE_PPR, and OSS_CONTAINING_TYPE_END_PPR types are passed to the user. The decoded value ("decodedContent") and the encoding for the value of OCTET/BIT STRING with CONTAINING are passed in the OSS_ASN1_TYPE_PPR record.
For example:
T ::= BIT STRING (CONTAINING INTEGER)
v T ::= CONTAINING 33

000.typeId                   : OSS_PDU_BEGIN_PPR
  typeName                   : "T"
001.typeId                   : OSS_CONTAINING_TYPE_BEGIN_PPR
  typeName                   : "T"
002.typeId                   : OSS_ASN1_TYPE_PPR
  possibleLast               : TRUE
  decodedContent             : '00000001 00100001'B
  typeName                   : "T"
  encodedContent             : .10 ...
  length                     : 24
  numberOfSimplestRecord     : 2
  simplestRecords            : 
    {                          
      {                          
        typeId                     : OSS_LENGTH_TYPE_PPR
        length                     : 8
        encodedContent             : .10 ...
        addition->comments         : "(decoded as 16)"
      },                         
      {                          
        typeId                     : OSS_CONTENTS_TYPE_PPR
        length                     : 16
        encodedContent             : .01 ...
      }                          
    }                          
  bitsPerContentUnit         : 8
  offset                     : 0
  addition->typeInformation  : "BIT STRING (CONTAINING ...)"
003.typeId                   : OSS_CONTAINING_TYPE_END_PPR
  typeName                   : "T"
004.typeId                   : OSS_TOTAL_LENGTH_PPR
  length                     : 3
005.typeId                   : OSS_PDU_END_PPR
  typeName                   : "T"
OSS_NO_CONTAINED_TYPE_TRACE
Is used for types with a contents constraint applied. It disables the printing of comments with trace data for a contained type encoded by any non-PER rule. This flag affects only the output of the ossPrintPER() and ossPrintXPER() functions and does not affect records passed to the user.
cer OBJECT IDENTIFIER ::=
    {joint-iso-itu-t(2) asn1(1) ber-derived(2) canonical-encoding(0)}
T ::= BIT STRING (CONTAINING INTEGER ENCODED BY cer)
v T ::= CONTAINING 33
The ossPrintPER() output without the OSS_NO_CONTAINED_TYPE_TRACE flag:
value T ::=  '00000010 00000001 00100001'B
  --TYPE INFORMATION: BIT STRING (CONTAINING ... ENCODED BY CER)
  --LEVEL NUMBER: 1
  --OFFSET: 0,0; LENGTH: 4,0
  --length: .18 (decoded as 24)
  --contents: .02.01.21
  /*
  INTEGER: tag = [UNIVERSAL 2] primitive; length = 1
    33
  */
--TOTAL LENGTH: 4,0
Output with the OSS_NO_CONTAINED_TYPE_TRACE flag set:
value T ::=  '00000010 00000001 00100001'B
  --TYPE INFORMATION: BIT STRING (CONTAINING ... ENCODED BY CER)
  --LEVEL NUMBER: 1
  --OFFSET: 0,0; LENGTH: 4,0
  --length: .18 (decoded as 24)
  --contents: .02.01.21
--TOTAL LENGTH: 4,0
OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS
Is used for SEQUENCE OF and SET OF types with the OSS.Truncate directive applied. If the flag is set, the records for each of the truncated elements are not passed to the user, and only records of OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR and OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR types are passed. Additionally, the information about the truncated elements is not printed by ossPrintPER() and ossPrintXPER().
OSS_PRE_10_1_COMPAT
Disables the passing of post-v10.x records to the "userPrintPer()" callback function. If this flag is set, the behavior of ossPrintPER() is the same as in previous versions: the truncated elements are processed and not skipped. In other words, this flag provides backward compatibility for applications written using the v8.4 "userPrintPer()" records with a version 10.1 ossPrintPER() library.

ossPrintPER( ) | ossPrintXPER( ) API

The ossPrintPER() | ossPrintXPER() function allows you to access the formatted data that is received after decoding, so you can format and print the data by providing your own API. This is especially useful when analyzing the PER encoding for any purposes.

The "userPrintPer" argument pertaining to the ossPrintPER() | ossPrintXPER() function is a pointer to your function of the following type:

typedef ossBoolean (DLL_ENTRY_FPTR *_System UserPrintPer) 
                   (struct ossGlobal *world, PrintPerRecord *data);

world is an argument that is common to all API functions.

data is a pointer to the PrintPerRecord structure that contains the data provided by ossPrintPER() | ossPrintXPER(). The structure corresponds to a logical part of encoded data.

Your function is called for every PrintPerRecord filled by ossPrintPER() | ossPrintXPER(). If you want to print the record by ossPrintPER() | ossPrintXPER(), it returns FALSE; otherwise, it returns TRUE.

To better understand the format of the "data" that the ossPrintPER() | ossPrintXPER() function generates, think of it as a tree. Each leaf corresponds to a few bits in the encoding. It defines a place where the bits are stored, as well as the significance of the bits. Other elements of the tree correspond to the decoded ASN.1 values.

In C, the PrintPerRecord structure is defined by the following:

struct PrintPerRecord {
   unsigned char typeId;       /* identifies a significance of the present record */
      ossBoolean possibleLast; /* used only when typeId = OSS_ASN1_TYPE 
      TRUE, if there are any chances that this one is last element of the set/sequence */
      
      void *decodedContent;   /* points to the decoded content in "displayed" format */
      char *typeName;         /* points to the name of corresponding 
ASN.1 type */
       char *fieldName;       /* ... ASN.1 name */
       char *qualifiedName;   /* name1.name2...nameN.fieldName */
       char *qualifiedNameOfTopRecord; 	/* name1.name2...nameN */
       char *qualifiedNumber;           /* for example, 1.2.1.3 */
       char *qualifiedNumberOfTopRecord;  /* for example, 1.2.1 */
       
       unsigned int depth;
       unsigned char *encodedContent; 	/* this pointer defines a first byte of encoding; */ 
       unsigned char encodedBitOffset; 	/* defines a first bit of 									encoding*/
       unsigned long length;            /* length (in bits) */
       
       unsigned int numberOfSimplestRecord;   /* number of simplestRecords */
       struct PrintPerSimplestRecord *simplestRecords; /* array of pointers to logical parts */
       unsigned char bitsPerContentUnit;  /* usually - 8, but could be differ for strings with constraints  specified*/

       unsigned int offset;      /* offset from a start of encoded data */
       ossBoolean allocMem;      /* TRUE if decodedContent points to 
* allocated memory*/

       struct PrintPerRecordAddition *addition;
       unsigned char timeEncId; /* TIME type kind, added in version 8.4 */ 
};
       
       struct PrintPerSimplestRecord {
       /* a significance of fields is same as */
       /* in the PrintPerRecord-structure     */
       unsigned char typeId;
       unsigned long length;
       unsigned char *encodedContent;
       unsigned char encodedBitOffset;
       struct PrintPerSimplestRecordAddition *addition;
       };
       
       
       struct PrintPerSimplestRecordAddition {
          char *comments;
          ossBoolean commentsAllocMem;
          unsigned char *reserved;
          };
          
       struct PrintPerRecordAddition {
       char *typeInformation;
       struct PrintPerRecord *top_level_record;
       ossBoolean optional;
       void *internal_ptr;
       unsigned char *reserved;
 };

Each record corresponds either to a decoded ASN.1 value, or to any logical part of encoded data, which is a set of bits with certain significance in PER encoding. Generally, PrintPerRecord contains a reference to decoded data. The references to encoded data are contained in the array of SimplestPerRecord.

"typeId"
Identifies the type of the present record. The first group contains "high" level types. The typeId of PrintPerRecord must be equal to one of the types listed in the table below:
Type Description
OSS_ASN1_TYPE
The record corresponds to the ASN.1 value.
OSS_CHOICE_BEGIN
The record corresponds to CHOICE values.
OSS_SEQ_SET_BEGIN
The record corresponds to SET or SEQUENCE values.
OSS_CHOICE_FINISHED
OSS_SEQ_SET_FINISHED
Indicates that the decoding of CHOICE, SEQUENCE, or SET is finished.
OSS_VERSION_BRACKETS_BEGIN
OSS_VERSION_BRACKETS_FINISHED
Indicates that the version brackets started and ended. It is useful when using PDUs that contain version brackets.
OSS_VERSION_BRACKETS_FINISHED
Indicates that the version brackets started and ended. It is useful when using PDUs that contain version brackets.
OSS_ADDITIONS_TYPE
This record does not contain useful information; however it contains "simplest" records that correspond to certain bits in encoding.

All the other types identify a PER-specific part of the encoding. This group contains "low"-level references to the PER-encoding. The typeId of a SimplestPerRecord has one of the following types:

OSS_CONTENTS_TYPE 
OSS_LENGTH_TYPE 
OSS_PADDING_TYPE
OSS_EXTENSION_LENGTH_TYPE    
OSS_EXTENSION_RANGE_TYPE     
OSS_CHOICE_INDEX             
OSS_EXTENSION_CHOICE         
OSS_PDU_PADDING_TYPE         
OSS_REALINF_TYPE             
OSS_EXPLENGTH_TYPE          
OSS_EXP_TYPE                
OSS_MANTISSA_TYPE    
OSS_UNKNOWN_EXTENSION       
OSS_LENGTH_LEADING_BIT      
OSS_EXTENSION_FLAG_TYPE     
OSS_EXTENSION_COUNT_TYPE    
OSS_EXTENSION_PREAMBLE_TYPE 
OSS_PREAMBLE_TYPE           

The following new types of records are added in version 2.0:

OSS_PDU_BEGIN
OSS_PDU_END
OSS_EXTENSION_BEGIN
OSS_EXTENSION_FINISHED
OSS_EXTENSIONS_BEGIN
OSS_EXTENSIONS_FINISHED
OSS_TOTAL_LENGTH

Note that if the OSS_PRE_2_0_COMPAT flag is set, the new types of records are not passed to the user. This flag does not affect the format of generated output.

The following new types of records have been added in version 8.4:

Type Description
OSS_TIME8601_BEGIN_PPR
The record corresponds to TIME values, does not contain "simplest" records and useful information about its type. It indicates that the decoding of TIME value has started.
OSS_TIME8601_FINISHED_PPR
It indicates that decoding of TIME values has finished. The record contains timeEncId and decodedContent fields. It may contain "simplest" records with encodedContent, if OSS_NO_TIME_DETAILS flag is set.
OSS_CONTAINING_TYPE_BEGIN_PPR,
OSS_CONTAINING_TYPE_END_PPR
The records correspond to the value of a type with a contents constraint applied. They do not contain "simplestRecords" or other useful information about theirtype. They indicate that the decoding of a CONTAINING type value has started (OSS_CONTAINING_TYPE_BEGIN_PPR) or has finished (OSS_CONTAINING_TYPE_END_PPR).

The following new types of records have been added in version 10.1:

Type Description
OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
The records correspond to the value of a SEQUENCE OF or SET OF type with the OSS.Truncate directive applied; they do not contain "simplestRecords" or other useful information about their type. They indicate that the truncation of elements has started.
Note that all records generated for truncated elements are passed to the user if the new OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS flag is not set. Records of the truncated elements do not change; however, the corresponding values are printed as ASN.1 comments.
OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR
The records correspond to the value of a SEQUENCE OF or SET OF type with the OSS.Truncate directive applied; they do not contain "simplestRecords". They indicate that truncation of elements has finished; the value of the length field contains the number of skipped elements.
Note that if the OSS_PRE_10_1_COMPAT flag is set, the new types of records are not passed to the user.
possibleLast
Is used only when typeId has one of the following values:
OSS_ASN1_TYPE, 
OSS_CHOICE_BEGIN 
or OSS_SEQ_SET_BEGIN. 
If the ASN.1 value that corresponds to this record is contained in any constructed type (SEQUENCE, SET, CHOICE, SET OF, SEQUENCE OF), possibleLast is TRUE if the ASN.1 value is the "last" field; otherwise, it is FALSE.
The ossPrintXPER() function does not maintain this field because it is not required for XML formatting.
*decodedContent
Is used only if typeId is equal to OSS_ASN1_TYPE. It is a pointer to decoded content in "displayed format" or NULL.
*typeName
Is used only if typeId is equal to OSS_ASN1_TYPE. It is a pointer to the name of the ASN.1 type that corresponds to the present value.
char *fieldName
Is used only if typeId is equal to OSS_ASN1_TYPE. It is a pointer to the name of the corresponding field in the ASN.1 notation.
*qualifiedName
Is used only if typeId is equal to OSS_ASN1_TYPE. It is a pointer to the "full" name (absolute reference). It is useful for an identification in the ASN.1 notation.
*qualifiedNameOfTopRecord
Pointer to the "full" name of the top level record. For example, when a SEQUENCE is decoded:
  • You receive a PrintPerRecord which contains the name of the SEQUENCE.
  • You receive a PrintPerRecords corresponding to the SEQUENCE content. The "qualifiedNameOfTopRecord" will point to the name of this SEQUENCE.
*qualifiedNumber
Identifier for the record in the set of the PrintPerRecords (for example "1.12.3").
*qualifiedNumberOfTopRecord
Identifier for the top level record (for example "1.12"). The significance of qualifiedNumber is similar to the significance of qualifiedName.
*encodedContent
The field points to the encoding of the record.
encodedBitOffset
length
Contains a length of encoding (counted in bits). The value of the length is equal to –1 (unknown) for any record that has typeId equal to OSS_CHOICE_BEGIN or OSS_SEQ_SET_BEGIN. In other words, the length is considered unknown for constructed types. Starting with version 10.1 the field contains the number of skipped elements if the "typeId" value is OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR.
numberOfSimplestRecords
Number of "simplestRecords" contained here.
**simplestRecords
Array of pointers to PrintPerSimplestRecords; each PrintPerRecord contains a reference to a set of logical parts in encoding. For example, when decoding an INTEGER type, the encoding has two parts related to its value: one defines a length of the content, and the other is the content. So, you will receive a PrintPerRecord with two "simplestRecords".
bitsPerContentUnit
Usually, it is equal to 8 (one byte).
offset
Defines an offset (in bits) of encoded data related to a present record.
depth
Shows the "indentation" of the present record in the output. This number is incremented by 1 when the decoding of constructive types begins.
For ossPrintXPER(), the indentation depth is calculated cumulatively. Each level of nested XML tags increases the indentation by 1. The example below provides values for "depth" using both ossPrintPER() and ossPrintXPER().
addition
Pointer to the following structure:
struct PrintPerRecordAddition {
    char *typeInformation;
    struct PrintPerRecord *top_level_record;
    ossBoolean optional;
    void *internal_ptr;
    unsigned char *reserved;
};
This pointer is NULL except when the OSS_PRINT_TYPE_INFO flag is specified. Note that the structure is used only for records where typeId is equal to OSS_ASN1_TYPE. The fields of this structure are:
  • typeInformation, that points to the ASN.1 type description in displayed format; starting with version 8.4, the description contains information about TIME types, for example: ""CENTURY-ENCODING" INTEGER (0..99)"
  • typeInformation, that is equal to TRUE if the record corresponds to the OPTIONAL field of SET or SEQUENCE, otherwise, it is FALSE.
timeEncId
Identifies the TIME type for the present record.
OSS_TEK_NOTIME
The TIME type is not defined or the record does not correspond to the TIME value.

The TIME types corresponding to named ASN.1 types of ITU-T Rec. X.691:2002 | ISO/IEC 8825-2:2002 are:

OSS_TEK_CENTURY
OSS_TEK_ANY_CENTURY
OSS_TEK_YEAR
OSS_TEK_ANY_YEAR
OSS_TEK_YEAR_MONT0048
OSS_TEK_ANY_YEAR_MONTH
OSS_TEK_DATE          
OSS_TEK_ANY_DATE      
OSS_TEK_YEAR_DAY      
OSS_TEK_ANY_YEAR_DAY  
OSS_TEK_YEAR_WEEK     
OSS_TEK_ANY_YEAR_WEEK 
OSS_TEK_YEAR_WEEK_DAY 
OSS_TEK_ANY_YEAR_WEEK_DAY
OSS_TEK_HOURS            
OSS_TEK_HOURS_UTC        
OSS_TEK_HOURS_AND_DIFF   
OSS_TEK_MINUTES          
OSS_TEK_MINUTES_UTC      
OSS_TEK_MINUTES_AND_DIFF 
OSS_TEK_TIME_OF_DAY                   
OSS_TEK_TIME_OF_DAY_UTC               
OSS_TEK_TIME_OF_DAY_AND_DIFF          
OSS_TEK_HOURS_AND_FRACTION            
OSS_TEK_HOURS_UTC_AND_FRACTION        
OSS_TEK_HOURS_AND_DIFF_AND_FRACTION   
OSS_TEK_MINUTES_AND_FRACTION          
OSS_TEK_MINUTES_UTC_AND_FRACTION      
OSS_TEK_MINUTES_AND_DIFF_AND_FRACTION 
OSS_TEK_TIME_OF_DAY_AND_FRACTION      
OSS_TEK_TIME_OF_DAY_UTC_AND_FRACTION  
OSS_TEK_TIME_OF_DAY_AND_DIFF_AND_FRACTION
OSS_TEK_DATE_TIME                        
OSS_TEK_START_END_DATE_INTERVAL          
OSS_TEK_START_END_TIME_INTERVAL          
OSS_TEK_START_END_DATE_TIME_INTERVAL     
OSS_TEK_DURATION_INTERVAL                
OSS_TEK_START_DATE_DURATION_INTERVAL     
OSS_TEK_START_TIME_DURATION_INTERVAL     
OSS_TEK_START_DATE_TIME_DURATION_INTERVAL
OSS_TEK_DURATION_END_DATE_INTERVAL       
OSS_TEK_DURATION_END_TIME_INTERVAL       
OSS_TEK_DURATION_END_DATE_TIME_INTERVAL 
OSS_TEK_REC_START_END_DATE_INTERVAL     
OSS_TEK_REC_START_END_TIME_INTERVAL     
OSS_TEK_REC_START_END_DATE_TIME_INTERVAL
OSS_TEK_REC_DURATION_INTERVAL           
OSS_TEK_REC_START_DATE_DURATION_INTERVAL
OSS_TEK_REC_START_TIME_DURATION_INTERVAL
OSS_TEK_REC_START_DATE_TIME_DURATION_INTERVAL
OSS_TEK_REC_DURATION_END_DATE_INTERVAL       
OSS_TEK_REC_DURATION_END_TIME_INTERVAL       
OSS_TEK_REC_DURATION_END_DATE_TIME_INTERVAL  

The TIME types for unnamed ASN.1 types of ITU-T Rec. X.691:2002 | ISO/IEC 8825-2:2002 are:

OSS_TEK_MIXED 
OSS_TEK_INTEGER        
OSS_TEK_ENUMERATED     
OSS_TEK_FRACTIONAL_TIME
OSS_TEK_FRACTIONAL_PART
OSS_TEK_DATE_TYPE      
OSS_TEK_TIME_TYPE      
OSS_TEK_TIME_TYPE_TIME 
OSS_TEK_TIME_DIFFERENCE

Example

Given the following ASN.1 type and value definition:

Foo ::= SEQUENCE {
   a    INTEGER (250..253) OPTIONAL,
   ...,
   b    NumericString (SIZE(3)),
   ...,
   c    BOOLEAN OPTIONAL
}

value Foo ::= { a  253,  b  "123" }

"value" is encoded in PER Aligned (shown in hexadecimal): D8080223 40.

If you provide your own function, it is called by turns, with the following records as arguments:

0.	typeId: OSS_PDU_BEGIN
possibleLast: -
decodedContent: -
typeName: "Foo"
fieldName: -
qualifiedName: -
qualifiedNameOfTopRecord: -
qualifiedNumber: -
qualifiedNumberOfTopRecord: -
depth: 0
encodedContent: -
encodedBitOffset: -
length: -
numberOfSimplestRecord: -
simplestRecords: -
bitsPerContentUnit: -
offset: -

1.	typeId: OSS_SEQ_SET_BEGIN
possibleLast: TRUE
decodedContent: NULL
typeName: "Foo"
fieldName: "value"
qualifiedName: "value"
qualifiedNameOfTopRecord: NULL
qualifiedNumber: "1"
qualifiedNumberOfTopRecord: NULL
depth: 0 (1, for XPER)
encodedContent: [0]
encodedBitOffset: 0
length: -1 (unknown)
numberOfSimplestRecord: 2
simplestRecords: 
	{
		{
			typeId: OSS_EXTENSION_FLAG_TYPE
			length: 1
			encodedContent: [0]
			encodedBitOffset: 0
		},
		{
			typeId: OSS_PREAMBLE_TYPE,
			length: 2
			encodedContent: [0]
			encodedBitOffset: 1
		}
	}

bitsPerContentUnit: 8
offset: 0

     2. typeId: OSS_ASN1_TYPE
	  possibleLast: TRUE
        decodedContent: "253"
        typeName: "INTEGER"
        fieldName: "a"
        qualifiedName: "value.a"
        qualifiedNameOfTopRecord: "value"
        qualifiedNumber: "1.1"
        qualifiedNumberOfTopRecord: "1"
        depth: 1 (2, for XPER)
        encodedContent: [0]
        encodedBitOffset: 3
        length: 2
        numberOfSimplestRecord: 1
        simplestRecords: 
             {
               {
                 typeId: OSS_CONTENTS_TYPE,
                 length: 2
                 encodedContent: [0]
                 encodedBitOffset: 3
               }
             }

        bitsPerContentUnit: 8
        offset: 3

    2a. typeId: OSS_EXTENSIONS_BEGIN
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 1 (2, for XPER)
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

     3. typeId: OSS_ADDITIONS_TYPE
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: "value"
        qualifiedNumber: "1.2"
        qualifiedNumberOfTopRecord: "1"
        depth: 1 (3, for XPER)
        encodedContent: [0]
        encodedBitOffset: 5
        length: 8
        numberOfSimplestRecord: 2
        simplestRecords: 
             {
               {
                 typeId: OSS_EXTENSION_COUNT_TYPE    
                 length: 7
                 encodedContent: [0]
                 encodedBitOffset: 5
               },
               {
                 typeId: OSS_EXTENSION_PREAMBLE_TYPE    
                 length: 1
                 encodedContent: [1]
                 encodedBitOffset: 4
               }
             }

        bitsPerContentUnit: 8
        offset: 5

    3a. typeId: OSS_EXTENSION_BEGIN
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 1 (3, for XPER)
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

     4. typeId: OSS_ADDITIONS_TYPE
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: "value"
        qualifiedNumber: "1.3"
        qualifiedNumberOfTopRecord: "1"
        depth: 1 (4, for XPER)
        encodedContent: [1]
        encodedBitOffset: 5
        length: 13
        numberOfSimplestRecord: 2
        simplestRecords: 
             {
               {
                 typeId: OSS_PADDING_TYPE    
                 length: 3
                 encodedContent: [1]
                 encodedBitOffset: 5
               },
               {
                 typeId: OSS_EXTENSION_LENGTH    
                 length: 8
                 encodedContent: [2]
                 encodedBitOffset: 0
               }
             }

        bitsPerContentUnit: 8
        offset: 13

     5. typeId: OSS_ASN1_TYPE
	  possibleLast: TRUE
        decodedContent: "123"
        typeName: "NumericString"
        fieldName: "b"
        qualifiedName: "value.b"
        qualifiedNameOfTopRecord: "value"
        qualifiedNumber: "1.4"
        qualifiedNumberOfTopRecord: "1"
        depth: 1 (4, for XPER)
        encodedContent: [3]
        encodedBitOffset: 0
        length: 12
        numberOfSimplestRecord: 1
        simplestRecords: 
             {
               {
                 typeId: OSS_CONTENT_TYPE    
                 length: 12
                 encodedContent: [3]
                 encodedBitOffset: 0
               }
             }

        bitsPerContentUnit: 4
        offset: 24

    5a. typeId: OSS_EXTENSION_FINISHED
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 1 (3, for XPER)
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

    5b. typeId: OSS_EXTENSIONS_FINISHED
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 1 (2, for XPER)
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

     6. typeId: OSS_SEQ_SET_FINISHED
        decodedContent: - 
        typeName: -
        fieldName: -
        qualifiedName: - 
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 0 (1, for XPER)
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

     7. typeId: OSS_ADDITIONS_TYPE
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: "value"
        qualifiedNumber: "1.5"
        qualifiedNumberOfTopRecord: "1"
        depth: 0 (1, for XPER)
        encodedContent: [4]
        encodedBitOffset: 4
        length: 4
        numberOfSimplestRecord: 1
        simplestRecords: 
             {
               {
                 typeId: OSS_PADDING_TYPE    
                 length: 4
                 encodedContent: [4]
                 encodedBitOffset: 4
               }
             }

        bitsPerContentUnit: 8
        offset: 36

    7a. typeId: OSS_TOTAL_LENGTH
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 0 (1, for XPER) 
        encodedContent: -
        encodedBitOffset: -
        length: 5
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

    7b. typeId: OSS_PDU_END_TYPE
	  possibleLast: -
        decodedContent: -
        typeName: -
        fieldName: -
        qualifiedName: -
        qualifiedNameOfTopRecord: -
        qualifiedNumber: -
        qualifiedNumberOfTopRecord: -
        depth: 0 
        encodedContent: -
        encodedBitOffset: -
        length: -
        numberOfSimplestRecord: -
        simplestRecords: -
        bitsPerContentUnit: -
        offset: -

Example with TIME type

Given the following ASN.1 type and value definition:

MIX ::= TIME ((SETTINGS "Year=Basic") | (SETTINGS "Year=Proleptic"))

value MIX ::= "23:59,99999+12:59"

"value" is encoded in PER Aligned as (shown in hexadecimal): 700104BF 70030186 9FB3A0.

If you provide your own function, it is called by turns with the following records as arguments:

000.typeId:                    OSS_PDU_BEGIN_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
001.typeId:                    OSS_TIME8601_BEGIN_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
    addition->typeInformation: "TIME"
    addition->optional:        No
002.typeId:                    OSS_CHOICE_BEGIN_PPR
    timeEncId:                 OSS_TEK_MIXED
    typeName:                  "MIX"
    addition->typeInformation: ""MIXED-ENCODING" CHOICE"
    addition->optional:        No
    encodedContent:            6 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CHOICE_INDEX_PPR
          comments:         "(index = 28)"
          encodedContent:   6 bits
        }
      }
003.typeId:                    OSS_SEQ_SET_BEGIN_PPR
    timeEncId:                 OSS_TEK_FRACTIONAL_TIME
    fieldName:                 "row-29"
    addition->typeInformation: ""FRACTIONAL-TIME" SEQUENCE"
    addition->optional:        No
004.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "number-of-digits"
    addition->typeInformation: "INTEGER (1..MAX)"
    addition->optional:        No
    decodedContent:            5
    encodedContent:            16 bits
    numberOfSimplestRecord:    2
    simplestRecords:
      {
        {
          typeId:           OSS_LENGTH_TYPE_PPR
          comments:         "(decoded as 1)"
          encodedContent:   8 bits
        },
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   8 bits
        }
      }
005.typeId:                    OSS_SEQ_SET_BEGIN_PPR
    timeEncId:                 OSS_TEK_MINUTES_AND_DIFF_AND_FRACTION
    fieldName:                 "time-value"
    addition->typeInformation: ""MINUTES-AND-DIFF-AND-FRACTION-ENCODING" SEQUENCE
    addition->optional:        No
006.typeId:                    OSS_SEQ_SET_BEGIN_PPR
    timeEncId:                 OSS_TEK_MINUTES_AND_FRACTION
    fieldName:                 "local-time"
    addition->typeInformation: "SEQUENCE"
    addition->optional:        No
007.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "hours"
    addition->typeInformation: "INTEGER (0..24)"
    addition->optional:        No
    decodedContent:            23
    encodedContent:            5 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   5 bits
        }
      }
008.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "minutes"
    addition->typeInformation: "INTEGER (0..59)"
    addition->optional:        No
    decodedContent:            59
    encodedContent:            6 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   6 bits
        }
      }
009.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "fraction"
    addition->typeInformation: "INTEGER (0..999, ..., 1000..MAX)"
    addition->optional:        No
    decodedContent:            99999
    encodedContent:            33 bits
    numberOfSimplestRecord:    3
    simplestRecords:
      {
        {
          typeId:           OSS_EXTENSION_RANGE_TYPE_PPR
          encodedContent:   1 bits
        },
        {
          typeId:           OSS_LENGTH_TYPE_PPR
          comments:         "(decoded as 3)"
          encodedContent:   8 bits
        },
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   24 bits
        }
      }
010.typeId:                    OSS_SEQ_SET_FINISHED_PPR
    timeEncId:                 OSS_TEK_MINUTES_AND_FRACTION
    fieldName:                 "fraction"
011.typeId:                    OSS_SEQ_SET_BEGIN_PPR
    timeEncId:                 OSS_TEK_TIME_DIFFERENCE
    fieldName:                 "time-difference"
    addition->typeInformation: ""TIME-DIFFERENCE" SEQUENCE"
    addition->optional:        No
    encodedContent:            1 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_PREAMBLE_TYPE_PPR
          comments:         "--bit #0 = 1: 'minutes' is present"
          encodedContent:   1 bits
        }
      }
012.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_ENUMERATED
    fieldName:                 "sign"
    addition->typeInformation: "ENUMERATED"
    addition->optional:        No
    decodedContent:            positive
    encodedContent:            1 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   1 bits
        }
      }
013.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "hours"
    addition->typeInformation: "INTEGER (0..15)"
    addition->optional:        No
    decodedContent:            12
    encodedContent:            4 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   4 bits
        }
      }
014.typeId:                    OSS_ASN1_TYPE_PPR
    timeEncId:                 OSS_TEK_INTEGER
    fieldName:                 "minutes"
    addition->typeInformation: "INTEGER (1..59) OPTIONAL"
    addition->optional:        Yes
    decodedContent:            59
    encodedContent:            6 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_CONTENTS_TYPE_PPR
          encodedContent:   6 bits
        }
      }
015.typeId:                    OSS_SEQ_SET_FINISHED_PPR
    timeEncId:                 OSS_TEK_TIME_DIFFERENCE
    fieldName:                 "time-difference"
016.typeId:                    OSS_SEQ_SET_FINISHED_PPR
    timeEncId:                 OSS_TEK_MINUTES_AND_DIFF_AND_FRACTION
    fieldName:                 "time-value"
017.typeId:                    OSS_SEQ_SET_FINISHED_PPR
    timeEncId:                 OSS_TEK_FRACTIONAL_TIME
    fieldName:                 "row-29"
018.typeId:                    OSS_CHOICE_FINISHED_PPR
    timeEncId:                 OSS_TEK_MIXED
    typeName:                  "MIX"
019.typeId:                    OSS_TIME8601_FINISHED_PPR
    timeEncId:                 OSS_TEK_MIXED
    typeName:                  "MIX"
    decodedContent:            "23:59.99999+12:59"
020.typeId:                    OSS_ADDITIONS_TYPE_PPR
    timeEncId:                 OSS_TEK_NOTIME
    encodedContent:            2 bits
    numberOfSimplestRecord:    1
    simplestRecords:
      {
        {
          typeId:           OSS_PDU_PADDING_TYPE_PPR
          encodedContent:   2 bits
        }
      }
021.typeId:                    OSS_TOTAL_LENGTH_PPR
    timeEncId:                 OSS_TEK_NOTIME
022.typeId:                    OSS_PDU_END_PPR
    timeEncId:                 OSS_TEK_NOTIME
    typeName:                  "MIX"
ossPrintPER output:

value MIX ::=
  --TYPE INFORMATION: TIME
  --LEVEL NUMBER: 1
--value MIX ::=
  --TYPE INFORMATION: "MIXED-ENCODING" CHOICE
  --LEVEL NUMBER: 1
  --OFFSET: 0,0
  --choice index: <.011100> (index = 28)
  --row-29 :
  --{
    --TYPE INFORMATION: "FRACTIONAL-TIME" SEQUENCE
    --LEVEL NUMBER: 1.1
    --OFFSET: 0,6
    --number-of-digits 5,
      --TYPE INFORMATION: INTEGER (1..MAX)
      --LEVEL NUMBER: 1.1.1
      --OFFSET: 0,6; LENGTH: 2,2
      --padding: <00>
      --length: .01 (decoded as 1)
      --contents: .04
    --time-value
    --{
      --TYPE INFORMATION: "MINUTES-AND-DIFF-AND-FRACTION-ENCODING" SEQUENCE
      --LEVEL NUMBER: 1.1.2
      --OFFSET: 3,0
      --local-time
      --{
        --TYPE INFORMATION: SEQUENCE
        --LEVEL NUMBER: 1.1.2.1
        --OFFSET: 3,0
        --hours 23,
          --TYPE INFORMATION: INTEGER (0..24)
          --LEVEL NUMBER: 1.1.2.1.1
          --OFFSET: 3,0; LENGTH: 0,5
          --contents: <.10111>
        --minutes 59,
          --TYPE INFORMATION: INTEGER (0..59)
          --LEVEL NUMBER: 1.1.2.1.2
          --OFFSET: 3,5; LENGTH: 0,6
          --contents: <111.011>
        --fraction 99999
          --TYPE INFORMATION: INTEGER (0..999, ..., 1000..MAX)
          --LEVEL NUMBER: 1.1.2.1.3
          --OFFSET: 4,3; LENGTH: 4,5
          --extension range: <1>
          --padding: <0000>
          --length: .03 (decoded as 3)
          --contents: .01.86.9F
      --},
      --time-difference
      --{
        --TYPE INFORMATION: "TIME-DIFFERENCE" SEQUENCE
        --LEVEL NUMBER: 1.1.2.2
        --OFFSET: 9,0
        --preamble: <.1>
          --bit #0 = 1: 'minutes' is present
        --sign positive,
          --TYPE INFORMATION: ENUMERATED
          --LEVEL NUMBER: 1.1.2.2.1
          --OFFSET: 9,1; LENGTH: 0,1
          --contents: <0>
        --hours 12,
          --TYPE INFORMATION: INTEGER (0..15)
          --LEVEL NUMBER: 1.1.2.2.2
          --OFFSET: 9,2; LENGTH: 0,4
          --contents: <1100>
        --minutes 59
          --TYPE INFORMATION: INTEGER (1..59) OPTIONAL
          --LEVEL NUMBER: 1.1.2.2.3
          --OFFSET: 9,6; LENGTH: 0,6
          --contents: <11.1010>
      --}
    --}
  --}
 "23:59.99999+12:59"
--PDU padding: <0000>
--TOTAL LENGTH: 11,0

ossPrintXPER output:

<PDU name="MIX">
  <Value type="time">
    <Details>
      <metadata type="TYPE INFORMATION">TIME</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
    </Details>
    <Value type="choice">
      <Details>
        <metadata type="TYPE INFORMATION">"MIXED-ENCODING" CHOICE</metadata>
        <metadata type="LEVEL NUMBER">1</metadata>
        <position offset="0,0"/>
        <encoding type="choice index">
          [.011100]
          <description>(index = 28)</description>
        </encoding>
      </Details>
      <Field name="row-29">
        <Value type="seqset">
          <Details>
            <metadata type="TYPE INFORMATION">"FRACTIONAL-TIME"
                                                   SEQUENCE</metadata>
            <metadata type="FULL NAME">row-29</metadata>
            <metadata type="LEVEL NUMBER">1.1</metadata>
            <position offset="0,6"/>
          </Details>
          <Field name="number-of-digits">
            <Value>
              5
              <Details>
                <metadata type="TYPE INFORMATION">INTEGER (1..MAX)</metadata>
                <metadata type="FULL NAME">row-29.number-of-digits</metadata>
                <metadata type="LEVEL NUMBER">1.1.1</metadata>
                <position offset="0,6" length="2,2"/>
                <encoding type="padding">[00]</encoding>
                <encoding type="length">
                  .01
                  <description>(decoded as 1)</description>
                </encoding>
                <encoding type="contents">.04</encoding>
              </Details>
            </Value>
          </Field>
          <Field name="time-value">
            <Value type="seqset">
              <Details>
                <metadata type="TYPE INFORMATION">"MINUTES-AND-DIFF-AND-
                                       FRACTION-ENCODING" SEQUENCE</metadata>
                <metadata type="FULL NAME">row-29.time-value</metadata>
                <metadata type="LEVEL NUMBER">1.1.2</metadata>
                <position offset="3,0"/>
              </Details>
              <Field name="local-time">
                <Value type="seqset">
                  <Details>
                    <metadata type="TYPE INFORMATION">SEQUENCE</metadata>
                    <metadata type="FULL NAME">row-29.time-value.local-
                                                             time</metadata>
                    <metadata type="LEVEL NUMBER">1.1.2.1</metadata>
                    <position offset="3,0"/>
                  </Details>
                  <Field name="hours">
                    <Value>
                      23
                      <Details>
                        <metadata type="TYPE INFORMATION">INTEGER
                                                             (0..24)</metadata>
                        <metadata type="FULL NAME">row-29.time-value.local-
                                                          time.hours</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.1.1</metadata>
                        <position offset="3,0" length="0,5"/>
                        <encoding type="contents">[.10111]</encoding>
                      </Details>
                    </Value>
                  </Field>
                  <Field name="minutes">
                    <Value>
                      59
                      <Details>
                        <metadata type="TYPE INFORMATION">INTEGER
                                                           (0..59)</metadata>
                        <metadata type="FULL NAME">row-29.time-value.local-
                                                      time.minutes</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.1.2</metadata>
                        <position offset="3,5" length="0,6"/>
                        <encoding type="contents">[111.011]</encoding>
                      </Details>
                    </Value>
                  </Field>
                  <Field name="fraction">
                    <Value>
                      99999
                      <Details>
                        <metadata type="TYPE INFORMATION">INTEGER (0..999,
                                                   ...,1000..MAX)</metadata>
                        <metadata type="FULL NAME">row-29.time-value.local-
                                                    time.fraction</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.1.3</metadata>
                        <position offset="4,3" length="4,5"/>
                        <encoding type="extension range">[1]</encoding>
                        <encoding type="padding">[0000]</encoding>
                        <encoding type="length">
                          .03
                          <description>(decoded as 3)</description>
                        </encoding>
                        <encoding type="contents">.01.86.9F</encoding>
                      </Details>
                    </Value>
                  </Field>
                </Value>
              </Field>
              <Field name="time-difference">
                <Value type="seqset">
                  <Details>
                    <metadata type="TYPE INFORMATION">"TIME-DIFFERENCE"
                                                       SEQUENCE</metadata>
                    <metadata type="FULL NAME">row-29.time-value.time-
                                                     difference</metadata>
                    <metadata type="LEVEL NUMBER">1.1.2.2</metadata>
                    <position offset="9,0"/>
                    <encoding type="preamble">
                      [.1]
                      <description>bit #0 = 1: 'minutes' is
                                                      present</description>
                    </encoding>
                  </Details>
                  <Field name="sign">
                    <Value>
                      positive
                      <Details>
                        <metadata type="TYPE INFORMATION">ENUMERATED</metadata>
                        <metadata type="FULL NAME">row-29.time-value.time-
                                                     difference.sign</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.2.1</metadata>
                        <position offset="9,1" length="0,1"/>
                        <encoding type="contents">[0]</encoding>
                      </Details>
                    </Value>
                  </Field>
                  <Field name="hours">
                    <Value>
                      12
                      <Details>
                        <metadata type="TYPE INFORMATION">INTEGER
                                                          (0..15)</metadata>
                        <metadata type="FULL NAME">row-29.time-value.time-
                                                 difference.hours</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.2.2</metadata>
                        <position offset="9,2" length="0,4"/>
                        <encoding type="contents">[1100]</encoding>
                      </Details>
                    </Value>
                  </Field>
                  <Field name="minutes">
                    <Value>
                      59
                      <Details>
                        <metadata type="TYPE INFORMATION">INTEGER (1..59)
                                                          OPTIONAL</metadata>
                        <metadata type="FULL NAME">row-29.time-value.time-
                                                difference.minutes</metadata>
                        <metadata type="LEVEL NUMBER">1.1.2.2.3</metadata>
                        <position offset="9,6" length="0,6"/>
                        <encoding type="contents">[11.1010]</encoding>
                      </Details>
                    </Value>
                  </Field>
                </Value>
              </Field>
            </Value>
          </Field>
        </Value>
      </Field>
    </Value>
    "23:59.99999+12:59"
  </Value>
  <encoding type="PDU padding">[0000]</encoding>
  <TotalLength bytecount="11"/>
</PDU>

The following example uses a type with a contents constraint applied. Note that if a record with information about a contained type value is present, the "decodedContent" for the value of a containing type is NULL, if no information about the contained type value is passed to the user, then "decodedContent" contains the decoded value of an OCTET or BIT STRING with CONTAINING.

enc-CER OBJECT IDENTIFIER ::=                                        
	{joint-iso-itu-t(2) asn1(1) ber-derived(2) canonical-encoding(0)}
enc-PER-Aligned OBJECT IDENTIFIER ::= 
	{joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) aligned(0)}
enc-PER-Unaligned OBJECT IDENTIFIER ::= 
	{joint-iso-itu-t(2) asn1(1) packed-encoding(3) basic(0) unaligned(1)}

T ::= [1] BIT STRING (
  CONTAINING BIT STRING (
    CONTAINING BIT STRING (
	   CONTAINING BIT STRING (
	     CONTAINING BIT STRING (
		 CONTAINING PrintableString --<UNBOUNDED>--
		 ENCODED BY enc-PER-Unaligned
	     ) ENCODED BY enc-PER-Aligned
	   ) ENCODED BY enc-CER
	 ) ENCODED BY enc-PER-Unaligned
  ) ENCODED BY enc-PER-Aligned
)
v T ::= CONTAINING CONTAINING CONTAINING CONTAINING CONTAINING "AaBbC"

The value "value" is encoded in PER Aligned as (shown in hexadecimal): 60585003 08052B05 83861628 60.

If you provide your own function, it will be called by turns with the following records as an argument:

000.typeId                   : OSS_PDU_BEGIN_PPR
  typeName                   : "T"
001.typeId                   : OSS_CONTAINING_TYPE_BEGIN_PPR
  typeName                   : "T"
002.typeId                   : OSS_ASN1_TYPE_PPR
  decodedContent             : -
  typeName                   : "T"
  qualifiedNumber            : "1"
  depth                      : 0
  encodedContent             : .60 ...
  length                     : 104
  numberOfSimplestRecord     : 2
  simplestRecords            : 
    {                          
      {                          
        typeId                     : OSS_LENGTH_TYPE_PPR
        length                     : 8
        encodedContent             : .60 ...
        addition->comments         : "(decoded as 96)"
      },                         
      {                          
        typeId                     : OSS_CONTENTS_TYPE_PPR
        length                     : 96
        encodedContent             : .58 ...
      }                          
    }                          
  bitsPerContentUnit         : 8
  offset                     : 0
  addition->typeInformation  : "BIT STRING (CONTAINING ... ENCODED BY PER_ALIGNED)"
003.typeId                   : OSS_CONTAINING_TYPE_BEGIN_PPR
  typeName                   : "Contents Constraint"
004.typeId                   : OSS_ASN1_TYPE_PPR
  decodedContent             : -
  typeName                   : "Contents Constraint"
  qualifiedName              : "*"
  qualifiedNumber            : "1.1"
  qualifiedNumberOfTopRecord : "1"
  depth                      : 1
  encodedContent             : .58 ...
  length                     : 96
  numberOfSimplestRecord     : 2
  simplestRecords            : 
    {                          
      {                          
        typeId                     : OSS_LENGTH_TYPE_PPR
        length                     : 8
        encodedContent             : .58 ...
        encodedBitOffset           : 0
        addition->comments         : "(decoded as 88)"
      },                         
      {                          
        typeId                     : OSS_CONTENTS_TYPE_PPR
        length                     : 88
        encodedContent             : .50 ...
        encodedBitOffset           : 0
      }                          
    }                          
  offset                     : 8
  addition->typeInformation  : "BIT STRING (CONTAINING ... ENCODED BY PER_UNALIGNED)"
005.typeId                   : OSS_CONTAINING_TYPE_BEGIN_PPR
  typeName                   : "Contents Constraint"
006.typeId                   : OSS_ASN1_TYPE_PPR
  possibleLast               : TRUE
  decodedContent             : '00000011 00001000 00000101 00101011 000 ...'B
  typeName                   : "Contents Constraint"
  qualifiedName              : "*.*"
  qualifiedNameOfTopRecord   : "*"
  qualifiedNumber            : "1.1.1"
  qualifiedNumberOfTopRecord : "1.1"
  depth                      : 2
  encodedContent             : .50 ...
  length                     : 88
  numberOfSimplestRecord     : 2
  simplestRecords            : 
    {                          
      {                          
        typeId                     : OSS_LENGTH_TYPE_PPR
        length                     : 8
        encodedContent             : .50 ...
        addition->comments         : "(decoded as 80)"
      },                         
      {                          
        typeId                     : OSS_CONTENTS_TYPE_PPR
        length                     : 80
        encodedContent             : .03 ...
      }                          
    }                          
  offset                     : 16
  addition->typeInformation  : "BIT STRING (CONTAINING ... ENCODED BY CER)"
  addition->optional         : FALSE
007.typeId                   : OSS_CONTAINING_TYPE_END_PPR
  typeName                   : "Contents Constraint"
008.typeId                   : OSS_CONTAINING_TYPE_END_PPR
  typeName                   : "Contents Constraint"
009.typeId                   : OSS_CONTAINING_TYPE_END_PPR
  typeName                   : "T"
010.typeId                   : OSS_TOTAL_LENGTH_PPR
  length                     : 13
011.typeId                   : OSS_PDU_END_PPR
  typeName                   : "T"

ossPrintPER output:

value T ::= CONTAINING
  --TYPE INFORMATION: BIT STRING (CONTAINING ... ENCODED BY PER_ALIGNED)
  --LEVEL NUMBER: 1
  --OFFSET: 0,0; LENGTH: 13,0
  --length: .60 (decoded as 96)
  --contents: .58.50.03.08.05.2B.05.83.86.16.28.60
  CONTAINING
    --TYPE INFORMATION: BIT STRING (CONTAINING ... ENCODED BY PER_UNALIGNED)
    --LEVEL NUMBER: 1.1
    --OFFSET: 1,0; LENGTH: 12,0
    --length: .58 (decoded as 88)
    --contents: .50.03.08.05.2B.05.83.86.16.28.60
     '00000011 00001000 00000101 00101011 000 ...'B
      --TYPE INFORMATION: BIT STRING (CONTAINING ... ENCODED BY CER)
      --LEVEL NUMBER: 1.1.1
      --OFFSET: 2,0; LENGTH: 11,0
      --length: .50 (decoded as 80)
      --contents: .03.08.05.2B.05.83.86.16.28.60
      /*
  Contents Constraint: tag = [UNIVERSAL 3] primitive; length = 8
    0x052b058386162860
    BIT STRING [length = 5.3]
      0x058386162860
      PrintableString [length = 5.0]
        "AaBbC"
      */
--TOTAL LENGTH: 13,0

ossPrintXPER output:

<PDU name="T">
  <Value type="containing">
    <Details>
      <metadata type="TYPE INFORMATION">BIT STRING (CONTAINING ... ENCODED BY PER_ALIGNED)</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0" length="13,0"/>
      <encoding type="length">
        .60
        <description>(decoded as 96)</description>
      </encoding>
      <encoding type="contents">.58.50.03.08.05.2B.05.83.86.16.28.60</encoding>
    </Details>
    <Value type="containing">
      <Details>
        <metadata type="TYPE INFORMATION">BIT STRING (CONTAINING ... ENCODED BY PER_UNALIGNED)</metadata>
        <metadata type="FULL NAME">*</metadata>
        <metadata type="LEVEL NUMBER">1.1</metadata>
        <position offset="1,0" length="12,0"/>
        <encoding type="length">
          .58
          <description>(decoded as 88)</description>
        </encoding>
        <encoding type="contents">.50.03.08.05.2B.05.83.86.16.28.60</encoding>
      </Details>
      <Value type="containing">
        '00000011 00001000 00000101 00101011 000 ...'B
        <Details>
          <metadata type="TYPE INFORMATION">BIT STRING (CONTAINING ... ENCODED BY CER)</metadata>
          <metadata type="FULL NAME">*.*</metadata>
          <metadata type="LEVEL NUMBER">1.1.1</metadata>
          <position offset="2,0" length="11,0"/>
          <encoding type="length">
            .50
            <description>(decoded as 80)</description>
          </encoding>
          <encoding type="contents">.03.08.05.2B.05.83.86.16.28.60</encoding>
          <!--
  Contents Constraint: tag = [UNIVERSAL 3] primitive; length = 8
    0x052b058386162860
    BIT STRING [length = 5.3]
      0x058386162860
      PrintableString [length = 5.0]
        "AaBbC"
          -->
        </Details>
      </Value>
    </Value>
  </Value>
  <TotalLength bytecount="13"/>
</PDU>

The following example contains a SEQUENCE OF type with the OSS.Truncate directive applied:

--<OSS.Truncate M.S 2>--
--<OSS.Truncate M.S.* 1>--
M DEFINITIONS AUTOMATIC TAGS ::= BEGIN
S::= SEQUENCE OF SEQUENCE OF BOOLEAN

s S ::= {
    { FALSE, TRUE, TRUE },
    { TRUE, FALSE, TRUE },
    { TRUE, TRUE, FALSE }
}
END

If you provide your own function, it is called by turns with the following records as an argument:

000.typeId:                    OSS_PDU_BEGIN_PPR
001.typeId:                    OSS_SEQ_SET_BEGIN_PPR
002.typeId:                    OSS_SEQ_SET_BEGIN_PPR
003.typeId:                    OSS_ASN1_TYPE_PPR
004.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
005.typeId:                    OSS_ASN1_TYPE_PPR
006.typeId:                    OSS_ASN1_TYPE_PPR
007.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 2)
008.typeId:                    OSS_SEQ_SET_FINISHED_PPR
009.typeId:                    OSS_SEQ_SET_BEGIN_PPR
010.typeId:                    OSS_ASN1_TYPE_PPR
011.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
012.typeId:                    OSS_ASN1_TYPE_PPR
013.typeId:                    OSS_ASN1_TYPE_PPR
014.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 2)
015.typeId:                    OSS_SEQ_SET_FINISHED_PPR
016.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
017.typeId:                    OSS_SEQ_SET_BEGIN_PPR
018.typeId:                    OSS_ASN1_TYPE_PPR
019.typeId:                    OSS_ASN1_TYPE_PPR
020.typeId:                    OSS_ASN1_TYPE_PPR
021.typeId:                    OSS_SEQ_SET_FINISHED_PPR
022.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 1)
023.typeId:                    OSS_SEQ_SET_FINISHED_PPR
024.typeId:                    OSS_ADDITIONS_TYPE_PPR
025.typeId:                    OSS_PDU_END_PPR

The sequence of records with OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS flag:

000.typeId:                    OSS_PDU_BEGIN_PPR
001.typeId:                    OSS_SEQ_SET_BEGIN_PPR
002.typeId:                    OSS_SEQ_SET_BEGIN_PPR
003.typeId:                    OSS_ASN1_TYPE_PPR
004.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
005.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 2)
006.typeId:                    OSS_SEQ_SET_FINISHED_PPR
007.typeId:                    OSS_SEQ_SET_BEGIN_PPR
008.typeId:                    OSS_ASN1_TYPE_PPR
009.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
010.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 2)
011.typeId:                    OSS_SEQ_SET_FINISHED_PPR
012.typeId:                    OSS_START_TRUNCATED_ELEMENTS_TRACE_PPR
013.typeId:                    OSS_END_TRUNCATED_ELEMENTS_TRACE_PPR(length = 1)
014.typeId:                    OSS_SEQ_SET_FINISHED_PPR
015.typeId:                    OSS_ADDITIONS_TYPE_PPR
016.typeId:                    OSS_PDU_END_PPR

ossPrintPER output:

value S ::=.
{
  --length: <.00000011>
  {
    --length: <.00000011>
     FALSE
      --contents: <.0>
    --, the rest of elements are not decoded due to OSS.Truncate
    --TRUE,
      --contents: <1>
    --TRUE
      --contents: <1>
    --skipped 2 elements
  },
  {
    --padding: <00000>
    --length: <.00000011>
     TRUE
      --contents: <.1>
    --, the rest of elements are not decoded due to OSS.Truncate
    --FALSE,
      --contents: <0>
    --TRUE
      --contents: <1>
    --skipped 2 elements
  }
  --, the rest of elements are not decoded due to OSS.Truncate
  --{
    --padding: <00000>
    --length: <.00000011>
    --TRUE
      --contents: <.1>
    --,
    --TRUE,
      --contents: <1>
    --FALSE
      --contents: <0>
  --}
  --skipped 1 elements
}
--PDU padding: <00000>

ossPrintPER output with OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS flag:

value S ::=.
{
  --length: <.00000011>
  {
    --length: <.00000011>
     FALSE
      --contents: <.0>
    --the rest of elements are not decoded due to OSS.Truncate
    --skipped 2 elements
  },
  {
    --padding: <00000>
    --length: <.00000011>
     TRUE
      --contents: <.1>
    --the rest of elements are not decoded due to OSS.Truncate
    --skipped 2 elements
  }
  --the rest of elements are not decoded due to OSS.Truncate
  --skipped 1 elements
}
--PDU padding: <00000>

ossPrintXPER output:

<PDU name="S">
  <Value type="seqsetof">
    <Details>
      <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0"/>
      <encoding type="length">
        [.00000011]
        <description>(decoded as 3)</description>
      </encoding>
    </Details>
    <Value type="seqsetof">
      <Details>
        <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
        <metadata type="FULL NAME">[0]</metadata>
        <metadata type="LEVEL NUMBER">1.1</metadata>
        <position offset="1,0"/>
        <encoding type="length">
          [.00000011]
          <description>(decoded as 3)</description>
        </encoding>
      </Details>
      <Value>
        FALSE
        <Details>
          <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
          <metadata type="FULL NAME">[0].[0]</metadata>
          <metadata type="LEVEL NUMBER">1.1.1</metadata>
          <position offset="2,0" length="0,1"/>
          <encoding type="contents">[.0]</encoding>
        </Details>
      </Value>
      <Truncated>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[0].[1]</metadata>
            <metadata type="LEVEL NUMBER">1.1.2</metadata>
            <position offset="2,1" length="0,1"/>
            <encoding type="contents">[1]</encoding>
          </Details>
        </Value>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[0].[2]</metadata>
            <metadata type="LEVEL NUMBER">1.1.3</metadata>
            <position offset="2,2" length="0,1"/>
            <encoding type="contents">[1]</encoding>
          </Details>
        </Value>
        <Skipped elements="2"/>
      </Truncated>
    </Value>
    <Value type="seqsetof">
      <Details>
        <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
        <metadata type="FULL NAME">[1]</metadata>
        <metadata type="LEVEL NUMBER">1.2</metadata>
        <position offset="2,3"/>
        <encoding type="padding">[00000]</encoding>
        <encoding type="length">
          [.00000011]
          <description>(decoded as 3)</description>
        </encoding>
      </Details>
      <Value>
        TRUE
        <Details>
          <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
          <metadata type="FULL NAME">[1].[0]</metadata>
          <metadata type="LEVEL NUMBER">1.2.1</metadata>
          <position offset="4,0" length="0,1"/>
          <encoding type="contents">[.1]</encoding>
        </Details>
      </Value>
      <Truncated>
        <Value>
          FALSE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[1].[1]</metadata>
            <metadata type="LEVEL NUMBER">1.2.2</metadata>
            <position offset="4,1" length="0,1"/>
            <encoding type="contents">[0]</encoding>
          </Details>
        </Value>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[1].[2]</metadata>
            <metadata type="LEVEL NUMBER">1.2.3</metadata>
            <position offset="4,2" length="0,1"/>
            <encoding type="contents">[1]</encoding>
          </Details>
        </Value>
        <Skipped elements="2"/>
      </Truncated>
    </Value>
    <Truncated>
      <Value type="seqsetof">
        <Details>
          <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
          <metadata type="FULL NAME">[2]</metadata>
          <metadata type="LEVEL NUMBER">1.3</metadata>
          <position offset="4,3"/>
          <encoding type="padding">[00000]</encoding>
          <encoding type="length">
            [.00000011]
            <description>(decoded as 3)</description>
          </encoding>
        </Details>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[2].[0]</metadata>
            <metadata type="LEVEL NUMBER">1.3.1</metadata>
            <position offset="6,0" length="0,1"/>
            <encoding type="contents">[.1]</encoding>
          </Details>
        </Value>
        <Value>
          TRUE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[2].[1]</metadata>
            <metadata type="LEVEL NUMBER">1.3.2</metadata>
            <position offset="6,1" length="0,1"/>
            <encoding type="contents">[1]</encoding>
          </Details>
        </Value>
        <Value>
          FALSE
          <Details>
            <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
            <metadata type="FULL NAME">[2].[2]</metadata>
            <metadata type="LEVEL NUMBER">1.3.3</metadata>
            <position offset="6,2" length="0,1"/>
            <encoding type="contents">[0]</encoding>
          </Details>
        </Value>
      </Value>
      <Skipped elements="1"/>
    </Truncated>
  </Value>
  <encoding type="PDU padding">[00000]</encoding>
  <TotalLength bytecount="7"/>
</PDU>

ossPrintXPER output with OSS_NO_TRACE_FOR_TRUNCATED_ELEMENTS flag:

<PDU name="S">
  <Value type="seqsetof">
    <Details>
      <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
      <metadata type="LEVEL NUMBER">1</metadata>
      <position offset="0,0"/>
      <encoding type="length">
        [.00000011]
        <description>(decoded as 3)</description>
      </encoding>
    </Details>
    <Value type="seqsetof">
      <Details>
        <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
        <metadata type="FULL NAME">[0]</metadata>
        <metadata type="LEVEL NUMBER">1.1</metadata>
        <position offset="1,0"/>
        <encoding type="length">
          [.00000011]
          <description>(decoded as 3)</description>
        </encoding>
      </Details>
      <Value>
        FALSE
        <Details>
          <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
          <metadata type="FULL NAME">[0].[0]</metadata>
          <metadata type="LEVEL NUMBER">1.1.1</metadata>
          <position offset="2,0" length="0,1"/>
          <encoding type="contents">[.0]</encoding>
        </Details>
      </Value>
      <Truncated>
        <Skipped elements="2"/>
      </Truncated>
    </Value>
    <Value type="seqsetof">
      <Details>
        <metadata type="TYPE INFORMATION">SEQUENCE OF</metadata>
        <metadata type="FULL NAME">[1]</metadata>
        <metadata type="LEVEL NUMBER">1.2</metadata>
        <position offset="2,3"/>
        <encoding type="padding">[00000]</encoding>
        <encoding type="length">
          [.00000011]
          <description>(decoded as 3)</description>
        </encoding>
      </Details>
      <Value>
        TRUE
        <Details>
          <metadata type="TYPE INFORMATION">BOOLEAN</metadata>
          <metadata type="FULL NAME">[1].[0]</metadata>
          <metadata type="LEVEL NUMBER">1.2.1</metadata>
          <position offset="4,0" length="0,1"/>
          <encoding type="contents">[.1]</encoding>
        </Details>
      </Value>
      <Truncated>
        <Skipped elements="2"/>
      </Truncated>
    </Value>
    <Truncated>
      <Skipped elements="1"/>
    </Truncated>
  </Value>
  <encoding type="PDU padding">[00000]</encoding>
  <TotalLength bytecount="7"/>
</PDU>

Restrictions

When printing the PER encoding of a BIT STRING or OCTET STRING with a contents constraint applied (when a CONTAINING keyword is present), the function never displays encoding information for a contained type if it cannot be automatically decoded. The function will only explain the BIT STRING or OCTET STRING encoding. If automatic decoding is only possible with any non-PER encoding rule specified in ENCODED BY for the type with CONTAINING, then the function writes out trace data for the contained type value as the decoder does when the DEBUGPDU runtime flag is set for decoding.

The ossPrintPER() function prints such trace data inside /* */ comments and ossPrintXPER() prints it inside <!-- --> comments. No records for such a contained type encoding are passed to the user.

Partial Decoding

The partial decoding feature can be used instead of, or in addition to standard (full) decoding. The advantages of using partial decoding include the following:

  • Decoding preselected fields from incoming messages at a very high speed. The remaining fields are bypassed.
  • Modifying the value of a preselected field, which allows you to directly replace the field value in the binary message (we recommend against doing this for PER or UPER messages).

For each decoded field, the offset, length, and value are provided by the decoder to the user application via callback functions. This means that you don't need to know the details of the PDU C representation or to write code to access a deeply nested field.

Your callback function can analyze the decoded value, and by setting the return code, instructs the decoder whether to terminate or continue decoding.

The ossPartialDecode() does not return the decoded PDU value. Instead, it calls a user-defined callback function upon decoding each field marked by the OSS.DataCallback or OSS.InfoCallback compiler directive and, optionally, passes the decoded field value to it.

The partial decoding feature requires TOED runtime version 10.2+.

The following example illustrates how data and info callback functions may be combined to allow one of them to handle zipcode values from homeAddress and ignore zipcode from companyAddress. The sample code is provided for illustrative purposes. In a real-life situation, the flag in the sample code would need only one bit so that, instead of using the world->userVar field as a pointer to the user data, the field could directly store the flag.

Example

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

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
/* A structure for data exchange between callback function calls.
 * In case of multithreading it should be allocated per thread * /
typedef struct {
    int    flag;
} UserData;

UserData data;

/* Info callback function. It sets the flag indicating
 * that homeAddress field is being decoded */
int DLL_ENTRY_FDEF homeAddressField(OssGlobal *world, char *fieldname, unsigned int flags)
{
    UserData *udP = (UserData *)world->userVar;

    if ((flags & OSS_DECODING_OF_COMPONENT_IN_PROGRESS)
       /* decoding of homeAddress is to be started */
        udP->flag = 1;
    else /*  decoding of homeAddress is completed */
        udP->flag = 0;
    return OSS_CONTINUE_DECODING;
}

/* Data callback function.
 * It prints binary encoding and value of zipcode field
 * if it belongs to home address but not to company address */

int DLL_ENTRY_FDEF myZipcode(OssGlobal *world,  long offset,
            long length, unsigned int flags, unsigned int *zipcode).
{
    UserData *udP = (UserData *)world->userVar;

    if (udP->flag != 1)  /* we are not in homeAddress */
        return OSS_CONTINUE_DECODING;

    /* Print homeAddress zipcode */
    ossPrint(world, "zipcode = %05d\n", *zipcode);

    return OSS_SKIP_TO_PDU_END;
}

See Also


This documentation applies to OSS® ASN.1 Tools for C release 10.3 and later. For earlier versions, consult the PDF manual available in your product installation.

Copyright © 2017 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 C 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 C are available to you.