Previous section.

Inter-Domain Management: Specification Translation (JIDM_ST)
Copyright © 2000 The Open Group

Translation of ASN.1 to IDL

Introduction

In this part, the Specification Translation process for ASN.1 modules is described in terms of inputs and outputs and a rough outline of the process is given. The process will be implemented via a compiler which operates on a set of input files and results in some output files. Since IDL definitions are processed in terms of files which determine the granularity and reusability of the IDL definitions, it is necessary to specify which definitions are generated and what files they are defined in. In addition, ASN.1 adds some complexities by using lengthy module names such as InformationNetwork. Since such names are used to import parts of specifications, there must be a way for the translation process to access the files containing these specifications. In addition, it is desirable to be able to associate the resulting IDL files with the original ASN.1 to facilitate browsing and reuse. This will be done by providing a "nickname database" which maps from the unique registered name of the ASN.1 document (or relevant Object Identifier) to a short nickname suitable for use as a filename base. This nickname will be used to find imported files and to control the names of the generated IDL files.
Figure: Inputs and Outputs for ASN.1 Specification Translation

Since nicknames will be used as the basis for naming files, generated IDL files will only be reusable in an environments where identical nickname databases are used. Therefore, it is desirable to make the nickname database as standard as possible (for example, have standard nicknames for all registered ASN.1 modules). In order to facilitate this, the following nickname selection method is recommended (but not mandatory):

As it is illegal to modify the existing contents of a standard in this context, it is assumed that the nickname always refers to the latest version of the standard. If, for any reason, parts of the original standard are modified in a revision, the last 2 digits of the revision year can be appended to the nickname.

In order to translate between ASN.1 and CORBA IDL, (hereinafter referred to simply as IDL), it is necessary to be able to map the basic definitions (that is, mapping between ASN.1 types and IDL type definitions).

There are two versions of ASN.1 defined, ASN.1:1990 (see reference ASN1) and ASN.1:1994 (see reference ASN1:1994). Since GDMO explicitly builds on ASN.1 and all new GDMO will provide ASN.1:1990 versions (at least in the short term), this document focuses on that version. However, translation is also provided for all the basic types (for example, BMPString and UniversalString) from ASN.1:1994 as a step towards migration to ASN.1:1994. Further steps on this path may be taken in the future.

In this document, unless otherwise noted, the unqualified name ASN.1 refers to ASN.1:1990.

ASN.1 has a much more complex type system than IDL. As a result, the translation necessarily loses some information; for instance in terms of tag values, sub-range types, compound type constants, etc. Capturing this information for subsequent use in the run-time system is a key issue. A number of schemes have been proposed including the use of string constants and #pragma directives, but the definitive statement is deferred to Interaction Translation.

In a number of cases, the complexity of some data types makes it desirable to define operations for manipulating their values. In CORBA, the standard technique is to define pseudo-IDL (PIDL) which allows a fairly tight definition of the operations but with the implication that these operations have library implementations and can only be invoked locally. For example, this is used to provide access methods to support manipulation of the BitString data type.

Outline of the Translation Process

The algorithm that is used to map ASN.1 modules comprises the following steps:

  1. Use as input the original published document. The same order of identifiers is fundamental to consistently generating the same IDL code.

  2. Map each ASN.1 module to an IDL module in a separate IDL file.

  3. Prior to mapping each of the clauses contained in an ASN.1 module, transform it into a canonical form by means of:

  4. Traverse the contents of the canonical ASN.1 module in order, and map each of the clauses as follows:

    Lexical disambiguation is done when generating the mapping in this step, as described in the following sections in this document.

  5. Re-order the generated IDL code to obtain valid OMG IDL code by eliminating forward references.

File Names and IDL Modules

The output of the above translation is a set of IDL modules and interfaces. These must be organised into files in a way which facilitates reuse and effective generation of code by the CORBA IDL compiler. Thus the Specification Translation process results in potentially several IDL files. During that process, the following rules determine the number and content of each file:

Standard Files for Specification Translation

The specification translation assumes the existence of a number of standard files containing base definitions and classes. These are as follows:

ASN1Types.idl
contains the base definitions for translating ASN.1 types (see IDL Modules for Builtin ASN.1 Types).

ASN1Limits.idl
contains the definitions for ASN.1 limits (see ASN1Limits.idl File).


Example

Using the recommended nickname convention, the nickname for the standard X.501 InformationFramework ASN.1 module will be X501Inf, and will also be used as the filename for the generated IDL:

Example: File X501Inf.idl




// Generated from X501.asn1
// X501Inf.idl file:
 
#ifndef _X501INF_IDL_
#define _X501INF_IDL_
 
module X501Inf {
 
 
};
 
#endif /* _X501INF_IDL_ */


Lexical Translation

Since the IDL uses ISO Latin-1 (ISO/IEC 8859-1) character set, each character in the ASN.1 character set maps to itself in IDL.

In ASN.1, names for type-references, identifiers, value-references and module-references consist of an arbitrary sequence of one or more letters, digits, and hyphens. The letters and digits in ASN.1 names are mapped directly retaining case. The ASN.1 hyphen ("-") maps to IDL underscore ("_"). Subject to the possible suffixing as described below, names will be preserved, for example, CMISFilter type in ASN.1 will be mapped as CMISFilterType[n] in IDL.

ASN.1 names are case sensitive and IDL identifiers are not. In addition, ASN.1 has different name-spaces for types-references, identifiers and value-references, whereas IDL has a single namespace. Both have scoped naming spaces, but the naming scopes do not directly map. For example, enumerated types create a new naming scope in ASN.1 but not in IDL. It is therefore not possible to simply map ASN.1 names to IDL identifiers. To handle this, the translator must maintain a table of all identifiers in each resulting IDL scope and modify colliding identifiers within a given IDL scope to avoid such conflicts.

Not all conflicts arise from ASN.1 names. Additional conflicts could arise from clashes with IDL reserved words such as "interface" and identifiers and types defined in the base IDL file ASN1Types.idl and ASN1Limits.idl. The translator must take account of such existing definitions and use the same disambiguating mechanism to avoid clashes.

Clearly, all identifiers could be modified in order to disambiguate, however the goal of the translation mechanism to generate the simplest mapping where possible, leads to a slightly more complex algorithm but preserves a direct mapping where no ambiguity arises. The mapping used will therefore be context dependent and hence the final mapping will be a necessary input to the Interaction Translation since any gateway must be able to replicate such mappings. The rules for disambiguation are as follows:

Rule 1

The first identifier is mapped "as it is", that is, the case is preserved. Second and subsequent identifiers in the same IDL scope that do not differ or differ only in case will be suffixed with a single underbar followed by a numeric disambiguator, for example, <_n> where n is the lowest integer number (counting from 1) that would not produce a clash. Thus the first identifier will not have a suffix, and the second and subsequent clashing identifiers will have suffixes <_1>, <_2> and so on. For example, the ASN.1 identifiers aab, aAB and aaB would be translated as aab, aAB_1 and aaB_2 respectively.

When disambiguating CHOICE values, this rule is also applied where "Choice" is appended to the IDL enumerator; in this case, "Choice<_n>" should be appended instead.

Rule 2

The first type reference is mapped with the suffix "Type". Second and subsequent type references in the same IDL scope that differ only in case will be suffixed with "Type<n>" where <n> is the count of such instances. Thus the first type reference will not have the suffix "Type", and second and subsequent clashing type references will have suffixes "Type1", "Type2" and so on. The case of the type reference is always preserved. For example, the ASN.1 type references Aab, AAB and AaB would be translated as AabType, AABType1 and AaBType2 respectively.

This rule is also applied in the case where "Choice" and "Choice<n>" is appended in order to disambiguate CHOICE types.

Rule 3

Value-references are handled as identifiers.

With this scheme, type references cannot clash with identifiers or value references and these latter two are disambiguated numerically. Since the most commonly occurring clash is between data type names and identifiers differentiated only by the case of the first letter, this algorithm avoids the majority of clashes. For example, constructs such as eventRecord EventRecord translate to EventRecordType eventRecord;. In addition, it is easy to recover the original ASN.1 type identifier by finding the "Type" suffix and removing it and all subsequent text from the identifier. Giving preference to identifiers ensures minimum impact on the names of members of sequences and sets and such like so that application code is minimally impacted. The "Type<n>" suffix marks all types explicitly which, in many ways, helps code readability.

Further ambiguity can arise from directly translated names colliding with generated names. For example, a type may have name MyData, and a variable may have name myDataType. The translation of MyData would be MyDataType and this would collide with the direct translation of myDataType. This would be resolved by the normal disambiguation rule. In the same way, translations of ASN.1 constructs may append Opt, Default, etc, to type names, so this must also be considered during disambiguation.

The use of single underbar to separate numeric disambiguators does not guarantee that there will be no further clashes with other ASN.1 identifiers. This should be resolved using the same disambiguation rule.

In subsequent examples, it is assumed that there are no clashes with identifiers or types outside the given ASN.1 text. Thus identifiers largely map unchanged and type identifiers are mapped mapped with the "Type" suffix.

Warning

This disambiguation scheme is order sensitive. Thus changes to the ASN.1 which make no difference to the meaning and hence are allowed, may impact the translation changing some of the disambiguation. It is important that users are aware of this and use identical source for both manager and agent sides. It is recommended that the unmodified standard texts are used.

For lexical mapping collisions, a strict order of the ASN.1 input actually mapped to IDL is applied. That is, identifiers, names or literals that are not used for the translation process, are not considered for lexical collision purposes. This means that:

Whenever mapping says that the original ASN.1 code should first be modified or expanded prior to being translated, the resulting code is the valid one for the lexical mapping collision algorithm. This actually affects selection types, COMPONENTS OF clauses, WITH COMPONENTS clauses, and other unused identifiers, (for example, value numbers of an enumerated literal when the value is specified as a defined value, or identifiers of unreachable code as in the examples below).

Example

Here the "x" representing the value of the enumerated literal "b" in type "A" is not used in the translation process:

A ::= ENUMERATED { a(1), b(x) } B ::= ENUMERATED { x(1), y(2) } x ::= INTEGER x_1 = 3

The defined value "x" of the "b" literal in "A" type is not mapped, so it is not considered. Thus resulting mapping code would be:


enum AType = {a, b}; enum BType = {x, y}; const ASN1_Integer x_1 = 3;

Mapping ASN.1 Module to IDL Module

ASN.1 Modules are defined according to the production rules in Production of Module Definition which are printed here for reference for the discussion of the subsequent text.



<ModuleDefinition> ::=
<ModuleIdentifier> DEFINITIONS <TagDefault> ::= BEGIN
<ModuleBody>
END
 
<ModuleIdentifier> ::=
<modulereference>
<DefinitiveIdentifier>
 
<TagDefault> ::= EXPLICIT TAGS | IMPLICIT TAGS |
AUTOMATIC TAGS | empty
 
<ModuleBody> ::=
<Exports> <Imports> <AssignmentList> | empty
 
<Exports> ::=
EXPORTS <SymbolsList> ; | empty
 
<Imports> ::=
IMPORTS <SymbolsImported> ; | empty
 
<SymbolsImported> ::=
<SymbolsFromModuleList> | empty
 
<SymbolsFromModuleList> ::=
<SymbolsFromModule> | <SymbolsFromModuleList> <SymbolsFromModule>
 
<SymbolsFromModule> ::=
<SymbolList> FROM <GlobalModuleReference>
 
<GlobalModuleReference> ::=
<modulereference AssignedIdentifier>
 
<SymbolList> ::=
<Symbol> | <SymbolList> , <Symbol>
 
<AssignmentList> ::=
<Assignment> | <AssignmentList> <Assignment>


Table: Production of Module Definition

Mapping of Module Identifier

A module identifier is mapped as the name of the corresponding IDL module. Module identifier information is mapped as an IDL comment as follows:

// ModuleIdentifier:<ModuleIdentifier>

In addition, an IDL #pragma is generated as follows:


#pragma ID <moduleNickname> "OSIOID:<DefinitiveIdentifier>"

If there is no definitive identifier then nothing is generated.

Mapping of Tag Default

The information in the TagDefault production is ignored during the mapping of ASN.1 to IDL.

Mapping of Exports

The information pertaining to Exports productions is ignored during the mapping of ASN.1 to IDL.

Mapping of Imports

IMPORTS clauses are mapped as a list of #include directives for the file corresponding to the imported module inside the ASN.1 module and a list of typedefs and constants. The relevant references enable the module to be disambiguated and hence its nickname identified. It should also be noted that the compiler must parse any imported document in order to apply the disambiguation rules for clashing identifiers. Naturally, this is recursive; when parsing an imported document, any documents it imports must also be parsed. If a required document is not available, the behaviour of the translator is implementation defined. If this recursive inclusion of modules has circular dependencies, the generated IDL will be uncompilable. This situation must be solved before attempting translation.

For each module imported from, an include directive is generated before the importing module definitions as follows:


#include <module_nickname>.idl

The production:


IMPORTS <symbol1>, ..., <symboln> FROM <GlobalModuleReference>

is mapped as follows for imported types:


typedef <moduleNickname>::<mapped symbol1> <mapped symbol1> ........ typedef <moduleNickname>::<mapped symboln> <mapped symboln>

The two mapped symbols may be different as they are disambiguated in different naming contexts - the imported from and the importing modules.

For imported ASN.1 values, code will be generated depending on whether their types are simple or complex (see Mapping of Values). For simple types the following code will be generated:


const <type of imported const> <mapped symbol1> = <moduleNickname>::<mapped symbol1> ... const <type of imported const> <mapped symboln> = <moduleNickname>::<mapped symboln>

For imported ASN.1 values of complex types, a method with a corresponding identifier will be generated within the constValues interface of the IDL module being generated.

Note that <GlobalModuleReference> is only used to identify the unique module nickname and does not appear in the IDL.

Mapping of Referencing Type and Value Definition

When identifiers from external modules are referenced, they must be mapped as IDL scoped identifiers where the scope is <moduleNickname> since these are unique in the translation environment, no further scoping is required.

Otherwise, they are mapped as per normal.

Mapping of Assigning Types

Type assignments are mapped to typedefs in IDL.

Mapping of Assigning Values

Each constant value defined in ASN.1 is mapped as the definition of a constant literal in IDL. IDL supports constants for only the primitive types, and values not directly representable in IDL are mapped via the mechanism in Mapping of Values.

Mapping of ASN.1 Comments

The mapping of comments is optional. If they are mapped, it should be noted that re-ordering of the IDL may fail to re-order the associated comments, thus rendering the translated comments of doubtful value.

An ASN.1 comment commences with a pair of adjacent hyphens ("--") and ends with the next pair of adjacent hyphens or at the end of the line, which ever comes first. In IDL, comments are delimited as per C++, using either /* and */ or // and end of line. The choice of which delimitation to use is left as an implementation concern.

Mapping of Primitive ASN.1 Types and Values

Mapping of ASN.1 Primitive Types

Mapping of ASN.1 Types to IDL Types describes the default mapping of primitive ASN.1 types to IDL types. The algorithm for mapping types is based on reducing all types to primitive elements and mapping each primitive according to this table. As an aid to mapping back to the ASN.1 document, typedefs are provided which define the ASN.1 primitive typename in IDL. For example, the ASN.1 type INTEGER maps to the IDL type long. However, a typedef of the IDL identifier ASN1_Integer to long is provided to improve consistency between the original document and the resultant IDL.




ASN.1 Type IDL Type

BOOLEAN typedef boolean ASN1_Boolean;

INTEGER typedef long ASN1_Integer;

REAL typedef double ASN1_Real;

NULL
typedef char ASN1_Null;
const ASN1_Null ASN1_NullValue = '\x00';


ENUMERATED enum <enumName> {<elem1>,......, <elemn>};

BIT STRING typedef sequence<octet> ASN1_BitString;
  // supported by PIDL

OCTET STRING typedef sequence<octet> ASN1_OctetString;

IA5 STRING typedef string ASN1_IA5String;
ISO646 STRING typedef string ASN1_ISO646String;
NUMERIC STRING typedef string ASN1_NumericString;
PRINTABLE STRING typedef string ASN1_PrintableString;
TELETEXT STRING typedef string ASN1_TeletextString;
T61 STRING typedef string ASN1_T61String;
VIDEO STRING typedef string ASN1_VideoString;
VISIBLE STRING typedef string ASN1_VisibleString;

GENERAL STRING typedef sequence<octet> ASN1_GeneralString;
GRAPHIC STRING typedef sequence<octet> ASN1_GraphicString;

BMP STRING typedef sequence<unsigned short> ASN1_BMPString;
UNIVERSAL STRING typedef sequence<unsigned long> ASN1_UniversalString;

OBJECT IDENTIFIER typedef string ASN1_ObjectIdentifier;

ANY typedef any ASN1_Any;
ANY DEFINED BY typedef any ASN1_DefinedAny;

Tagged as untagged type

EXTERNAL
struct ASN1_External {
ASN1_ObjectIdentifier syntax;
ASN1_DefinedAny data_value; // by 'syntax'
};


Table: Mapping of ASN.1 Types to IDL Types

Mapping of Values

IDL only permits constants of primitive types (integer, boolean, floating-point, character and string types). As such it cannot adequately represent constant values of more complex ASN.1 types. Simple and typesafe access to these constants, which are widely used for such things as default values, is important. For all constant values which cannot be represented in IDL, a special constant values PIDL interface is defined in the same module as the module containing the const declaration. Whilst this does not actually define a constant, the constant value is accessible as the return value of an operation in this interface. The implementation of this constant values interface is responsible for ensuring that the values returned correspond to the declaration2.

Functions defined as translation of complex ASN.1 constants are included at the end of the IDL modules in the ConstValues interface. In general, a constant declaration of type <ConstType> with identifier <constName> would result in the generation of the operation:


interface ConstValues { <ConstType> <constName>(); // returns "<text of constant value definition>" ........ };

For examples, see Examples.

Mapping of NULL types

The ASN.1 NULL type is a type which has only one value, NULL. This type is mostly used to indicate the absence of a component of a sequence or set, or of an alternative of a choice, The ASN.1 NULL type is mapped to a typedef and a constant with the value:

typedef char ASN1_Null; const ASN1_Null ASN1_NullValue = '\x00';

Mapping of Boolean Type

ASN.1 BOOLEAN types map directly to the IDL boolean type however, a typedef for the type ASN1_Boolean is provided in IDL. This is used in the translation to give greater textual correspondence to the source ASN.1.
Examples

Example: Mapping of Boolean Type





ASN.1 IDL

Married ::= BOOLEAN typedef ASN1_Boolean MarriedType;
maritalStatus Married ::= TRUE const MarriedType maritalStatus = TRUE;


Mapping of Integer Type

Unconstrained ASN.1 INTEGER is mapped to the IDL type long. As the type ASN1_Integer is provided in the IDL, the mapping uses this directly. ASN.1 integer subtypes with value ranges have special mappings covered in Mapping of Value Range. It is assumed that non-subtyped INTEGER can be handled by 32-bit integer. In addition, name-number lists result in additional IDL constants holding the named values.

For a named-number, the form <name>(<number>) within an integer type <type> generates the IDL const declaration:


const <type>Type[n] <name> = <number>;

The form <name>(<identifier>) within an integer type <type> generates the IDL const declaration:


const <type>Type[n] <name> = <translated identifier>;

Note that the identifier must also be translated in order to ensure that the right IDL identifier is referenced. Note also that these named values are subject to disambiguation in the normal way.

Examples

Example: Mapping of Integer Type





ASN.1 IDL

T0 ::= INTEGER typedef ASN1_Integer T0;

a INTEGER ::= 1 const ASN1_Integer a = 1;

T1::= INTEGER { a(2) } typedef ASN1_Integer T1Type;
  const T1Type a = 2;

ax INTEGER ::= 1 const ASN1_Integer ax = 1;
aX INTEGER ::= 2 const ASN1_Integer aX_1 = 2;
T2 ::= INTEGER { a(3), b(aX) } typedef ASN1_Integer T2Type;
c T2 ::= b const T2Type a = 3;
d T2 ::= a const T2Type b = aX_1;
  const T2Type c = b;
  const T2Type d = a;



Mapping of Real Type

The ASN.1 REAL type is mapped to IDL type double, and a typedef for the type ASN1_Real is provided and is used in the translated IDL. The reason for selecting double is to provide maximum precision since there is no way of determining the required precision.

ASN.1 constants PLUS-INFINITY and MINUS-INFINITY are mapped to IDL double precision floating point constants plus_infinity and minus_infinity respectively. The values of these constants are defined int the file ASN1Limits.idl. In ASN.1, a floating point value may be defined as a triple of mantissa, base and exponent. In this case, the value of the constant is calculated and the corresponding double precision decimal floating point constant is used

Examples

Example: Mapping of Real Type





ASN.1 IDL

AngleInRadians ::= REAL typedef ASN1_Real AngleInRadiansType;

pi REAL ::= { 3141592653897, 10, -12} const ASN1_Real pi =3.141592653897;



Mapping of Enumerated Type

As shown in Mapping of ASN.1 Types to IDL Types, ASN.1 ENUMERATED is mapped to an IDL enum type. The primary benefit of this mapping is that it is more natural for the programmer. With the OMGs C++ mapping, IDL enum is mapped to a C++ enum so there is also strong type checking available.

This does not preserve the actual values of the element values and the Interaction Translation process would need to support the dynamic mapping between values received on the wire and the corresponding enum values. As a consequence of this, any application requiring to access the actual values would be able to retrieve them via the management knowledge at run-time. The full details of the creation and maintenance of the management knowledge is addressed in the Interaction Translation Document.

It is important to note that the names of values in an enumerated type are identifiers in the enclosing scope since in IDL, enum does not define a new naming scope. This means that enum names are subject to disambiguation in the normal way but in a slightly wider scope than might otherwise be expected.

Examples

Example: Mapping of Enumerated Type





ASN.1 IDL


Message ::= ENUMERATED {basic(0),
extended(1)}

enum MessageType { basic, extended };



DayOfTheWeek ::= ENUMERATED {
sunday(0),monday(1), tuesday (2),
wednesday (3), thursday(4),
friday(5), saturday(7)}
first DayOfTheWeek ::= sunday

enum DayOfTheWeekType {
sunday, monday, tuesday,
wednesday, thursday,
friday, saturday };

interface ConstValues {
....
DayOfTheWeekType first(); // returns "sunday";
....
};



MaritalStatus ::= ENUMERATED {
single(0), married(2), widowed(1)}
}

enum MaritalStatusType {
single, married, widowed
};



Mapping of Bit String Type

ASN.1 BIT STRING is mapped to the IDL type ASN1_BitString defined as sequence<octet> with global scope in the IDL file ASN1Types.idl. ASN1_BitString is defined as follows to match the BER encoding of BIT STRING. The sequence of octet shall have an initial octet followed by zero, one, or more subsequent octets.The bits in the bitstring, commencing with the first bit and proceeding to the trailing bit, shall be placed in bits 8 to 1 of the second octet, followed by bits 8 to 1 of each octet in turn, followed by as many bits as are need in the final octet, commencing with bit 8 (the notation "first bit" and ""trailing bit" is specified in ISO 8824). The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of unused bits in the final octet. The number shall be in the range zero to seven. If the bitstring is empty, there shall be no subsequent octets, and the initial octet shall be zero.

BIT STRING literal values encoded in this way, cannot be represented as IDL constants and are handled via the mechanism defined in Mapping of Values.

Each named bit is mapped as an IDL constant of type unsigned long with value equal to the offset into the bit string. The name of the constant is the given name, disambiguated by the usual rules:


const unsigned long <bitname> = <offset>;

Note that offset may be defined by another constant.

PIDL for BitString Access Functions
To facilitate manipulation of BitString values, the following PIDL interface is proposed. This essentially defines a library of utility BitString access functions to standardise and simplify BitString manipulation.

interface ASN1_BitStringHandler { // PIDL typedef short BitValue; ASN1_BitString createBitString (in unsigned long number_of_bits); BitValue getBit (in ASN1_BitString bit_string, in unsigned long position); void setBit (inout ASN1_BitString bit_string, in unsigned long position, in BitValue new_bit_value); long length (in ASN1_BitString bit_string); string asString (in ASN1_BitString bit_string); // produces a string with binary values ("1001011B") void setFromString (inout ASN1_BitString bit_string, in string string_value); };

Examples

Example: Mapping of Bit String Type





ASN.1 IDL


MessageFlag ::= BIT STRING {
posResp (0), negResp (1),
doNotForward (2) }

typedef ASN1_BitString MessageFlagType;
const unsigned long posResp = 0;
const unsigned long negResp = 1;
const unsigned long doNotForward = 2;



T0 ::= BIT STRING
a INTEGER ::= 1
T1::= INTEGER { a(2) }
T2 ::= BIT STRING { a(3), b(a) }

typedef ASN1_BitString T0Type;
const ASN1_Integer a = 1;
typedef ASN1_Integer T1Type;
const T1Type a_1 = 2;
typedef ASN1_BitString T2Type;
const unsigned long a_2 = 3;
const unsigned long b = a;



G3FacsimilePage ::= BIT STRING
-- a sequence of bits conforming to
-- Recommendation T.4
image G3FacsimilePage ::=
'100110100100001110110'B
trailer BIT STRING ::=
'0123456789ABCDEF'H

typedef ASN1_BitString G3FacsimilePageType;
interface ConstValues {
G3FacsimilePageType image();
// "'100110100100001110110'B";
BitString trailer();
// "'0123456789ABCDEF'H";
};



PersonalStatus ::= BIT STRING
{married (0), employed (1),
veteran(2), collegeGraduate (3)}
johnDoe PersonalStatus ::=
{married, employed,
collegeGraduate}

typedef ASN1_BitString PersonalStatusType;
const unsigned long married = 0;
const unsigned long employed = 1;
const unsigned long veteran = 2;
const unsigned long collegeGraduate = 3;
interface ConstValues {
PersonalStatusType johnDoe();
// "married, employed, collegeGraduate";
};



Mapping of Octet String Type

ASN.1 OCTET STRING is mapped to the IDL type ASN1_OctetString which is defined as sequence<octet>.

OCTET STRING literals can be either binary of hexadecimal strings. In either case, the value is expanded from left to right as a sequence of bits with octets taken in order from the left. The final octet is padded with 0 bits if necessary.

Octet string literals cannot be represented as constants in IDL and hence, are mapped in accordance with Mapping of Values.

Examples

Example: Mapping of Octet String Type





ASN.1 IDL


G4FacsimilePage ::= OCTET STRING
-- a sequence of octets conforming to
-- Recommendation T.5 and T.6
image G4FacsimilePage ::=
'3FE2EABAD471005'H

typedef ASN1_OctetString G4FacsimilePageType;
interface ConstValues {
G4FacsimilePageType image();
// "'3FE2EABAD471005'H";
};



Mapping of ASN.1 String Types

Builtin ASN.1 string types can be divided into two categories: those that cannot contain value X'00; in them and those that can. Strings in the first category are simply mapped as string. Strings in the second category are further classified in two groups:

The IDL file ASN1Types.idl contains the following typedefs which can then be used directly in the translated IDL:


// These can contain X'00' typedef sequence<octet> ASN1_GeneralString; typedef sequence<octet> ASN1_IA5String; typedef sequence<octet> ASN1_VideotexString; // These should support wide characters typedef sequence<unsigned short> ASN1_BMPString; typedef sequence<unsigned long> ASN1_UniversalString; // These cannot typedef string ASN1_NumericString; typedef string ASN1_PrintableString; typedef string ASN1_VisibleString; typedef ASN1_VisibleString ASN1_ISO646String; typedef string ASN1_GraphicString; typedef ASN1_GraphicString ASN1_ObjectDescriptor; typedef string ASN1_TeletexString; typedef ASN1_TeletexString ASN1_T61String;
Mapping of Useful Type
ASN.1 provides a further set of useful sub-types of string. The main difference between these types and their base types is that they have well-defined Tags. Since the tag information is ignored in Specification Translation, these types are defined by the following IDL typedefs and they are used directly in generated IDL.

typedef ASN1_GraphicString ASN1_ObjectDescriptor;

GeneralizedTime and UTCTime are formatted string representations of time values (see reference ASN.1, clause 32). As strings, these values are not particularly easy to manipulate, for example, for comparison. For this reason PIDL functions are provided to manipulate these values converting to and from a more useful form corresponding to the POSIX timeval structure. Note that both GeneralizedTime and UTCTime optionally contain time zone information. Where this is not present, all operations assume that the time is local time and work accordingly.


PIDL for Time Access Functions
This PIDL provides routines to convert between string and struct and also a comparison routine. This routine takes two values, t1 and t2, and returns -1, 0 or 1 depending on whether t1 < t2, t1 = t2 or t1 > t2 respectively.

#include "ASN1Types.idl" interface ASN1_GeneralizedTimeHandler // PIDL { void set(inout ASN1_GeneralizedTime t, in unsigned long seconds, in long useconds, in long tzp); void get(in ASN1_GeneralizedTime t, out unsigned long seconds, out long useconds, out long tzp); short compare(in ASN1_GeneralizedTime t1, in ASN1_GeneralizedTime t2); void setFromString( inout ASN1_GeneralizedTime t, in string s ); string asString(in ASN1_GeneralizedTime t ); } interface ASN1_UTCTimeHandler // PIDL { void set(inout ASN1_UTCTime t, in unsigned long seconds, in long useconds, in long tzp); void get(in ASN1_UTCTime t, out unsigned long seconds, out long useconds, out long tzp); short compare(in ASN1_UTCTime t1, in ASN1_UTCTime t2); void setFromString(inout ASN1_UTCTime t, in string s ); string asString(in ASN1_UTCTime t ); }

The format of the time strings is the ASN.1 value notation.


Mapping of Object Identifier

ASN.1 OBJECT IDENTIFIER is mapped to IDL type ASN1_ObjectIdentifier which is defined as string:

typedef string ASN1_ObjectIdentifier;

The contents of an ASN.1_ObjectIdentifier will be the value of the ASN.1 object identifier, in dot string notation (sequence of decimal representation of numbers in the object identifier, separated by dots, without any intervening spaces).

Constants of this ASN1_ObjectIdentifier type are represented as string literals which contain the value of the object identifier, in dot string notation.

Examples

Example: Mapping of Object Identifier





ASN.1 IDL

AttributeId ::= OBJECT IDENTIFIER typedef ASN1_ObjectIdentifier
  AttributeIdType;


arfProbableCause OBJECT IDENTIFIER ::= {
joint-iso-ccitt ms(9) smi(3) part2(2)
standardSpecificExtension(0) arf(0)
}

adapterError OBJECT IDENTIFIER ::= {
arfProbableCause 1 }

const ASN1_ObjectIdentifier
arfProbableCause = "2.9.3.2.0.0";

const ASN1_ObjectIdentifier
adapterError = "2.9.3.2.0.0.1";



Mapping of Any Type

ASN.1 ANY, ANY DEFINED BY are mapped to the IDL types ASN1_Any, ASN1_DefinedAny respectively. The defined IDL types are derived from the IDL any type. This is because there is no way of capturing the defining information available from ANY DEFINED BY. However, the IDL any type is capable of carrying arbitrary types and ANY DEFINED BY primarily references parameters types which are mapped as IDL types and hence may be carried - only the strong typing is lost. To preserve the information, the defining clause of an ANY DEFINED BY is mapped as a comment.
Examples

Example: Mapping of Any Type





ASN.1 IDL


Attribute ::= SEQUENCE {
attributeId OBJECT IDENTIFIER,
attributeValue ANY DEFINED BY attributeId
}

struct AttributeType {
ASN1_ObjectIdentifier attributeId;
ASN1_DefinedAny attributeValue;
// defined by attributeId
};


Mapping of Tagged Type

Tag information is ignored during Specification Translation and a tagged type is mapped in the same way as an untagged type. Mapping tag information is an issue for Interaction Translation.

Mapping of External Type

The ASN.1 EXTERNAL type is mapped to the following:

typedef X208Ext::ExternalType ASN1_External;

The module X208Ext is the direct application of the ASN.1 to IDL mapping to the ASN.1 definition of the EXTERNAL type, as follows:

ASN.1
External ::= [UNIVERSAL 8] IMPLICIT SEQUENCE {
direct-reference OBJECT IDENTIFIER OPTIONAL,
indirect-reference INTEGER OPTIONAL,
data-value-descriptor ObjectDescriptor OPTIONAL,
encoding CHOICE {
single-ASN1-type [0] ANY,
octet-aligned [1] IMPLICIT OCTET STRING,
arbitrary [2] IMPLICIT BIT STRING
}
}
IDL
module X208Ext {
 
union ASN1_ObjectIdentifierOpt switch (boolean) {
case TRUE: ASN1_ObjectIdentifier value;
};
 
union ASN1_IntegerOpt switch (boolean) {
case TRUE: ASN1_Integer value;
};
 
union ASN1_ObjectDescriptorOpt switch (boolean) {
case TRUE: ASN1_ObjectDescriptor value;
};
 
enum ExternalEncodingTypeChoice {
single_ASN1_typeChoice,
octet_alignedChoice,
arbitraryChoice
};
 
union ExternalEncodingType switch(ExternalEncodingTypeChoice) {
case single_ASN1_typeChoice: ASN1_Any single_ASN1_type;
case octet_alignedChoice: ASN1_OctetString octet_aligned;
case arbitraryChoice: ASN1_BitString arbitrary;
};
 
struct ExternalType {
ASN1_ObjectIdentifierOpt direct_reference;
ASN1_IntegerOpt indirect_reference;
ASN1_ObjectDescriptorOpt data_value_descriptor;
ExternalEncodingType encoding;
};

Note that this follows the rules described in Mapping of ASN.1 Constructed Types for Constructed Types. The use of this type is therefore the same as that of any other ASN.1 constructed type.

Mapping of ASN.1 Constructed Types

Constructed types are those which are composed of several other types (constructed or not). ASN.1 supports the following constructors: CHOICE, SET, SEQUENCE, SET OF and SEQUENCE OF.

The approach to mapping constructed types is to map them according to Mapping of ASN.1 Type Constructors to IDL. However, there are some additional complexities which must be addressed. To facilitate type manipulation by programmers, complex data structures will be simplified by creating new IDL intermediate types (see Composite Types). Some ASN.1 constructs must be resolved to name-type pairs and then translated. These include selection types and COMPONENTS OF. On the other hand, OPTIONAL elements are converted to an IDL union type which is then used as the type of the optional element. Subtype constraints based on the use of WITH COMPONENTS clause will be explained in the corresponding section for subtype mapping. It is anticipated that an intermediate ASN.1 type is generated, equivalent to the original one but without the subtype constraint, and then the common mapping is applied.

CHOICE, SET and SEQUENCE result from the aggregation of other types. From the ASN.1 grammar point of view, they are a list of elements. Each element is basically formed by an ASN.1 Named Type and some optional extra features.

In the case of SET and SEQUENCE type definitions, elements can be OPTIONAL or have default values. Also the COMPONENTS OF structure may be specified instead of the set of elements it represents.

In the case of the CHOICE type definition, none of these features are allowed , but instead the type of the Named Type can take the form of a selection type (which is not allowed for SET or SEQUENCE).

SET OF and SEQUENCE OF result from the aggregation of several instances of the same type which will be called an "item" in this document.

ASN.1 Construct IDL Construct Comments
CHOICE union/switch An enum is to be defined for switch case-constant.
SET SEQUENCE struct struct members are declared in the same order as they are declared in the ASN.1 type.
Selection Type mapped as type of selected element i.e. the selection type is first transformed to obtain its real element type
OPTIONAL
union <mapped-type-name> Opt
switch (boolean) {
case TRUE: <type> value;
}
an extra type is created to indicate whether the element is present.
DEFAULT typedef <mapped-type-name>Opt
<mapped=-type-name>Def;
same as OPTIONAL, with an extra typedef created to indicate whether the element is Default.
SET-OF type
SEQUENCE-OF type
sequence<mapped-type-name>  

Table: Mapping of ASN.1 Type Constructors to IDL

Rules to map each element or item in a constructed type are provided below:

  1. If element contains a selection type or it is a COMPONENTS OF clause, expand it to obtain the correponding element(s).

  2. Obtain the name of the element or item. Apply rules for Anonymous Elements and Items if necessary. (See Anonymous Elements and Items).

  3. Resolve name collisions with previous elements inside the scope of the constructed type.

  4. Once the name of the element or item has been obtained, then obtain its corresponding type:

  5. Once the name and the type of the element/item is obtained, create the corresponding IDL mapped element/item.

  6. Finally, if default values were defined, create the corresponding constant as described in DEFAULT Components.

Composite Types

As described above, an element or item always has a type definition, and depending of its type, special treatment may be needed. In such cases, these types are called Composite Types. Composite Types include the following:

When used in the elements or items of a constructed type, these are considered composite types. To avoid nested type declarations, they are extracted and a new IDL type is defined (unless recursion is detected). Thus a named IDL type definition is created.

The name of the new IDL type assignment is formed by concatenating the ASN.1 name of the container type (that is, the name of the constructed type that contains the current element/item without the "Type[<n>]" suffix) with the name of the element/item with an upper case first letter. Note that if it is an element, disambiguation with prior element names in the same constructed type must be done. Then the general rules for lexical mapping disambiguation of types are applied (that is, the corresponding Type[<n>] suffix is added).

Note that if recursion is detected, the rules described in Mapping of Recursive Types (Mapping of Recursive Types) apply.

Examples

Example: Mapping of ASN.1 Constructed Types





ASN.1 IDL


-- example with elements
Bar ::= SEQUENCE {
-- definition of an composite type
paff SEQUENCE {
a INTEGER,
b VisibleString
},
-- definition of a composite type
dummy ENUMERATED {
one(1), two(2)
},
-- reference to primitive type,
c INTEGER
}

// mapping of the ASN.1 Bar type
struct BarPaffType
{
ASN1_Integer a;
ASN1_VisibleString b;
};

enum BarDummyType {one, two);

struct BarType
{
BarPaffType paff;
BarDummyType dummy;
ASN1_Integer c;
};


-- example with items (no composite type)
Array ::= SET OF INTEGER

//mapping of the ASN.1 Array type
typedef sequence<ASN1_Integer> ArrayType;


Anonymous Elements and Items

As stated before, an element is basically composed of an ASN.1 Named Type. This is composed of an identifier which is the name of the Named Type and the type itself. The presence of this name is optional in the ASN.1 grammar, though this is strongly discouraged (and is not allowed in ASN.1:1994).

IDL does not fully support similar constructs, therefore the mapping needs to resolve these issues.

An element without an explicit name is converted to an element with a name taking the form elem<n>, where <n> specifies the position of such element within the definition of the constructed type..

When dealing with an item, if it represents a composite type, a new type is created as described above. A name has to be provided because these definition types have no name associated with them. To provide an homogeneous way to solve the mapping, the identifier item will be used as if it were the name of an element whose type is the one defined by the item. Thus the item identifier will be used as the name of the item when Composite Type rules are to be applied (see Composite Types).

Note that when SET OF and SEQUENCE OF constructs have an item which is a Composite Type, it is always an anonymous item also. This is because items do not have an identifier name in the ASN.1 grammar.

Examples

Example: Anonymous Elements and Items





ASN.1 IDL


-- example with anonymous elements
A::=SEQUENCE {
INTEGER,
b INTEGER,
BOOLEAN,
ENUMERATED {
one(1), two(2)
}
}

//mapping of ASN.1 A type
enum AElem4Type {one, two};

struct AType {
ASN1_Integer elem1;
ASN1_Integer b;
ASN1_Boolean elem3;
AElem4Type elem4;
};


-- example of anonymous items
CorrelNotif ::= SET OF SEQUENCE {
correlNotif SET OF
NotificationIdentifier
}

// mapping of ASN.1 CorrelNotif type
typedef sequence <NotificationIdentifierType>
CorrelNotifItemCorrelNotifType;

struct CorrelNotifItemType {
CorrelNotifItemCorrelNotifType
correlNotif;
};

typedef sequence<CorrelNotifItemType>
CorrelNotifType;



Mapping of Choice

ASN.1 CHOICE types are mapped to IDL union types. Since IDL unions are discriminated, an IDL enum type will be defined for the type of the discriminator. In order to do this it is first necessary to transform any composite types in the list of alternates. This is done via the mechanism described in Anonymous Elements and Items. Similarly SELECTION alternates are first resolved as per Mapping of Selection.

The IDL identifier for the type of the discriminator is formed by adding a suffix "Choice" to the translated identifier of the CHOICE type. For each alternate, there will be one identifier in the discriminator type. This translated identifier is the identifier of the alternate suffixed by "Choice" and disambiguated within its IDL scope in the normal way.

Finally, the union type itself is constructed with one case for each alternate with label according to the enum type and the type and identifier according to the (intermediate) alternate. The resulting IDL looks like:


enum <choicetype>Choice { <translated-identifier>Choice, ....... // one for each alternate } union <choicetype> switch (<choicetype>Choice) { case <translated-identifier>Choice: <type> <translated-identifier>; ....... // one for each alternate }

where <choicetype> is the name of the translated type (including "Type" suffix and numeric disambiguator if necessary).

Examples
The following shows examples of Choice and Recursion. Note that Composite Type Removal is not illustrated.

Example: Examples of Choice and Recursion





ASN.1 IDL


Context ::= CHOICE {
id INTEGER,
data EXTERNAL
}

enum ContextTypeChoice { idChoice, dataChoice };
union ContextType switch (ContextTypeChoice) {
case idChoice: ASN1_Integer id;
case dataChoice: ASN1_External data;
};


Here is a fragment of the X.721 | ISO/IEC 10165-2 (1992) definition of ProbableCause showing the constants adapterError and applicationSubsystemFailure.


Attribute-ASN1Module { joint-iso-ccitt ms(9) smi(3) part2(2) asn1Module(2) 1} DEFINITIONS IMPLICIT TAGS ::= BEGIN IMPORTS .......; .... adapterError ProbableCause ::= globalValue : { arfProbableCause 1 } applicationSubsystemFailure ProbableCause ::= globalValue : { arfProbableCause 2 } .......... ProbableCause ::= CHOICE { globalValue OBJECT IDENTIFIER, localValue INTEGER } .......... END


This translates as (assuming that X721Att is the nickname for the Attribute module):

module X721Att { enum ProbableCauseTypeChoice { globalValueChoice, localValueChoice }; union ProbableCauseType switch (ProbableCauseTypeChoice) { case globalValueChoice: ASN1_ObjectIdentifier globalValue; case localValueChoice: ASN1_Integer localValue; }; ... interface ConstValues { ProbableCauseType adapterError(); ProbableCauseType applicationSubsystemFailure(); ... }; };

Then an application program might access the values along the following lines:


// obtain access to the predefined ConstValue object for accessing the // interface defined constants. This would probably be a pseudo object X721Att::ConstValues constants (...); // use constant in an assignment ProbableCauseType pc; pc = constants.adapterError();

Mapping of Selection

The ASN.1 SELECTION construct provides a mechanism where a type may be defined by reference to a named element of a CHOICE. This is handled by first resolving the SELECTION to the selected <identifier,type> pair and using these instead. Where SELECTION is used to identify a type, it is resolved to the named type of the selected element only. When it is used as a name-type pair, it is resolved to the selected <identifier,type> pair.
Examples

Example: Mapping of Selection





ASN.1 IDL


Attribute ::= CHOICE {
number INTEGER,
name VisibleString
}
Ident ::= CHOICE {
id number < Attribute,
name < Attribute
}

// map the Attribute type
enum AttributeTypeChoice {
numberChoice,
nameChoice
};
union AttributeType switch
(AttributeTypeChoice) {
case numberChoice:ASN1_Integer number;
case nameChoice: ASN1_VisibleString name;
};
// map the Ident type
enum IdentTypeChoice {
idChoice,
nameChoice_1
};
union IdentType switch (IdentTypeChoice) {
case idChoice: ASN1_Integer id;
case nameChoice_1: ASN1_VisibleString name;
};



Mapping of Sequence and Set

In ASN.1, a SEQUENCE type is an ordered collection of components which are typed and potentially named. SET types are identical except that the elements are unordered. Since IDL has no unordered record type, both of these types are handled identically and are mapped to an IDL struct. However, ASN.1 has some additional complexities in that it has additional mechanisms to extract elements of sub-records (via the COMPONENTS OF <type> construct) and also allows components to be declared as OPTIONAL.
COMPONENTS OF <type> Production
When a sequence or set type appears as <type> in COMPONENTS OF <type>, that sequence type is resolved to its list of elements before translating to IDL (that is, each component of the sub-record is extracted out effectively flattening the record structure).
OPTIONAL Components
If an element of a SEQUENCE or SET type is declared OPTIONAL, then that component may or may not be present. This is mapped to an IDL union with a boolean switch with only the True case having a value being that of the optional element. This new type is named by appending the type of the optional element with "Opt". The translator must take steps to avoid duplicating these types and this optional type should be generated at most once per module. For the ASN.1 clause:

SEQUENCE { <identifier> <type> OPTIONAL}

the resultant IDL would be:


union <mapped type name>Opt switch (boolean) {case TRUE: <mapped type name> value;};


DEFAULT Components
In ASN.1, clause 20.5 states that if DEFAULT occurs, the omission of a value of that type is exactly equivalent to insertion of the default value. Therefore, the value might be present or absent, with the semantics of the absence being the same as those of having the DEFAULT value specified. As there is no way in IDL to omit a value of a type, another type should be generated. However, this type would have exactly the same structure of the corresponding OPTIONAL (see OPTIONAL Components above). Therefore, this is mapped to an IDL typedef from the "Opt" type to another name formed by appending the type of the element with a DEFAULT value with "Def". The translator must take steps to avoid duplicating these types, and this definition should be generated at most once per module. For the ASN.1 clause:

SEQUENCE { <identifier> <type> DEFAULT <value>; }


the resultant IDL would be:


typedef <mapped type name>Opt <mapped type name>Def;

If the "Opt" type had not been generated yet, then it should be generated here, as described above in OPTIONAL Components.

To access the default value, either a constant or a PIDL function returning such value is provided. Where a programmer in the CORBA domain wants to use the default value, this constant or function call must be explicitly used. So if an ASN.1 DEFAULT value is present for a set or sequence element, an IDL constant will be generated, named by appending Default to the element's identifier and defined as the given default value. For example:


const <mapped type name> <mapped identifier>Default = <value>;


If the type of the element precludes the use of an IDL constant then the mechanism described in Mapping of Values applies. Disambiguation Rule 2 (see Lexical Translation must be applied to the generated constant name.

Translating to IDL
Once any COMPONENTS OF, OPTIONAL, and DEFAULT, clauses have been translated, an IDL struct is generated. Now each element is mapped as a member of the IDL struct in the order in which they appear in the resolved member list.


Examples
Example: Mapping of Sequence and Set

ASN.1 IDL
T::= SEQUENCE {a Ta, b Tb, c Tc} E::= SEQUENCE {f1 E1, f2 T, f3 E3} struct TType {TaType a; TbType b; TcType c;}; struct EType {E1Type f1; TType f2; E3Type f3;};
T::= SEQUENCE { a Ta, b SEQUENCE {b1 T1, b2 T2,b3 T3} c Tc } struct TbType {T1Type b1; T2Type b2; T3Type b3;}; struct TType {TaType a; TbType b; TcType c;}; // The algorithm results in an additional // type TbType for the composite SEQUENCE // type in T.
W ::= SEQUENCE {x Wx, COMPONENTS OF T, y Wy} struct WbType { T1Type b1; T2Type b2; T3Type b3; }; struct WType { WxType x; TaType a; WbType b; TcType c; WyType y; }; // The algorithm expands the // COMPONENTS OF part of W before // translating to IDL.
UserName ::= SET { -- SET treated as -- SEQUENCE personalName VisibleString, countryName VisibleString OPTIONAL } union ASN1_VisibleStringOpt switch (boolean) { case TRUE: ASN1_VisibleString value; }; struct UserName { ASN1_VisibleString personalName, ASN1_VisibleStringOpt countryName, };
A ::= SET { a ENUMERATED {a, b}, b ENUMERATED {a, b} } enum AaType {a, b}; enum AbType {a_1, b_1}; struct AType { AaType a; AbType b; };
Data :: = SEQUENCE { replaceWithDefault BOOLEAN DEFAULT FALSE, defaultValue ValueSpecifier, keyword SEQUENCE { type-reference DefinedType, field Identifier } OPTIONAL, createModifier BIT STRING { withRefObject (0), withAutoNaming (1) } } union ASN1_BooleanOpt switch (boolean) { case TRUE: ASN1_Boolean value; }; typedef ASN1_BooleanOpt ASN1_BooleanDef; const ASN1_Boolean replaceWithDefaultDefault=FALSE; struct DataKeywordType { DefinedTypeType type_reference; IdentifierType field; }; union DataKeywordTypeOpt switch (boolean) { case TRUE: DataKeywordType value; }; typedef ASN1_BitString DataCreateModifierType; const unsigned long withRefObject = 0; const unsigned long withAutoNaming = 1; struct DataType { ASN1_BooleanDef replaceWithDefault; ValueSpecifierType defaultValue; DataKeywordTypeOpt keyword; DataCreateModifierType createModifier; }

The last example is worth looking at in more detail. First, all composite types are handled by explicitly generating named types DataKeywordType and DataCreateModifierType.

Next, apply the rules for OPTIONAL and DEFAULT, generating a named value for the default and union types for both the OPTIONAL and DEFAULT components, that is, replaceWithDefaultDefault, ASN1_BooleanDef, and DataKeywordTypeOpt.

Now all the problem cases have been removed, the translation proceeds naturally resulting in the IDL as shown in the table above.


Mapping of SequenceOf and SetOf

ASN.1 SEQUENCE OF is an ordered list of items of the same type. SET OF is similar except that the list is unordered. Both of these are mapped into the IDL sequence construct. Again, first remove Composite Types by generating a new type on the same basis as Composite Types and Anonymous Elements and Items. That is, using item as the name of the item in the concatenation to generate the new type name.
Examples

Example: Mapping of SequenceOf and SetOf





ASN.1 IDL

RDNSequence::= SEQUENCE OF RDN typedef sequence<RDNType>
  RDNSequenceType;

Status ::= SEQUENCE OF INTEGER { typedef ASN1_Integer StatusItemType;
initializationRequired (0), const StatusItemType initializationRequired = 0;
notInitialized (1), const StatusItemType notInitialized = 1;
initializing (2), const StatusItemType initializing= 2;
reporting(3), const StatusItemType reporting = 3;
terminating (4) const StatusItemType terminating = 4;
}  
  typedef sequence<StatusItemType> StatusType;

A ::= SEQUENCE OF typedef sequence<ASN1_Integer> AItemType;
SEQUENCE OF typedef sequence<AItemType> AType;
INTEGER  


Mapping of EmbeddedPDV and Character String Types

Mapping of EmbeddedPDVType and CHARACTER STRING from ASN.1:1994 to IDL is not addressed at this point. A good approach would be to use the associated SEQUENCE type for value definition and subtyping.

Mapping of Recursive Types

We call recursive types those constructed types that contain an element/item of its own type. Direct recursion is when the element/item is explicitly of the container type or the element/item is a SET OF/SEQUENCE OF the container type, while indirect recursion is when the element/item is of another type, which contains a direct or indirect reference to the initial type. We call nested recusive types those types where indirect recursion happens within an element/item of a composite type (see Resolution of Direct Recursion for further definitions).

Unlike ASN.1, IDL does not support recursive type definitions, except direct recursions when they are used in sequences. Indirect mutual recursion is explicitly prohibited in IDL. The only solution to indirect mutual recursion, then, is to loose type safety by introducing an IDL "any", and semantically restricting its contents to those of the recursive type (applications are responsible for enforcing the semantics).

Resolution of Direct Recursion

ASN.1 direct recursive type definitions are mapped by converting the recursive type reference to an IDL sequence. If such reference occurs directly in a SET OF or SEQUENCE OF, then the sequence is unbounded (or bounded according to size constraints), otherwise, it is bounded to size 1. ASN.1 direct recursive type definitions are expanded in place. Note that if the element has the OPTIONAL or DEFAULT property, then the mapping is also done in place, as specified in the following clauses.

For the ASN.1 clause:


<type> ::= SEQUENCE { <identifier> <type> OPTIONAL }

the resultant IDL would be:


struct <mapped type name> { union <mapped identifier name>Opt switch (boolean) { case TRUE: sequence<<mapped type name>, 1> value; } <mapped identifier name>; };

and for the ASN.1 clause:


<type> ::= SEQUENCE { <identifier> [SET|SEQUENCE] OF <type> OPTIONAL }

the resultant IDL would be:


struct <mapped type name> { union <mapped identifier name>Opt switch (boolean) { case TRUE: sequence<<mapped type name>> value; } <mapped identifier name>; };

Note:
If in this case, the SET/SEQUENCE OF has a size contraint, this constraint should be applied inside the generated IDL sequence, making it bounded, as described in the section on Constrained Types.

These same rules apply when a named type has the DEFAULT property, changing the "Opt" suffix of the IDL union to "Def".

Examples:





ASN.1 IDL


Filter ::= CHOICE {
item [8] FilterItem,
and [9] IMPLICIT SET OF Filter,
or [10] IMPLICIT SET OF Filter,
not [11] Filter
}

enum FilterTypeChoice {
itemChoice, andChoice, orChoice, notChoice
};
union FilterType switch (FilterTypeChoice) {
case itemChoice: FilterItemType item;
case andChoice: sequence<FilterType> and;
case orChoice: sequence<FilterType> or;
case notChoice: sequence<FilterType, 1> not;
}



NameTree ::= SEQUENCE {
generation INTEGER,
parents SET SIZE(0..2) OF NameTree,
children SET OF NameTree
}

struct NameTreeType {
ASN1_Integer generation;
sequence <NameTreeType, 2> parents;
// SIZE constraint: [0..2]
sequence <NameTreeType> children;
}



NumberTree ::= SEQUENCE {
level INTEGER,
root NumberTree OPTIONAL,
children SET OF NumberTree OPTIONAL
}

struct NumberTreeType {
ASN1_Integer level;
union rootDef switch (boolean) {
case TRUE: sequence <NumberTreeType, 1> value;
} root;
union childrenOpt switch (boolean) {
case TRUE: sequence <NumberTreeType> value;
} children;
}


Resolution of Indirect Recursion

When mapping the ASN.1 type assignment corresponding to an indirectly recursive type, all ASN.1 recursive references to this type should be considered from this moment as references to the ASN.1 ANY type. Because of this substitution, recursion is broken, and therefore the general mapping rules are applied.

Note that types must be considered in their ASN.1 input order, and that only recursive references to the type being considered are substituted by this rule.

The references substituted by this rule should be mapped to the IDL type named ASN1_Recursive, defined in the ASN1Types.idl file as:


typedef any ASN1_Recursive;

A comment is added to identify the IDL type corresponding to the substituted reference. Only values of this IDL type are allowed in the ASN1_Recursive. If a value of a different type is inserted, the behavior is undefined.

Examples:





ASN.1 IDL


NameTree ::= SEQUENCE {
rdnInfo RDNInfo,
family SET OF SEQUENCE {
coparent NameTree,
children SET OF NameTree
}
}

typedef sequence<ASN1_Recursive>
NameTreeFamilyItemChildrenType;
// must be NameTreeType
struct NameTreeFamilyItemType {
ASN1_Recursive coparent;
// must be NameTreeType
NameTreeFamilyItemChildrenType children;
};
typedef sequence<NameTreeFamilyItemType>
NameTreeFamilyType;
struct NameTreeType {
RDNInfoType rdnInfo;
NameTreeFamilyType family;
};



A ::= SET OF B
B ::= SEQUENCE { a INTEGER, b A }

struct BType {
ASN1_Integer a;
ASN1_Recursive b; // must be AType
};
typedef sequence<BType> AType;



B ::= SEQUENCE { a INTEGER, b A }
A ::= SET OF B

typedef sequence<ASN1_Recursive> AType;
// must be BType
struct BType { ASN1_Integer a; AType b; };



A ::= SEQUENCE { a INTEGER, b B }
B ::= SEQUENCE { x INTEGER, y C }
C ::= SET OF A

typedef sequence<ASN1_Recursive> CType;
// must be AType
struct BType { ASN1_Integer x; CType y; };
struct AType { ASN1_Integer a; BType b; };



-- NOTE: Unusable ASN.1 types
-- due to circular references.
A ::= SEQUENCE { b B, c C }
B ::= SEQUENCE { a A, c C }
C ::= SEQUENCE { a A, b B }

struct CType {
ASN1_Recursive a; // must be AType
ASN1_Recursive b; // must be BType
};
struct BType {
ASN1_Recursive a; // must be AType
CType c;
};
struct AType {
CType c;
BType b;
}


Mapping of Constraints and Subtypes

Mapping of Constrained Type

When a size constraint is used for either SET OF and SEQUENCE OF constructions, it is mapped to IDL as a bounded sequence, where the upper bound of the IDL sequence is determined by the upper bound on the permitted integer values specified in the constraint. In addition a constant giving the permitted values list is given. The resulting definition is along the lines of:

typedef sequence <<ItemType>, <upper-bound>> <type>;

where <ItemType> is the type of the SEQUENCE OF/SET OF item and <type> is the translated name of the compound type. For example:


T1 ::= SEQUENCE SIZE(0..10) OF ObjectInstance T2 ::= SEQUENCE SIZE(1|3|5) OF INTEGER

maps to:


typedef sequence<ObjectInstanceType, 10> T1Type; // T1Type SIZE(0..10) typedef sequence<ASN1_Integer, 5> T2Type; // T2Type SIZE(1|3|5)

Dynamic access to size information in order to ensure validity may be covered as part of Interaction Translation.

When a size constraint is applied to BIT STRING types, a constant integer literal is generated in the IDL. The identifier for the integer literal is generated by adding "_size" as a suffix to the translated parent type. This is because the mapping of BIT STRING to sequence of octet does not have the necessary granularity to usefully bound the sequence.

When a size constraint is applied to OCTET STRING or other STRING derived types, these are mapped as string or bounded sequences depending on the original type definition, where the upper-bound of the sequence in IDL is determined by the upper-end-value of the size constraint.

However, when the size constraint is applied to a non-composite named type marked as OPTIONAL or DEFAULT it is ignored, except for comment purposes.

More complex type constraints are ignored by Specification Translation and may be addressed in Interaction Translation.

Mapping of Subtype Elements

ASN.1 supports different constructs from IDL and, in particular, subtypes are not easy to represent in IDL. Where possible, a fairly coarse grained approach has been defined. It is left as an implementation concern to check other value ranges and the details are left to the Interaction Translation.
Mapping of Value Range
When a value range is applied to a type INTEGER or a type derived from it, it will be mapped to one of several IDL integer types, depending on the value range, as shown in Mapping ASN.1 INTEGER with Value Range to IDL Types . However, when the value range constraint is applied to a non-composite named type marked as OPTIONAL or DEFAULT, it is ignored except for comment purposes. The default mapping of INTEGER with no subtyping is long.


Mapping ASN.1 INTEGER with Value Range to IDL Types illustrates the selection of the IDL integer subtype based on the ASN.1 value range interval (Min,Max):







Type Min Max Condition IDL Native Type

ASN1_Unsigned16 0 2^16-1 none unsigned short

ASN1_Unsigned 0 2^32-1 2^16 <= upper unsigned long

ASN1_Unsigned64 0 2^64-1 2^32 <= upper unsigned long long

ASN1_Integer16 -2^15 2^15-1 lower < 0 short

ASN1_Integer -2^31 2^31-1 lower < 0 && long
      (lower < -2^15 || 2^15 <=upper)  

ASN1_Integer64 -2^63 2^63-1 lower < 0 && long long
      (lower < -2^31 || 2^31 <= upper)  



Table: Mapping ASN.1 INTEGER with Value Range to IDL Types

The lower and upper bounds for the ASN.1 INTEGER subtype must be within the minimum and maximum stated values for the corresponding IDL type (inclusive), and they should also fulfill the corresponding condition. In the table, the lower bound value of the ASN.1 value range INTEGER subtype is referred to as "lower", and the corresponding upper bound value as "upper"

If the ASN.1 type has a value range INTEGER subtype specification that is beyond the stated limits for ASN1_Unsigned64 and ASN1_Integer64, an ASN1_Unsigned64 will be generated if the lower bound has a positive or 0 value, otherwise an ASN1_Integer64 will be generated. The user will be informed of the translation limitation.

REAL is mapped as double in IDL. Value ranges are ignored.

Mapping of SingleValue
Even if a type is constrained to one or more single values, then this is still a type and translated accordingly. In addition, the set of allowable values is translated as a comment. The type is generated according to the same rules in Mapping of Value Range, with the "lower" and "upper" bounds being the extremes of the set of allowable values. Thus constructs such as:


A ::= INTEGER(1|3|5|7)

would be translated as


typedef ASN1_Unsigned16 AType; // AType (1|3|5|7)
Mapping of ASN.1 MIN and MAX
ASN.1 supports the literals MIN and MAX in value range constraints to denote the smallest resp. the biggest value of the parent type. The use of one of these special values will cause the compiler to select the appropriate ASN1_Integer type as defined in Mapping of Value Range.
Mapping of Permitted Alphabet
If the sub-typing restricts the alphabet of a type then an IDL constant will be generated. Its value will be the allowable alphabet and its identifier is formed based on the rules described for the mapping of Single Values, with the addition of the suffix "_permittedAlphabet".
Mapping of INCLUDES
INCLUDES contained subtypes are ignored. The translator will generate an unconstrained type.
Mapping of InnerTypeConstraints
Inner Type Constraints can be applied to SET OF, SEQUENCE OF, SET, SEQUENCE and CHOICE types or types formed from them by tagging.

Single Type Constraint (WITH COMPONENT) is ignored during Specification Translation. The specified type is generated without constraints.

Multiple Type Constraints (WITH COMPONENTS) are used for SET, SEQUENCE and CHOICE. This kind of constraint contains a list of constraints on the component of types of the parent type. The inner type to which the constraint applies is determined by the identifier.

When Full Specification is used there is an implied presence constraint of ABSENT on all inner types which can be constrained to be absent and which is not explicitly listed.

When Partial Specification is used and the parent type is a SET or SEQUENCE type, there are no implied constraints and any inner type can be omitted from the list. When an empty Presence Constraint is used, it is equivalent to a constraint PRESENT for a SET or SEQUENCE component marked OPTIONAL. In a Partial Specification no such constraint is imposed.

When Partial Specification is used and the parent type is a CHOICE type, there is implied presence constraint of PRESENT on all inner types which are not explicitly listed.

When Multiple Type Constraints are used to define a type from another type, the translation process generates a new type. Its components depend on the Multiple Type Constraints. The rules are as follows:

1.
Define an intermediate ASN.1 type as that of that parent type. The type reference of the new type is that of the constrained type. The new ASN.1 type and the parent are of identical type (SET, SEQUENCE or CHOICE)

2.
for each NamedConstraint in TypeConstraint of Full Specification:

2.1
if the parent type is a SET or SEQUENCE then:

2.1.1
if ValueConstraint is not empty, PresenceConstraint is empty and if the corresponding component type is OPTIONAL, then the corresponding component is copied from parent type to the new ASN.1 type. The ValueConstraint is applied as ConstrainedType of the form Type Constraint for the component.

2.1.2
if PresenceConstraint is not empty:

2.1.2.1
if PRESENT is used then the corresponding component is copied from parent type to the new ASN.1 type; if the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component. If the component has OPTIONAL label, this label will be dropped from the component in the new type.

2.1.2.2
if OPTIONAL is used then the corresponding component is copied from parent type to the new ASN.1 type; if the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component. If the component does not have an OPTIONAL label then component will be labelled OPTIONAL.

2.2
if the parent type is a CHOICE:

2.2.1
if PresenceConstraint is not empty:

2.2.1.1
if ABSENT is used then the corresponding component is not copied from the parent type to the new ASN.1 type; if the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component.

3.
For each NamedConstraint in TypeConstraint of Partial Specification:

3.1
if the parent type is a SET or SEQUENCE and:

3.1.1
if PresenceConstraint is not empty:

3.1.1.1
if PRESENT is used then the corresponding component is copied from parent type to the new ASN.1 type; if the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component. If the component has OPTIONAL label, this label will be dropped from the component in the new type.

3.1.1.2
if OPTIONAL is used then the corresponding component is copied from parent type to the new ASN.1 type; if the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component. If the component does not have an OPTIONAL label then component will be labelled OPTIONAL.

3.1.2
if ValueConstraint is not empty then the corresponding component is copied from parent type to the new ASN.1 type and the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component.

3.1.3
if both ValueConstraint and PresenceConstraint are empty then the corresponding component is copied from parent type to the new ASN.1 type and labelled OPTIONAL. If the ValueConstraint in ComponentConstraint is not empty then the ValueConstraint is applied as ConstrainedType of the form TypeConstraint for the component.

3.2
if the parent type is a CHOICE:

3.2.1
if PresenceConstraint is not empty then copy all the components of parent type to the component:

3.2.1.1
if ABSENT is used then remove the corresponding component from the new ASN.1 type.

4.
Finally, map the new ASN.1 type to IDL as defined for any other ASN.1 type.

When the Inner Type Constraint is applied to a base type that is defined in a different module, it may occur that the types of some of the members of the new subtype are not known in the scope of the current module. In this case they must be explicitly imported from the module in which the base type is defined3.

Examples
Example 1

PDU ::= SET { alpha INTEGER, beta IA5String OPTIONAL, gamma SEQUENCE OF Parameter, delta BOOLEAN } TestPDU ::= PDU (WITH COMPONENTS { ..., delta (FALSE), alpha (MIN..<0) }) -- PartialSpecification FurtherTestPdu ::= TestPDU (WITH COMPONENTS {... , beta (SIZE (5|12)) PRESENT})

is treated as if it were the following equivalent ASN.1 form which can be translated to IDL via the usual rules.


TestPDU ::= SET { alpha INTEGER (MIN..<0), beta IA5String OPTIONAL, gamma SEQUENCE OF Parameter OPTIONAL, delta BOOLEAN (FALSE) } FurtherTestPdu ::= SET { alpha INTEGER (MIN..<0) OPTIONAL, beta IA5String (SIZE (5|12) , gamma SEQUENCE OF Parameter OPTIONAL, delta BOOLEAN (FALSE) OPTIONAL }
Example 2

TestPDU ::= PDU (WITH COMPONENTS { delta (FALSE), alpha (MIN..<0) }) -- FullSpecification

would change the equivalent ASN.1 form to:


TestPDU ::= SET { alpha INTEGER (MIN..<0), delta BOOLEAN (FALSE) }

Now apply the SetType mapping scheme for ASN.1 to IDL on TestPDU and FurtherTestPDU.


Z ::= CHOICE { a A, b B, c C, d D, e E} V::= Z (WITH COMPONENTS { ..., a ABSENT, b ABSENT}) -- TaU & TbU must be absent W::= Z (WITH COMPONENTS { ..., a PRESENT }) -- TaU must be present X::= Z (WITH COMPONENTS { a PRESENT }) -- TaU must be present Y::= Z (WITH COMPONENTS { a ABSENT, b, c}) -- TaU, TdU and TeU must be absent

is treated as the following equivalent ASN.1 form:


V ::= CHOICE { c C, d D, e E} W ::= CHOICE { a A, b B, c C, d D, e E} X ::= CHOICE { a A} W ::= CHOICE { b B, c C}

IDL Modules for Builtin ASN.1 Types

Two modules are declared automatically. The first, stored in a file, called ASN1Types.idl, declares all the IDL types used to translate builtin ASN.1 types. This file will be imported into all other IDL modules which do not, therefore, need to redefine these IDL types. The second, stored in a file called ASN1Limits.idl, contains implementation-defined values for some ASN.1 limits. It is automatically included with the ASN1Types.idl file, and therefore never needs to be explicitly imported.

The two files are contained in ASN1Types.idl File and ASN1Limits.idl File.


Footnotes

1.
Macros are not ignored when translating SNMP.

2.
These routines could be automatically generated as part of the translation process.

3.
This module may have imported the type from yet another module, but there is no need to follow a chain back to the original definition.


Why not acquire a nicely bound hard copy?
Click here to return to the publication details or order a copy of this publication.

Contents Next section Index