File: gen_C99_ops.md

package info (click to toggle)
cyclonedds 0.10.5-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 21,372 kB
  • sloc: ansic: 224,361; perl: 1,904; xml: 1,894; yacc: 1,018; sh: 882; python: 106; makefile: 94
file content (284 lines) | stat: -rw-r--r-- 8,769 bytes parent folder | download | duplicates (2)
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.