Unions Table Constraint Model

The unions table constraint model originated from common patterns used in a series of ASN.1 specifications in-use in 3rd Generation Partnership Project (3GPP) standards. These standards include Node Application Part B (NBAP), Radio Access Network Application Part (RANAP), and Radio Network Subsystem Application Part (RNSAP) in the current 3G network and in S1AP and X2AP protocols in the newer 4G network (LTE) standards. This model was later extended to generate these type of structures for other specifications that made use of table constraints including security and legacy telecom speifications.

Generated C Type Definitions for Message Types

The standard message type used by many specifications that employ table constraints is usually a SEQUENCE type with elements that use a relational table constraint that uses fixed-type and type fields. The general form is as follows:

   <Type> ::= SEQUENCE {
      <element1> <Class>.&<fixed-type-field> ({<ObjectSet>}), 
      <element2> <Class>.&<fixed-type-field> ({<ObjectSet>)){@element1}
      <element3> <Class>.&<type-field> ({<ObjectSet>)){@element1}
}

In this definition, <Class> would be replaced with a reference to an Information Object Class, <fixed-type-field> would be a fixed-type field wtihin that class, and <type-field> would be a type field within the class. <ObjectSet> would be a reference to an Information Object Set which would define all of the possibilities for content within the message. The first element (<element1>) would be used as the index element in the object set relation.

An example of this pattern from the S1AP LTE specification is as follows:

   InitiatingMessage ::= SEQUENCE {
      procedureCode   S1AP-ELEMENTARY-PROCEDURE.&procedureCode   
                         ({S1AP-ELEMENTARY-PROCEDURES}),
      criticality     S1AP-ELEMENTARY-PROCEDURE.&criticality      
                         ({S1AP-ELEMENTARY-PROCEDURES}{@procedureCode}),
      value           S1AP-ELEMENTARY-PROCEDURE.&InitiatingMessage   
                         ({S1AP-ELEMENTARY-PROCEDURES}{@procedureCode})
   }

In this definition, procedureCode and criticality are defined to be a enumerated fixed types, and value is defined to be an open type field to hold variable content as defined in the object set definition.

In the legacy model defined below, a loose coupling would be defined for the open type field using the built-in ASN1Object structure. This structure uses a void pointer to hold a link to a variable of the typed data structure. This is inconvenient for the developer because he would need to consult the object set definition within the ASN.1 specification in order to determine what type of data is to be used with each procedure code. It is also error prone in that the void pointer provides for no type checking at compile time.

In the new model, the generated structure is designed to be similar as to what is used to represent a CHOICE type. That is to say, the structure is a union with a choice selector value and all possible types listed out in a union structure. This is the general form:

   typedef struct <Type> {
      <Element1Type> <element1>;
      <Element2Type> <element2>;

      /**
       * information object selector
       */
      <SelectorEnumType> t;
   
      /**
       * <ObjectSet> information objects
       */
      union {
         /**
          * <element1> : <object1-element1-value>
          * <element2> : <object1-element2-value>
          */
         <object1-element3-type>* <object1-name>;
   
         /**
          * <element1> : <object2-element1-value>
          * <element2> : <object2-element2-value>
          */
         <object2-element3-type>* <object2-name>;
   
         ...
      } u;
   } ;

In this definition, the first two elements of the sequence would use the equivalent C or C++ type as defined in the fixed-type field in the information object. This is the same as in the legacy model. The open type field (element3) would be expanded into the union structure as is shown. The <SelectorEnumType> would be an enumerated type that is generated to represent each of the choices in the referenced information object set. The union then contains an entry for each of the possible types as defined in the object set that can be used in the open type field. Comments are used to list the fixed-type fields corresponding to each open type field.

An example of the code that is generated from the S1AP sample ASN.1 snippet above is as follows:

   typedef enum {
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_handoverPreparation, 
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_handoverResourceAllocation, 
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_pathSwitchRequest, 
      etc..
   } S1AP_ELEMENTARY_PROCEDURE_TVALUE;

   typedef struct InitiatingMessage {
      ProcedureCode procedureCode;
      Criticality criticality;
   
      /**
       * information object selector
       */
      S1AP_ELEMENTARY_PROCEDURE_TVALUE t;
   
      /**
       * S1AP-ELEMENTARY-PROCEDURE information objects
       */
      union {
         /**
          * procedureCode: id-HandoverPreparation
          * criticality: reject
          */
         HandoverRequired*  handoverPreparation;
   
         /**
          * procedureCode: id-HandoverResourceAllocation
          * criticality: reject
          */
         HandoverRequest*  handoverResourceAllocation;
   
         /**
          * procedureCode: id-HandoverNotification
          * criticality: ignore
          */
         HandoverNotify*  handoverNotification;
   
         etc..
    
     } u;
   } InitiatingMessage;

Note that the long names generated in the S1AP_ELEMENTARY_PROCEDURE_TVALUE type can be reduced by using the <alias> configuration element.

Generated C Type Definitions for Information Element (IE) Types

In addition to message types, another common pattern in 3GPP specifications is protocol information element (IE) types. The general form of these types is a list of information elements as follows:

   <ProtocolIEsType> ::= <ProtocolIE-ContainerType> { <ObjectSet> }

   <ProtocolIE-ContainerType> { <Class> : <ObjectSetParam> } ::=
      SEQUENCE (SIZE (<size>)) OF <ProtocolIE-FieldType> {{ObjectSetParam}}
   
   <ProtocolIE-FieldType> { <Class> : <ObjectSetParam> } ::= SEQUENCE {
      <element1> <Class>.&<fixed-type-field> ({ObjectSetParam}), 
      <element2> <Class>.&<fixed-type-field> ({ObjectSetParam}{@element1}), 
      <element3> <Class>.&<Type-field> ({ObjectSetParam}{@element1})
   }

There are a few different variations of this, but the overall pattern is similar in all cases. A parameterized type is used as a shorthand notation to pass an information object set into a container type. The container type holds a list of the IE fields. The structure of an IE field type is similar to a message type: the first element is used as an index element to the remaining elements. That is followed by one or more fixed type or variable type elements. In the case defined above, only a single fixed-type and variable type element is shown, but there may be more.

An example of this pattern from the S1AP LTE specification follows:

   HandoverRequired ::= SEQUENCE {
      protocolIEs   ProtocolIE-Container   { { HandoverRequiredIEs} },
      ...
   }

   ProtocolIE-Container {S1AP-PROTOCOL-IES : IEsSetParam} ::= 
         SEQUENCE (SIZE (0..maxProtocolIEs)) OF ProtocolIE-Field {{IEsSetParam}}
   
   ProtocolIE-Field {S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE {
         id             S1AP-PROTOCOL-IES.&id            ({IEsSetParam}),
         criticality    S1AP-PROTOCOL-IES.&criticality   ({IEsSetParam}{@id}),
         value          S1AP-PROTOCOL-IES.&Value         ({IEsSetParam}{@id})
   }

In this case, standard parameterized type instantiation is used to create a type definition for the protocolIEs element. This results in a list type being generated:

/* List of HandoverRequired_protocolIEs_element */
typedef OSRTDList HandoverRequired_protocolIEs;

The type for the protocol IE list element is created in much the same way as the main message type was above:

   typedef struct HandoverRequired_protocolIEs_element {
      ProtocolIE_ID id;
      Criticality criticality;
      struct {
         /**
          * information object selector
          */
         HandoverRequiredIEs_TVALUE t;
   
         /**
          * HandoverRequiredIEs information objects
          */
         union {
            /**
             * id: id-MME-UE-S1AP-ID
             * criticality: reject
             * presence: mandatory
             */
            MME_UE_S1AP_ID  *_HandoverRequiredIEs_id_MME_UE_S1AP_ID;
            /**
             * id: id-HandoverType
             * criticality: reject
             * presence: mandatory
             */
            HandoverType  *_HandoverRequiredIEs_id_HandoverType;
            /**
             * id: id-Cause
             * criticality: ignore
             * presence: mandatory
             */
             ...
         } u;
      } value;
   } HandoverRequired_protocolIEs_element;

In this case, the protocol IE id field and criticality are generated as usual using the fixed-type field type definitions. The open type field once again results in the generation of a union structure of all possible type fields that can be used. Note in this case the field names are automatically generated (_HandoverRequiredIEs_id_MME_UE_S1AP_ID, etc.). The reason for this was the use of inline information object definitions in the information object set as opposed to defined object definitions. This is a sample from that set:

   HandoverRequiredIEs S1AP-PROTOCOL-IES ::= {   
      { ID id-MME-UE-S1AP-ID         CRITICALITY reject   TYPE MME-UE-S1AP-ID   PRESENCE mandatory   } |
      { ID id-HandoverType           CRITICALITY reject   TYPE HandoverType     PRESENCE mandatory   } |
   ...

In this case, the name is formed by combining the information object set name with the name of each key field within the set.

Generated IE Append Function

A user would need to allocate objects of this structure, populate them, and add them to the protocol IE list. In order to make this easier, helper functions are generated assist in adding information items to the list. The general format of these append functions is as follows:

   int asn1Append_<ProtocolIEsType>_<KeyValueName>
      (OSCTXT* pctxt, <ProtocolIEsType>* plist, <ValueType> value);

In this definition, <ProtocolIEsType> refers to the main list type (SEQUENCE OF) defining the information element list. <KeyValueName> is the name of the primary key field defined in the associated information object set. <ValueType> is the type of the value for the indexed information object set item.

An example of this type of function from the S1AP definitions is as follows:

   /* Append IE with value type MME_UE_S1AP_ID to list */
   int asn1Append_HandoverRequired_protocolIEs_id_MME_UE_S1AP_ID (OSCTXT* pctxt, 
      HandoverRequired_protocolIEs* plist, MME_UE_S1AP_ID value);

Generated IE Get Function

In addition to the list append function, a second type of helper function is generated to make it easier to find an item in the list based on the key field. The general format for this type of function is as follows:

   <ProtocolIE-FieldType>* asn1Get_<ProtocolIEsType> 
      (<KeyFieldType> <key>, <ProtocolIEsType>* plist);

In this definition, <ProtocolIEsType> refers to the main list type (SEQUENCE OF) definiing the information element list. <ProtocolIE-FieldType> is the type of an element within this list and <KeyFieldType> is the type of index key field.

An example of this type of function from the S1AP definitions is as follows:

   /* Get IE using id key value */
   HandoverRequired_protocolIEs_element* asn1Get_HandoverRequired_protocolIEs 
      (ProtocolIE_ID id, HandoverRequired_protocolIEs* plist);

Generated C++ Classes and Methods

This section discusses items that are generated idfferently for C++ for union table constraints.

Choice Selector TVALUE Type

For C, an enumerated type is generated for each of the options in a type field union. These correspond to each of the items in the information object set associated with the union. For example, the TVALUE type generated for S1AP_ELEMENTARY_PROCEDURES is as follows:

   typedef enum {
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_UNDEF_,
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_handoverPreparation,
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_handoverResourceAllocation,
      T_S1AP_PDU_Descriptions_S1AP_ELEMENTARY_PROCEDURES_pathSwitchRequest,
      ...
   } S1AP_ELEMENTARY_PROCEDURES_TVALUE;

The generated names include the name of the module, object set, and object in order to ensure that no name clashes occur between enumerations with common names. For C++, this type is generated as a class with TVALUE as a public member inside:

   class S1AP_ELEMENTARY_PROCEDURES {
   public:
      enum TVALUE {
         T_UNDEF_,
         T_handoverPreparation,
         T_handoverResourceAllocation,
         T_pathSwitchRequest,
      ...
      } ;
   } ;

In this case, the type module and object set names are not needed because the class name provides for unambiguous enumerated item names.

Generated Helper Methods

For C, special asn1Append_<name> and asn1GetIE_<name> functions are generated to help a user append information elements (IE's) to a list and get an indexed IE respectively. For C++, these are added as methods to the generated control class for the list type.

For example, for the HandoverRequired_protocolIEs type, the following methods are added to the control class:

   class EXTERN ASN1C_HandoverRequired_protocolIEs : public ASN1CSeqOfList
   {
      ...
      /* Append IE with value type ASN1T_MME_UE_S1AP_ID to list */
      int Append_id_MME_UE_S1AP_ID (ASN1T_MME_UE_S1AP_ID value);
   
      /* Append IE with value type ASN1T_HandoverType to list */
      int Appendid_eNB_UE_S1AP_ID (ASN1T_HandoverType value);
      ...
      /* Get IE using id key value */
      ASN1T_HandoverRequired_protocolIEs_element* GetIE (ASN1T_ProtocolIE_ID id);
   } ;