The ASN.1 SEQUENCE OF type is converted into one of the following C/C++ types:
A doubly-linked list structure (OSRTDList for C, or ASN1TSeqOfList, a class derived from OSRTDList, for C++)
A structure containing an integer count of elements and a pointer to hold an array of the referenced data type (a dynamic array)
A structure containing an integer count of elements and a fixed-sized array of the referenced data type (a static array)
A C++ Standard Library container class, such as std::list (when -cpp11 is specified on the command line)
The linked list option is the default for constructed types. An array is used for a sequence of primitive types. The allocation for the contents field of the array depends on how the SEQUENCE OF is specified in the ASN.1 definition. If a size constraint is used, a static array of that size is generated; otherwise, a pointer variable is generated to hold a dynamically allocated array of values. The decoder will automatically allocate memory to hold parsed SEQUENCE OF data values.
The default type may be altered through the use of command-line options:
The -array option may be used to indicate an array type should be used as the default instead of a linked list for all types. If the type does not contain a size constraint, a dynamic array will be used; otherwise a static array of the given size will be used. The -arraySize option may be used to force use of a static array for all types. SEQUENCE OF types not having a size constraint will result in a static array being generated of the size specified in the -arraySize option. The -cpp11 option will alter the code to use std::array instead of plain C++ static arrays.
The -dynamicArray option will result in the use of a dynamic array for all SEQUENCE OF types.
The -linkedList option will result in the use of a linked list for all of these types. The -cpp11 option will result in using std::list wherever a linked list would have been used othewise.
The type used for a given SEQUENCE OF construct can be modified by the use of a configuration item. The <storage> qualifier is used for this purpose. The dynamicArray keyword can be used at the global, module, or production level to specify that dynamic memory (i.e., a pointer) is used for the array. The syntax of this qualifier is as follows:
<storage>dynamicArray</storage>
The array keyword is used to specify that a static array is to be generated to hold the data. In this case, if the SEQUENCE OF production does not contain a size constraint, the maxSize attribute must be used to specify the maximum size of the array. For example:
<storage maxSize="100">array</storage>
If maxSize is not specified and the ASN.1 production contains no size constraint, then a dynamic array is used.
The std::array keyword is identical to the array keyword except that it specifies use of std::array instead of a plain C++ static array. Its use requires the -cpp11 command line option.
The list keyword can also be used in a similar fashion to specify the use of a linked-linked structure to hold the elements:
<storage>list</storage>
When -cpp11 is specified on the command line, you can also use the following storage options to select the use of C++ Standard Library container classes:
<storage>std::list</storage> <storage>std::vector</storage> <storage>std::deque</storage>
See the section entitled Compiler Configuration File for further details on setting up a configuration file.
ASN.1 production:
<name> ::= SEQUENCE OF <type>
Generated C code:
typedef struct { OSUINT32 n; <type>* elem; } <name>;
Generated C++ code:
typedef struct [ : public ASN1TPDU ] { OSUINT32 n; <type>* elem; ASN1T_<name>(); [~ASN1T_<name>();] } ASN1T_<name>;
Note that parsed values can be accessed from the dynamic data variable just as they would be from a static array variable; i.e., an array subscript can be used (ex: elem[0], elem[1]...).
In the case of C++, a constructor is generated to initialize the element count to zero. If the type represents a PDU type (either by default by not referencing any other types or explicitly via the -pdu command-line option), the ASN1TPDU base class is extended and a destructor is added. This destructor ensures that memory allocated for elements is freed upon destruction of the object.
ASN.1 production:
<name> ::= SEQUENCE (SIZE (<len>)) OF <type>
Generated C code:
typedef struct { OSUINT32 n; <type> elem[<len>]; } <name>;
Generated C++ code:
typedef struct { OSUINT32 n; <type> elem[<len>]; } ASN1T_<name>;
Generated C++ code with -cpp11:
typedef struct { OSUINT32 n; std::array<<type>, <len>> elem; } ASN1T_<name>;
If the -strict-size command-line option is used, the n component within this type definition may be of a different type (OSUINT8 or OSUINT16) or eliminated completely if the type is constrained to be a fixed-size.
A doubly-linked list header type (OSRTDList) is used for the type definition if the list storage configuration setting is used (see above). This can be used for either a sized or unsized SEQUENCE OF construct. The generated C or C++ code is as follows:
Generated C code:
typedef OSRTDList <name>;
Generated C++ code:
typedef ASN1TSeqOfList ASN1T_<name>;
The type definition of the OSRTDList structure can be found in the osSysTypes.h header file. The common run-time utility functions beginning with the prefix rtxDList are available for initializing and adding elements to the list. See the C/C++ Common Run-time Reference Manual for a full description of these functions.
For C++, the ASN1TSeqOfList class is used, or, in the case of PDU types, the ASN1TPDUSeqOfList class. The ASN1TSeqOfList extends the C OSRTDList structure and adds constructors and other helper methods. The ASN1TPDUSeqOfList is similar except that it also extends the ASN1TPDU base class to add additional memory management capabilities needed by PDU types to automatically release memory on destruction. See the ASN1CSeqOfList section in the C/C++ Common Run-time Reference Manual for details on all of the methods available in this class.
Populating generated list-based SEQUENCE OF structures for the most part requires the use of dynamic memory to allocate elements to be added to the list (note that it is possible to use static elements for this, but this is unusual). The recommended method is to use the built in run-time memory management facilities available within the ASN1C runtime library. This allows all list memory to be freed with one call after encoding is complete.
In the case of C, the rtxMemAlloc or rtxMemAllocType function would first be used to allocate a record of the element type. This element would then be initialized and populated with data. The rtxDListAppend function would then be called to append it to the given list.
For C++, the compiler generates the helper methods NewElement and Append in the generated control class for a SEQUENCE OF type. An instance of this class can be created using the list element within a generated structure as a parameter. The helper methods can then be used to allocate and initialize an element and then append it to the list after it is populated.
See the cpp/sample_ber/employee/writer.cpp
file for an example of how
these methods are used. In this program, the following logic is used to populate one
of the elements in the children
list for
encoding:
ASN1T_ChildInformation* pChildInfo; ASN1C__SeqOfChildInformation listHelper (encodeBuffer, msgData.children); ... pChildInfo = listHelper.NewElement(); fill_Name (&pChildInfo->name, "Ralph", "T", "Smith"); pChildInfo->dateOfBirth = "19571111"; listHelper.Append (pChildInfo);
In this example, msgData
is an instance of the main PDU class being
encoded (PersonnelRecord
). This object contains an element called
children
which is a linked-list of ChildInformation
records. The code snippet illustrates how to use the generated control class for the
list to allocate a record, populate it, and append it to the list.
ASN1C also generates helper methods in SEQUENCE, SET, and CHOICE control classes
to assist in allocating and adding elements to inline SEQUENCE OF lists. These
methods are named new_<elem>_element
and
append_to_<elem>
where <elem>
would be replaced
with the name of the element they apply to.
As noted above, -cpp11 can be used (among other things) to specify that std::list should be used for SEQUENCE OF types where a linked list would otherwise be used, while other C++ Standard library containers (e.g. std::vector) can be specified by using a configuration file. When a C++ STL class is used, the generated C++ code will resemble the following:
typedef std::list<ASN1T_<ElementTypeName*> ASN1T_<SeqOfTypeName>;
The contained type will always be a pointer type.
As with other constructed types, the <type>
variable can reference
any ASN.1 type, including other ASN.1 constructed types. Therefore, it is possible to
have a SEQUENCE OF SEQUENCE, SEQUENCE OF CHOICE, etc.
When a constructed type or type that maps to a C structured type is referenced, a temporary type is generated for use in the final production. The format of this temporary type name is as follows:
<prodName>_element
In this definition, <prodName>
refers to the name of the
production containing the SEQUENCE OF type.
For example, a simple (and very common) single level nested SEQUENCE OF construct might be as follows:
A ::= SEQUENCE OF SEQUENCE { a INTEGER, b BOOLEAN }
In this case, a temporary type is generated for the element of the SEQUENCE OF production. This results in the following two equivalent ASN.1 types:
A-element ::= SEQUENCE { a INTEGER, b BOOLEAN } A ::= SEQUENCE OF A-element
These types are then converted into the equivalent C or C++ typedef
s
using the standard mapping that was previously described.
Frequently, a SEQUENCE OF construct is used to define an array of some common type in an element in some other constructed type (for example, a SEQUENCE). An example of this is as follows:
SomePDU ::= SEQUENCE { addresses SEQUENCE OF AliasAddress, ... }
Normally, this would result in the addresses
element being pulled out
and used to create a temporary type with a name equal to
SomePDU-addresses
as
follows:
SomePDU-addresses ::= SEQUENCE OF AliasAddress SomePDU ::= SEQUENCE { addresses SomePDU-addresses, ... }
However, when the SEQUENCE OF element references a simple defined type as above with no additional tagging or constraint information, an optimization is done to reduce the size of the generated code. This optimization is to generate a common name for the new temporary type that can be used for other similar references. The form of this common name is as follows:
_SeqOf<elementProdName>
So instead of this:
SomePDU-addresses ::= SEQUENCE OF AliasAddress
The following equivalent type would be generated:
_SeqOfAliasAddress ::= SEQUENCE OF AliasAddress
The advantage is that the new type can now be easily reused if SEQUENCE OF AliasAddress is used in any other element declarations. Note the (illegal) use of an underscore in the first position. This is to ensure that no name collisions occur with other ASN.1 productions defined within the specification.
Some SEQUENCE OF elements in constructed types are inlined. In other words, no
temporary type is created; instead, either the OSRTDList
reference (for
linked list) or the array definition is inserted directly into the generated C
structure. This is particularly true when XSD files are being compiled.