TOC PREV NEXT INDEX


SEQUENCE



This section discusses the mapping of an ASN.1 SEQUENCE type to C. The C++ mapping is similar but there are some differences. These are discusses in the C++ Mapping of SEQUENCE subsection at the end of this section.

An ASN.1 SEQUENCE is a constructed type consisting of a series of element definitions. These elements can be of any ASN.1 type including other constructed types. For example, it is possible to nest a SEQUENCE definition within another SEQUENCE definition as follows:

     A ::= SEQUENCE {
 
        x  SEQUENCE {
 
           a1 INTEGER,
 
           a2 BOOLEAN
 
        },
 
        y OCTET STRING SIZE (10)
 
     }
 

In this example, the production has two elements - x and y. The nested SEQUENCE x has two additional elements - a1 and a2.

The ASN1C compiler first recursively pulls all of the embedded constructed elements out of the SEQUENCE and forms new temporary types. The names of the temporary types are of the form <name>_<element-name1>_<element-name2>_ ... <element-nameN>. For example, in the definition above, two temporary types would be generated: A_x and A_y (A_y is generated because a static OCTET STRING maps to a C++ struct type).

The general form is as follows:
     ASN.1 production:  <name> ::= SEQUENCE {
 
		           <element1-name> <element1-type>,
 
		           <element2-name> <element2-type>,
 
		           ...
 
		        } 
 

    Generated C code:  typedef struct {
 
		          <type1> <element1-name>;
 
		          <type2> <element2-name>;
 
		          ...
 
		       } <name>;
 

- or -

		       typedef struct {
 
		          ...
 
		       } <tempName1>
 

 
		       typedef struct {
 
		          ...
 
		       } <tempName2>
 

 
		       typedef struct {
 
		          <tempName1> <element1-name>;
 
		          <tempName2> <element2-name>;
 
		          ...
 
		       } <name>;
 


The <type1> and <type2> placeholders represent the equivalent C types for the ASN.1 types <element1-type> and <element2-type> respectively. This form of the structure will be generated if the internal types are primitive. <tempName1> and <tempName2> are formed using the algorithm described above for pulling structured types out of the definition. This form is used for constructed elements and elements that map to structured C types.

The example above would result in the following generated C typedefs:

     typedef struct _A_x {
 
        ASN1INT   a1;
 
        ASN1BOOL  a2;
 
     } A_x;
 

 
     typedef struct A_y {
 
        ASN1UINT numocts;
 
        ASN1OCTET data[10];
 
     } A_y;
 

 
     typedef struct _A {
 
        A_x  x;
 
        A_y  y;
 
     } A;
 

In this case, elements x and y map to structured C types, so temporary typedefs are generated.

In the case of nesting levels greater than two, all of the intermediate element names are used to form the final name. For example, consider the following type definition that contains three nesting levels:

      X ::= SEQUENCE {
 
         a  SEQUENCE {
 
            aa  SEQUENCE { x INTEGER, y BOOLEAN },
 
            bb  INTEGER
 
         }
 
      }
 

In this case, the generation of temporary types results in the following equivalent type definitions:

	X-a-aa ::= SEQUENCE { x INTEGER, y BOOLEAN }
 

 
	X-a ::= SEQUENCE { aa X-a-aa, bb INTEGER }
 

 
	X ::= SEQUENCE { X-a a }
 

Note that the name for the aa element type is X-a-aa. It contains both the name for a (at level 1) and aa (at level 2). This is a change from v5.1x and lower where only that production name and last element name would be used (i.e., X-aa). The change was made to ensure uniqueness of the generated names when multiple nesting levels are used.

Note that although the compiler can handle embedded constructed types within productions, it is generally not considered good style to define productions this way. It is much better to manually define the constructed types for use in the final production definition. For example, the production defined at the start of this section can be rewritten as the following set of productions:

	X ::= SEQUENCE {
 
	   a1 INTEGER,
 
	   a2 BOOLEAN
 
	}
 

 
	Y ::= OCTET STRING
 
	
 
	A ::= SEQUENCE {
 
	   X x,
 
	   Y y
 
	}	
 

This makes the generated code easier to understand for the end user.


Unnamed Elements

Note: as of X.680, unnamed elements are not allowed - elements must be named. ASN1C still provides backward compatibility support for this syntax however.

In an ASN.1 SEQUENCE definition, the <element-name> tokens at the beginning of element declarations are optional. It is possible to include only a type name without a field identifier to define an element. This is normally done with defined type elements, but can be done with built-in types as well. An example of a SEQUENCE with unnamed elements would be as follows:

      AnInt ::= [PRIVATE 1] INTEGER
 

 
      Aseq ::= [PRIVATE 2] SEQUENCE {
 
         x      INTEGER,
 
                AnInt
 
      }
 

In this case, the first element (x) is named and the second element is unnamed.

ASN1C handles this by generating an element name using the type name with the first character set to lower case. For built-in types, a constant element name is used for each type (for example, aInt is used for INTEGER). There is one caveat, however. ASN1C cannot handle multiple unnamed elements in a SEQUENCE or SET with the same type names. Element names must be used in this case to distinguish the elements.

So, for the example above, the generated code would be as follows:

      typedef ASN1INT AnInt;
 

 
      typedef struct Aseq {
 
         ASN1INT   x;
 
         AnInt     anInt;
 
      } Aseq;
 


OPTIONAL keyword

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

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

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

For example, we will change the production in the previous example to make both elements optional:

 
      Aseq ::= [PRIVATE 2] SEQUENCE {
 
         x      INTEGER OPTIONAL,
 
                AnInt OPTIONAL
 
      }
 

In this case, the following C typedef is generated:

      typedef struct Aseq {
 
         struct {
 
            unsigned xPresent : 1,
 
            unsigned anIntPresent : 1
 
         } m;
 
         ASN1INT   x;
 
         AnInt     anInt;
 
      } Aseq;
 

When this structure is populated for encoding, the developer must set the xPresent and anIntPresent flags accordingly to indicate whether the elements are to be included in the encoded message or not. Conversely, when a message is decoded into this structure, the developer must test the flags to determine if the element was provided in the message or not.

The C++ version of the compiler will generate a constructor for the structured type for a SEQUENCE if OPTIONAL elements are present. This constructor will set all optional bits to zero when a variable of the structured type is declared. The programmer therefore does not have to be worried about clearing bits for elements that are not used; only with setting bits for the elements that are to be encoded.


Objective Systems, Inc.

102 Pickering Way, Suite #506
Exton, Pennsylvania 19341
http://www.obj-sys.com
Phone: (484) 875-9841
Toll-free: (877) 307-6855 (US only)
Fax: (484) 875-9830
info@obj-sys.com
TOC PREV NEXT INDEX