-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
definitions.j2
263 lines (229 loc) · 12.2 KB
/
definitions.j2
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
{#-
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# Copyright (C) 2020 UAVCAN Development Team <uavcan.org>
# This software is distributed under the terms of the MIT License.
# Authors: David Lenfesty, Scott Dixon <dixonsco@amazon.com>, Pavel Kirienko <pavel@uavcan.org>,
# Peter van der Perk <peter.vanderperk@nxp.com>
-#}
{% if options.target_endianness == 'little' %}
{% set LITTLE_ENDIAN = True %}
{% elif options.target_endianness in ('any', 'big') %}
{% set LITTLE_ENDIAN = False %}
{% else %}{% assert False %}
{% endif %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro generate_metadata(t) %}
{% set ref = t | full_reference_name %}
#define {{ ref }}_FULL_NAME_ "{{ t.full_name }}"
#define {{ ref }}_FULL_NAME_AND_VERSION_ "{{ t.full_name }}.{{ t.version.major }}.{{ t.version.minor }}"
{% endmacro %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro generate_composite(t) %}
{{ generate_metadata(t) }}
{% assert t.extent % 8 == 0 %}
{% assert t.inner_type.extent % 8 == 0 %}
/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible
/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type.
/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long.
/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large.
/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation
/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type
/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere.
#define {{ t | full_reference_name }}_EXTENT_BYTES_ {{ t.extent // 8 }}UL
#define {{ t | full_reference_name }}_SERIALIZATION_BUFFER_SIZE_BYTES_ {{ t.inner_type.extent // 8 }}UL
static_assert({{ t | full_reference_name }}_EXTENT_BYTES_ >= {# -#}
{{ t | full_reference_name }}_SERIALIZATION_BUFFER_SIZE_BYTES_,
"Internal constraint violation");
{% for constant in t.constants %}
/// {{ constant }}
#define {{ t | full_reference_name }}_{{ constant.name }} ({{ constant | constant_value }})
{%- endfor %}
{% for f in t.fields_except_padding if f.data_type is ArrayType %}
/// Array metadata for: {{ f }}
{%- if options.enable_override_variable_array_capacity and f.data_type is VariableLengthArrayType %}
#ifndef {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_
{%- endif %}
#define {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ {{ f.data_type.capacity }}U
{%- if options.enable_override_variable_array_capacity and f.data_type is VariableLengthArrayType %}
#elif !defined({{ t | full_reference_name }}_DISABLE_SERIALIZATION_BUFFER_CHECK_)
# define {{ t | full_reference_name }}_DISABLE_SERIALIZATION_BUFFER_CHECK_
#endif
#if {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U
# error {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U
#endif
{%- endif %}
#define {{ t | full_reference_name }}_{{ f.name }}_ARRAY_IS_VARIABLE_LENGTH_ {# -#}
{{ valuetoken_true if f.data_type is VariableLengthArrayType else valuetoken_false }}
{%- endfor %}
{% if t.inner_type is UnionType %}
/// The number of fields in the union. Valid tag values range from zero to this value minus one, inclusive.
#define {{ t | full_reference_name }}_UNION_OPTION_COUNT_ {{ t.fields | length }}U
{% endif %}
{% if t.inner_type is StructureType %}
{{ _define_structure(t.inner_type) }}
{% elif t.inner_type is UnionType %}
{{ _define_union(t.inner_type) }}
{% else %}{% assert False %}{# Not a valid composite type. #}
{% endif %}
{{ _define_functions(t) }}
{% endmacro %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{%- macro assert(expression) -%}
{%- if options.enable_serialization_asserts -%}
NUNAVUT_ASSERT({{ expression }});
{%- endif -%}
{%- endmacro -%}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro _define_structure(t) %}
{% assert t is StructureType %}
typedef struct
{
{%- for f in t.fields_except_padding %}
{%- if not loop.first %}
{# Blank line between fields. #}
{%- endif %}
/// {{ f }}
{{ _define_field(t, f.data_type, f.name) | indent }};
{%- else %}{#- To maintain consistency between C and C++ we define any empty composite type with a dummy byte. #}
{{ typename_byte }} _dummy_;
{%- endfor %}
} {{ t | full_reference_name }};
{% endmacro %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro _define_union(t) %}
{% assert t is UnionType %}
typedef struct
{
union /// The union is placed first to ensure that the active element address equals the struct address.
{
{%- for f in t.fields_except_padding %}
{%- if not loop.first %}
{# Blank line between fields. #}
{%- endif %}
/// {{ f }}
{{ _define_field(t, f.data_type, f.name) | indent | indent }};
{%- endfor %}
};
{{ t.tag_field_type | type_from_primitive }} _tag_;
} {{ t | full_reference_name }};
{% endmacro %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro _define_field(t, f, name, suffix='') %}
{%- if f is PrimitiveType -%}
{{ f | type_from_primitive }} {{ name | id }}{{ suffix }}
{%- elif f is CompositeType -%}
{{ f | full_reference_name }} {{ name | id }}{{ suffix }}
{%- elif f is FixedLengthArrayType -%}
{%- if f.element_type is BooleanType -%}
{{ _define_bitpacked_array_field(name + '_bitpacked_', f.capacity) }}{{ suffix }}
{%- else -%}
{{ _define_field(t, f.element_type, name, '[%s]'|format(f.capacity)) }}{{ suffix }}
{%- endif -%}
{%- elif f is VariableLengthArrayType -%}
struct /// Array address equivalence guarantee: &elements[0] == &{{ name }}
{
{%- if f.element_type is BooleanType %}
{{ _define_bitpacked_array_field('bitpacked', f.capacity) | indent }};
{%- else %}
{{ _define_field(t, f.element_type, 'elements', '[%s_%s_ARRAY_CAPACITY_]'|format(t|full_reference_name, name)) }};
{%- endif %}
{{ typename_unsigned_length }} count;
} {{ name | id }}{{ suffix }}
{%- else -%}{% assert False %}{# Not a valid field type. #}
{%- endif -%}
{% endmacro %}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro _define_bitpacked_array_field(name, capacity) -%}
/// Bitpacked array, capacity {{ capacity }} bits. Access via @ref nunavutSetBit(), @ref nunavutGetBit().
{{ typename_byte }} {{ name | id }}[{{ capacity | bits2bytes_ceil }}]
{%- endmacro %}{# https://en.wiktionary.org/wiki/bitpacked #}
{# ----------------------------------------------------------------------------------------------------------------- #}
{% macro _define_functions(t) %}
{% if not nunavut.support.omit %}
/// Serialize an instance into the provided buffer.
/// The lifetime of the resulting serialized representation is independent of the original instance.
/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision
/// we may define a zero-copy alternative that keeps references to the original object where possible.
///
/// @param obj The object to serialize.
///
/// @param buffer The destination buffer. There are no alignment requirements.
/// @see {{ t | full_reference_name }}_SERIALIZATION_BUFFER_SIZE_BYTES_
///
/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes.
/// Upon return this value will be updated with the size of the constructed serialized
/// representation (in bytes); this value is then to be passed over to the transport
/// layer. In case of error this value is undefined.
///
/// @returns Negative on error, zero on success.
static inline {{ typename_error_type }} {{ t | full_reference_name }}_serialize_(
const {{ t | full_reference_name }}* const obj, {# -#}
{{ typename_byte }}* const buffer, {# -#}
{{ typename_unsigned_length }}* const inout_buffer_size_bytes)
{
{% from 'serialization.j2' import serialize -%}
{{ serialize(t) | trim }}
}
/// Deserialize an instance from the provided buffer.
/// The lifetime of the resulting object is independent of the original buffer.
/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision
/// we may define a zero-copy alternative that keeps references to the original buffer where possible.
///
/// @param obj The object to update from the provided serialized representation.
///
/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements.
/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated,
/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules.
///
/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized
/// representation, in bytes. Upon return this value will be updated with the
/// size of the consumed fragment of the serialized representation (in bytes),
/// which may be smaller due to the implicit truncation rule, but it is guaranteed
/// to never exceed the original buffer size even if the implicit zero extension rule
/// was activated. In case of error this value is undefined.
///
/// @returns Negative on error, zero on success.
static inline {{ typename_error_type }} {{ t | full_reference_name }}_deserialize_(
{{ t | full_reference_name }}* const out_obj, {# -#}
const {{ typename_byte }}* buffer, {# -#}
{{ typename_unsigned_length }}* const inout_buffer_size_bytes)
{
{% from 'deserialization.j2' import deserialize -%}
{{ deserialize(t) | trim }}
}
/// Initialize an instance to default values. Does nothing if @param out_obj is {{ valuetoken_null }}.
/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length
/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field
/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)).
static inline void {{ t | full_reference_name }}_initialize_({{ t | full_reference_name }}* const out_obj)
{
if (out_obj != {{ valuetoken_null }})
{
{{ typename_unsigned_length }} size_bytes = 0;
const {{ typename_byte }} buf = 0;
const {{ typename_error_type }} err = {{ t | full_reference_name }}_deserialize_(out_obj, &buf, &size_bytes);
{{ assert('err >= 0') }}
(void) err;
}
}
{% endif %} {# if not nunavut.support.omit #}
{% for f in t.fields_except_padding %}
{% if t.inner_type is UnionType %}
/// Mark option "{{ f.name }}" active without initializing it. Does nothing if @param obj is {{ valuetoken_null }}.
static inline void {{ t | full_reference_name }}_select_{{ f.name }}_{# -#}
({{ t | full_reference_name }}* const obj)
{
if (obj != {{valuetoken_null}})
{
obj->_tag_ = {{ loop.index0 }};
}
}
/// Check if option "{{ f.name }}" is active. Returns false if @param obj is {{ valuetoken_null }}.
static inline {{ typename_boolean }} {{ t | full_reference_name }}_is_{{ f.name }}_{# -#}
(const {{ t | full_reference_name }}* const obj)
{
return ((obj != {{ valuetoken_null }}) && (obj->_tag_ == {{ loop.index0 }}));
}
{% endif %}
{% endfor %}
{% endmacro %}