1#![allow(clippy::module_name_repetitions)]
46
47use core::fmt::Write as _;
48
49use zerodds_idl::ast::{
50 Annotation, AnnotationParams, ConstExpr, ConstrTypeDecl, Definition, FloatingType, IntegerType,
51 LiteralKind, ModuleDef, PrimitiveType, Specification, StructDef, TypeDecl, TypeSpec,
52};
53
54use crate::error::CppGenError;
55
56fn unsupported(kind: &'static str) -> CppGenError {
57 CppGenError::UnsupportedConstruct {
58 construct: kind.to_string(),
59 context: None,
60 }
61}
62
63#[derive(Debug, Clone, Default)]
65pub struct CGenOptions {
66 pub include_guard: Option<String>,
68 pub file_header: Option<String>,
70}
71
72pub fn generate_c_header(ast: &Specification, opts: &CGenOptions) -> Result<String, CppGenError> {
79 let mut ctx = Ctx::new(opts);
80 ctx.emit_preamble();
81 ctx.walk_definitions(&ast.definitions, &[])?;
82 ctx.emit_postamble();
83 Ok(ctx.out)
84}
85
86struct Ctx<'a> {
91 out: String,
92 opts: &'a CGenOptions,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96enum Extensibility {
97 Final,
98 Appendable,
99 Mutable,
100}
101
102impl<'a> Ctx<'a> {
103 fn new(opts: &'a CGenOptions) -> Self {
104 Self {
105 out: String::new(),
106 opts,
107 }
108 }
109
110 fn emit_preamble(&mut self) {
111 let guard = self
112 .opts
113 .include_guard
114 .clone()
115 .unwrap_or_else(|| "ZERODDS_GENERATED_H".to_string());
116 if let Some(h) = &self.opts.file_header {
117 for line in h.lines() {
118 let _ = writeln!(self.out, "/* {line} */");
119 }
120 } else {
121 let _ = writeln!(
122 self.out,
123 "/* Generated by zerodds idl-cpp c_mode. Do not edit. */"
124 );
125 }
126 let _ = writeln!(self.out, "#ifndef {guard}");
127 let _ = writeln!(self.out, "#define {guard}");
128 let _ = writeln!(self.out, "#include <stddef.h>");
129 let _ = writeln!(self.out, "#include <stdint.h>");
130 let _ = writeln!(self.out, "#include <string.h>");
131 let _ = writeln!(self.out, "#include <stdlib.h>");
132 let _ = writeln!(self.out, "#include \"zerodds.h\"");
133 let _ = writeln!(self.out, "#include \"zerodds_xcdr2.h\"");
134 let _ = writeln!(self.out, "#ifdef __cplusplus");
135 let _ = writeln!(self.out, "extern \"C\" {{");
136 let _ = writeln!(self.out, "#endif");
137 let _ = writeln!(self.out);
138 }
139
140 fn emit_postamble(&mut self) {
141 let _ = writeln!(self.out, "#ifdef __cplusplus");
142 let _ = writeln!(self.out, "}}");
143 let _ = writeln!(self.out, "#endif");
144 let _ = writeln!(self.out, "#endif");
145 }
146
147 fn walk_definitions(
148 &mut self,
149 defs: &[Definition],
150 scope: &[String],
151 ) -> Result<(), CppGenError> {
152 for d in defs {
153 match d {
154 Definition::Module(m) => self.walk_module(m, scope)?,
155 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(sd))) => {
156 if let zerodds_idl::ast::StructDcl::Def(def) = sd {
157 self.emit_struct(def, scope)?;
158 }
159 }
160 Definition::Type(_) => {
161 return Err(unsupported("non-struct type"));
162 }
163 Definition::Const(_)
165 | Definition::Annotation(_)
166 | Definition::TypeId(_)
167 | Definition::TypePrefix(_)
168 | Definition::Import(_) => {
169 }
171 _ => {
172 return Err(unsupported("non-struct definition"));
173 }
174 }
175 }
176 Ok(())
177 }
178
179 fn walk_module(&mut self, m: &ModuleDef, scope: &[String]) -> Result<(), CppGenError> {
180 let mut new_scope = scope.to_vec();
181 new_scope.push(m.name.text.clone());
182 self.walk_definitions(&m.definitions, &new_scope)
183 }
184
185 fn emit_struct(&mut self, def: &StructDef, scope: &[String]) -> Result<(), CppGenError> {
186 let ext = extensibility_of(&def.annotations);
187 let c_name = c_identifier(scope, &def.name.text);
188 let dds_name = dds_type_name(scope, &def.name.text);
189
190 let _ = writeln!(self.out, "typedef struct {c_name}_s {{");
192 for member in &def.members {
193 for decl in &member.declarators {
194 let m_name = decl.name();
195 let c_type = c_type_for(&member.type_spec)?;
196 let _ = writeln!(self.out, " {c_type} {field};", field = m_name.text);
197 }
198 }
199 if def.members.is_empty() {
200 let _ = writeln!(self.out, " uint8_t _zerodds_empty;");
202 }
203 let _ = writeln!(self.out, "}} {c_name}_t;");
204 let _ = writeln!(self.out);
205
206 let _ = writeln!(
208 self.out,
209 "static int {c_name}_encode(const void* sample, uint8_t* out_buf, size_t out_cap, size_t* out_len);"
210 );
211 let _ = writeln!(
212 self.out,
213 "static int {c_name}_decode(const uint8_t* buf, size_t len, void* out_sample);"
214 );
215 let _ = writeln!(self.out, "static void {c_name}_sample_free(void* sample);");
216 let has_key = struct_has_key(def);
217 if has_key {
218 let _ = writeln!(
219 self.out,
220 "static int {c_name}_key_hash(const void* sample, uint8_t out_hash[16]);"
221 );
222 }
223 let _ = writeln!(self.out);
224
225 let _ = writeln!(
227 self.out,
228 "static const char {c_name}_type_name[] = \"{dds_name}\";"
229 );
230 let _ = writeln!(
231 self.out,
232 "static const zerodds_typesupport_t {c_name}_typesupport = {{"
233 );
234 let _ = writeln!(self.out, " .type_hash = {{0}},");
235 let _ = writeln!(self.out, " .type_name = {c_name}_type_name,");
236 let _ = writeln!(self.out, " .is_keyed = {},", if has_key { 1 } else { 0 });
237 let _ = writeln!(self.out, " .extensibility = {},", ext.as_u8());
238 let _ = writeln!(self.out, " ._reserved = {{0}},");
239 let _ = writeln!(self.out, " .encode = {c_name}_encode,");
240 let _ = writeln!(self.out, " .decode = {c_name}_decode,");
241 if has_key {
242 let _ = writeln!(self.out, " .key_hash = {c_name}_key_hash,");
243 } else {
244 let _ = writeln!(self.out, " .key_hash = NULL,");
245 }
246 let _ = writeln!(self.out, " .sample_free = {c_name}_sample_free,");
247 let _ = writeln!(self.out, "}};");
248 let _ = writeln!(self.out);
249
250 self.emit_encode_body(&c_name, def, ext)?;
252 self.emit_decode_body(&c_name, def, ext)?;
254 self.emit_free_body(&c_name, def);
256 if has_key {
258 self.emit_key_hash_body(&c_name, def);
259 }
260 Ok(())
261 }
262
263 fn emit_encode_body(
264 &mut self,
265 c_name: &str,
266 def: &StructDef,
267 ext: Extensibility,
268 ) -> Result<(), CppGenError> {
269 let _ = writeln!(
270 self.out,
271 "static int {c_name}_encode(const void* sample, uint8_t* out_buf, size_t out_cap, size_t* out_len) {{"
272 );
273 let _ = writeln!(
274 self.out,
275 " const {c_name}_t* s = (const {c_name}_t*)sample;"
276 );
277 let _ = writeln!(self.out, " (void)s;");
278 let _ = writeln!(self.out, " /* Two-pass: grow the buffer, then copy. */");
282 let _ = writeln!(self.out, " uint8_t* w_buf = NULL;");
283 let _ = writeln!(self.out, " size_t w_len = 0;");
284 let _ = writeln!(self.out, " size_t w_cap = 0;");
285 let _ = writeln!(
289 self.out,
290 " if (out_buf == NULL && out_cap > 0) goto fail;"
291 );
292 match ext {
293 Extensibility::Final => {
294 self.emit_struct_body_writes(def)?;
295 }
296 Extensibility::Appendable => {
297 let _ = writeln!(
299 self.out,
300 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + 4) != 0) goto fail;"
301 );
302 let _ = writeln!(self.out, " size_t dheader_pos = w_len;");
303 let _ = writeln!(self.out, " w_len += 4;");
304 let _ = writeln!(self.out, " size_t body_start = w_len;");
305 self.emit_struct_body_writes(def)?;
306 let _ = writeln!(
307 self.out,
308 " uint32_t dheader_len = (uint32_t)(w_len - body_start);"
309 );
310 let _ = writeln!(
311 self.out,
312 " zerodds_xcdr2_c_put_u32_at(w_buf, dheader_pos, dheader_len);"
313 );
314 }
315 Extensibility::Mutable => {
316 let _ = writeln!(
318 self.out,
319 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + 4) != 0) goto fail;"
320 );
321 let _ = writeln!(self.out, " size_t dheader_pos = w_len;");
322 let _ = writeln!(self.out, " w_len += 4;");
323 let _ = writeln!(self.out, " size_t mut_body_start = w_len;");
324 self.emit_mutable_member_writes(def)?;
325 let _ = writeln!(
326 self.out,
327 " uint32_t dheader_len = (uint32_t)(w_len - mut_body_start);"
328 );
329 let _ = writeln!(
330 self.out,
331 " zerodds_xcdr2_c_put_u32_at(w_buf, dheader_pos, dheader_len);"
332 );
333 }
334 }
335 let _ = writeln!(self.out, " if (out_len) *out_len = w_len;");
337 let _ = writeln!(
338 self.out,
339 " if (out_buf == NULL || out_cap < w_len) {{ free(w_buf); return -13; }}"
340 );
341 let _ = writeln!(
342 self.out,
343 " if (w_len > 0) memcpy(out_buf, w_buf, w_len);"
344 );
345 let _ = writeln!(self.out, " free(w_buf);");
346 let _ = writeln!(self.out, " return 0;");
347 let _ = writeln!(self.out, "fail:");
348 let _ = writeln!(self.out, " free(w_buf);");
349 let _ = writeln!(self.out, " return -1;");
350 let _ = writeln!(self.out, "}}");
351 let _ = writeln!(self.out);
352 Ok(())
353 }
354
355 fn emit_struct_body_writes(&mut self, def: &StructDef) -> Result<(), CppGenError> {
356 for member in &def.members {
357 for decl in &member.declarators {
358 let f = &decl.name().text;
359 self.emit_member_write(&format!("s->{f}"), &member.type_spec)?;
360 }
361 }
362 Ok(())
363 }
364
365 fn emit_member_write(&mut self, var: &str, type_spec: &TypeSpec) -> Result<(), CppGenError> {
366 match type_spec {
367 TypeSpec::Primitive(p) => self.emit_primitive_write(var, *p),
368 TypeSpec::String(st) => {
369 if st.wide {
370 return Err(unsupported("wstring"));
371 }
372 let _ = writeln!(
373 self.out,
374 " if (zerodds_xcdr2_c_write_string(&w_buf, &w_len, &w_cap, {var}) != 0) goto fail;"
375 );
376 Ok(())
377 }
378 TypeSpec::Sequence(seq) => self.emit_sequence_write(var, &seq.elem),
379 TypeSpec::Scoped(_) => Err(unsupported("nested struct reference")),
380 TypeSpec::Fixed(_) => Err(unsupported("fixed")),
381 TypeSpec::Map(_) => Err(unsupported("map")),
382 TypeSpec::Any => Err(unsupported("any")),
383 }
384 }
385
386 fn emit_primitive_write(&mut self, var: &str, p: PrimitiveType) -> Result<(), CppGenError> {
387 let helper = match p {
388 PrimitiveType::Boolean | PrimitiveType::Octet => "u8",
389 PrimitiveType::Char => "u8",
390 PrimitiveType::WideChar => "u16",
391 PrimitiveType::Integer(IntegerType::Short)
392 | PrimitiveType::Integer(IntegerType::Int16) => "i16",
393 PrimitiveType::Integer(IntegerType::UShort)
394 | PrimitiveType::Integer(IntegerType::UInt16) => "u16",
395 PrimitiveType::Integer(IntegerType::Long)
396 | PrimitiveType::Integer(IntegerType::Int32) => "i32",
397 PrimitiveType::Integer(IntegerType::ULong)
398 | PrimitiveType::Integer(IntegerType::UInt32) => "u32",
399 PrimitiveType::Integer(IntegerType::LongLong)
400 | PrimitiveType::Integer(IntegerType::Int64) => "i64",
401 PrimitiveType::Integer(IntegerType::ULongLong)
402 | PrimitiveType::Integer(IntegerType::UInt64) => "u64",
403 PrimitiveType::Integer(IntegerType::Int8) => "i8",
404 PrimitiveType::Integer(IntegerType::UInt8) => "u8",
405 PrimitiveType::Floating(FloatingType::Float) => "f32",
406 PrimitiveType::Floating(FloatingType::Double) => "f64",
407 PrimitiveType::Floating(FloatingType::LongDouble) => {
408 return Err(unsupported("long double"));
409 }
410 };
411 let _ = writeln!(
412 self.out,
413 " if (zerodds_xcdr2_c_write_{helper}(&w_buf, &w_len, &w_cap, {var}) != 0) goto fail;"
414 );
415 Ok(())
416 }
417
418 fn emit_sequence_write(&mut self, var: &str, elem: &TypeSpec) -> Result<(), CppGenError> {
419 let non_primitive = !matches!(elem, TypeSpec::Primitive(_));
424 let _ = writeln!(self.out, " {{");
426 if non_primitive {
427 let _ = writeln!(
428 self.out,
429 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + 4) != 0) goto fail;"
430 );
431 let _ = writeln!(self.out, " size_t seq_dheader_pos = w_len;");
432 let _ = writeln!(self.out, " w_len += 4;");
433 let _ = writeln!(self.out, " size_t seq_body_start = w_len;");
434 }
435 let _ = writeln!(
436 self.out,
437 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, ({var}).len) != 0) goto fail;"
438 );
439 let _ = writeln!(
440 self.out,
441 " for (uint32_t i = 0; i < ({var}).len; ++i) {{"
442 );
443 match elem {
444 TypeSpec::Primitive(p) => {
445 self.emit_primitive_write(&format!("({var}).elems[i]"), *p)?
446 }
447 TypeSpec::String(st) => {
448 if st.wide {
449 return Err(unsupported("wstring"));
450 }
451 let _ = writeln!(
452 self.out,
453 " if (zerodds_xcdr2_c_write_string(&w_buf, &w_len, &w_cap, ({var}).elems[i]) != 0) goto fail;"
454 );
455 }
456 _ => {
457 return Err(unsupported("non-primitive sequence element"));
458 }
459 }
460 let _ = writeln!(self.out, " }}");
461 if non_primitive {
462 let _ = writeln!(
463 self.out,
464 " uint32_t seq_dheader_len = (uint32_t)(w_len - seq_body_start);"
465 );
466 let _ = writeln!(
467 self.out,
468 " zerodds_xcdr2_c_put_u32_at(w_buf, seq_dheader_pos, seq_dheader_len);"
469 );
470 }
471 let _ = writeln!(self.out, " }}");
472 Ok(())
473 }
474
475 fn emit_mutable_member_writes(&mut self, def: &StructDef) -> Result<(), CppGenError> {
476 for member in &def.members {
477 let id = id_annotation(&member.annotations)
478 .ok_or_else(|| unsupported("@mutable member without @id(N)"))?;
479 for decl in &member.declarators {
480 let f = &decl.name().text;
481 let _ = writeln!(self.out, " {{");
483 let _ = writeln!(self.out, " uint8_t* m_buf = NULL;");
484 let _ = writeln!(self.out, " size_t m_len = 0;");
485 let _ = writeln!(self.out, " size_t m_cap = 0;");
486 self.emit_member_write_to_named_buffer(
488 &format!("s->{f}"),
489 &member.type_spec,
490 "m_buf",
491 "m_len",
492 "m_cap",
493 )?;
494 let emheader: u32 = (4u32 << 28) | id;
496 let _ = writeln!(
497 self.out,
498 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, 0x{emheader:08X}u) != 0) {{ free(m_buf); goto fail; }}"
499 );
500 let _ = writeln!(
502 self.out,
503 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, (uint32_t)m_len) != 0) {{ free(m_buf); goto fail; }}"
504 );
505 let _ = writeln!(
506 self.out,
507 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + m_len) != 0) {{ free(m_buf); goto fail; }}"
508 );
509 let _ = writeln!(
510 self.out,
511 " if (m_len > 0) memcpy(w_buf + w_len, m_buf, m_len);"
512 );
513 let _ = writeln!(self.out, " w_len += m_len;");
514 let _ = writeln!(self.out, " free(m_buf);");
515 let _ = writeln!(self.out, " }}");
516 }
517 }
518 Ok(())
519 }
520
521 fn emit_member_write_to_named_buffer(
522 &mut self,
523 var: &str,
524 type_spec: &TypeSpec,
525 buf: &str,
526 len: &str,
527 cap: &str,
528 ) -> Result<(), CppGenError> {
529 match type_spec {
533 TypeSpec::Primitive(p) => {
534 let helper = primitive_helper(*p)?;
535 let _ = writeln!(
536 self.out,
537 " if (zerodds_xcdr2_c_write_{helper}(&{buf}, &{len}, &{cap}, {var}) != 0) {{ free({buf}); goto fail; }}"
538 );
539 Ok(())
540 }
541 TypeSpec::String(st) => {
542 if st.wide {
543 return Err(unsupported("wstring"));
544 }
545 let _ = writeln!(
546 self.out,
547 " if (zerodds_xcdr2_c_write_string(&{buf}, &{len}, &{cap}, {var}) != 0) {{ free({buf}); goto fail; }}"
548 );
549 Ok(())
550 }
551 _ => Err(unsupported("complex type in @mutable member")),
552 }
553 }
554
555 fn emit_decode_body(
556 &mut self,
557 c_name: &str,
558 def: &StructDef,
559 ext: Extensibility,
560 ) -> Result<(), CppGenError> {
561 let _ = writeln!(
564 self.out,
565 "static int {c_name}_decode(const uint8_t* buf, size_t len, void* out_sample) {{"
566 );
567 let _ = writeln!(self.out, " {c_name}_t* s = ({c_name}_t*)out_sample;");
568 let _ = writeln!(self.out, " size_t pos = 0;");
569 match ext {
570 Extensibility::Final => {
571 self.emit_struct_body_reads(def)?;
572 }
573 Extensibility::Appendable => {
574 let _ = writeln!(self.out, " uint32_t dheader = 0;");
575 let _ = writeln!(
576 self.out,
577 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &dheader) != 0) return -7;"
578 );
579 let _ = writeln!(self.out, " size_t body_end = pos + dheader;");
580 let _ = writeln!(self.out, " if (body_end > len) return -7;");
581 self.emit_struct_body_reads(def)?;
582 let _ = writeln!(self.out, " pos = body_end;");
583 }
584 Extensibility::Mutable => {
585 let _ = writeln!(self.out, " uint32_t dheader = 0;");
586 let _ = writeln!(
587 self.out,
588 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &dheader) != 0) return -7;"
589 );
590 let _ = writeln!(self.out, " size_t body_end = pos + dheader;");
591 let _ = writeln!(self.out, " if (body_end > len) return -7;");
592 let _ = writeln!(self.out, " while (pos < body_end) {{");
593 let _ = writeln!(self.out, " uint32_t emheader = 0;");
594 let _ = writeln!(
595 self.out,
596 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &emheader) != 0) return -7;"
597 );
598 let _ = writeln!(self.out, " uint32_t mid = emheader & 0x0FFFFFFFu;");
599 let _ = writeln!(self.out, " uint32_t lc = (emheader >> 28) & 0x7u;");
600 let _ = writeln!(self.out, " uint32_t nextint = 0;");
601 let _ = writeln!(self.out, " size_t body_len = 0;");
602 let _ = writeln!(
603 self.out,
604 " if (lc == 0) body_len = 1; else if (lc == 1) body_len = 2; else if (lc == 2) body_len = 4; else if (lc == 3) body_len = 8; else {{"
605 );
606 let _ = writeln!(
607 self.out,
608 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &nextint) != 0) return -7;"
609 );
610 let _ = writeln!(self.out, " body_len = nextint;");
611 let _ = writeln!(self.out, " }}");
612 let _ = writeln!(
613 self.out,
614 " if (pos + body_len > body_end) return -7;"
615 );
616 self.emit_mutable_member_dispatch(def)?;
617 let _ = writeln!(self.out, " }}");
618 }
619 }
620 let _ = writeln!(self.out, " (void)s;");
621 let _ = writeln!(self.out, " return 0;");
622 let _ = writeln!(self.out, "}}");
623 let _ = writeln!(self.out);
624 Ok(())
625 }
626
627 fn emit_struct_body_reads(&mut self, def: &StructDef) -> Result<(), CppGenError> {
628 for member in &def.members {
629 for decl in &member.declarators {
630 let f = &decl.name().text;
631 self.emit_member_read(&format!("s->{f}"), &member.type_spec)?;
632 }
633 }
634 Ok(())
635 }
636
637 fn emit_member_read(&mut self, var: &str, type_spec: &TypeSpec) -> Result<(), CppGenError> {
638 match type_spec {
639 TypeSpec::Primitive(p) => {
640 let helper = primitive_helper(*p)?;
641 let _ = writeln!(
642 self.out,
643 " if (zerodds_xcdr2_c_read_{helper}(buf, len, &pos, &({var})) != 0) return -7;"
644 );
645 Ok(())
646 }
647 TypeSpec::String(st) => {
648 if st.wide {
649 return Err(unsupported("wstring"));
650 }
651 let _ = writeln!(
652 self.out,
653 " if (zerodds_xcdr2_c_read_string(buf, len, &pos, &({var})) != 0) return -7;"
654 );
655 Ok(())
656 }
657 TypeSpec::Sequence(seq) => self.emit_sequence_read(var, &seq.elem),
658 _ => Err(unsupported("complex member read")),
659 }
660 }
661
662 fn emit_sequence_read(&mut self, var: &str, elem: &TypeSpec) -> Result<(), CppGenError> {
663 let _ = writeln!(self.out, " {{");
664 if !matches!(elem, TypeSpec::Primitive(_)) {
667 let _ = writeln!(self.out, " uint32_t seq_dheader = 0;");
668 let _ = writeln!(
669 self.out,
670 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &seq_dheader) != 0) return -7;"
671 );
672 let _ = writeln!(self.out, " (void)seq_dheader;");
673 }
674 let _ = writeln!(self.out, " uint32_t seq_len = 0;");
675 let _ = writeln!(
676 self.out,
677 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &seq_len) != 0) return -7;"
678 );
679 let elem_c = c_type_for(elem)?;
680 let _ = writeln!(self.out, " ({var}).len = seq_len;");
681 let _ = writeln!(
682 self.out,
683 " ({var}).elems = ({elem_c}*)calloc(seq_len ? seq_len : 1, sizeof({elem_c}));"
684 );
685 let _ = writeln!(
686 self.out,
687 " if (({var}).elems == NULL && seq_len > 0) return -7;"
688 );
689 let _ = writeln!(
690 self.out,
691 " for (uint32_t i = 0; i < seq_len; ++i) {{"
692 );
693 match elem {
694 TypeSpec::Primitive(p) => {
695 let helper = primitive_helper(*p)?;
696 let _ = writeln!(
697 self.out,
698 " if (zerodds_xcdr2_c_read_{helper}(buf, len, &pos, &({var}).elems[i]) != 0) return -7;"
699 );
700 }
701 TypeSpec::String(st) => {
702 if st.wide {
703 return Err(unsupported("wstring"));
704 }
705 let _ = writeln!(
706 self.out,
707 " if (zerodds_xcdr2_c_read_string(buf, len, &pos, &({var}).elems[i]) != 0) return -7;"
708 );
709 }
710 _ => {
711 return Err(unsupported("complex sequence element read"));
712 }
713 }
714 let _ = writeln!(self.out, " }}");
715 let _ = writeln!(self.out, " }}");
716 Ok(())
717 }
718
719 fn emit_mutable_member_dispatch(&mut self, def: &StructDef) -> Result<(), CppGenError> {
720 let _ = writeln!(self.out, " switch (mid) {{");
721 for member in &def.members {
722 let Some(id) = id_annotation(&member.annotations) else {
723 return Err(unsupported("@mutable member without @id(N)"));
724 };
725 for decl in &member.declarators {
726 let f = &decl.name().text;
727 let _ = writeln!(self.out, " case {id}: {{");
728 self.emit_member_read(&format!("s->{f}"), &member.type_spec)?;
729 let _ = writeln!(self.out, " break;");
730 let _ = writeln!(self.out, " }}");
731 }
732 }
733 let _ = writeln!(self.out, " default: pos += body_len; break;");
734 let _ = writeln!(self.out, " }}");
735 Ok(())
736 }
737
738 fn emit_free_body(&mut self, c_name: &str, def: &StructDef) {
739 let _ = writeln!(
740 self.out,
741 "static void {c_name}_sample_free(void* sample) {{"
742 );
743 let _ = writeln!(self.out, " if (sample == NULL) return;");
744 let _ = writeln!(self.out, " {c_name}_t* s = ({c_name}_t*)sample;");
745 let _ = writeln!(self.out, " (void)s;");
746 for member in &def.members {
747 for decl in &member.declarators {
748 let f = &decl.name().text;
749 match &member.type_spec {
750 TypeSpec::String(_) => {
751 let _ = writeln!(self.out, " free(s->{f}); s->{f} = NULL;");
752 }
753 TypeSpec::Sequence(seq) => match seq.elem.as_ref() {
754 TypeSpec::String(_) => {
755 let _ = writeln!(
756 self.out,
757 " for (uint32_t i = 0; i < s->{f}.len; ++i) free(s->{f}.elems[i]);"
758 );
759 let _ = writeln!(
760 self.out,
761 " free(s->{f}.elems); s->{f}.elems = NULL; s->{f}.len = 0;"
762 );
763 }
764 _ => {
765 let _ = writeln!(
766 self.out,
767 " free(s->{f}.elems); s->{f}.elems = NULL; s->{f}.len = 0;"
768 );
769 }
770 },
771 _ => {}
772 }
773 }
774 }
775 let _ = writeln!(self.out, "}}");
776 let _ = writeln!(self.out);
777 }
778
779 fn emit_key_hash_body(&mut self, c_name: &str, def: &StructDef) {
780 let _ = writeln!(
783 self.out,
784 "static int {c_name}_key_hash(const void* sample, uint8_t out_hash[16]) {{"
785 );
786 let _ = writeln!(
787 self.out,
788 " const {c_name}_t* s = (const {c_name}_t*)sample;"
789 );
790 let _ = writeln!(self.out, " uint8_t* kh_buf = NULL;");
791 let _ = writeln!(self.out, " size_t kh_len = 0;");
792 let _ = writeln!(self.out, " size_t kh_cap = 0;");
793 for member in &def.members {
794 if !is_key(&member.annotations) {
795 continue;
796 }
797 for decl in &member.declarators {
798 let f = &decl.name().text;
799 match &member.type_spec {
800 TypeSpec::Primitive(p) => {
801 let helper = match primitive_helper(*p) {
802 Ok(h) => h,
803 Err(_) => continue,
804 };
805 let _ = writeln!(
806 self.out,
807 " if (zerodds_xcdr2_c_kh_write_{helper}(&kh_buf, &kh_len, &kh_cap, s->{f}) != 0) {{ free(kh_buf); return -1; }}"
808 );
809 }
810 TypeSpec::String(_) => {
811 let _ = writeln!(
812 self.out,
813 " if (zerodds_xcdr2_c_kh_write_string(&kh_buf, &kh_len, &kh_cap, s->{f}) != 0) {{ free(kh_buf); return -1; }}"
814 );
815 }
816 _ => {}
817 }
818 }
819 }
820 let _ = writeln!(
821 self.out,
822 " zerodds_xcdr2_c_compute_key_hash(kh_buf, kh_len, /*max_size_static=*/0, out_hash);"
823 );
824 let _ = writeln!(self.out, " free(kh_buf);");
825 let _ = writeln!(self.out, " return 0;");
826 let _ = writeln!(self.out, "}}");
827 let _ = writeln!(self.out);
828 }
829}
830
831fn dds_type_name(scope: &[String], name: &str) -> String {
836 if scope.is_empty() {
837 name.to_string()
838 } else {
839 let mut s = scope.join("::");
840 s.push_str("::");
841 s.push_str(name);
842 s
843 }
844}
845
846fn c_identifier(scope: &[String], name: &str) -> String {
847 if scope.is_empty() {
848 name.to_string()
849 } else {
850 let mut s = scope.join("_");
851 s.push('_');
852 s.push_str(name);
853 s
854 }
855}
856
857impl Extensibility {
858 fn as_u8(self) -> u8 {
859 match self {
860 Self::Final => 0,
861 Self::Appendable => 1,
862 Self::Mutable => 2,
863 }
864 }
865}
866
867fn extensibility_of(annotations: &[Annotation]) -> Extensibility {
868 for a in annotations {
869 if let Some(name) = a.name.parts.last() {
870 match name.text.as_str() {
871 "final" | "Final" => return Extensibility::Final,
872 "appendable" | "Appendable" => return Extensibility::Appendable,
873 "mutable" | "Mutable" => return Extensibility::Mutable,
874 _ => {}
875 }
876 }
877 }
878 Extensibility::Appendable
880}
881
882fn is_key(annotations: &[Annotation]) -> bool {
883 annotations.iter().any(|a| {
884 a.name
885 .parts
886 .last()
887 .is_some_and(|p| p.text == "key" || p.text == "Key")
888 })
889}
890
891fn struct_has_key(def: &StructDef) -> bool {
892 def.members.iter().any(|m| is_key(&m.annotations))
893}
894
895fn id_annotation(annotations: &[Annotation]) -> Option<u32> {
896 for a in annotations {
897 let last = a.name.parts.last()?;
898 if last.text != "id" && last.text != "Id" {
899 continue;
900 }
901 if let AnnotationParams::Single(ConstExpr::Literal(lit)) = &a.params {
902 if lit.kind == LiteralKind::Integer {
903 if let Ok(v) = lit.raw.parse::<u32>() {
904 return Some(v);
905 }
906 }
907 }
908 }
909 None
910}
911fn c_type_for(type_spec: &TypeSpec) -> Result<String, CppGenError> {
913 let s = match type_spec {
914 TypeSpec::Primitive(p) => match p {
915 PrimitiveType::Boolean => "uint8_t",
916 PrimitiveType::Octet => "uint8_t",
917 PrimitiveType::Char => "char",
918 PrimitiveType::WideChar => "uint16_t",
919 PrimitiveType::Integer(IntegerType::Short)
920 | PrimitiveType::Integer(IntegerType::Int16) => "int16_t",
921 PrimitiveType::Integer(IntegerType::UShort)
922 | PrimitiveType::Integer(IntegerType::UInt16) => "uint16_t",
923 PrimitiveType::Integer(IntegerType::Long)
924 | PrimitiveType::Integer(IntegerType::Int32) => "int32_t",
925 PrimitiveType::Integer(IntegerType::ULong)
926 | PrimitiveType::Integer(IntegerType::UInt32) => "uint32_t",
927 PrimitiveType::Integer(IntegerType::LongLong)
928 | PrimitiveType::Integer(IntegerType::Int64) => "int64_t",
929 PrimitiveType::Integer(IntegerType::ULongLong)
930 | PrimitiveType::Integer(IntegerType::UInt64) => "uint64_t",
931 PrimitiveType::Integer(IntegerType::Int8) => "int8_t",
932 PrimitiveType::Integer(IntegerType::UInt8) => "uint8_t",
933 PrimitiveType::Floating(FloatingType::Float) => "float",
934 PrimitiveType::Floating(FloatingType::Double) => "double",
935 PrimitiveType::Floating(FloatingType::LongDouble) => {
936 return Err(unsupported("long double"));
937 }
938 },
939 TypeSpec::String(st) => {
940 if st.wide {
941 return Err(unsupported("wstring"));
942 }
943 "char*"
944 }
945 TypeSpec::Sequence(seq) => {
946 let elem = c_type_for(&seq.elem)?;
947 return Ok(format!("struct {{ uint32_t len; {elem}* elems; }}"));
948 }
949 _ => {
950 return Err(unsupported("non-primitive type in field"));
951 }
952 };
953 Ok(s.to_string())
954}
955
956fn primitive_helper(p: PrimitiveType) -> Result<&'static str, CppGenError> {
957 Ok(match p {
958 PrimitiveType::Boolean | PrimitiveType::Octet => "u8",
959 PrimitiveType::Char => "u8",
960 PrimitiveType::WideChar => "u16",
961 PrimitiveType::Integer(IntegerType::Short) | PrimitiveType::Integer(IntegerType::Int16) => {
962 "i16"
963 }
964 PrimitiveType::Integer(IntegerType::UShort)
965 | PrimitiveType::Integer(IntegerType::UInt16) => "u16",
966 PrimitiveType::Integer(IntegerType::Long) | PrimitiveType::Integer(IntegerType::Int32) => {
967 "i32"
968 }
969 PrimitiveType::Integer(IntegerType::ULong)
970 | PrimitiveType::Integer(IntegerType::UInt32) => "u32",
971 PrimitiveType::Integer(IntegerType::LongLong)
972 | PrimitiveType::Integer(IntegerType::Int64) => "i64",
973 PrimitiveType::Integer(IntegerType::ULongLong)
974 | PrimitiveType::Integer(IntegerType::UInt64) => "u64",
975 PrimitiveType::Integer(IntegerType::Int8) => "i8",
976 PrimitiveType::Integer(IntegerType::UInt8) => "u8",
977 PrimitiveType::Floating(FloatingType::Float) => "f32",
978 PrimitiveType::Floating(FloatingType::Double) => "f64",
979 PrimitiveType::Floating(FloatingType::LongDouble) => {
980 return Err(unsupported("long double"));
981 }
982 })
983}
984
985#[cfg(test)]
986mod tests {
987 #![allow(clippy::expect_used)]
988 use super::*;
989 use zerodds_idl::config::ParserConfig;
990
991 fn gen_c(src: &str) -> String {
992 let ast = zerodds_idl::parse(src, &ParserConfig::default()).expect("parse ok");
993 generate_c_header(&ast, &CGenOptions::default()).expect("c-gen ok")
994 }
995
996 #[test]
997 fn empty_final_struct_emits_typedef_and_typesupport() {
998 let h = gen_c("@final struct Empty {};");
999 assert!(h.contains("typedef struct Empty_s"));
1000 assert!(h.contains("Empty_typesupport"));
1001 assert!(h.contains(".extensibility = 0"));
1002 }
1003
1004 #[test]
1005 fn primitive_struct_maps_types() {
1006 let h = gen_c("@final struct Point { long x; long y; };");
1007 assert!(h.contains("int32_t x;"));
1008 assert!(h.contains("int32_t y;"));
1009 assert!(h.contains("\"Point\""));
1010 }
1011
1012 #[test]
1013 fn nested_module_yields_scoped_type_name() {
1014 let h = gen_c("module Outer { module Inner { @final struct S { long x; }; }; };");
1015 assert!(h.contains("typedef struct Outer_Inner_S_s"));
1016 assert!(h.contains("\"Outer::Inner::S\""));
1017 }
1018
1019 #[test]
1020 fn appendable_default_when_no_annotation() {
1021 let h = gen_c("struct V { long a; long b; };");
1022 assert!(h.contains(".extensibility = 1"));
1023 }
1024
1025 #[test]
1026 fn mutable_struct_marks_extensibility() {
1027 let h = gen_c("@mutable struct M { @id(1) long a; };");
1028 assert!(h.contains(".extensibility = 2"));
1029 }
1030
1031 #[test]
1032 fn key_member_sets_is_keyed_and_emits_key_hash() {
1033 let h = gen_c("@final struct Sensor { @key long id; double value; };");
1034 assert!(h.contains(".is_keyed = 1"));
1035 assert!(h.contains("Sensor_key_hash"));
1036 }
1037}