Legacy Table Constraint Model

The primary difference as to what a user sees and works with in the legacy model as opposed to the unions model lies in the representation of open type elements that contain a table constraint. The standard form of an open type element constrained with a table constraint within a SEQUENCE container is as follows:

   <Type> ::= SEQUENCE {
      <element> <Class>.&<type-field> ({<ObjectSet>)){@index-element}
   }

If -tables is not specified on the command line, a standard open type structure is used to hold the element value:

   typedef struct <Type> {
      ASN1OpenType <element>;
   }

The ASN1OpenType built-in type holds the element data in encoded form. The only validation that is done on the element is to verify that is a well-formed tag-length-value (TLV) structure if BER encoding is used or a valid length prefixed structure for PER.

If the -tables command line option is selected, the code generated is different. In this case, ASN1OpenType above is replaced with ASN1Object (or ASN1TObject for C++). This is defined in asn1type.h as follows:

   typedef struct { /* generic table constraint value holder */
      ASN1OpenType encoded;
      void*        decoded;
      OSINT32      index;     /* table index */
   } ASN1Object;

This allows a value of any ASN.1 type to be represented in both encoded and decoded forms. Encoded form is the open type form shown above. It is simply a pointer to a byte buffer and a count of the number of byes in the encoded message component. The decoded form is a pointer to a variable of a specific type. The pointer is void because there could be a potentially large number of different types that can be represented in the table constraint used to constrain a type field to a given set of values. The index member of the type is for internal use by table constraint processing functions to keep track of which row in a table is being referenced.

In addition to this change in how open types are represented, a large amount of supporting code is generated to support the table constraint validation process. This additional code is described below. Note that it is not necessary for the average user to understand this as it is not for use by users in accomplishing encoding and decoding of messages. It is only described for completeness in order to know what that additional code is used for.

CLASS specification

All of the Class code will be generated in a module class header file with the following filename format:

   <ModuleName>Class.h

In this definition, <ModuleName> would be replaced with the name of the ASN.1 module name for this class definition.

C Code generation

The C structure definition generated to model an ASN.1 class contains member variables for each of the fields within the class.

For each of the following class fields, the corresponding member variable is included in the generated C structure as follows:

For a Value Field:

   <TypeName> <FieldName>;

For TypeField definitions, an encode and decode function pointer and type size field is generated to hold the information of the type for the OpenType. If the -print option is selected, a print function pointer is also added.

   int <FieldName>Size;
   int (*encode<FieldName>) (... );
   int (*decode<FieldName>) (... );
   void (*print<FieldName>) (...);

For an Object Field:

   <ClassName>* <FieldName>;

For an ObjectSetField definition, an array of class definitions is generated to hold the list of information objects.

   <ClassName>* <FieldName>;

In each of these definitions:

<FieldName> would be replaced with the name of the field (without the leading '&').

<TypeName> would be replaced with the C type name for the ASN.1 Type.

<ClassName> would be replaced with the C type name of the class for the Information Object.

As an example, consider the following ASN.1 class definition :

   ATTRIBUTE ::= CLASS {
      &Type,
      &id OBJECT IDENTIFIER UNIQUE }
   WITH SYNTAX { &Type ID &id }

This would result in the following definition in the C source file:

   typedef struct ATTRIBUTE {
      int TypeSize;
      int (*encodeType) (OSCTXT* , void *, ASN1TagType );
      int (*decodeType) (OSCTXT* , void *, ASN1TagType, int );
      ASN1OBJID id;
   }

C++ Code generation

The C++ abstract class generated to model an ASN.1 CLASS contains member variables for each of the fields within the class. Derived information object classes are required to populate these variables with the values defined in the ASN.1 information object specification. The C++ class also contains virtual methods representing each of the type fields within the ASN.1 class specification. If the field is not defined to be OPTIONAL in the ASN.1 specification, then it is declared to be abstract in the generated class definition. A class generated for an ASN.1 information object that references this base class is required to implement these abstract virtual methods.

For each of the following CLASS fields, a corresponding member variable is generated in the C++ class definition as follows:

For a Value Field definition, the following member variable will be added. Also, an Equals() method will be added if required for table constraint processing.

   <TypeName> <FieldName>;

   inline OSBOOL idEquals (<TypeName>* pvalue)

For a Type Field definition, a virtual method is added for each encoding rules type to call the generated C encode and decode functions. If -print is specified, a print method is also generated.

   virtual int encode<ER><FieldName>
      (OSCTXT* pctxt, ASN1TObject& object) { return 0; }

   virtual int decode<ER><FieldName>
      (OSCTXT* pctxt, ASN1TObject& object) { return 0; }

   virtual void print<FieldName>
      (ASN1ConstCharPtr name, ASN1TObject& object) {}

For an Object Field:

   class <ClassName>* <FieldName>;

In each of these definitions:

<FieldName> would be replaced with the name of the field (without the leading '&').

<TypeName> would be replaced with the C type name for the ASN.1 Type.

<ClassName> would be replaced with the C type name of the class for the Information Object.

<ER> would be replaced by an encoding rules type (BER, PER, or XER).

As an example, consider the following ASN.1 class definition :

   ATTRIBUTE ::= CLASS {
      &Type,
      &ParameterType OPTIONAL,
      &id            OBJECT IDENTIFIER UNIQUE }
   WITH SYNTAX { &Type ID &id }

This would result in the following definition in the C++ source file:

   class EXTERN ATTRIBUTE {
     protected:
        ASN1TObjId id;

        ATTRIBUTE ();
     public:
        virtual int encodeBERType
           (OSCTXT* pctxt, ASN1TObject& object) = 0;
        
        virtual int decodeBERType
           (OSCTXT* pctxt, ASN1TObject& object) = 0;

        OSBOOL isParameterTypePresent() {
           if(m.ParameterTypePresent) {return TRUE;} else {return FALSE;}
        }
        virtual int encodeBERParameterType
           (OSCTXT* pctxt, ASN1TObject& object) { return 0; }

        virtual int decodeBERParameterType
           (OSCTXT* pctxt, ASN1TObject& object) { return 0; }

        inline OSBOOL idEquals (ASN1TObjId* pvalue)
        {
           return (0 == rtCmpTCOID (&id, pvalue));
        }
   } ;

This assumes that only BER or DER was specified as the encoding rules type.

First notice that member variables have been generated for the fixed-type fields in the definition. These include the id field. Information object classes derived from this definition are expected to populate these fields in their constructors.

Also, virtual methods have been generated for each of the type fields in the class. These include the Type fields. The method generated for Type is abstract and must be implemented in a derived information object class. The method generated for the ParameterType field has a default implementations that does nothing. That is because it is a optional field.

Also generated are Equals methods for the fixed-type fields. These are used by the generated code to verify that data in a generated structure to be encoded (or data that has just been decoded) matches the table constraint values. This method will be generated only if it is required to check a table constraint.

OPTIONAL keyword

Fields within a CLASS can be declared to be optional using the OPTIONAL keyword. This indicates that the field is not required in the information object. An additional construct is added to the generated code to indicate whether an optional field is present in the information object or not. This construct is a bit structure placed at the beginning of the generated structure. This structure always has variable name 'm' and contains single-bit elements of the form <fieldname>Present as follows:

   struct {
      unsigned <field-name1>Present : 1,
      unsigned <field-name2>Present : 1,
      ...
   } m;

In this case, the fields included in this construct correspond to only those fields marked as OPTIONAL within the CLASS. If a CLASS contains no optional fields, the entire construct is omitted.

For example, we will change the CLASS in the previous example to make one field optional:

   ATTRIBUTE ::= CLASS {
      &Type OPTIONAL,
      &id OBJECT IDENTIFIER UNIQUE
   }

In this case, the following C typedef is generated in C struct or C++ class definition:

   struct {
      unsigned TypePresent : 1;
   } m;

When this structure is populated for encoding, the information object processing code will set TypePresent flag accordingly to indicate whether the field is present or not.

In C++ code generation, an additional method is generated for an optional field as follows:

   OSBOOL is<FieldName>Present() {
      if (m.<FieldName>Present) {return TRUE;} else {return FALSE;}
   }

This function is used to check if the field value is present in an information object definition.

Generation of New ASN.1 Assignments from CLASS Assignments:

During CLASS definition code generation, the following new assignments are created for C or C++ code generation:

  1. A new Type Assignment is created for a TypeField's type definition, as follows:

       _<ClassName>_<FieldName> ::= <Type>

    Here ClassName is replaced with name of the Class Assignment and FieldName is replaced with name of this field. Type is the type definition in CLASS's TypeField.

    This type is used as a defined type in the information object definition for an absent value of the TypeField. It is also useful for generation of a value for a related Open Type definition in a table constraint.

  2. A new Type Assignment is created for a Value Field or Value Set Field type definition as follows (if the type definition is one of the following: ConstraintedType / ENUMERATED / NamedList BIT STRING / SEQUENCE / SET / CHOICE / SEQUENCE OF / SET OF ):

       _<ClassName>_<FieldName> ::= <Type>

    Here ClassName is replaced with the name of the CLASS assignment and FieldName is replaced with name of the ValueField or ValueSetField. Type is the type definition in the CLASS's ValueField or ValueSetField. This type will appear as a defined type in the CLASS's ValueField or ValueSetField.

    This new type assignment is used for compiler internal code generation purpose. It is not required for a user to understand this logic.

  3. A new Value Assignment is created for a ValueField's default value definition as follows:

       _<ClassName>_<FieldName>_default <Type> ::= <Value>

    Here ClassName is replaced with name of the Class Assignment and FieldName is replaced with name of this ValueField. Value is the default value in the Class's ValueField and Type is the type in Class's ValueField.

    This value is used as a defined value in the information object definition for an absent value of the field. This new value assignment is used for compiler internal code generation purpose. It is not required for user to understand this logic.

ABSTRACT-SYNTAX and TYPE-IDENTIFIER

The ASN.1 ABTRACT-SYNTAX and TYPE-IDENTIFIER classes are useful ASN.1 definitions. These classes are described using the following ASN.1 definitions:

   TYPE-IDENTIFIER ::= CLASS {
      &id OBJECT IDENTIFIER UNIQUE,
      &Type
   }
   WITH SYNTAX { &Type IDENTIFIED BY &id }

   ABSTRACT-SYNTAX ::= CLASS {
      &id OBJECT IDENTIFIER UNIQUE,
      &Type,
      &property BIT STRING { handles-invalid-encoding(0)} DEFAULT {}
   }
   WITH SYNTAX {
      &Type IDENTIFIED BY &id [HAS PROPERTY &property]
   }

The ASN1C compiler generates code for these constructs when they are referenced in the ASN.1 source file that is being compiled. The generated code for these constructs is written to the RtClass.h and .c/.cpp source files.

Information Object

Information Object code will be generated in a header and source file with a C struct / C++ class to hold the values. The name of the header and source file are of the following format:

   <ModuleName>Table.h
   <ModuleName>Table.c/cpp

In this definition, <ModuleName> would be replaced with the name of the ASN.1 module in which the information object is defined.

C Code Generation

For C, a global variable is generated to hold the information object definition. This is very similar to the code generated for a value definition.

An example of an information object definition that is derived from the ASN.1 ATTRIBUTE class above is as follows:

   name ATTRIBUTE ::= {
      WITH SYNTAX    VisibleString
      ID             { 0 1 1 } }

This results in the generation of the following C constant:

   ATTRIBUTE name;

Code generated in information object initialization function:

   name.TypeSize = sizeof(_name_Type);
   name.encodeType = &asn1E__name_Type;
   name.decodeType = &asn1D__name_Type;
   name.id.numids = 3;
   name.id.subid[0] = 0;
   name.id.subid[1] = 1;
   name.id.subid[2] = 1;

C++ Code Generation

The C++ classes generated for ASN.1 information objects are derived from the ASN.1 class objects. The constructors in these classes populate the fixed-type field member variables with the values specified in the information object. The classes also implement the virtual methods generated for the information object type fields. All non-optional methods are required to be implemented. The optional methods are only implemented if they are defined in the information object definition.

An example of an information object definition that is derived from the ASN.1 class above is as follows:

   name ATTRIBUTE ::= {
   WITH SYNTAX       VisibleString
   ID                { 0 1 1 } }

This results in the generation of the following C++ class:

   class EXTERN name : public ATTRIBUTE {
     public:
        name();

        virtual int encodeBERType
           (OSCTXT* pctxt, ASN1TObject& object);

        virtual int decodeBERType
           (OSCTXT* pctxt, ASN1TObject& object);
   } ;

The constructor implementation for this class (not shown) sets the fixed type fields (id) to the assigned values ({0 1 1}). The class also implements the virtual methods for the type field virtual methods defined in the base class. These methods simply call the BER encode or decode method for the assigned type (this example assumes -ber was specified for code generation - other encode rules could have been used as well).

Generated Type Assignments

If the information object contains an embedded type definition, it is extracted from the definition to form a new type to be added to the generated C or C++ code. The format of the new type name is as follows:

   _<ObjectName>_<FieldName>

where <ObjectName> is replaced with the information object name and <FieldName> is replaced with the name of the field from within the object.

Information Object Set

Table constraint processing code to support Information Object Sets is generated in a header and source file with a C struct / C++ class to hold the values. The name of the header and source file are of the following format:

   <ModuleName >Table.h
   <ModuleName >Table.c/cpp

In this definition, <ModuleName> would be replaced with the name of the ASN.1 module in which the information object is defined.

C Code Generation

A C global variable is generated containing a static array of values for the ASN.1 CLASS definition. Each structure in the array is the equivalent C structure representing the corresponding ASN.1 information object

An example of an Information Object Set definition that is derived from the ASN.1 ATTRIBUTE class above is as follows:

   SupportedAttributes ATTRIBUTE ::= { name | commonName }

This results in the generation of the following C constant:

   ATTRIBUTE SupportedAttributes[2];
   int SupportedAttributes_Size = 2;

Code generated in the Information Object Set initialization function:

   SupportedAttributes[0].TypeSize = sizeof(_name_Type);
   SupportedAttributes[0].encodeType = &asn1E__name_Type;
   SupportedAttributes[0].decodeType = &asn1D__name_Type;
   SupportedAttributes[0].id.numids = 3;
   SupportedAttributes[0].id.subid[0] = 0;
   SupportedAttributes[0].id.subid[1] = 1;
   SupportedAttributes[0].id.subid[2] = 1;

   SupportedAttributes[1].TypeSize = sizeof(_commonName_Type);
   SupportedAttributes[1].encodeType = &asn1E__commonName_Type;
   SupportedAttributes[1].decodeType = &asn1D__commonName_Type;
   SupportedAttributes[1].id.numids = 3;
   SupportedAttributes[1].id.subid[0] = 0;
   SupportedAttributes[1].id.subid[1] = 1;
   SupportedAttributes[1].id.subid[2] = 1;
   SupportedAttributes[1].id.subid[3] = 1;

C++ Code Generation

In C++, ASN.1 information object sets are mapped to C++ classes. In this case, a C++ singleton class is generated. This class contains a container to hold an instance of each of the ASN.1 information object C++ objects in a static array. The class also contains an object lookup method for each of the key fields. Key fields are identified in the class as either a) fields that are marked unique, or b) fields that are referenced in table constraints with the '@' notation.

The generated constructor initializes all required values and information objects.

An example of an information object set that uses the information object class defined above is as follows:

   SupportedAttributes ATTRIBUTE ::= { name | commonName }

This results in the generation of the following C++ class:

   class EXTERN SupportedAttributes {
     protected:
        ATTRIBUTE* mObjectSet[2];
        const size_t mNumObjects;
        static SupportedAttributes* mpInstance;
        SupportedAttributes (OSCTXT* pctxt);

     public:
        ATTRIBUTE* lookupObject (ASN1TObjId _id);
        static SupportedAttributes* instance(OSCTXT* pctxt);
   } ;

The mObjectSet array is the container for the information object classes. These objects are created and this array populated in the class constructor. Note that this is a singleton class (as evidenced by the protected constructor and instance methods). Therefore, the object set array is only initialized once the first time the instance method is invoked.

The other method of interest is the lookupObject method. This was generated for the id field because it was identified as a key field. This determination was made because id was declared to be UNIQUE in the class definition above. A field can also be determined to be a key field if it is referenced via the @ notation in a table constraint in a standard type definition. For example, in the following element assignment:

   argument OPERATION.&Type ({SupportedAttributes}{@opcode})

the opcode element's ATTRIBUTE class field is identified as a key field.