1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
|
# 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:
```C
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):
```C
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:
```C
struct M {
string str;
string<4> str4;
};
```
will need the following operation codes:
```C
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:
```C
struct M {
sequence<long> longs;
sequence<string> strings;
};
```
will need the following operation codes:
```C
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:
```C
struct M {
long arr[4][5];
};
```
wil need the following operation codes:
```C
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:
```C
struct coord_t{
long x, y;
unsigned long z;
};
struct M {
sequence<coord_t> coords;
};
```
will need the following operation codes:
```C
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:
```C
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:
```C
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:
1. Whenever a recursion is detected.
2. 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:
```C
struct x {
char ch;
sequence<x> xs;
}
```
The following operation codes can be used:
```C
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.
|