Generating C99 source file: ops structure
As part of the C99 source file, there is an array of uint32_t specifying the operation codes for (de)marshalling the data from and to the data structures defined in the generated header file.
This sequence is generated by starting with the struct and output some operation codes for each of the members of the struct, followed by a return operation code.
Basic types
For members with a basic type, at least two uint32_t values are used. The first specifies the operation and the second and following the operands. The operation is specified by combining (with bit-or) flags. The flags specify:
The type of the member. For the basic type this specifies the size of the type. For string and bounded string, separate flags are used.
Whether the field is a key field
Whether it is an array. If it is an extra operand will speficify the total size of the array (by multiplying the sizes of the various dimensions).
Whether the type is a sequence. (In case the elment type is a struct, additional information will follow the operation and its operands, as described below.) In case of a bounded string, an extra operand will follow, to specify the size (including its terminating null-character) of the bounded string.
The operand for a simple type makes such of ‘offsetof’, which returns the offset of a field in a struct.
Examples of nummeric fields
Given the IDL struct definition:
struct M {
char ch;
short i;
unsigned long ul;
long long ll;
float f;
double d;
};
will need the following operation codes (using the predefinted macros):
DDS_OP_ADR | DDS_OP_TYPE_1BY, offsetof (M, chr),
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (M, i),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (M, ul),
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (M, ll),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (M, f),
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (M, d),
DDS_OP_RTS,
The DDS_OP_ADR macro indicates that the operand will be an address.
The DDS_OP_TYPE_1BY to DDS_OP_TYPE_8BY specify the size of the
data type. Note that there is no difference between signed and unsigned
integer values. In case one of the fields is a key the | DDS_OP_FLAG_KEY
will be added to the operator.
The DDS_OP_RTS is to indicate the end of the operation codes for the
structure.
Examples of strings
Given the IDL struct definition:
struct M {
string str;
string<4> str4;
};
will need the following operation codes:
DDS_OP_ADR | DDS_OP_TYPE_STR, offsetof (M, str),
DDS_OP_ADR | DDS_OP_TYPE_BST, offsetof (M, str4), 5,
DDS_OP_RTS,
Example of a sequence of a basic type
Given the IDL struct definition:
struct M {
sequence<long> longs;
sequence<string> strings;
};
will need the following operation codes:
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_4BY, offsetof (M, longs),
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_STR, offsetof (M, strings),
DDS_OP_RTS,
In this the DDS_OP_SUBTYPE_<x> define that the element type of the sequence
as being DDS_OP_TYPE_<x>.
Example of an array of a basic type
Given the IDL struct definition:
struct M {
long arr[4][5];
};
wil need the following operation codes:
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (M, arr), 20,
DDS_OP_RTS,
Note that 20 is the product of 4 and 5.
Member that is a struct itself
When a member is a struct (both embedded and refered), the members of that
struct are also included, where in offsetof the selection of the nested
field is used. For example, coord.x when the member has the name coord
and uses a struct that has a member with name x.
Sequence of struct or sequence
In case of a member that is a sequence of struct, the definition of the struct
also needs to be included. The operation codes need to include the size of
the struct and two addresses that specify where the operation codes of for
the structure start and where they end. The, so called, jsr address specifies
the offset from the start of the current operand to the start of the operation
codes for the structure. The, so called, jmp address specifies the offset
from the start of the current operand to the operation codes for the following
element. Both addresses are 16 bits and stored together in one 32 unsigned
long, such that the lower order 16 bits contain the jsr address and the
higher order the jmp address. The operation codes describing the struct
are terminated with a return operation (DDS_OP_RTS).
The case of a member is a sequence of sequence of a certain type, is handled in the same way, where the definition of the sequences is included at the place of the struct.
Example of sequence of struct
Given the IDL struct definitions:
struct coord_t{
long x, y;
unsigned long z;
};
struct M {
sequence<coord_t> coords;
};
will need the following operation codes:
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_STU, offsetof (M, coords),
sizeof (coord_t), (11u << 16u) + 4u,
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, x),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, y),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, z),
DDS_OP_RTS,
DDS_OP_RTS,
In expression (11u << 16u) + 4u in the codes, the 11u represents the
jmp address and the 4u the jsr address.
Note that there are two DDS_OP_RTS. The last one is to indicate the
end of the IDL struct M. The first indicates the end of the IDL stuct
coord_t, which is used in the sequence.
Union
The operation code of a union is followed by a number specifying the number of cases. This is followed by a combination of jsr and jmp addresses. The jsr address specifies the start of the cases. A case consists of a JEQ operation with a type and an optional jump address. When a case exists of a structure, the jump address specifies the offset (from the start of the current case) of the struct definition (which is terminated with a return operation).
Example of a union with a struct
Given the IDL definitions:
struct coord_t{
long x, y;
unsigned long z;
};
union u switch (short) {
case 0:
char ch;
case 1:
coord_t coord;
};
struct s{
u u_val;
};
will need the following operation codes:
DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_2BY, offsetof (s, u_val._d), 2u, (17u << 16) + 4u,
DDS_OP_JEQ | DDS_OP_TYPE_1BY | 0, 0, offsetof (s, u_val._u.ch),
DDS_OP_JEQ | DDS_OP_TYPE_STU | 3, 1, offsetof (s, u_val._u.coord),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, x),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, y),
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (coord_t, z),
DDS_OP_RTS,
DDS_OP_RTS,
In thus u_val stands for C-struct that is used for defining the IDL-union,
where u_val._d represents the switch parameter and the u_val._u the
union. In this 2u stands for the number of cases. For the union 3
represents the jmp address to the operation code for the coord_t
struct.
Recursive types
The current implementation does not support recursive types. With a recursive type it is not possible to follow the above procedure because it will lead to an infinite sequence of operation codes. The operation also contain a jump subroutine operation, called JSR, that has a 16 bit signed integer value as an offset to the location where the code starts. It is thus possible to execute operation codes that have been defined before. There are two methods for deciding when to use a jump subroutine operation:
Whenever a recursion is detected.
Whenever a description of a struct already has been generated. The second method could lead to less operations, where the first method could lead to slightly efficient execution. In practice, this probably won’t make much difference. The second method is slightly easier to implement: simple maintain a map of structures to offsets (of the start of a struct description from the start of the sequence).
Example
Give the IDL defintion:
struct x {
char ch;
sequence<x> xs;
}
The following operation codes can be used:
DDS_OP_ADR | DDS_OP_TYPE_1BY, offsetof (x, ch),
DDS_OP_ADR | DDS_OP_TYPE_SEQ | DDS_OP_SUBTYPE_STU, offsetof (x, xs),
sizeof (coord_t), (7u << 16u) + 4u,
DDS_OP_JSR, -6,
DDS_OP_RTS,
DDS_OP_RTS,
Here the -6 specifies that operation codes for the structure of the
sequence, start 6 positions before the DDS_OP_JSR operation. The first
DDS_OP_RTS indicates the end of the sequence type.
Implementation
During the generation of operation codes, the tree needs to be traversed (at least) twice, because the values for the various jmp addresses need to be calculated.