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!(
282 self.out,
283 " /* Two-pass: Buffer wachsen lassen, dann kopieren. */"
284 );
285 let _ = writeln!(self.out, " uint8_t* w_buf = NULL;");
286 let _ = writeln!(self.out, " size_t w_len = 0;");
287 let _ = writeln!(self.out, " size_t w_cap = 0;");
288 let _ = writeln!(
292 self.out,
293 " if (out_buf == NULL && out_cap > 0) goto fail;"
294 );
295 match ext {
296 Extensibility::Final => {
297 self.emit_struct_body_writes(def)?;
298 }
299 Extensibility::Appendable => {
300 let _ = writeln!(
302 self.out,
303 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + 4) != 0) goto fail;"
304 );
305 let _ = writeln!(self.out, " size_t dheader_pos = w_len;");
306 let _ = writeln!(self.out, " w_len += 4;");
307 let _ = writeln!(self.out, " size_t body_start = w_len;");
308 self.emit_struct_body_writes(def)?;
309 let _ = writeln!(
310 self.out,
311 " uint32_t dheader_len = (uint32_t)(w_len - body_start);"
312 );
313 let _ = writeln!(
314 self.out,
315 " zerodds_xcdr2_c_put_u32_at(w_buf, dheader_pos, dheader_len);"
316 );
317 }
318 Extensibility::Mutable => {
319 let _ = writeln!(
321 self.out,
322 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + 4) != 0) goto fail;"
323 );
324 let _ = writeln!(self.out, " size_t dheader_pos = w_len;");
325 let _ = writeln!(self.out, " w_len += 4;");
326 let _ = writeln!(self.out, " size_t mut_body_start = w_len;");
327 self.emit_mutable_member_writes(def)?;
328 let _ = writeln!(
329 self.out,
330 " uint32_t dheader_len = (uint32_t)(w_len - mut_body_start);"
331 );
332 let _ = writeln!(
333 self.out,
334 " zerodds_xcdr2_c_put_u32_at(w_buf, dheader_pos, dheader_len);"
335 );
336 }
337 }
338 let _ = writeln!(self.out, " if (out_len) *out_len = w_len;");
340 let _ = writeln!(
341 self.out,
342 " if (out_buf == NULL || out_cap < w_len) {{ free(w_buf); return -13; }}"
343 );
344 let _ = writeln!(
345 self.out,
346 " if (w_len > 0) memcpy(out_buf, w_buf, w_len);"
347 );
348 let _ = writeln!(self.out, " free(w_buf);");
349 let _ = writeln!(self.out, " return 0;");
350 let _ = writeln!(self.out, "fail:");
351 let _ = writeln!(self.out, " free(w_buf);");
352 let _ = writeln!(self.out, " return -1;");
353 let _ = writeln!(self.out, "}}");
354 let _ = writeln!(self.out);
355 Ok(())
356 }
357
358 fn emit_struct_body_writes(&mut self, def: &StructDef) -> Result<(), CppGenError> {
359 for member in &def.members {
360 for decl in &member.declarators {
361 let f = &decl.name().text;
362 self.emit_member_write(&format!("s->{f}"), &member.type_spec)?;
363 }
364 }
365 Ok(())
366 }
367
368 fn emit_member_write(&mut self, var: &str, type_spec: &TypeSpec) -> Result<(), CppGenError> {
369 match type_spec {
370 TypeSpec::Primitive(p) => self.emit_primitive_write(var, *p),
371 TypeSpec::String(st) => {
372 if st.wide {
373 return Err(unsupported("wstring"));
374 }
375 let _ = writeln!(
376 self.out,
377 " if (zerodds_xcdr2_c_write_string(&w_buf, &w_len, &w_cap, {var}) != 0) goto fail;"
378 );
379 Ok(())
380 }
381 TypeSpec::Sequence(seq) => self.emit_sequence_write(var, &seq.elem),
382 TypeSpec::Scoped(_) => Err(unsupported("nested struct reference")),
383 TypeSpec::Fixed(_) => Err(unsupported("fixed")),
384 TypeSpec::Map(_) => Err(unsupported("map")),
385 TypeSpec::Any => Err(unsupported("any")),
386 }
387 }
388
389 fn emit_primitive_write(&mut self, var: &str, p: PrimitiveType) -> Result<(), CppGenError> {
390 let helper = match p {
391 PrimitiveType::Boolean | PrimitiveType::Octet => "u8",
392 PrimitiveType::Char => "u8",
393 PrimitiveType::WideChar => "u16",
394 PrimitiveType::Integer(IntegerType::Short)
395 | PrimitiveType::Integer(IntegerType::Int16) => "i16",
396 PrimitiveType::Integer(IntegerType::UShort)
397 | PrimitiveType::Integer(IntegerType::UInt16) => "u16",
398 PrimitiveType::Integer(IntegerType::Long)
399 | PrimitiveType::Integer(IntegerType::Int32) => "i32",
400 PrimitiveType::Integer(IntegerType::ULong)
401 | PrimitiveType::Integer(IntegerType::UInt32) => "u32",
402 PrimitiveType::Integer(IntegerType::LongLong)
403 | PrimitiveType::Integer(IntegerType::Int64) => "i64",
404 PrimitiveType::Integer(IntegerType::ULongLong)
405 | PrimitiveType::Integer(IntegerType::UInt64) => "u64",
406 PrimitiveType::Integer(IntegerType::Int8) => "i8",
407 PrimitiveType::Integer(IntegerType::UInt8) => "u8",
408 PrimitiveType::Floating(FloatingType::Float) => "f32",
409 PrimitiveType::Floating(FloatingType::Double) => "f64",
410 PrimitiveType::Floating(FloatingType::LongDouble) => {
411 return Err(unsupported("long double"));
412 }
413 };
414 let _ = writeln!(
415 self.out,
416 " if (zerodds_xcdr2_c_write_{helper}(&w_buf, &w_len, &w_cap, {var}) != 0) goto fail;"
417 );
418 Ok(())
419 }
420
421 fn emit_sequence_write(&mut self, var: &str, elem: &TypeSpec) -> Result<(), CppGenError> {
422 let _ = writeln!(
424 self.out,
425 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, ({var}).len) != 0) goto fail;"
426 );
427 let _ = writeln!(
428 self.out,
429 " for (uint32_t i = 0; i < ({var}).len; ++i) {{"
430 );
431 match elem {
432 TypeSpec::Primitive(p) => {
433 self.emit_primitive_write(&format!("({var}).elems[i]"), *p)?
434 }
435 TypeSpec::String(st) => {
436 if st.wide {
437 return Err(unsupported("wstring"));
438 }
439 let _ = writeln!(
440 self.out,
441 " if (zerodds_xcdr2_c_write_string(&w_buf, &w_len, &w_cap, ({var}).elems[i]) != 0) goto fail;"
442 );
443 }
444 _ => {
445 return Err(unsupported("non-primitive sequence element"));
446 }
447 }
448 let _ = writeln!(self.out, " }}");
449 Ok(())
450 }
451
452 fn emit_mutable_member_writes(&mut self, def: &StructDef) -> Result<(), CppGenError> {
453 for member in &def.members {
454 let id = id_annotation(&member.annotations)
455 .ok_or_else(|| unsupported("@mutable member without @id(N)"))?;
456 for decl in &member.declarators {
457 let f = &decl.name().text;
458 let _ = writeln!(self.out, " {{");
460 let _ = writeln!(self.out, " uint8_t* m_buf = NULL;");
461 let _ = writeln!(self.out, " size_t m_len = 0;");
462 let _ = writeln!(self.out, " size_t m_cap = 0;");
463 self.emit_member_write_to_named_buffer(
465 &format!("s->{f}"),
466 &member.type_spec,
467 "m_buf",
468 "m_len",
469 "m_cap",
470 )?;
471 let emheader: u32 = (4u32 << 28) | id;
473 let _ = writeln!(
474 self.out,
475 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, 0x{emheader:08X}u) != 0) {{ free(m_buf); goto fail; }}"
476 );
477 let _ = writeln!(
479 self.out,
480 " if (zerodds_xcdr2_c_write_u32(&w_buf, &w_len, &w_cap, (uint32_t)m_len) != 0) {{ free(m_buf); goto fail; }}"
481 );
482 let _ = writeln!(
483 self.out,
484 " if (zerodds_xcdr2_c_grow(&w_buf, &w_cap, w_len + m_len) != 0) {{ free(m_buf); goto fail; }}"
485 );
486 let _ = writeln!(
487 self.out,
488 " if (m_len > 0) memcpy(w_buf + w_len, m_buf, m_len);"
489 );
490 let _ = writeln!(self.out, " w_len += m_len;");
491 let _ = writeln!(self.out, " free(m_buf);");
492 let _ = writeln!(self.out, " }}");
493 }
494 }
495 Ok(())
496 }
497
498 fn emit_member_write_to_named_buffer(
499 &mut self,
500 var: &str,
501 type_spec: &TypeSpec,
502 buf: &str,
503 len: &str,
504 cap: &str,
505 ) -> Result<(), CppGenError> {
506 match type_spec {
510 TypeSpec::Primitive(p) => {
511 let helper = primitive_helper(*p)?;
512 let _ = writeln!(
513 self.out,
514 " if (zerodds_xcdr2_c_write_{helper}(&{buf}, &{len}, &{cap}, {var}) != 0) {{ free({buf}); goto fail; }}"
515 );
516 Ok(())
517 }
518 TypeSpec::String(st) => {
519 if st.wide {
520 return Err(unsupported("wstring"));
521 }
522 let _ = writeln!(
523 self.out,
524 " if (zerodds_xcdr2_c_write_string(&{buf}, &{len}, &{cap}, {var}) != 0) {{ free({buf}); goto fail; }}"
525 );
526 Ok(())
527 }
528 _ => Err(unsupported("complex type in @mutable member")),
529 }
530 }
531
532 fn emit_decode_body(
533 &mut self,
534 c_name: &str,
535 def: &StructDef,
536 ext: Extensibility,
537 ) -> Result<(), CppGenError> {
538 let _ = writeln!(
541 self.out,
542 "static int {c_name}_decode(const uint8_t* buf, size_t len, void* out_sample) {{"
543 );
544 let _ = writeln!(self.out, " {c_name}_t* s = ({c_name}_t*)out_sample;");
545 let _ = writeln!(self.out, " size_t pos = 0;");
546 match ext {
547 Extensibility::Final => {
548 self.emit_struct_body_reads(def)?;
549 }
550 Extensibility::Appendable => {
551 let _ = writeln!(self.out, " uint32_t dheader = 0;");
552 let _ = writeln!(
553 self.out,
554 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &dheader) != 0) return -7;"
555 );
556 let _ = writeln!(self.out, " size_t body_end = pos + dheader;");
557 let _ = writeln!(self.out, " if (body_end > len) return -7;");
558 self.emit_struct_body_reads(def)?;
559 let _ = writeln!(self.out, " pos = body_end;");
560 }
561 Extensibility::Mutable => {
562 let _ = writeln!(self.out, " uint32_t dheader = 0;");
563 let _ = writeln!(
564 self.out,
565 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &dheader) != 0) return -7;"
566 );
567 let _ = writeln!(self.out, " size_t body_end = pos + dheader;");
568 let _ = writeln!(self.out, " if (body_end > len) return -7;");
569 let _ = writeln!(self.out, " while (pos < body_end) {{");
570 let _ = writeln!(self.out, " uint32_t emheader = 0;");
571 let _ = writeln!(
572 self.out,
573 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &emheader) != 0) return -7;"
574 );
575 let _ = writeln!(self.out, " uint32_t mid = emheader & 0x0FFFFFFFu;");
576 let _ = writeln!(self.out, " uint32_t lc = (emheader >> 28) & 0x7u;");
577 let _ = writeln!(self.out, " uint32_t nextint = 0;");
578 let _ = writeln!(self.out, " size_t body_len = 0;");
579 let _ = writeln!(
580 self.out,
581 " 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 {{"
582 );
583 let _ = writeln!(
584 self.out,
585 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &nextint) != 0) return -7;"
586 );
587 let _ = writeln!(self.out, " body_len = nextint;");
588 let _ = writeln!(self.out, " }}");
589 let _ = writeln!(
590 self.out,
591 " if (pos + body_len > body_end) return -7;"
592 );
593 self.emit_mutable_member_dispatch(def)?;
594 let _ = writeln!(self.out, " }}");
595 }
596 }
597 let _ = writeln!(self.out, " (void)s;");
598 let _ = writeln!(self.out, " return 0;");
599 let _ = writeln!(self.out, "}}");
600 let _ = writeln!(self.out);
601 Ok(())
602 }
603
604 fn emit_struct_body_reads(&mut self, def: &StructDef) -> Result<(), CppGenError> {
605 for member in &def.members {
606 for decl in &member.declarators {
607 let f = &decl.name().text;
608 self.emit_member_read(&format!("s->{f}"), &member.type_spec)?;
609 }
610 }
611 Ok(())
612 }
613
614 fn emit_member_read(&mut self, var: &str, type_spec: &TypeSpec) -> Result<(), CppGenError> {
615 match type_spec {
616 TypeSpec::Primitive(p) => {
617 let helper = primitive_helper(*p)?;
618 let _ = writeln!(
619 self.out,
620 " if (zerodds_xcdr2_c_read_{helper}(buf, len, &pos, &({var})) != 0) return -7;"
621 );
622 Ok(())
623 }
624 TypeSpec::String(st) => {
625 if st.wide {
626 return Err(unsupported("wstring"));
627 }
628 let _ = writeln!(
629 self.out,
630 " if (zerodds_xcdr2_c_read_string(buf, len, &pos, &({var})) != 0) return -7;"
631 );
632 Ok(())
633 }
634 TypeSpec::Sequence(seq) => self.emit_sequence_read(var, &seq.elem),
635 _ => Err(unsupported("complex member read")),
636 }
637 }
638
639 fn emit_sequence_read(&mut self, var: &str, elem: &TypeSpec) -> Result<(), CppGenError> {
640 let _ = writeln!(self.out, " {{");
641 let _ = writeln!(self.out, " uint32_t seq_len = 0;");
642 let _ = writeln!(
643 self.out,
644 " if (zerodds_xcdr2_c_read_u32(buf, len, &pos, &seq_len) != 0) return -7;"
645 );
646 let elem_c = c_type_for(elem)?;
647 let _ = writeln!(self.out, " ({var}).len = seq_len;");
648 let _ = writeln!(
649 self.out,
650 " ({var}).elems = ({elem_c}*)calloc(seq_len ? seq_len : 1, sizeof({elem_c}));"
651 );
652 let _ = writeln!(
653 self.out,
654 " if (({var}).elems == NULL && seq_len > 0) return -7;"
655 );
656 let _ = writeln!(
657 self.out,
658 " for (uint32_t i = 0; i < seq_len; ++i) {{"
659 );
660 match elem {
661 TypeSpec::Primitive(p) => {
662 let helper = primitive_helper(*p)?;
663 let _ = writeln!(
664 self.out,
665 " if (zerodds_xcdr2_c_read_{helper}(buf, len, &pos, &({var}).elems[i]) != 0) return -7;"
666 );
667 }
668 TypeSpec::String(st) => {
669 if st.wide {
670 return Err(unsupported("wstring"));
671 }
672 let _ = writeln!(
673 self.out,
674 " if (zerodds_xcdr2_c_read_string(buf, len, &pos, &({var}).elems[i]) != 0) return -7;"
675 );
676 }
677 _ => {
678 return Err(unsupported("complex sequence element read"));
679 }
680 }
681 let _ = writeln!(self.out, " }}");
682 let _ = writeln!(self.out, " }}");
683 Ok(())
684 }
685
686 fn emit_mutable_member_dispatch(&mut self, def: &StructDef) -> Result<(), CppGenError> {
687 let _ = writeln!(self.out, " switch (mid) {{");
688 for member in &def.members {
689 let Some(id) = id_annotation(&member.annotations) else {
690 return Err(unsupported("@mutable member without @id(N)"));
691 };
692 for decl in &member.declarators {
693 let f = &decl.name().text;
694 let _ = writeln!(self.out, " case {id}: {{");
695 self.emit_member_read(&format!("s->{f}"), &member.type_spec)?;
696 let _ = writeln!(self.out, " break;");
697 let _ = writeln!(self.out, " }}");
698 }
699 }
700 let _ = writeln!(self.out, " default: pos += body_len; break;");
701 let _ = writeln!(self.out, " }}");
702 Ok(())
703 }
704
705 fn emit_free_body(&mut self, c_name: &str, def: &StructDef) {
706 let _ = writeln!(
707 self.out,
708 "static void {c_name}_sample_free(void* sample) {{"
709 );
710 let _ = writeln!(self.out, " if (sample == NULL) return;");
711 let _ = writeln!(self.out, " {c_name}_t* s = ({c_name}_t*)sample;");
712 let _ = writeln!(self.out, " (void)s;");
713 for member in &def.members {
714 for decl in &member.declarators {
715 let f = &decl.name().text;
716 match &member.type_spec {
717 TypeSpec::String(_) => {
718 let _ = writeln!(self.out, " free(s->{f}); s->{f} = NULL;");
719 }
720 TypeSpec::Sequence(seq) => match seq.elem.as_ref() {
721 TypeSpec::String(_) => {
722 let _ = writeln!(
723 self.out,
724 " for (uint32_t i = 0; i < s->{f}.len; ++i) free(s->{f}.elems[i]);"
725 );
726 let _ = writeln!(
727 self.out,
728 " free(s->{f}.elems); s->{f}.elems = NULL; s->{f}.len = 0;"
729 );
730 }
731 _ => {
732 let _ = writeln!(
733 self.out,
734 " free(s->{f}.elems); s->{f}.elems = NULL; s->{f}.len = 0;"
735 );
736 }
737 },
738 _ => {}
739 }
740 }
741 }
742 let _ = writeln!(self.out, "}}");
743 let _ = writeln!(self.out);
744 }
745
746 fn emit_key_hash_body(&mut self, c_name: &str, def: &StructDef) {
747 let _ = writeln!(
750 self.out,
751 "static int {c_name}_key_hash(const void* sample, uint8_t out_hash[16]) {{"
752 );
753 let _ = writeln!(
754 self.out,
755 " const {c_name}_t* s = (const {c_name}_t*)sample;"
756 );
757 let _ = writeln!(self.out, " uint8_t* kh_buf = NULL;");
758 let _ = writeln!(self.out, " size_t kh_len = 0;");
759 let _ = writeln!(self.out, " size_t kh_cap = 0;");
760 for member in &def.members {
761 if !is_key(&member.annotations) {
762 continue;
763 }
764 for decl in &member.declarators {
765 let f = &decl.name().text;
766 match &member.type_spec {
767 TypeSpec::Primitive(p) => {
768 let helper = match primitive_helper(*p) {
769 Ok(h) => h,
770 Err(_) => continue,
771 };
772 let _ = writeln!(
773 self.out,
774 " if (zerodds_xcdr2_c_kh_write_{helper}(&kh_buf, &kh_len, &kh_cap, s->{f}) != 0) {{ free(kh_buf); return -1; }}"
775 );
776 }
777 TypeSpec::String(_) => {
778 let _ = writeln!(
779 self.out,
780 " if (zerodds_xcdr2_c_kh_write_string(&kh_buf, &kh_len, &kh_cap, s->{f}) != 0) {{ free(kh_buf); return -1; }}"
781 );
782 }
783 _ => {}
784 }
785 }
786 }
787 let _ = writeln!(
788 self.out,
789 " zerodds_xcdr2_c_compute_key_hash(kh_buf, kh_len, /*max_size_static=*/0, out_hash);"
790 );
791 let _ = writeln!(self.out, " free(kh_buf);");
792 let _ = writeln!(self.out, " return 0;");
793 let _ = writeln!(self.out, "}}");
794 let _ = writeln!(self.out);
795 }
796}
797
798fn dds_type_name(scope: &[String], name: &str) -> String {
803 if scope.is_empty() {
804 name.to_string()
805 } else {
806 let mut s = scope.join("::");
807 s.push_str("::");
808 s.push_str(name);
809 s
810 }
811}
812
813fn c_identifier(scope: &[String], name: &str) -> String {
814 if scope.is_empty() {
815 name.to_string()
816 } else {
817 let mut s = scope.join("_");
818 s.push('_');
819 s.push_str(name);
820 s
821 }
822}
823
824impl Extensibility {
825 fn as_u8(self) -> u8 {
826 match self {
827 Self::Final => 0,
828 Self::Appendable => 1,
829 Self::Mutable => 2,
830 }
831 }
832}
833
834fn extensibility_of(annotations: &[Annotation]) -> Extensibility {
835 for a in annotations {
836 if let Some(name) = a.name.parts.last() {
837 match name.text.as_str() {
838 "final" | "Final" => return Extensibility::Final,
839 "appendable" | "Appendable" => return Extensibility::Appendable,
840 "mutable" | "Mutable" => return Extensibility::Mutable,
841 _ => {}
842 }
843 }
844 }
845 Extensibility::Appendable
847}
848
849fn is_key(annotations: &[Annotation]) -> bool {
850 annotations.iter().any(|a| {
851 a.name
852 .parts
853 .last()
854 .is_some_and(|p| p.text == "key" || p.text == "Key")
855 })
856}
857
858fn struct_has_key(def: &StructDef) -> bool {
859 def.members.iter().any(|m| is_key(&m.annotations))
860}
861
862fn id_annotation(annotations: &[Annotation]) -> Option<u32> {
863 for a in annotations {
864 let last = a.name.parts.last()?;
865 if last.text != "id" && last.text != "Id" {
866 continue;
867 }
868 if let AnnotationParams::Single(ConstExpr::Literal(lit)) = &a.params {
869 if lit.kind == LiteralKind::Integer {
870 if let Ok(v) = lit.raw.parse::<u32>() {
871 return Some(v);
872 }
873 }
874 }
875 }
876 None
877}
878fn c_type_for(type_spec: &TypeSpec) -> Result<String, CppGenError> {
880 let s = match type_spec {
881 TypeSpec::Primitive(p) => match p {
882 PrimitiveType::Boolean => "uint8_t",
883 PrimitiveType::Octet => "uint8_t",
884 PrimitiveType::Char => "char",
885 PrimitiveType::WideChar => "uint16_t",
886 PrimitiveType::Integer(IntegerType::Short)
887 | PrimitiveType::Integer(IntegerType::Int16) => "int16_t",
888 PrimitiveType::Integer(IntegerType::UShort)
889 | PrimitiveType::Integer(IntegerType::UInt16) => "uint16_t",
890 PrimitiveType::Integer(IntegerType::Long)
891 | PrimitiveType::Integer(IntegerType::Int32) => "int32_t",
892 PrimitiveType::Integer(IntegerType::ULong)
893 | PrimitiveType::Integer(IntegerType::UInt32) => "uint32_t",
894 PrimitiveType::Integer(IntegerType::LongLong)
895 | PrimitiveType::Integer(IntegerType::Int64) => "int64_t",
896 PrimitiveType::Integer(IntegerType::ULongLong)
897 | PrimitiveType::Integer(IntegerType::UInt64) => "uint64_t",
898 PrimitiveType::Integer(IntegerType::Int8) => "int8_t",
899 PrimitiveType::Integer(IntegerType::UInt8) => "uint8_t",
900 PrimitiveType::Floating(FloatingType::Float) => "float",
901 PrimitiveType::Floating(FloatingType::Double) => "double",
902 PrimitiveType::Floating(FloatingType::LongDouble) => {
903 return Err(unsupported("long double"));
904 }
905 },
906 TypeSpec::String(st) => {
907 if st.wide {
908 return Err(unsupported("wstring"));
909 }
910 "char*"
911 }
912 TypeSpec::Sequence(seq) => {
913 let elem = c_type_for(&seq.elem)?;
914 return Ok(format!("struct {{ uint32_t len; {elem}* elems; }}"));
915 }
916 _ => {
917 return Err(unsupported("non-primitive type in field"));
918 }
919 };
920 Ok(s.to_string())
921}
922
923fn primitive_helper(p: PrimitiveType) -> Result<&'static str, CppGenError> {
924 Ok(match p {
925 PrimitiveType::Boolean | PrimitiveType::Octet => "u8",
926 PrimitiveType::Char => "u8",
927 PrimitiveType::WideChar => "u16",
928 PrimitiveType::Integer(IntegerType::Short) | PrimitiveType::Integer(IntegerType::Int16) => {
929 "i16"
930 }
931 PrimitiveType::Integer(IntegerType::UShort)
932 | PrimitiveType::Integer(IntegerType::UInt16) => "u16",
933 PrimitiveType::Integer(IntegerType::Long) | PrimitiveType::Integer(IntegerType::Int32) => {
934 "i32"
935 }
936 PrimitiveType::Integer(IntegerType::ULong)
937 | PrimitiveType::Integer(IntegerType::UInt32) => "u32",
938 PrimitiveType::Integer(IntegerType::LongLong)
939 | PrimitiveType::Integer(IntegerType::Int64) => "i64",
940 PrimitiveType::Integer(IntegerType::ULongLong)
941 | PrimitiveType::Integer(IntegerType::UInt64) => "u64",
942 PrimitiveType::Integer(IntegerType::Int8) => "i8",
943 PrimitiveType::Integer(IntegerType::UInt8) => "u8",
944 PrimitiveType::Floating(FloatingType::Float) => "f32",
945 PrimitiveType::Floating(FloatingType::Double) => "f64",
946 PrimitiveType::Floating(FloatingType::LongDouble) => {
947 return Err(unsupported("long double"));
948 }
949 })
950}
951
952#[cfg(test)]
953mod tests {
954 #![allow(clippy::expect_used)]
955 use super::*;
956 use zerodds_idl::config::ParserConfig;
957
958 fn gen_c(src: &str) -> String {
959 let ast = zerodds_idl::parse(src, &ParserConfig::default()).expect("parse ok");
960 generate_c_header(&ast, &CGenOptions::default()).expect("c-gen ok")
961 }
962
963 #[test]
964 fn empty_final_struct_emits_typedef_and_typesupport() {
965 let h = gen_c("@final struct Empty {};");
966 assert!(h.contains("typedef struct Empty_s"));
967 assert!(h.contains("Empty_typesupport"));
968 assert!(h.contains(".extensibility = 0"));
969 }
970
971 #[test]
972 fn primitive_struct_maps_types() {
973 let h = gen_c("@final struct Point { long x; long y; };");
974 assert!(h.contains("int32_t x;"));
975 assert!(h.contains("int32_t y;"));
976 assert!(h.contains("\"Point\""));
977 }
978
979 #[test]
980 fn nested_module_yields_scoped_type_name() {
981 let h = gen_c("module Outer { module Inner { @final struct S { long x; }; }; };");
982 assert!(h.contains("typedef struct Outer_Inner_S_s"));
983 assert!(h.contains("\"Outer::Inner::S\""));
984 }
985
986 #[test]
987 fn appendable_default_when_no_annotation() {
988 let h = gen_c("struct V { long a; long b; };");
989 assert!(h.contains(".extensibility = 1"));
990 }
991
992 #[test]
993 fn mutable_struct_marks_extensibility() {
994 let h = gen_c("@mutable struct M { @id(1) long a; };");
995 assert!(h.contains(".extensibility = 2"));
996 }
997
998 #[test]
999 fn key_member_sets_is_keyed_and_emits_key_hash() {
1000 let h = gen_c("@final struct Sensor { @key long id; double value; };");
1001 assert!(h.contains(".is_keyed = 1"));
1002 assert!(h.contains("Sensor_key_hash"));
1003 }
1004}